基於 Jenkins2.0 的 CI/CD (一)

提到基於Kubernete的CI/CD,可以使用的工具有很多,比如Jenkins、Gitlab CI已經新興的drone之類的,
這裏會使用大家最爲熟悉的Jenkins來做CI/CD的工具。

安裝

既然要基於Kubernetes來做CI/CD,這裏需要將 Jenkins 安裝到 Kubernetes 集羣當中,
新建一個 Deployment:(jenkins_deployment.yaml)

apiVersion: apps/v1
kind: Deployment
metadata:
  name: deploy-jenkins            #deployment名稱
  namespace: jenkins         #命名空間
spec:
  replicas: 1
  selector:
    matchLabels:
      app: jenkins
  template:
    metadata:
      labels:
        app: jenkins
    spec:
      terminationGracePeriodSeconds: 10        #優雅停止pod
      serviceAccount: jenkins                #後面還需要創建服務賬戶
      containers:
      - name: jenkins
        image: registry:8304/jenkins:lts                 #鏡像版本
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 8080                 #外部訪問端口
          name: web
          protocol: TCP
        - containerPort: 50000                #jenkins save發現端口
          name: agent
          protocol: TCP
        resources:
          limits:
            cpu: 1000m
            memory: 1Gi
          requests:
            cpu: 500m
            memory: 512Mi
        livenessProbe:
          httpGet:
            path: /login
            port: 8080
          initialDelaySeconds: 60          #容器初始化完成後,等待60秒進行探針檢查
          timeoutSeconds: 5
          failureThreshold: 12            #當Pod成功啓動且檢查失敗時,Kubernetes將在放棄之前嘗試failureThreshold次。放棄生存檢查意味着重新啓動Pod。而放棄就緒檢查,Pod將被標記爲未就緒。默認爲3.最小值爲1
        readinessProbe:
          httpGet:
            path: /login
            port: 8080
          initialDelaySeconds: 60
          timeoutSeconds: 5
          failureThreshold: 12
        volumeMounts:                        #需要將jenkins_home目錄掛載出來
        - name: jenkinshome
          subPath: jenkins
          mountPath: /var/jenkins_home
        env:
        - name: LIMITS_MEMORY
          valueFrom:
            resourceFieldRef:
              resource: limits.memory
              divisor: 1Mi
        - name: JAVA_OPTS
          value: -Xmx$(LIMITS_MEMORY)m -XshowSettings:vm -Dhudson.slaves.NodeProvisioner.initialDelay=0 -Dhudson.slaves.NodeProvisioner.MARGIN=50 -Dhudson.slaves.NodeProvisioner.MARGIN0=0.85 -Duser.timezone=Asia/Shanghai
      securityContext:
        fsGroup: 1000
      volumes:
      - name: jenkinshome
        persistentVolumeClaim:
          claimName: pvc-jenkins                #這裏將上面創建的pv關聯到pvc上

所有的對象資源都放置在一個名爲 jenkins 的 namespace 下面,所以需要添加創建一個 namespace:

$ kubectl create namespace jenkins

這裏使用一個稱爲jenkins / jenkins:lts的副本,這是jenkins官方的Docker所有權,
然後也有一些環境變量,當然也可以根據自己的需求來定製一個實例,
這樣可以將一些插件打包在自定義的其中實質,
可以參考文檔:https : //github.com/jenkinsci/docker,這裏使用替換的官方替代就行,
另外一個還需要注意的是將容器的/data/jenkins/jenkins_volume目錄掛載到了一個稱爲opspvc的PVC對象上面,
所以同樣還得提前創建了一個對應的PVC對象,當然也可以使用前面的StorageClass對象來自動創建:
(jenkins_pv.yaml)

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-jenkins
  namespace: jenkins
spec:
  capacity:
    storage: 10Gi
  accessModes:
  - ReadWriteMany
  persistentVolumeReclaimPolicy: Delete
  nfs:
    server: k8s-node-1
    path: /data/jenkins/jenkins_volume

---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: pvc-jenkins
  namespace: jenkins
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 10Gi

創建需要用到的PVC對象:

$ kubectl create -f jenkins_pv.yaml

另外這裏還需要使用到一個擁有相關權限的服務帳戶:jenkins,
這裏只是給jenkins授予了一些必要的權限,當然如果你對serviceAccount的權限不是很熟悉的話,
給這個sa綁定一個cluster-admin的授權角色權限也是可以的,當然這樣具有一定的安全風險:(jenkins_rbac.yaml)

apiVersion: v1
kind: ServiceAccount
metadata:
  name: jenkins
  namespace: jenkins

---

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: jenkins
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: jenkins
  namespace: jenkins
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: jenkins
subjects:
  - kind: ServiceAccount
    name: jenkins
    namespace: jenkins

創建rbac相關的資源對象:

$ kubectl create -f rbac.yaml
serviceaccount "jenkins" created
role.rbac.authorization.k8s.io "jenkins" created
rolebinding.rbac.authorization.k8s.io "jenkins" created

最後爲了方便測試,這裏通過NodePort的形式來暴露Jenkins的web服務,固定爲30002端口,另外還需要暴露一個代理的端口,這個端口主要是使用Jenkins的主控和slave之間通信使用的。

一切準備的資源準備好過後,直接創建Jenkins服務:

$ kubectl create -f jenkins_deployment.yaml
deployment.extensions "jenkins" created
service "jenkins" created

創建完成後,要去拉取適當可能需要等待一會兒,然後查看下Pod的狀態:

$ kubectl get pods -n jenkins
NAME                        READY     STATUS    RESTARTS   AGE
jenkins-7f5494cd44-pqpzs   0/1       Running   0          2m

可以看到該Pod處於運行狀態,但READY值確實爲0,然後用describe命令去查看下該Pod的詳細信息:

$ kubectl describe pod jenkins-7f5494cd44-pqpzs -n jenkins
...
Normal   Created                3m                kubelet, k8s-node-1    Created container
  Normal   Started                3m                kubelet, k8s-node-1    Started container
  Warning  Unhealthy              1m (x10 over 2m)  kubelet, k8s-node-1    Liveness probe failed: Get http://10.244.1.165:8080/login: dial tcp 10.244.1.165:8080: getsockopt: connection refused
  Warning  Unhealthy              1m (x10 over 2m)  kubelet, k8s-node-1    Readiness probe failed: Get http://10.244.1.165:8080/login: dial tcp 10.244.1.165:8080: getsockopt: connection refused

可以看到上方的警告信息,健康檢查沒有通過,具體原因是什麼引起的呢??可以通過查看日誌進一步瞭解:

$ kubectl logs -f jenkins-7f5494cd44-pqpzs -n jenkins
touch: cannot touch '/data/jenkins/jenkins_volume/copy_reference_file.log': Permission denied
Can not write to /var/jenkins_home/copy_reference_file.log. Wrong volume permissions?

很明顯可以看到上面的錯誤信息,意味着就是沒有權限在jenkins的目錄下創建文件,這是因爲交替的替換使用的是jenkins這個用戶,而通過PVC掛載到nfs服務器的共享數據目錄下面卻是root用戶的,所以沒有權限訪問該目錄,
要解決該問題,也很簡單,我只需要在nfs共享數據目錄下面把的目錄權限重新分配下即可:

$ chown -R 1000 /data/jenkins/jenkins_volume

然後再重新創建:

$ kubectl delete -f jenkins_deployment.yaml
deployment.extensions "jenkins" deleted
service "jenkins" deleted
$ kubectl create -f jenkins_deployment.yaml
deployment.extensions "jenkins" created
service "jenkins" created

現在再去查看新生成的Pod已經沒有錯誤信息了:

$ kubectl get pods -n jenkins
NAME                        READY     STATUS        RESTARTS   AGE
jenkins-7f5494cd44-smn2r   1/1       Running       0          25s

等到服務啓動成功後,就可以根據任意優先級的IP:30002端口就可以訪問jenkins服務了,
可以根據提示信息進行安裝配置即可:初始化的密碼可以在jenkins的容器的日誌中進行查看,也可以直接在nfs的共享數據目錄中查看:
在這裏插入圖片描述

$ cat /data/jenkins/jenkins_volume/jenkins/secrets/initAdminPassword

然後選擇安裝推薦的插件即可。

在這裏插入圖片描述

安裝完成後添加管理員帳號即可進入到jenkins主界面:

在這裏插入圖片描述

優點

Jenkins安裝完成了,瞭解下在Kubernetes環境下面使用Jenkins有什麼好處。
持續整合與發佈是日常工作中必不可少的一個步驟,目前主要公司都採用Jenkins進行來構建符合需求的CI / CD流程,而傳統的Jenkins Slave一主多從方式會存在一些痛點,例如:

  • 主節點發生單點故障時,整個流程都不可用了
  • 每個Slave的配置環境不一樣,來完成不同語言的編譯打包等操作,但是這些差異化的配置導致管理起來非常不方便,維護起來也是比較費勁
  • 資源分配不均衡,有的從屬要運行的作業出現排隊等待,而有的從屬處於空閒狀態
  • 資源有浪費,每臺Slave可能是物理機或虛擬機,當Slave處於空閒狀態時,也不會完全釋放掉資源。

正因爲上面的這些這些種種痛點,渴望一種更高效更可靠的方式來完成這個CI / CD流程,而Docker虛擬化容器技術能很好的解決這個痛點,又特別是在Kubernetes可行的環境下面能夠更好來解決上面的問題,下圖是基於Kubernetes構造的Jenkins實現的簡單示意圖:
在這裏插入圖片描述

從圖上可以看到Jenkins Master和Jenkins Slave以Pod形式運行在Kubernetes的節點上,Master運行在其中一個實例,並且將其配置數據存儲到一個Volume上去,Slave運行在多個上面,並且它不是一直處於運行狀態,它會按照需求動態的創建並自動刪除。

這種方式的工作流程大致爲:當Jenkins Master接受到Build請求時,會根據配置的標籤動態創建一個運行在Pod中的Jenkins Slave並註冊到Master上,當運行完Job後,這個Slave會被替換並且這個Pod也會自動刪除,恢復到最初狀態。

好處:

  • 服務高可用,當Jenkins Master出現故障時,Kubernetes會自動創建一個新的Jenkins Master容器,並將卷分配給新創建的容器,保證數據不丟失,從而達到可用服務高可用。
  • 動態擴展,合理使用資源,每次運行時,會自動創建一個Jenkins Slave,Job完成後,Slave自動替換並刪除容器,資源自動釋放,並且Kubernetes會根據每個資源的使用情況,動態分配Slave到本身的上游上創造,降低出現因某某資源的累積高,還排隊等待在該例程的情況下。
  • 擴展性好,當Kubernetes擴展的資源嚴重不足而導致Job排隊等待時,可以很容易的添加一個Kubernetes Node到可以中,從而實現擴展。

配置

配置Jenkins,讓他能夠動態的生成Slave的Pod。

  • 第1步。需要安裝kubernetes插件,單擊管理Jenkins- >管理插件->可用-> Kubernetes插件即可安裝即可。
    在這裏插入圖片描述
  • 第2步。安裝完畢後,單擊管理Jenkins->配置系統->(拖到最下方)添加新雲->選擇Kubernetes,然後填充Kubernetes和Jenkins配置信息。
    在這裏插入圖片描述
    注意namespace,這裏填jenkins,然後單擊Test Connection,
    如果出現Connection test success的提示信息證明Jenkins已經可以和Kubernetes系統正常通信了,
    下方的Jenkins URL地址:http://jenkins.jenkins .svc.cluster.local:8080,
    此處的格式爲:服務名.namespace.svc.cluster.local:8080,根據某些創建的jenkins的服務名填寫,
    我這裏是之前創建的稱爲jenkins,如果是用上面創造的就應該是jenkins
  • 第3步。配置Pod模板,實際上就是配置Jenkins Slave運行的Pod模板,命名空間同樣是用jenkins,
    標籤這裏也非常重要,對於後面執行Job的時候需要用到該值,然後這裏使用的是cnych/jenkinsjnlp這個更大,
    這個附加是在官方的jnlp之上基礎上定製的,加入了kubectl等一些實用的工具。

在這裏插入圖片描述

另外需要注意這裏需要在下面掛載兩個主機目錄,一個是/var/run/docker.sock,該文件是用於Pod中的容器能夠共享託管機的Docker,這就是大家說的docker in docker的方式,Docker二進制文件已經打包到上面的高度中了,另外一個目錄下/root/.kube目錄,將這個目錄掛載到容器的/root/.kube目錄下面這是爲了讓能夠在Pod的容器中能夠使用kubectl工具來訪問的Kubernetes。在Slave Pod部署Kubernetes應用。

在這裏插入圖片描述

另外還有幾個參數需要注意,如下所示的時間,以分鐘爲單位在空閒時保留從屬,這個參數表示的意思是當處於位置狀態的時候保留Slave Pod多連續,這個參數最好保存最小就行了,如果您設置過大的話,工作任務執行完成後,對應的Slave Pod就不會立即被銷燬刪除。

在這裏插入圖片描述

另外一些同學在配置了後運行Slave Pod的時候出現了權限問題,因爲Jenkins Slave Pod中沒有配置權限,所以需要配置上ServiceAccount,在Slave Pod配置的地方單擊下面的高級,添加上對應的ServiceAccount即可:

在這裏插入圖片描述

還有一些同學在配置完成後發現啓動Jenkins Slave Pod的時候,出現Slave Pod連接不上,然後嘗試100次連接之後銷燬Pod,然後會再創建一個Slave Pod繼續嘗試連接,無限循環,一片下面的信息:

在這裏插入圖片描述

如果出現這種情況的話就需要將Slave Pod中的運行命令和參數兩個值給清空掉

在這裏插入圖片描述

到這裏的Kubernetes插件插件就算配置完成了。

測試

Kubernetes插件的配置工作完成了,添加一個Job任務,看是否能夠在Slave Pod中執行,任務執行完成後看Pod是否會被銷燬。

在Jenkins首頁點擊創造新工作,創建一個測試的任務,輸入任務名稱,然後選擇Freestyle project類型的任務:

在這裏插入圖片描述
注意在下面的Label Expression這裏要填入jenkins-slave,就是前面配置的Slave Pod中的Label,這兩個地方必須保持一致

在這裏插入圖片描述

然後往下拉,在Build區域選擇Execute shell

在這裏插入圖片描述

然後輸入測試命令

echo "測試 Kubernetes 動態生成 jenkins slave"
echo "==============docker in docker==========="
docker info

echo "=============kubectl============="
kubectl get pods --all-namespaces
echo "=============kubectl============="
echo 172.17.180.105 git-host >> /etc/hosts
ping git-host -c4
chmod +x /usr/local/maven/bin/mvn

/usr/local/maven/bin/mvn -version

現在直接在頁面上點擊生成的立即構建觸發並立即,然後觀察Kubernetes移植中Pod的變化

在這裏插入圖片描述

可以看到在點擊立即生成的時候可以看到一個新的Pod:jenkins-slave被創造了,這就是的Jenkins Slave。任務執行完成後可以看到任務信息

在這裏插入圖片描述

到這裏證明的任務已經完成,然後這個時候再去繼承查看的Pod列表,發現jenkins這個命名空間下面已經沒有之前的Slave這個Pod了

在這裏插入圖片描述

jenkins-slave 模式部署完成!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章