基於 Kubernetes 構建企業 Jenkins 持續集成平臺

2021-12-23 DevOps技術棧
來源:https://www.jianshu.com/p/1440b5b4b980

目錄[-]

1、部署Jenkins

2、Kubernetes 動態創建 Jenkins Slave 配置

3、Jnekins Pipeline 介紹與動態生成 Jenkins Slave 

4、Jenkins Pipeline 自動化在 kubernetes 部署應用

5、Jenkins新皮膚:BlueOcean

1、部署Jenkins

新建kube-ops 命名空間

$ kubectl create namespace kube-ops

新建Deployment文件(jenkins2.yaml)

---apiVersion: extensions/v1beta1kind: Deploymentmetadata:  name: jenkins2  namespace: kube-opsspec:  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        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: opspvc
---apiVersion: v1kind: Servicemetadata: name: jenkins2 namespace: kube-ops labels: app: jenkins2spec: selector: app: jenkins2 type: NodePort ports: - name: web port: 8080 targetPort: web nodePort: 30002 - name: agent port: 50000 targetPort: agent

創建PVC對象:

我們將容器的 /var/jenkins_home 目錄掛載到了一個名為 opspvc 的 PVC 對象上面,所以我們同樣還得提前創建一個對應的 PVC 對象,我們可以使用 StorageClass 對象來自動創建。

apiVersion: v1kind: PersistentVolumemetadata:  name: opspvspec:  capacity:    storage: 20Gi  accessModes:  - ReadWriteMany  persistentVolumeReclaimPolicy: Delete  nfs:    server: 192.168.16.239    path: /data/k8s
---kind: PersistentVolumeClaimapiVersion: v1metadata: name: opspvc namespace: kube-opsspec: accessModes: - ReadWriteMany resources: requests: storage: 20Gi

創建需要用到的 PVC 對象:

$ kubectl  create -f  opspvc.yaml

我們這裡還需要使用到一個擁有相關權限的 serviceAccount:jenkins2,我們這裡只是給 jenkins 賦予了一些必要的權限,當然如果你對 serviceAccount 的權限不是很熟悉的話,我們給這個 sa 綁定一個 cluster-admin 的集群角色權限也是可以的,當然這樣具有一定的安全風險:(rbac.yaml)

apiVersion: v1kind: ServiceAccountmetadata:  name: jenkins2  namespace: kube-ops
---
kind: RoleapiVersion: rbac.authorization.k8s.io/v1beta1metadata: name: jenkins2 namespace: kube-opsrules: - 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/v1beta1kind: RoleBindingmetadata: name: jenkins2 namespace: kube-opsroleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: jenkins2subjects: - kind: ServiceAccount name: jenkins2 namespace: kube-ops

因為默認的鏡像使用的是 jenkins 這個用戶,而我們通過 PVC 掛載到 nfs 伺服器的共享數據目錄下面卻是 root 用戶的,所以沒有權限訪問該目錄,要解決該問題,也很簡單,我只需要在 nfs 共享數據目錄下面把我們的目錄權限重新分配下即可:

$ chown -R 1000 /data/k8s/jenkins2

直接創建 Jenkins 服務:

$ kubectl create -f opspvc.yaml -f   rbac.yaml -f jenkins2.yaml

訪問Jenkins:

$ kubectl  get  svc -n kube-opsNAME       TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)                          AGEjenkins2   NodePort   10.111.112.3   <none>        8080:30005/TCP,50000:30340/TCP 3h

訪問 30005埠:

2、Kubernetes 動態創建 Jenkins Slave 配置


從圖上可以看到 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 plugin,讓他能夠動態的生成 Slave 的 Pod。

安裝完畢後,點擊 Manage Jenkins —> Configure System —> (拖到最下方)Add a new cloud —> 選擇 Kubernetes,然後填寫 Kubernetes 和 Jenkins 配置信息。

注意 namespace,我們這裡填 kube-ops,然後點擊Test Connection,如果出現 Connection test successful 的提示信息證明 Jenkins 已經可以和 Kubernetes 系統正常通信了,然後下方的 Jenkins URL 地址:http://jenkins2.kube-ops.svc.cluster.local:8080,這裡的格式為:服務名.namespace.svc.cluster.local:8080,根據上面創建的jenkins 的服務名填寫,我這裡是之前創建的名為jenkins,如果是用上面我們創建的就應該是jenkins2。


第3步配置 Pod Template,其實就是配置 Jenkins Slave 運行的 Pod 模板,命名空間我們同樣是用 kube-ops,Labels 這裡也非常重要,對於後面執行 Job 的時候需要用到該值,然後我們這裡使用的是 cnych/jenkins:jnlp 這個鏡像,這個鏡像是在官方的 jnlp 鏡像基礎上定製的,加入了 kubectl 等一些實用的工具。

另外需要注意我們這裡需要在下面掛載兩個主機目錄,一個是 /var/run/docker.sock,該文件是用於 Pod 中的容器能夠共享宿主機的 Docker,這就是大家說的 docker in docker 的方式,Docker 二進位文件我們已經打包到上面的鏡像中了,另外一個目錄下 /root/.kube 目錄,我們將這個目錄掛載到容器的 /home/jenkins/.kube 目錄下面這是為了讓我們能夠在 Pod 的容器中能夠使用 kubectl 工具來訪問我們的 Kubernetes 集群,方便我們後面在 Slave Pod 部署 Kubernetes 應用。

另外還有幾個參數需要注意,如下圖中的Time in minutes to retain slave when idle,這個參數表示的意思是當處於空閒狀態的時候保留 Slave Pod 多長時間,這個參數最好我們保存默認就行了,如果你設置過大的話,Job 任務執行完成後,對應的 Slave Pod 就不會立即被銷毀刪除。

測試:
Kubernetes 插件的配置工作完成了,接下來我們就來添加一個 Job 任務,看是否能夠在 Slave Pod 中執行,任務執行完成後看 Pod 是否會被銷毀。

在 Jenkins 首頁點擊create new jobs,創建一個測試的任務,輸入任務名稱,然後我們選擇 Freestyle project 類型的任務:

注意在下面的 Label Expression 這裡要填入haimaxy-jnlp,就是前面我們配置的 Slave Pod 中的 Label,這兩個地方必須保持一致


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

然後輸入我們測試命令:

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

保存後我們直接在頁面點擊做成的 Build now 觸發構建即可,然後觀察 Kubernetes 集群中 Pod 的變化

$ kubectl get pods -n kube-opsNAME                       READY     STATUS              RESTARTS   AGEjenkins2-7c85b6f4bd-rfqgv   1/1       Running             3          1djnlp-hfmvd

可以看到在我們點擊立刻構建的時候可以看到一個新的 Pod:jnlp-hfmvd 被創建了,這就是我們的 Jenkins Slave。任務執行完成後我們可以看到任務信息,比如我們這裡是 花費了 5.2s 時間在 jnlp-hfmvd 這個 Slave上面

到這裡證明我們的任務已經構建完成,然後這個時候我們再去集群查看我們的 Pod 列表,發現 kube-ops 這個 namespace 下面已經沒有之前的 Slave 這個 Pod 了。

$ kubectl get pods -n kube-opsNAME                       READY     STATUS    RESTARTS   AGEjenkins2-7c85b6f4bd-rfqgv   1/1       Running   3          1d

到這裡我們就完成了使用 Kubernetes 動態生成 Jenkins Slave 的方法。

3、Jnekins Pipeline 介紹與動態生成 Jenkins Slave 

Jenkins Pipeline 介紹
要實現在 Jenkins 中的構建工作,可以有多種方式,我們這裡採用比較常用的 Pipeline 這種方式。Pipeline,簡單來說,就是一套運行在 Jenkins 上的工作流框架,將原來獨立運行於單個或者多個節點的任務連接起來,實現單個任務難以完成的複雜流程編排和可視化的工作。


Jenkins Pipeline 有幾個核心概念:

Node:節點,一個 Node 就是一個 Jenkins 節點,Master 或者 Agent,是執行 Step 的具體運行環境,比如我們之前動態運行的 Jenkins Slave 就是一個 Node 節點

Stage:階段,一個 Pipeline 可以劃分為若干個 Stage,每個 Stage 代表一組操作,比如:Build、Test、Deploy,Stage 是一個邏輯分組的概念,可以跨多個 Node

Step:步驟,Step 是最基本的操作單元,可以是列印一句話,也可以是構建一個 Docker 鏡像,由各類 Jenkins 插件提供,比如命令:sh 'make',就相當於我們平時 shell 終端中執行 make 命令一樣。


那麼我們如何創建 Jenkins Pipline 呢?

Pipeline 腳本是由 Groovy 語言實現的,但是我們沒必要單獨去學習 Groovy,當然你會的話最好

Pipeline 支持兩種語法:Declarative(聲明式)和 Scripted Pipeline(腳本式)語法

Pipeline 也有兩種創建方法:可以直接在 Jenkins 的 Web UI 界面中輸入腳本;也可以通過創建一個 Jenkinsfile 腳本文件放入項目源碼庫中

一般我們都推薦在 Jenkins 中直接從原始碼控制(SCMD)中直接載入 Jenkinsfile Pipeline 這種方法

在 Slave 中構建任務:

我們在添加 Slave Pod 的時候,記的添加的 label 嗎?沒錯,我們就需要用到這個 label,我們重新編輯上面創建的 Pipeline 腳本,給 node 添加一個 label 屬性,如下:

node('haimaxy-jnlp') {    stage('Clone') {      echo "1.Clone Stage"    }    stage('Test') {      echo "2.Test Stage"    }    stage('Build') {      echo "3.Build Stage"    }    stage('Deploy') {      echo "4. Deploy Stage"    }}

構建之前查看下 kubernetes 集群中的 Pod:

$ kubectl get pods -n kube-opsNAME                       READY     STATUS    RESTARTS   AGEjenkins-7c85b6f4bd-rfqgv   1/1       Running   4          6djnlp-0hrrz                 1/1       Running   0          23s

我們發現多了一個名叫jnlp-0hrrz的 Pod 正在運行,隔一會兒這個 Pod 就消失了:這也證明我們的 Job 構建完成了,同樣回到 Jenkins 的 Web UI 界面中查看 Console Output,可以看到如下的信息:

4、Jenkins Pipeline 自動化在 kubernetes 部署應用

要部署 Kubernetes 應用,我們就得對我們之前部署應用的流程要非常熟悉才行,我們之前的流程是怎樣的:

現在我們就需要把上面這些流程放入 Jenkins 中來自動幫我們完成(當然編碼除外),從測試到更新 YAML 文件屬於 CI 流程,後面部署屬於 CD 的流程。如果按照我們上面的示例,我們現在要來編寫一個 Pipeline 的腳本,應該怎麼編寫呢?

node('haimaxy-jnlp') {    stage('Clone') {      echo "1.Clone Stage"    }    stage('Test') {      echo "2.Test Stage"    }    stage('Build') {      echo "3.Build Docker Image Stage"    }    stage('Push') {      echo "4.Push Docker Image Stage"    }    stage('YAML') {      echo "5. Change YAML File Stage"    }    stage('Deploy') {      echo "6. Deploy Stage"    }}

我們來將一個簡單 golang 程序,部署到 kubernetes 環境中,代碼連結:(http://gitlab.unmin.club:82/root/jenkins-demo)。如果按照之前的示例,我們是不是應該像這樣來編寫 Pipeline 腳本:

第一步,clone 代碼,這個沒得說吧

第二步,進行測試,如果測試通過了才繼續下面的任務

第三步,由於 Dockerfile 基本上都是放入源碼中進行管理的,所* 以我們這裡就是直接構建 Docker 鏡像了

第四步,鏡像打包完成,就應該推送到鏡像倉庫中吧

第五步,鏡像推送完成,是不是需要更改 YAML 文件中的鏡像 TAG 為這次鏡像的 TAG

第六步,萬事俱備,只差最後一步,使用 kubectl 命令行工具進行部署了

到這裡我們的整個 CI/CD 的流程是不是就都完成了。

接下來我們就來對每一步具體要做的事情進行詳細描述就行了。


第一步,Clone 代碼

stage('Clone') {    echo "1.Clone Stage"    git url: "http://gitlab.unmin.club:82/root/jenkins-demo.git"}

第二步,測試

由於我們這裡比較簡單,忽略該步驟即可。
第三步,構建鏡像

stage('Build') {    echo "3.Build Docker Image Stage"    sh "docker build -t zam2017/jenkins-demo:${build_tag} ."}

我們平時構建的時候是不是都是直接使用docker build命令進行構建就行了,那麼這個地方呢?我們上節課給大家提供的 Slave Pod 的鏡像裡面是不是採用的 Docker In Docker 的方式,也就是說我們也可以直接在 Slave 中使用 docker build 命令,所以我們這裡直接使用 sh 直接執行 docker build 命令即可。

但是鏡像的 tag 呢?如果我們使用鏡像 tag,則每次都是 latest 的 tag,這對於以後的排查或者回滾之類的工作會帶來很大麻煩,我們這裡採用和git commit的記錄為鏡像的 tag,這裡有一個好處就是鏡像的 tag 可以和 git 提交記錄對應起來,也方便日後對應查看。但是由於這個 tag 不只是我們這一個 stage 需要使用,下一個推送鏡像是不是也需要,所以這裡我們把這個 tag 編寫成一個公共的參數,把它放在 Clone 這個 stage 中,這樣一來我們前兩個 stage 就變成了下面這個樣子:

stage('Clone') {    echo "1.Clone Stage"    git url: "http://gitlab.unmin.club:82/root/jenkins-demo.git"    script {        build_tag = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim()    }}stage('Build') {    echo "3.Build Docker Image Stage"    sh "docker build -t zam2017/jenkins-demo:${build_tag} ."}

第四步,推送鏡像

鏡像構建完成了,現在我們就需要將此處構建的鏡像推送到鏡像倉庫中去,當然如果你有私有鏡像倉庫也可以,我們這裡還沒有自己搭建私有的倉庫,所以直接使用 docker hub 即可。
我們需要提前註冊一個 docker hub 的帳號,記住用戶名和密碼,我們這裡需要使用。正常來說我們在本地推送 docker 鏡像的時候,是不是需要使用docker login命令,然後輸入用戶名和密碼,認證通過後,就可以使用docker push命令來推送本地的鏡像到 docker hub 上面去了。

stage('Push') {    echo "4.Push Docker Image Stage"    sh "docker login -u zam2017 -p xxxxx"    sh "docker push zam2017/jenkins-demo:${build_tag}"}

在 Jenkins 的 Web UI 界面中來完成這個任務的話,我們這裡的 Pipeline 是可以這樣寫的,但是我們是不是推薦使用 Jenkinsfile 的形式放入源碼中進行版本管理,這樣的話我們直接把 docker 倉庫的用戶名和密碼暴露給別人這樣很顯然是非常非常不安全的,更何況我們這裡使用的是 github 的公共代碼倉庫,所有人都可以直接看到我們的源碼,所以我們應該用一種方式來隱藏用戶名和密碼這種私密信息,幸運的是 Jenkins 為我們提供了解決方法。

在首頁點擊 Credentials -> Stores scoped to Jenkins 下面的 Jenkins -> Global credentials (unrestricted) -> 左側的 Add Credentials:添加一個 Username with password 類型的認證信息,如下:

輸入 docker hub 的用戶名和密碼,ID 部分我們輸入dockerHub,注意,這個值非常重要,在後面 Pipeline 的腳本中我們需要使用到這個 ID 值。

有了上面的 docker hub 的用戶名和密碼的認證信息,現在我們可以在 Pipeline 中使用這裡的用戶名和密碼了:

stage('Push') {    echo "4.Push Docker Image Stage"    withCredentials([usernamePassword(credentialsId: 'dockerHub', passwordVariable: 'dockerHubPassword', usernameVariable: 'dockerHubUser')]) {        sh "docker login -u ${dockerHubUser} -p ${dockerHubPassword}"        sh "docker push zam2017/jenkins-demo:${build_tag}"    }}

我們這裡在 stage 中使用了一個新的函數withCredentials,其中有一個 credentialsId 值就是我們剛剛創建的 ID 值,而對應的用戶名變量就是 ID 值加上 User,密碼變量就是 ID 值加上 Password,然後我們就可以在腳本中直接使用這裡兩個變量值來直接替換掉之前的登錄 docker hub 的用戶名和密碼,現在是不是就很安全了。

第五步,更改 YAML

上面我們已經完成了鏡像的打包、推送的工作,接下來我們是不是應該更新 Kubernetes 系統中應用的鏡像版本了,當然為了方便維護,我們都是用 YAML 文件的形式來編寫應用部署規則,比如我們這裡的 YAML 文件:(k8s.yaml)

apiVersion: extensions/v1beta1kind: Deploymentmetadata:  name: jenkins-demospec:  template:    metadata:      labels:        app: jenkins-demo    spec:      containers:      - image: zam2017/jenkins-demo:<BUILD_TAG>        imagePullPolicy: IfNotPresent        name: jenkins-demo

我們使用一個 Deployment 資源對象來管理 Pod,該 Pod 使用的就是我們上面推送的鏡像,唯一不同的地方是 Docker 鏡像的 tag 不是我們平常見的具體的 tag,而是一個 的標識,實際上如果我們將這個標識替換成上面的 Docker 鏡像的 tag,是不是就是最終我們本次構建需要使用到的鏡像?怎麼替換呢?其實也很簡單,我們使用一個sed命令就可以實現了:

stage('YAML') {    echo "5. Change YAML File Stage"    sh "sed -i 's/<BUILD_TAG>/${build_tag}/' k8s.yaml"}

第六步,部署

stage('Deploy') {    echo "6. Deploy Stage"    sh "kubectl apply -f k8s.yaml"}

到這裡基本流程已經配置完畢了,可以構建測試一下。

增加,人工確認

理論上來說我們上面的6個步驟其實已經完成了,但是一般在我們的實際項目實踐過程中,可能還需要一些人工幹預的步驟,這是為什麼呢?比如我們提交了一次代碼,測試也通過了,鏡像也打包上傳了,但是這個版本並不一定就是要立刻上線到生產環境的,對吧,我們可能需要將該版本先發布到測試環境、QA 環境、或者預覽環境之類的,總之直接就發布到線上環境去還是挺少見的,所以我們需要增加人工確認的環節,一般都是在 CD 的環節才需要人工幹預,比如我們這裡的最後兩步,我們就可以在前面加上確認,比如:

stage('YAML') {    echo "5. Change YAML File Stage"    def userInput = input(        id: 'userInput',        message: 'Choose a deploy environment',        parameters: [            [                $class: 'ChoiceParameterDefinition',                choices: "Dev\nQA\nProd",                name: 'Env'            ]        ]    )    echo "This is a deploy step to ${userInput.Env}"    sh "sed -i 's/<BUILD_TAG>/${build_tag}/' k8s.yaml"}

我們這裡使用了 input 關鍵字,裡面使用一個 Choice 的列表來讓用戶進行選擇,然後在我們選擇了部署環境後,我們當然也可以針對不同的環境再做一些操作,比如可以給不同環境的 YAML 文件部署到不同的 namespace 下面去,增加不同的標籤等等操作:

stage('Deploy') {    echo "6. Deploy Stage"    if (userInput.Env == "Dev") {      // deploy dev stuff    } else if (userInput.Env == "QA"){      // deploy qa stuff    } else {      // deploy prod stuff    }    sh "kubectl apply -f k8s.yaml"}

由於這一步也屬於部署的範疇,所以我們可以將最後兩步都合併成一步,我們最終的 Pipeline 腳本如下:

node('haimaxy-jnlp') {    stage('Clone') {        echo "1.Clone Stage"        git url: "http://gitlab.unmin.club:82/root/jenkins-demo.git"        script {            build_tag = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim()        }    }    stage('Test') {      echo "2.Test Stage"    }    stage('Build') {        echo "3.Build Docker Image Stage"        sh "docker build -t zam2017/jenkins-demo:${build_tag} ."    }    stage('Push') {        echo "4.Push Docker Image Stage"        withCredentials([usernamePassword(credentialsId: 'dockerHub', passwordVariable: 'dockerHubPassword', usernameVariable: 'dockerHubUser')]) {            sh "docker login -u ${dockerHubUser} -p ${dockerHubPassword}"            sh "docker push zam2017/jenkins-demo:${build_tag}"        }    }    stage('Deploy') {        echo "5. Deploy Stage"        def userInput = input(            id: 'userInput',            message: 'Choose a deploy environment',            parameters: [                [                    $class: 'ChoiceParameterDefinition',                    choices: "Dev\nQA\nProd",                    name: 'Env'                ]            ]        )        echo "This is a deploy step to ${userInput}"        sh "sed -i 's/<BUILD_TAG>/${build_tag}/' k8s.yaml"        if (userInput == "Dev") {            // deploy dev stuff        } else if (userInput == "QA"){            // deploy qa stuff        } else {            // deploy prod stuff        }        sh "kubectl apply -f k8s.yaml"    }}

現在我們在 Jenkins Web UI 中重新配置 jenkins-demo 這個任務,將上面的腳本粘貼到 Script 區域,重新保存,然後點擊左側的 Build Now,觸發構建,然後過一會兒我們就可以看到 Stage View 界面出現了暫停的情況:

選擇PROD,可以看到console控制臺列印信息:


到伺服器查看是否部署成功:

[root@master app]# kubectl  get podNAME                                      READY     STATUS             RESTARTS   AGEjenkins-demo-67446f58cd-mvpj8             0/1       CrashLoopBackOff   5          3mnfs-client-provisioner-6b688c9495-dc9gb   1/1       Running            6          10dnfs-web-0                                 1/1       Running            5          10dnfs-web-1                                 1/1       Running            2          10dnfs-web-2                                 1/1       Running            0          10dnginx-deployment1-6f98c47948-hqgf9        1/1       Running            0          3dstatic-web-master                         1/1       Running            11         99dtest-pod                                  0/1       Completed          0          10dweb-0                                     0/1       Pending            0          10d[root@master app]# kubectl   logs -f  jenkins-demo-67446f58cd-mvpj8Hello, Kubernetes!I'm from Jenkins CI/CD!

我們可以看到我們的應用已經正確的部署到了 Kubernetes 的集群環境中。

在實際的工作實踐中,我們更多的是將 Pipeline 腳本寫入到 Jenkinsfile 文件中,然後和代碼一起提交到代碼倉庫中進行版本管理。現在我們將上面的 Pipeline 腳本拷貝到一個 Jenkinsfile 中,將該文件放入上面的 git 倉庫中,但是要注意的是,現在既然我們已經在 git 倉庫中了,是不是就不需要 git clone 這一步驟了,所以我們需要將第一步 Clone 操作中的 git clone 這一步去掉,可以參考:http://www.unmin.club:82/root/jenkins-demo

然後我們更改上面的 jenkins-demo 這個任務,點擊 Configure -> 最下方的 Pipeline 區域 -> 將之前的 Pipeline Script 更改成 Pipeline Script from SCM,然後根據我們的實際情況填寫上對應的倉庫配置,要注意 Jenkinsfile 腳本路徑:

構建測試:

上面我們使用了 Jenkins Pipeline 來自動化部署一個 Kubernetes 應用,在實際的項目中,往往一個代碼倉庫都會有很多分支的,比如開發、測試、線上這些分支都是分開的,一般情況下開發或者測試的分支我們希望提交代碼後就直接進行 CI/CD 操作,而線上的話最好增加一個人工幹預的步驟。Jenkins 也是支持對代碼倉庫有多分支的流程。

現在我們新建一個dev分支,依然使用Jenkinsfile 的方式配置。參考代碼

node('haimaxy-jnlp') {    stage('Prepare') {        echo "1.Prepare Stage"        checkout scm        script {            build_tag = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim()            if (env.BRANCH_NAME != 'master') {                build_tag = "${env.BRANCH_NAME}-${build_tag}"            }        }    }    stage('Test') {      echo "2.Test Stage"    }    stage('Build') {        echo "3.Build Docker Image Stage"        sh "docker build -t zam2017/jenkins-demo:${build_tag} ."    }    stage('Push') {        echo "4.Push Docker Image Stage"        withCredentials([usernamePassword(credentialsId: 'dockerHub', passwordVariable: 'dockerHubPassword', usernameVariable: 'dockerHubUser')]) {            sh "docker login -u ${dockerHubUser} -p ${dockerHubPassword}"            sh "docker push zam2017/jenkins-demo:${build_tag}"        }    }    stage('Deploy') {        echo "5. Deploy Stage"        if (env.BRANCH_NAME == 'master') {            input "確認要部署線上環境嗎?"        }        sh "sed -i 's/<BUILD_TAG>/${build_tag}/' k8s.yaml"        sh "sed -i 's/<BRANCH_NAME>/${env.BRANCH_NAME}/' k8s.yaml"        sh "kubectl apply -f k8s.yaml --record"    }}

在第一步中我們增加了checkout scm命令,用來檢出代碼倉庫中當前分支的代碼,為了避免各個環境的鏡像 tag 產生衝突,我們為非 master 分支的代碼構建的鏡像增加了一個分支的前綴,在第五步中如果是 master 分支的話我們才增加一個確認部署的流程,其他分支都自動部署,並且還需要替換 k8s.yaml 文件中的環境變量的值。更改完成後,提交 dev 分支到 github 倉庫中。

5、Jenkins新皮膚:BlueOcean

我們這裡使用 BlueOcean 這種方式來完成此處 CI/CD 的工作,BlueOcean 是 Jenkins 團隊從用戶體驗角度出發,專為 Jenkins Pipeline 重新設計的一套 UI 界面,仍然兼容以前的 fressstyle 類型的 job,BlueOcean 具有以下的一些特性:

連續交付(CD)Pipeline 的複雜可視化,允許快速直觀的了解 Pipeline 的狀態

可以通過 Pipeline 編輯器直觀的創建 Pipeline

需要幹預或者出現問題時快速定位,BlueOcean 顯示了 Pipeline 需要注意的地方,便於異常處理和提高生產力

用於分支和拉取請求的本地集成可以在 GitHub 或者 Bitbucket 中與其他人進行代碼協作時最大限度提高開發人員的生產力。
BlueOcean 可以安裝在現有的 Jenkins 環境中,也可以使用 Docker 鏡像的方式直接運行,我們這裡直接在現有的 Jenkins 環境中安裝 BlueOcean 插件:登錄 Jenkins Web UI -> 點擊左側的 Manage Jenkins -> Manage Plugins -> Available -> 搜索查找 BlueOcean -> 點擊下載安裝並重啟

點擊開始創建一個新的 Pipeline:

Blue Ocean 會自動掃描倉庫中的每個分支,會為根文件夾中包含Jenkinsfile的每個分支創建一個 Pipeline,比如我們這裡有 master 和 dev 兩個分支,並且兩個分支下面都有 Jenkinsfile 文件,所以創建完成後會生成兩個 Pipeline:

我們可以看到有兩個任務在運行了,我們可以把 master 分支的任務停止掉,我們只運行 dev 分支即可,然後我們點擊 dev 這個 pipeline 就可以進入本次構建的各步驟詳細頁面:

可以看到本次構建的 Docker 鏡像的 Tag 為dev-d429515,是符合我們在jenkinsfile文件中的定義,非master分支是需要有分支前綴的。


查看dockerHub 倉庫鏡像是否已上傳:

現在我們本地克隆代碼,修改後提交到gitlab倉庫,查看jenkins是否基於我們定義的非master分支代碼自動觸發構建。

[root@master app]# git clone http://gitlab.unmin.club:82/root/jenkins-demo.git正克隆到 'jenkins-demo'...remote: Counting objects: 110, done.remote: Compressing objects: 100% (59/59), done.remote: Total 110 (delta 65), reused 93 (delta 50)接收對象中: 100% (110/110), 9.72 KiB | 0 bytes/s, done.處理 delta 中: 100% (65/65), done.[root@master app]# cd jenkins-demo/[root@master jenkins-demo]# git status# 位於分支 master無文件要提交,乾淨的工作區[root@master jenkins-demo]# git checkout dev分支 dev 設置為跟蹤來自 origin 的遠程分支 dev。切換到一個新分支 'dev'[root@master jenkins-demo]# vim main.go[root@master jenkins-demo]# git add .[root@master jenkins-demo]# git commit -m " test "[dev aca25d0]  test 1 file changed, 1 insertion(+), 1 deletion(-)[root@master jenkins-demo]# git push origin devUsername for 'http://gitlab.unmin.club:82': rootPassword for 'http://root@gitlab.unmin.club:82':Counting objects: 5, done.Delta compression using up to 2 threads.Compressing objects: 100% (3/3), done.Writing objects: 100% (3/3), 275 bytes | 0 bytes/s, done.Total 3 (delta 2), reused 0 (delta 0)
remote:remote: To create a merge request for dev, visit:remote: http://gitlab.unmin.club/root/jenkins-demo/merge_requests/new?merge_request%5Bsource_branch%5D=devremote:To http://gitlab.unmin.club:82/root/jenkins-demo.git d429515..aca25d0 dev -> dev

可以看到jenkins已經觸發構建,版本號也是我們gitlab的最新提交的版本號:

構建完成後,我們查看kubernetes是否為我們創建了響應的Pod:

[root@master jenkins-demo]# kubectl  get  podNAME                                      READY     STATUS             RESTARTS   AGEjenkins-demo-676769b5bf-q75dg             0/1       CrashLoopBackOff   285        23hnfs-client-provisioner-6b688c9495-dc9gb   1/1       Running            6          17dnfs-web-0                                 1/1       Running            5          17dnfs-web-1                                 1/1       Running            2          17dnfs-web-2                                 1/1       Running            0          17dstatic-web-master                         1/1       Running            11         106dtest-pod                                  0/1       Completed          0          17dweb-0                                     0/1       Pending            0          17d[root@master jenkins-demo]# kubectl logs  -f jenkins-demo-676769b5bf-q75dgHello, Kubernetes!I'm  dev    from Jenkins CI!BRANCH: dev

好的 ,如上所示dev的驗證是符合我們的預期。現在我們把dev分支的代碼合併到master,驗證jenkins是否和我們定義的規則相同。

[root@master jenkins-demo]# git status# 位於分支 dev無文件要提交,乾淨的工作區[root@master jenkins-demo]# git checkout master切換到分支 'master'[root@master jenkins-demo]# git merge  dev自動合併 main.goMerge made by the 'recursive' strategy. main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)[root@master jenkins-demo]# git push origin masterUsername for 'http://gitlab.unmin.club:82': rootPassword for 'http://root@gitlab.unmin.club:82':Counting objects: 1, done.Writing objects: 100% (1/1), 217 bytes | 0 bytes/s, done.Total 1 (delta 0), reused 0 (delta 0)To http://gitlab.unmin.club:82/root/jenkins-demo.git   992361a..852f3da  master -> master

jenkins上master分支代碼已經觸發自動構建了,可以查看到此處推送的鏡像 TAG 為852f3da ,沒有分支的前綴,鏡像推送完成後,進入 Deploy 階段的時候我們可以看到出現了一個暫停的操作,讓我們選擇是否需要部署到線上,我們前面在Jenkinsfile定義的如果是 master 分支的話,在部署的階段需要我們人工確認:

現在我們點擊Proceed按鈕發布到生產環境,查看伺服器Pod是否為我們合併到master分支上的代碼

[root@master jenkins-demo]# kubectl  get podNAME                                      READY     STATUS      RESTARTS   AGEjenkins-demo-6865cbf6bf-ckf2x             0/1       Completed   0          16snfs-client-provisioner-6b688c9495-dc9gb   1/1       Running     6          17dnfs-web-0                                 1/1       Running     5          17dnfs-web-1                                 1/1       Running     2          17dnfs-web-2                                 1/1       Running     0          17dstatic-web-master                         1/1       Running     11         106dtest-pod                                  0/1       Completed   0          17dweb-0                                     0/1       Pending     0          17d[root@master jenkins-demo]# kubectl  logs -f jenkins-demo-6865cbf6bf-ckf2xHello, Kubernetes!I'm   test dev    from Jenkins CI!BRANCH: master

可以看到列印出來的信息是 master,證明部署是沒有問題的。

由於篇幅較長,建議收藏或轉發朋友圈留存!

- END -

近期給大家整理了一套最新 k8s+helm+prometheus 視頻資料,絕對乾貨!想學習的朋友可以在本公眾號對話框回復關鍵字即可獲取:k8s 

《31天Kubernetes集訓營,拿下CKA全球認證!》

年輕時偷的懶,遲早是要還的。點亮

相關焦點

  • 使用Kubernetes-Jenkins實現CI/CD
    ,包括:Eclipse, IntelliJ, Jenkins, Bamboo, SonarQube等等,請看演示關於持續集成和持續發布,Martin Fowler給出了最好的定義:「持續集成是一種軟體開發實踐,團隊成員可以頻繁的集成他們的工作,通常每個人一天至少一次集成甚至多次集成。
  • Jenkins持續集成體系 | 最完整的介紹及資料
    web工具,持續構建說得更直白點,就是各種項目的"自動化"編譯、打包、分發部署。jenkins可以很好的支持各種語言(比如:java, c#, php等)的項目構建,也完全兼容ant、maven、gradle等多種第三方構建工具,同時跟svn、git能無縫集成,也支持直接與知名原始碼託管網站,比如github、bitbucket直接集成。簡單點說,Jenkins其實就是大的框架集,可以整個任何你想整合的內容,實現公司的整個持續集成體系!
  • Jenkins Kubernetes操作器成為Jenkins官方子項目!
    通過 Jenkins 操作器項目,我們希望社區能夠充分利用 Kubernetes 和公有雲功能:與公有雲服務在可觀察性、存儲和雲安全領域的原生集成使用 Kubernetes 自定義資源的聲明式配置完整的生命周期管理,最終將其轉換為自動駕駛(操作)參與走向世界的徵程開始貢獻[4]和發揮重要作用,創建自動化 Jenkis 的體驗!
  • 微服務下的持續集成-Jenkins自動化部署GitHub項目
    @目錄一、前言二、DevOps概念三、為什麼要做持續集成四、常見雲服務五、手動部署Jenkins 5.1 準備工作5.2 下載三、為什麼要做持續集成持續集成服務(Continuous Integration,簡稱 CI)它綁定 Github 上面的項目,只要有新的代碼,就會自動抓取。然後,提供一個運行環境,執行測試,完成構建,還能部署到伺服器。
  • Jenkins連接k8s的多種姿勢
    4.3 流水線構建測試1、概述本文分享的是基於k8s環境與jenkins實現CI/CD其中的一個配置具體實現即:不同環境下 jenkins 與 k8s 集群連接的問題我總結的原因如下:a、在實際生產環境中,由於某些歷史原因我們或許不能完美的實現所謂的一切皆「雲原生」,例如有傳統的jenkins和執行專有任務的slave節點b、存在多集群共一個jenkins服務端的情況,例如k8s中集群A用作基礎設施集群(包含日誌、存儲、devops平臺),集群B、
  • 實例演示:如何在Kubernetes上大規模運行CI/CD
    此外,Kubernetes主要用例之一是運行持續集成或持續交付(CI/CD)流水線。也就是說,我們部署一個CI/CD容器的唯一實例,該實例將監控代碼版本控制系統。所以,每當我們推送到該倉庫時,該容器都會運行流水線步驟。其最終目標是達到一個「true or false」的狀態。True即在集成階段commit通過了各種測試,False即未通過測試。
  • Docker+jenkins小知識,講解通俗易懂,有必要收藏
    持續集成是軟體開發中一個非常重要的環節,我們都知道持續集成的這種方式,不僅僅是影響軟體開發的效率,實際上還會對我們軟體開發的流程會產生一定的影響,例如:我們所選擇的構建的方式,那麼你的應用程式的部署的方式,軟體開發與測試需要不斷地對應用進行持續構建,這些都會受我們持續集成的構建的方案所影響。
  • SkyForm ECP基於Kubernetes構建AIaaS平臺
    社區中也出現了很多tensorflow on kubernetes的方案,比如最早使用jinja模板的方案(https://github.com/tensorflow/ecosystem/tree/master/kubernetes),以及後來的kubeflow(https://github.com/google/kubeflow)和Tensorflow/k8s(https://github.com
  • jenkins詳細介紹
    一、jenkins是什麼?  Jenkins是一個開源的、提供友好操作界面的持續集成(CI)工具,起源於Hudson(Hudson是商用的),主要用於持續、自動的構建/測試軟體項目、監控外部任務的運行。Jenkins用Java語言編寫,可在Tomcat等流行的servlet容器中運行,也可獨立運行。通常與版本管理工具(SCM)、構建工具結合使用。
  • Jenkins詳細教程
    Jenkins是一個開源的、提供友好操作界面的持續集成(CI)工具,起源於Hudson(Hudson是商用的),主要用於持續、自動的構建/測試軟體項目、監控外部任務的運行(這個比較抽象,暫且寫上,不做解釋)。Jenkins用Java語言編寫,可在Tomcat等流行的servlet容器中運行,也可獨立運行。通常與版本管理工具(SCM)、構建工具結合使用。
  • 中小團隊基於Docker的devops實踐
    文章是基於目前的環境和團隊規模做的devops實踐總結,方案簡單易懂,容易落地且效果顯著。  實現方法  先來看下流程圖:  工程師本地開發,開發完成後提交代碼到代碼倉庫,[自動]觸發jenkins進行持續集成與部署,部署完成會收到結果郵件。項目運行過程中可通過日誌系統查看程序日誌,有異常會觸發監控系統發送報警。
  • Kubernetes通過Dex集成企業外部認證系統
    OIDC集成Keyclock認證以及Webhook集成OpenStack Keystone認證。Kubernetes通過OIDC對接企業已有的統一身份認證系統是企業推薦的認證集成方式,方便進行用戶的統一管理和健全,實現Kubernetes的SSO單點登錄。
  • Kubernetes持續部署指南
    有些人將其稱為容器編排平臺,而 Kubernetes 並非唯一的此類平臺。不過,相比其它對手,其享譽已盛,且知名度仍在不斷提高;更別說你一旦習慣上它,就會發現它真的十分易用。如果你依然好奇為何有人能夠愉快地和 Kubernetes 玩耍,答案就是——簡單。Kubernetes 能夠讓部署、管理多個項目所需的大量集群變得更加容易。
  • kubernetes認證、授權、準入控制
    之後開始有RBAC(基於角色的訪問控制機制)授權檢查機制。,其常用的授權插件有以下幾種:1)Node(節點認證)2)ABAC(基於屬性的訪問控制)3)RBAC(基於角色的訪問控制)***4)Webhook(基於http回調機制的訪問控制)什麼是RBAC(基於角色的訪問控制
  • 基於k8s部署cicd之新建jenkins任務、編寫shell命令(5)
    2、對k8s有較熟練的認識3、對rancher 有過使用經驗,基礎操作要懂4、對ci/cd 的概念不陌生5、對golang的基礎有較熟悉6、知道jenkins的最基本使用7、知道git的基本使用,對gitlab、gitea 有基本了解在 基於k8s部署cicd之利用rancher快速部署jenkins
  • Jenkins 2.121.1 發布,可擴展的持續集成引擎 - OSCHINA - 中文...
    Jenkins 2.121.1 已發布,這是一個可擴展的持續集成引擎。
  • CVE-2019-10392:Jenkins Git client插件RCE復現
    本公眾號專注於漏洞復現,歡迎關注!
  • 乾貨 | Jenkins使用簡易教程
    Jenkins是一個廣泛用於持續構建的可視化Web工具,持續構建說得更直白點,就是各種項目的"自動化"編譯、打包、分發部署。Jenkins可以很好的支持各種語言(比如:Java,C#,PHP等)的項目構建,也完全兼容ant、maven、gradle等多種第三方構建工具,同時跟SVN、Git能無縫集成。
  • Gitee Jenkins Plugin gitee-1.2.2 已經發布,碼雲持續集成插件
    Gitee Jenkins Plugin gitee-1.2.2 已經發布,碼雲持續集成插件。