ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Jenkins for Kubernetes实现Slave动态伸缩

2022-01-04 16:35:33  阅读:121  来源: 互联网

标签:Slave name kubernetes jenkins Jenkins Kubernetes yml


本文章案例可用于参考Jenkins for Kubernetes部署。因每个公司的架构和环境不一样,需要改变一些部署的方式。

Jenkins for Kubernetes的好处:

  • Jenkins-Master的高可用。Kubernetes的RC或Deployment可以监控副本的存活状态(通过探针)和副本数量,如果Master出现无法提供服务的情况,就会重启或者迁移到其他节点。
  • Jenkins-Slave的动态伸缩。每次构建都会启动一个Pod用于部署Slave,构建完成后就会释放掉。那么Pod在创建的时候,Kubernetes就会选择集群内资源剩余较多的节点创建Slave的Pod,构建完成后Pod会自动删除。
  • 扩展性好。 因为可以同时拥有很多个Slave,可以配置Jenkins同时执行很多构建操作,减少排队等待构建的时间。

部署思路

首先在Kubernetes中部署Jenkins-Master然后使用Kubernetes Plugin插件进行Slave的动态伸缩。并且使用NFS作为后端存储的PersistentVolume来挂载Jenkins-Master的jenkins_home目录、构建时Slave的Maven缓存m2目录(可以利用缓存加快每次构建的速度)、保留Slave每次构建产生的数据(workspace目录中的每个Job)。

使用PersistentVolume的原因是Kubernetes任何节点都可以访问到挂载的目录,不会因为Master迁移节点导致数据丢失。NFS方便部署而且性能也满足Jenkins的使用需求所以选择了NFS,也可以使用其他的后端存储。

部署

部署方式可以自定义也可以使用Kubernetes Pugin官网提供的部署yml。自定义使用Deployment也是可以的,但是官网的部署方式使用了StatefulSet。Jenkins是一个有状态的应用,我感觉使用StatefulSet部署更加严谨一点。我这里使用了官网提供的文档进行部署的,但是也根据实际情况修改了一些东西。

首先需要在Kubernetes所有节点部署NFS客户端:

yum -y install nfs-utils

systemctl start nfs-utils

systemctl enable nfs-utils

rpcinfo -p

NFS服务端配置文件增加配置:

/data/dev_jenkins       10.0.0.0/24(rw,sync,no_root_squash,no_subtree_check)

dev环境Jenkins Slave节点挂载workspace

/data/dev_jenkins/workspace  0.0.0.0/0(rw,sync,no_root_squash,no_subtree_check)

dev环境Jenkins Slave节点挂载m2 Maven缓存目录

/data/dev_jenkins/m2 0.0.0.0/0(rw,sync,no_root_squash,no_subtree_check)

共享目录一定要给777权限。不然容器内部会报错没有写入权限。

service-account.yml此文件用于创建Kubernetes的RBAC,授权给后面的Jenkins应用可以创建和删除Slave的Pod。

# In GKE need to get RBAC permissions first with

# kubectl create clusterrolebinding cluster-admin-binding --clusterrole=cluster-admin [--user=<user-name>|--group=<group-name>]



---

apiVersion: v1

kind: ServiceAccount

metadata:

name: jenkins



---

kind: Role

apiVersion: rbac.authorization.k8s.io/v1beta1

metadata:

name: jenkins

rules:

- apiGroups: [""]

resources: ["pods"]

verbs: ["create","delete","get","list","patch","update","watch"]

- apiGroups: [""]

resources: ["pods/exec"]

verbs: ["create","delete","get","list","patch","update","watch"]

- apiGroups: [""]

resources: ["pods/log"]

verbs: ["get","list","watch"]

- apiGroups: [""]

resources: ["events"]

verbs: ["watch"]

- apiGroups: [""]

resources: ["secrets"]

verbs: ["get"]



---

apiVersion: rbac.authorization.k8s.io/v1beta1

kind: RoleBinding

metadata:

name: jenkins             #与jenkins.yml中的serviceAccountName: jenkins相对应

roleRef:

apiGroup: rbac.authorization.k8s.io

kind: Role

name: jenkins

subjects:

- kind: ServiceAccount

name: jenkins

jenkins-pv.yml和jenkins-pvc.yml用于创建挂载jenkins_home目录:

[root@dev-master1 kubernetes]# cat jenkins-pv.yml 

apiVersion: v1

kind: PersistentVolume

metadata:

name: jenkins-home

spec:

capacity:  #指定容量

storage: 20Gi

accessModes:

- ReadWriteOnce  #访问模式,还有ReadOnlyMany ##ReadOnlymany

#  persistenVolumeReclaimPolicy: Recycle

#  storageClassName: nfs  ##指定存储的类型

nfs:

path: /data/dev_jenkins  #指明NFS的路径

server: 10.0.0.250  #指明NFS的IP



[root@dev-master1 kubernetes]# cat jenkins-pvc.yml 

kind: PersistentVolumeClaim

apiVersion: v1

metadata:

namespace: kubernetes-plugin

name: jenkins-home

spec:

accessModes:

- ReadWriteOnce

resources:

requests:

    storage: 20Gi

创建Jenkins的Master,可以根据实际情况限制Jenkins的资源使用。

[root@dev-master1 kubernetes]# cat jenkins.yml 

# jenkins

---

apiVersion: apps/v1

kind: StatefulSet

metadata:

name: jenkins

labels:

name: jenkins

spec:

selector:

matchLabels:

  name: jenkins

serviceName: jenkins

replicas: 1

updateStrategy:

type: RollingUpdate

template:

metadata:

  name: jenkins

  labels:

    name: jenkins

spec:

  terminationGracePeriodSeconds: 10

  serviceAccountName: jenkins

  containers:

    - name: jenkins

      image: 10.0.0.59/jenkins/jenkins:lts-alpine #官方镜像为jenkins/jenkins:lts-alpine,为了节省下载时间已经push到自己到Harbor仓库

      imagePullPolicy: Always

      ports:

        - containerPort: 8080

        - containerPort: 50000

      resources:

        limits:

          cpu: 1

          memory: 1Gi

        requests:

          cpu: 0.5

          memory: 500Mi

      env:

        - name: LIMITS_MEMORY

          valueFrom:

            resourceFieldRef:

              resource: limits.memory

              divisor: 1Mi

        - name: JAVA_OPTS

          # value: -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=1 -XshowSettings:vm -Dhudson.slaves.NodeProvisioner.initialDelay=0 -Dhudson.slaves.NodeProvisioner.MARGIN=50 -Dhudson.slaves.NodeProvisioner.MARGIN0=0.85

          value: -Xmx$(LIMITS_MEMORY)m -XshowSettings:vm -Dhudson.slaves.NodeProvisioner.initialDelay=0 -Dhudson.slaves.NodeProvisioner.MARGIN=50 -Dhudson.slaves.NodeProvisioner.MARGIN0=0.85

      volumeMounts:         #挂载PVC存储到Jenkins容器的/var/jenkins_home

        - name: jenkinshome

          mountPath: /var/jenkins_home

      livenessProbe:

        httpGet:

          path: /login

          port: 8080

        initialDelaySeconds: 600        #存活探针时间改为600s,如果服务器配置低,Jenkins还没有启动成功就被重启了。

        timeoutSeconds: 5

        failureThreshold: 12 # ~2 minutes

      readinessProbe:

        httpGet:

          path: /login

          port: 8080

        initialDelaySeconds: 60

        timeoutSeconds: 5

        failureThreshold: 12 # ~2 minutes

  securityContext:

    fsGroup: 1000

  volumes:     #此处声明Jenkins的PVC存储

    - name: jenkinshome

      persistentVolumeClaim:

        claimName: jenkins-home

#      imagePullSecrets:                        如果使用私有仓库,并且仓库对镜像设置了访问权限,需要在Kubernetes Master创建一个secret

#        - name: registry-secret

jenkins-sv.yml用于创建Jenkins的Service。

[root@dev-master1 kubernetes]# cat jenkins-sv.yml 

apiVersion: v1

kind: Service

metadata:

name: jenkins

spec:

sessionAffinity: "ClientIP"

type: NodePort

selector:

name: jenkins

ports:

-

  name: http

  port: 80

  nodePort: 31006

  protocol: TCP

-

  name: agent

  port: 50000

  nodePort: 31007

  protocol: TCP

挂载Maven缓存目录。

[root@dev-master1 kubernetes]# cat m2-pv.yml 

m2是Maven的缓存,挂载以提高build速度

apiVersion: v1

kind: PersistentVolume

metadata:

name: maven-m2

spec:

capacity:  #指定容量

storage: 200Gi

accessModes:

- ReadWriteOnce  #访问模式,还有ReadOnlyMany ##ReadOnlymany

#  persistenVolumeReclaimPolicy: Recycle

#  storageClassName: nfs  ##指定存储的类型

nfs:

path: /data/dev_jenkins/m2  #指明NFS的路径

server: 10.0.0.250  #指明NFS的IP

[root@dev-master1 kubernetes]# cat m2-pvc.yml 

kind: PersistentVolumeClaim

apiVersion: v1

metadata:

namespace: kubernetes-plugin

name: maven-m2

spec:

accessModes:

- ReadWriteOnce

resources:

requests:

storage: 200Gi

挂载Slave节点保存构建结果的目录。

[root@dev-master1 kubernetes]# cat workspace-pv.yml 

m2是maven的缓存,挂载以提高build速度

apiVersion: v1

kind: PersistentVolume

metadata:

name: workspace

spec:

capacity:  #指定容量

storage: 200Gi

accessModes:

- ReadWriteOnce  #访问模式,还有ReadOnlyMany ##ReadOnlymany

#  persistenVolumeReclaimPolicy: Recycle

#  storageClassName: nfs  ##指定存储的类型

nfs:

path: /data/dev_jenkins/workspace  #指明NFS的路径

server: 10.0.0.250  #指明NFS的IP

[root@dev-master1 kubernetes]# cat workspace-pvc.yml 

kind: PersistentVolumeClaim

apiVersion: v1

metadata:

namespace: kubernetes-plugin

name: workspace

spec:

accessModes:

- ReadWriteOnce

resources:

requests:

storage: 200Gi

创建Jenkins的Ingress。因为我的Kubernetes集群里面使用的是Traefik,所以我把Traefik的配置文件和kubernetes-plugin官网给出的Ingress一起贴出来。

[root@dev-master1 kubernetes]# cat jenkins-traefik.yml 

apiVersion: extensions/v1beta1

kind: Ingress

metadata:

name: jenkins

namespace: kubernetes-plugin

annotations:

kubernetes.io/ingress.class: traefik

spec:

rules:

- host: jenkins-dev.doudou.com

http:

  paths:

  - path: /  

    backend:

      serviceName: jenkins

      servicePort: 80





[root@dev-master1 kubernetes]# cat jenkins-Ingress.yml 

因为集群使用Traefik所以此Ingress配置文件不创建,此文件为官方原版

apiVersion: extensions/v1beta1

kind: Ingress

metadata:

name: jenkins

annotations:

nginx.ingress.kubernetes.io/ssl-redirect: "true"

kubernetes.io/tls-acme: "true"

# "413 Request Entity Too Large" uploading plugins, increase client_max_body_size

nginx.ingress.kubernetes.io/proxy-body-size: 50m

nginx.ingress.kubernetes.io/proxy-request-buffering: "off"

# For nginx-ingress controller < 0.9.0.beta-18

ingress.kubernetes.io/ssl-redirect: "true"

# "413 Request Entity Too Large" uploading plugins, increase client_max_body_size

ingress.kubernetes.io/proxy-body-size: 50m

ingress.kubernetes.io/proxy-request-buffering: "off"

spec:

rules:

- http:

paths:

- path: /

backend:

serviceName: jenkins

servicePort: 80

host: jenkins.example.com

tls:

- hosts:

- jenkins.example.com

secretName: tls-jenkins

创建以上的配置文件:

kubectl create namespace kubernetes-plugin   #创建kubernetes-plugin namespace,下面创建的所有东西都归属到这个namespace

kubectl config set-context $(kubectl config current-context) --namespace=kubernetes-plugin  #修改Kubernetes默认的namespace为kubernetes-plugin,这样下面创建的都默认为kubernetes-plugin命名空间

kubectl create -f service-account.yml

kubectl create -f jenkins-Ingress.yml

kubectl create -f jenkins-pv.yml

kubectl create -f jenkins-pvc.yml

kubectl create -f jenkins-sv.yml

kubectl create -f jenkins.yml

kubectl create -f m2-pvc.yml

kubectl create -f m2-pv.yml

kubectl create -f workspace-pvc.yml

kubectl create -f workspace-pv.yml

查看创建状态:

[root@dev-master1 ~]# kubectl get service,pod,StatefulSet -o wide

NAME              TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)                        AGE   SELECTOR

service/jenkins   NodePort   10.105.123.193   <none>        80:31006/TCP,50000:31007/TCP   9d    name=jenkins



NAME            READY   STATUS    RESTARTS   AGE    IP             NODE        NOMINATED NODE   READINESS GATES

pod/jenkins-0   1/1     Running   0          6d5h   100.78.0.141   dev-node4   <none>           <none>



NAME                       READY   AGE   CONTAINERS   IMAGES

statefulset.apps/jenkins   1/1     7d    jenkins      10.0.0.59/jenkins/jenkins:lts-alpine

[root@dev-master1 ~]# kubectl get pv,pvc

NAME                            CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                            STORAGECLASS   REASON   AGE

persistentvolume/jenkins-home   20Gi       RWO            Retain           Bound    kubernetes-plugin/jenkins-home                           13d

persistentvolume/maven-m2       200Gi      RWO            Retain           Bound    kubernetes-plugin/maven-m2                               7d5h

persistentvolume/workspace      200Gi      RWO            Retain           Bound    kubernetes-plugin/workspace                              7d5h



NAME                                           STATUS    VOLUME         CAPACITY   ACCESS MODES   STORAGECLASS   AGE

persistentvolumeclaim/jenkins-home             Bound     jenkins-home   20Gi       RWO                           13d

persistentvolumeclaim/maven-m2                 Bound     maven-m2       200Gi      RWO                           7d5h

persistentvolumeclaim/workspace                Bound     workspace      200Gi      RWO                           7d5h

PV的状态为Bound状态表示已经绑定到对应的PVC上。Jenkins的Pod状态为1/1就说明启动成功了,可以通过绑定Ingress的域名访问了。或者使用Service配置中的nodePort端口访问Kubernetes任意节点IP:nodePort。

查看Jenkins密码:

kubectl exec -it jenkins-0 -n kubernetes-plugin -- cat /var/jenkins_home/secrets/initialAdminPassword

Jenkins配置

Jenkins安装完成后进入UI界面,首先需要安装需要的插件。

Jenkins可以根据实际情况选择适合的源:

系统管理->插件管理->高级

https://updates.jenkins.io/update-center.json #官方源

https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json #清华源

然后安装需要的插件:

  • Git pPugin

  • Maven Integration Plugin

  • Docker Plugin

  • Kubernetes Continuous Deploy Plugin

  • Kubernetes Plugin

  • Publish Over SSH Plugin

  • SSH Agent Plugin

  • SSH Build Agents Plugin

  • promoted builds plugin

  • Promoted Builds (Simple)

配置

Kubernetes Plugin插件安装完成后在Jenkins设置里面点击【系统配置】拉到最下面就可以看到一个Cloud。

image

单击之,添加一个云:

image

  • 名称:名字随便取,后面连接云的时候需要这个名字。
  • Kubernetes地址:访问Kubernetes Master上kube-apiserver服务的地址。
  • Kubernetes命名空间:Jenkins部署在哪个命名空间里面了。
  • Jenkins地址:Jenkins访问地址。
  • Jenkins通道(这特么是一个大坑) :访问Jenkins容器内50000端口地址。因为Jenkins的Service配置文件中我把50000端口映射为nodePort,再加上我配置了DNS所以我这里写了域名:端口号的格式,也可以使用IP地址+端口号。

因为Jenkins-Master和Jenkins-Slave都在Kubernetes集群内部,所以写ClusterIP:端口号应该也是可以的,但是我没试过,略略略:),地址只要能访问到容器内部的50000端口就可以,但是有一点需要注意,这里的格式不能加http不能加/感觉应该是协议的问题,但是还没搞懂。

点击连接测试,是否能够成功。

测试

连接成功后,创建一个流水线Job进行测试使用。

podTemplate(label: 'jnlp-slave', cloud: 'kubernetes', containers: [

containerTemplate(name: 'maven', image: '10.0.0.59/jenkins/maven:3.3.9-jdk-8-alpine', ttyEnabled: true, command: 'cat'),

],

volumes: [

persistentVolumeClaim(mountPath: '/root/.m2', claimName: 'maven-m2'),

persistentVolumeClaim(mountPath: '/home/jenkins/agent/workspace', claimName: 'workspace'),

]

)

{

node("jnlp-slave"){

  stage('Build'){

      git branch: 'master', url: 'http://root:qrGw1S_azFE3F77Rs7tA@gitlab.gemantic.com/java/$JOB_NAME.git'

      container('maven') {

          stage('Build a Maven project') {

              sh 'mvn clean package -U deploy'

          }

      }

  }

  stage('deploy'){

      sshPublisher(publishers: [sshPublisherDesc(configName: '76', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '/data/script/jenkins.sh $JOB_NAME', execTimeout: 120000000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '/data/kubernetes/service/$JOB_NAME', remoteDirectorySDF: false, removePrefix: 'target', sourceFiles: 'target/$JOB_NAME*.jar')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])

  }

}

} 

Pipeline解读:

  • podTemplate创建了一个Pod模版。Cloud字段指定了连接哪个Kubernetes云,Kubernetes就是刚才创建一个一个Kubernetes,云的名字就是kubernetes。

  • Maven镜像为了加快下载速度,我传到了私有仓库,官方镜像就是把IP地址去掉对应的镜像。

  • persistentVolumeClaim定义了目录挂载,把Maven构建的缓存目录.m2和构建产生的数据目录workspace都挂载了一下

  • 下面的Pipeline指定后面的操作在jnlp-slave中(也就是Pod模版同时也是Slave节点)

  • 在build操作中,需要先拉取代码,GitLab拉取代码这里使用了GitLab的root token进行拉取的。GitLab用户获取Token方法:

    image

  • 下面就是开始编译啦~,因为是一个Java服务,编译完成后会生成一个jar包。

  • deploy步骤就是开始发布了,下面的Pipeline是用流水线语法自动生成的。

    image

  • 然后点击构建进行测试。

    image

  • 构建过程中,可以看到Pod调度到master3上进行构建了。

  • 构建过程中用到了两个镜像,一个Maven(已被上传到了私有仓库),一个inbound-agent镜像。inbound-agent镜像是官方的镜像,和Maven的关系是都在同一个Pod中共享数据,并和Jenkins-master进行交互。(inbound-agent镜像怎么修改为私有仓库镜像还没搞明白,总是去公网下载速度慢)

  • 构建过程中不断的下载Java程序依赖的各种包,因为是第一次时间久了一点,但是我们已经把.m2缓存目录挂载出来了,下次再次构建的时候就可以大大缩减构建的时间。

  • workspace也被挂载了出来,每次构建的数据也会保留,以备不时之需。

构建成功后查看NFS共享目录中的数据:

image

root@sa-storage:/data/dev_jenkins# du -sh m2/

218M    m2/

root@sa-storage:/data/dev_jenkins# du -sh workspace/

65M workspace/

至此所有的需求都实现了,Slave实现了动态伸缩,相关的目录都被挂载出来了。

排错

kubectl get pod -n kubernetes-plugin -o wide命令可以查看Slave的Pod状态,如果出现问题Slave一直无限重启,需要查看Pod日志。

kubectl logs `kubectl get pod -n kubernetes-plugin -o wide|grep jnlp-slave|awk '{print $1}'` -n  kubernetes-plugin

每次重启Pod的名字都会重新生成,而且正在创建中的Pod是无法查看日志的,就算有问题Pod也是瞬间就重启了,所以只能上面的这个命令无限的刷。手速快的可以手动哦~手速跟不上的也可以写个循环哒。主要就是文中说的那个大坑,那个坑过去,小问题都可以通过看日志解决的。如果忘记大坑在哪里,可以ctrl+f搜索关键字 “大坑” 哦~

标签:Slave,name,kubernetes,jenkins,Jenkins,Kubernetes,yml
来源: https://blog.csdn.net/yyyy_11119/article/details/122305560

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有