Jenkins for kubernetes实现slave动态伸缩 思路和部署 (一)

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

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

部署思路:

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

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

部署

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

首先需要在k8s所有节点部署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 此文件用于创建k8s的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:                        如果使用私有仓库,并且仓库对镜像设置了访问权限,需要在k8s 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。因为我的k8s集群里面使用的是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	#修改k8s默认的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端口访问 k8s任意节点IP:nodePort

查看jenkins密码:

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

猜你喜欢

转载自blog.csdn.net/qq_36165389/article/details/106833259