k8s集群中搭建Jenkins
https://www.qikqiak.com/k8s-book/docs/36.Jenkins%20Slave.html
https://help.aliyun.com/document_detail/106712.html?spm=a2c4g.11186623.6.813.626e4330lQtSFi
基於Kubernete的CI/CD,可以使用的工具有很多,比如Jenkins、Gitlab CI已經新興的drone之類的,我還是最為熟悉的Jenkins來做CI/CD的工具。
Jenkins簡介jenkins(一):持續集成和Jenkins簡介
1 K8s集群安裝jenkins
1.1 新建命名空間
添加創建一個 namespace:
kubectl create namespace kube-ops
1.2 創建pv-pvc
我們將容器的 /var/jenkins_home 目錄掛載到了一個名為 opspvc 的 PVC 對象上面,所以我們同樣還得提前創建一個對應的 PVC 對象
kubernetes(13):k8s數據持久化-pv和pvc—NFS實現
mkdir /data/k8s chown -R 1000 /data/k8s #后面有坑,Jenkins權限問題,提前執行
apiVersion: v1 kind: PersistentVolume metadata: name: opspv spec: capacity: storage: 20Gi accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Delete nfs: server: 10.6.76.25 path: /data/k8s --- kind: PersistentVolumeClaim apiVersion: v1 metadata: name: opspvc namespace: kube-ops spec: accessModes: - ReadWriteMany resources: requests: storage: 20Gi
[root@k8s-master ops]# kubectl apply -f pvc.yaml persistentvolume/opspv created persistentvolumeclaim/opspvc created [root@k8s-master ops]# [root@k8s-master ops]# kubectl get pvc -n kube-ops NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE opspvc Bound opspv 20Gi RWX 60s [root@k8s-master ops]# kubectl get pv | grep ops opspv 20Gi RWX Delete Bound kube-ops/opspvc 73s [root@k8s-master ops]# kubectl get pvc -n kube-ops NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE opspvc Bound opspv 20Gi RWX 75s [root@k8s-master ops]#
1.3 創建ServiceAccount配置權限
需要使用到一個擁有相關權限的 serviceAccount:jenkins,這里只是給 jenkins 賦予了一些必要的權限,給這個 sa 綁定一個 cluster-admin 的集群角色權限也是可以的,當然這樣具有一定的安全風險:(rbac.yaml)
apiVersion: v1 kind: ServiceAccount metadata: name: jenkins2 namespace: kube-ops --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1beta1 metadata: name: jenkins2 rules: - apiGroups: ["extensions", "apps"] resources: ["deployments"] verbs: ["create", "delete", "get", "list", "watch", "patch", "update"] - apiGroups: [""] resources: ["services"] verbs: ["create", "delete", "get", "list", "watch", "patch", "update"] - 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: ["secrets"] verbs: ["get"] --- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRoleBinding metadata: name: jenkins2 namespace: kube-ops roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: jenkins2 subjects: - kind: ServiceAccount name: jenkins2 namespace: kube-ops
[root@k8s-master ops]# kubectl apply -f rbac.yaml serviceaccount/jenkins2 created clusterrole.rbac.authorization.k8s.io/jenkins2 created clusterrolebinding.rbac.authorization.k8s.io/jenkins2 created
1.4 創建Jenkins
通過 NodePort 的形式來暴露 Jenkins 的 web 服務,固定為30002端口,另外還需要暴露一個 agent 的端口,這個端口主要是用於 Jenkins 的 master 和 slave 之間通信使用的。
# cat jenkins2.yaml --- apiVersion: extensions/v1beta1 kind: Deployment metadata: name: jenkins2 namespace: kube-ops spec: template: metadata: labels: app: jenkins2 spec: terminationGracePeriodSeconds: 10 serviceAccount: jenkins2 containers: - name: jenkins image: jenkins/jenkins:lts imagePullPolicy: IfNotPresent ports: - containerPort: 8080 name: web protocol: TCP - containerPort: 50000 name: agent protocol: TCP resources: limits: cpu: 1000m memory: 1Gi requests: cpu: 500m memory: 512Mi livenessProbe: httpGet: path: /login port: 8080 initialDelaySeconds: 60 timeoutSeconds: 5 failureThreshold: 12 readinessProbe: httpGet: path: /login port: 8080 initialDelaySeconds: 60 timeoutSeconds: 5 failureThreshold: 12 volumeMounts: - name: jenkinshome subPath: jenkins2 mountPath: /var/jenkins_home securityContext: fsGroup: 1000 volumes: - name: jenkinshome persistentVolumeClaim: claimName: opspvc --- apiVersion: v1 kind: Service metadata: name: jenkins2 namespace: kube-ops labels: app: jenkins2 spec: selector: app: jenkins2 type: NodePort ports: - name: web port: 8080 targetPort: web nodePort: 30002 - name: agent port: 50000 targetPort: agent
[root@k8s-master ops]# kubectl apply -f jenkins2.yaml deployment.extensions/jenkins2 created service/jenkins2 created [root@k8s-master ops]#
創建完成后,要去拉取鏡像可能需要等待一會兒,然后我們查看下 Pod 的狀態
[root@k8s-master ops]# kubectl -n kube-ops get pod NAME READY STATUS RESTARTS AGE jenkins2-59764f8f65-rcvh5 0/1 CrashLoopBackOff 4 4m20s [root@k8s-master ops]# [root@k8s-master ops]# kubectl -n kube-ops describe pod jenkins2-59764f8f65-rcvh5 Liveness: http-get http://:8080/login delay=60s timeout=5s period=10s #success=1 #failure=12 Readiness: http-get http://:8080/login delay=60s timeout=5s period=10s #success=1 #failure=12 Environment: <none> Mounts: /var/jenkins_home from jenkinshome (rw,path="jenkins") /var/run/secrets/kubernetes.io/serviceaccount from jenkins2-token-97q9h (ro)
#查看日志
[root@k8s-master ops]# kubectl -n kube-ops logs jenkins2-59764f8f65-rcvh5 Can not write to /var/jenkins_home/copy_reference_file.log. Wrong volume permissions? touch: cannot touch '/var/jenkins_home/copy_reference_file.log': Permission denied [root@k8s-master ops]#
沒有權限在 jenkins 的 home 目錄下面創建文件,這是因為默認的鏡像使用的是 jenkins 這個用戶,而我們通過 PVC 掛載到 nfs 服務器的共享數據目錄下面卻是 root 用戶的,所以沒有權限訪問該目錄,要解決該問題,也很簡單,我只需要在 nfs 共享數據目錄下面把我們的目錄權限重新分配下即可:
chown -R 1000 /data/k8s/jenkins2
#再次執行
[root@k8s-master ops]# kubectl delete -f jenkins2.yaml deployment.extensions "jenkins2" deleted service "jenkins2" deleted [root@k8s-master ops]# kubectl apply -f jenkins2.yaml deployment.extensions/jenkins2 created service/jenkins created [root@k8s-master ops]#
#等待個2分鍾
[root@k8s-master ops]# kubectl -n kube-ops get pod NAME READY STATUS RESTARTS AGE jenkins2-59764f8f65-62r5w 1/1 Running 0 3m31s
1.5 密碼登陸
任意節點的 IP:30002 端口就可以訪問 jenkins 服務
初始化的密碼我們可以在 jenkins 的容器的日志中進行查看,也可以直接在 nfs 的共享數據目錄中查看:
[root@k8s-master ops]# cat /data/k8s/jenkins/secrets/initialAdminPassword 739b9126bc23434495a96d5d65619e70 [root@k8s-master ops]# [root@k8s-master ops]# kubectl -n kube-ops logs jenkins2-59764f8f65-62r5w ….. 739b9126bc23434495a96d5d65619e70 This may also be found at: /var/jenkins_home/secrets/initialAdminPassword ….
然后選擇安裝推薦的插件即可
安裝完成后添加管理員帳號即可進入到 jenkins 主界面:
2 k8s環境使用Jenkins優點
傳統的 Jenkins Slave 一主多從方式會存在一些痛點,比如:
- 主 Master 發生單點故障時,整個流程都不可用了
- 每個 Slave 的配置環境不一樣,來完成不同語言的編譯打包等操作,但是這些差異化的配置導致管理起來非常不方便,維護起來也是比較費勁
- 資源分配不均衡,有的 Slave 要運行的 job 出現排隊等待,而有的 Slave 處於空閑狀態
- 資源有浪費,每台 Slave 可能是物理機或者虛擬機,當 Slave 處於空閑狀態時,也不會完全釋放掉資源。
正因為上面的這些種種痛點,我們渴望一種更高效更可靠的方式來完成這個 CI/CD 流程,而 Docker 虛擬化容器技術能很好的解決這個痛點,又特別是在 Kubernetes 集群環境下面能夠更好來解決上面的問題,下圖是基於 Kubernetes 搭建 Jenkins 集群的簡單示意圖:
從圖上可以看到 Jenkins Master 和 Jenkins Slave 以 Pod 形式運行在 Kubernetes 集群的 Node 上,Master 運行在其中一個節點,並且將其配置數據存儲到一個 Volume 上去,Slave 運行在各個節點上,並且它不是一直處於運行狀態,它會按照需求動態的創建並自動刪除。
這種方式的工作流程大致為:當 Jenkins Master 接受到 Build 請求時,會根據配置的 Label 動態創建一個運行在 Pod 中的 Jenkins Slave 並注冊到 Master 上,當運行完 Job 后,這個 Slave 會被注銷並且這個 Pod 也會自動刪除,恢復到最初狀態。
那么我們使用這種方式帶來了哪些好處呢?
- 服務高可用,當 Jenkins Master 出現故障時,Kubernetes 會自動創建一個新的 Jenkins Master 容器,並且將 Volume 分配給新創建的容器,保證數據不丟失,從而達到集群服務高可用。
- 動態伸縮,合理使用資源,每次運行 Job 時,會自動創建一個 Jenkins Slave,Job 完成后,Slave 自動注銷並刪除容器,資源自動釋放,而且 Kubernetes 會根據每個資源的使用情況,動態分配 Slave 到空閑的節點上創建,降低出現因某節點資源利用率高,還排隊等待在該節點的情況。
- 擴展性好,當 Kubernetes 集群的資源嚴重不足而導致 Job 排隊等待時,可以很容易的添加一個 Kubernetes Node 到集群中,從而實現擴展。
是不是以前我們面臨的種種問題在 Kubernetes 集群環境下面是不是都沒有了啊?看上去非常完美。