最全教程 | Kubernetes + Jenkins + Helm + Springboot 實踐
- 2019 年 12 月 27 日
- 筆記
目錄[-]
一、Kubernetes 環境安裝 Jenkins
二、Jenkins 安裝插件
1、Git 插件
2、Docker 插件
3、Kubernetes
4、Kubernetes Cli
5、Config File Provider
6、Pipeline Utility Steps
三、Jenkins 配置插件
1、Git 插件配置及使用
2、Docker 插件配置及使用
3、Kubernetes 插件配置及使用
4、Kubernetes Cli 插件配置及使用
5、Config File Provider 插件
6、Pipeline Utility Steps 插件
四、測試插件
1、創建流水線任務
2、配置流水線任務
3、查看流水線日誌
五、部署前準備
1、配置文件存放位置比較
2、設置配置文件到項目中
3、測試運行環境是否可用
六、開始寫 Pipeline 腳本
1、Git 拉取
2、Maven 編譯
3、Docker 編譯
4、Helm 啟動應用
5、測試介面
七、完善 Pipeline 腳本
1、設置超時時間
2、設置郵箱通知
3、判斷成功失敗來發送郵件
4、將腳本放入到項目中
八、完整程式碼
環境介紹:
- Jenkins: 2.172
- Helm: 2.13.1
- Kubernetes: 1.14.0
- chart repo倉庫地址: http://chart.mydlq.club
- 項目Github地址: https://github.com/my-dlq/springboot-helloworld
- Helm的chart模板Github地址: https://github.com/my-dlq/springboot-chart

一、Kubernetes 環境安裝 Jenkins
詳情請看 Kubernetes 中安裝 Jenkins ,這裡不過多敘述。
二、Jenkins 安裝插件
為了方便集成 Maven、Kubernetes、配置文件等等,這裡需要安裝幾個別的插件,這裡插件可以在 系統管理—>插件管理—>可選插件 裡面安裝下面列出的插件。
- ① Git 插件
- ② Docker 插件
- ③ Kubernetes
- ④ Kubernetes Cli
- ⑤ Config File Provider
- ⑥ Pipeline Utility Steps
1、Git 插件
Jenkins 安裝中默認安裝 Git 插件,所以不需要單獨安裝。利用 git 工具可以將 github、gitlab 等等的地址下載源碼。
2、Docker 插件
Jenkins 安裝中默認安裝 Docker 插件,所以不需要單獨安裝。利用 Docker 插件可以設置 Docker 環境,運行 Docker 命令,配置遠程 Docker 倉庫憑據等。
3、Kubernetes
Kubernetes 插件的目的是能夠使用 Kubernetes 集群動態配置 Jenkins 代理(使用Kubernetes調度機制來優化負載),運行單個構建,等構建完成後刪除該代理。這裡我們需要用到這個插件來啟動 Jenkins Slave 代理鏡像,讓代理執行 Jenkins 要執行的 Job。
4、Kubernetes Cli
Kubernetes Cli 插件作用是在執行 Jenkins Job 時候提供 kubectl 與 Kubernetes 集群交互環境。可以在 Pipeline 或自由式項目中允許執行 kubectl 相關命令。它的主要作用是提供 kubectl 運行環境,當然也可以提供 helm 運行環境。
5、Config File Provider
Config File Provider 插件作用就是提供在 Jenkins 中存儲 properties、xml、json、settings.xml 等資訊,可以在執行 Pipeline 過程中可以寫入存儲的配置。
例如,存入一個 Maven 全局 Settings.xml 文件,在執行 Pipeline Job 時候引入該 Settings.xml ,這樣 Maven 編譯用的就是該全局的 Settings.xml。
6、Pipeline Utility Steps
這是一個操作文件的插件,例如讀寫 json、yaml、pom.xml、Properties 等等。在這裡主要用這個插件讀取 pom.xml 文件的參數設置,獲取變數,方便構建 Docker 鏡像。
三、Jenkins 配置插件
1、Git 插件配置及使用
(1)、配置憑據:
如果是私有項目 Git 一般需要配置一個憑據用於驗證,如果是公開項目,則無需任何配置。
憑據->系統->全局憑據->添加憑據

(2)、Pipeline 腳本中使用:
利用 Git 插件拉取源碼,分別可以設置拉取的「分支」、「顯示拉取日誌」、「拉取的憑據」、「拉取的地址」,可以將上面設置的憑據ID設置到 credentialsId 參數上
參考:https://jenkins.io/doc/pipeline/steps/git/#-git-%20git
git branch: "master" ,changelog: true , credentialsId: "xxxx-xxxx-xxxx", url: "https://github.com/xxxxxx"
2、Docker 插件配置及使用
(1)、功能描述:
此插件將提供一下功能:
- 記錄FROM中使用的Docker鏡像的跟蹤
- 記錄在容器中運行的Docker鏡像的跟蹤
- 在Docker容器中運行構建步驟
- 設置Docker註冊表端點,用於推送鏡像驗證
- 設置Docker伺服器端點,用於執行遠程Docker API
(2)、Pipeline 腳本中使用:
安裝 Jenkins 時候默認會安上此插件,這裡主要是利用插件提供一個 docker 登錄了的環境,以及執行一些 Docker 命令,具體請看參考,下面將寫一個簡單的執行例子來描述 Docker 鏡像的構建過程。
參考:https://jenkins.io/doc/pipeline/steps/docker-workflow/
// 此方法是設置docker倉庫地址,然後選擇存了用戶名、密碼的憑據ID進行驗證。注意,只有在此方法之中才生效。 docker.withRegistry("https://hub.docker.com/", "xxxxx-xxxx-xxxx-xxxx") { echo "構建鏡像" def customImage = docker.build("hub.mydlq.club/myproject/springboot-helloworld:0.0.1") echo "推送鏡像" customImage.push() echo "刪除鏡像" sh "docker rmi hub.mydlq.club/myproject/springboot-helloworld:0.0.1" }
3、Kubernetes 插件配置及使用
(1)、配置憑據:
配置連接 kubernetes 集群的憑據(Kubernetes ServiceAccount token),此憑據的賬戶許可權最好設置較大點,避免出現未知問題。配置完成後,需要在後面的 Cloud 雲配置中設置這個憑據。

(2)、雲配置
系統管理—>系統設置—>雲
參考:https://github.com/jenkinsci/kubernetes-plugin
這裡是配置連接Kubernetes集群,啟動 Jenkins Slave 代理的相關配置。
- 名稱: kubernetes
- Kubernetes 地址: https://kubernetes.default.svc.cluster.local (默認集群內調用 k8s api 地址)
- 禁用 HTTPS 證書檢查: 勾選 (不驗證https)
- 憑據: 新增憑據—>Secret text—>Secret 設置 kubernetes 的 Token (進入 k8s dashboard 的 token 等都行)
- Jenkins地址: http://jenkins.mydlqcloud:8080/jenkins (用於代理與 Jenkins 連接的地址,用的是 k8s 集群中 jenkins 服務的地址為「http://jenkins服務名.jenkins所在namespace:jenkins埠號/jenkins後綴」)
- 其他: 默認即可

(3)、Template 模板配置
這裡配置 Jenkins Slave 在 kubernetes 集群中啟動的 Pod 的配置,這裡將設置四個鏡像,分別是:
- Jenkins Slave: 用於執行 Jenkins Job 命令。
- Helm-Kuberctl: 用於執行 Helm 命令。
- Docker 用於編譯、推送 Docker 鏡像
- Maven: 用於Maven編譯、打包。
這裡將這四個鏡像融入到一個 Pod 之中,方便執行各種命令來完成持續部署交互過程。
Template 基本配置:

原始 Yaml 設置:
在 Pod 的原始 yaml 那欄中,填寫下面的 yaml 文件內容進行配置,將會以下面的 yaml 配置作為 Jenkins Slave Pod 的基本配置,如果上面介面上配置了某些資訊,會自動替換 yaml 中設置的值,相當於此 yaml 文件作為了一個默認(預設)配置了。

apiVersion: v1 kind: Pod metadata: labels: app: jenkins-slave spec: serviceAccountName: jenkins-admin securityContext: #容器安全設置 runAsUser: 0 #以ROOT用戶運行容器 privileged: true #賦予特權執行容器 containers: - name: jnlp #Jenkins Slave鏡像 image: registry.cn-shanghai.aliyuncs.com/mydlq/jenkins-jnlp-slave:3.27-1 #設置工作目錄 workingDir: /home/jenkins tty: true - name: docker #Docker鏡像 image: registry.cn-shanghai.aliyuncs.com/mydlq/docker:18.06.2-dind command: ['cat'] tty: true volumeMounts: - name: docker mountPath: /usr/bin/docker - name: docker-sock mountPath: /var/run/docker.sock - name: docker-config mountPath: /etc/docker - name: maven #Maven鏡像 image: registry.cn-shanghai.aliyuncs.com/mydlq/maven:3.6.0-jdk8-alpine command: - cat tty: true volumeMounts: - name: maven-m2 mountPath: /root/.m2 - name: helm-kubectl #Kubectl & Helm鏡像 image: registry.cn-shanghai.aliyuncs.com/mydlq/helm-kubectl:2.13.1 command: - cat tty: true volumes: - name: docker #將宿主機 Docker 文件夾掛進容器,方便存儲&拉取本地鏡像 hostPath: path: /usr/bin/docker - name: docker-sock #將宿主機 Docker.sock 掛進容器 hostPath: path: /var/run/docker.sock - name: docker-config #將宿主機 Docker 配置掛在進入容器 hostPath: path: /etc/docker - name: maven-m2 #Maven 本地倉庫掛在到 NFS 共享存儲,方便不同節點能同時訪問與存儲 nfs: server: 192.168.2.11 path: "/nfs/data/maven-m2" # nodeSelector: # kubernetes.io/hostname: node-2-12
4、Kubernetes Cli 插件配置及使用
(1)、配置憑據:
配置連接 kubernetes 集群的憑據,這個憑據可以和上面 kubernetes 插件的憑據一致,都是用於連接 Kubernetes 集群

(2)、Pipeline 腳本中使用:
此插件主要功能就是提供執行 kubectl 的環境設置,在此插件方法中相當於有 kubectl、helm 等環境設置,然後用相關鏡像就可以執行相關命令。
參考:https://jenkins.io/doc/pipeline/steps/kubernetes-cli/
// 提供 kubectl 執行的環境,其中得設置存儲了 token 的憑據ID和 kubernetes api 地址 withKubeConfig([credentialsId: "xxxx-xxxx-xxxx-xxxx",serverUrl: "https://kubernetes.default.svc.cluster.local"]) { sh "kubectl get nodes" }
5、Config File Provider 插件
(1)、配置 Maven settings.xml
在 Jenkins 安裝時候安裝了「config File Provider」插件,這個插件的作用就是提供在 Jenkins 中存儲properties、xml、json、settings.xml 等資訊,這裡打開下面列表,配置一個全局的 Maven 的 settings.xml 文件。
系統管理—>Managed files—>Add a new Config—>Global Maven settings.xml

在裡面添加一個全局的 setting.xml 設置,為了加快 jar 包的下載速度,這裡將倉庫地址指向 aliyun Maven 倉庫地址。
<mirror> <id>alimaven</id> <name>aliyun maven</name> <url>http://maven.aliyun.com/nexus/content/groups/public/</url> <mirrorOf>central</mirrorOf> </mirror>
(2)、Pipeline 腳本中使用:
參考:https://jenkins.io/doc/pipeline/steps/config-file-provider/
可以在 Pipeline 腳本中,用於生成上面設置的文件,用法如下:
// 生成 settings.xml 文件,這個方法第一個參數是引用文件ID,第二個是生成的文件名 configFileProvider([configFile(fileId: "75884c5a-4ec2-4dc0-8d87-58b6b1636f8a", targetLocation: "settings.xml")]) { // 只有在方法裡面該文件才存在 echo "cat settings.xml" }
6、Pipeline Utility Steps 插件
(1)、功能描述
此插件將提供一下功能:
- 提取Zip文件
- 創建Zip文件
- 創建一個普通文件。
- 生成一個Yaml文件。
- 編寫maven項目文件。
- 在工作區中查找文件。
- 讀取 properties 文件參數。
- 從工作區中的文件中讀取JSON。
- 讀取 maven 項目的 pom.xml 文件
- ……
(2)、Pipeline 腳本中使用:
這裡主要是用此插件讀取 pom.xml 的項目有關的參數,用於 docker 編譯鏡像時使用。另一個功能是在腳本進行時候用於生成文件,例如 yaml 文件、helm 證書等。
參考:https://jenkins.io/doc/pipeline/steps/pipeline-utility-steps/
// 讀取 pom.xml 文件 pom = readMavenPom file: "./pom.xml" echo "${pom.artifactId}:${pom.version}"
四、測試插件
為了保證插件配置正確且值執行,在 kubernetes 環境下啟動 Jenkins 代理執行任務,這裡將進行測試。
1、創建流水線任務
創建一個名為 「k8s-test」 的任務,類型選擇「流水線」。

2、配置流水線任務
(1)、常規配置
- 為了安全,禁止並發構建。
- 為了提升效率,這裡設置流水線效率,持久保存設置覆蓋。

(2)、流水線腳本
這裡寫一個簡單的腳本,將 Kubernetes 插件提供的 Pipeline 的方法引入,如下:

// 代理名稱,填寫系統設置中設置的 Cloud 中 Template 模板的 label def label = "jnlp-agent" // 調用Kubernetes提供的方法 podTemplate(label: label,cloud: 'kubernetes' ){ // 在代理節點上運行腳本 node (label) { echo "測試 kubernetes 中 jenkins slave 代理!~" } }
(3)、運行流水線任務
回到任務介面,點擊立即構造來執行任務。

3、查看流水線日誌
然後點擊執行歷史欄中點擊,查看控制台輸出的日誌資訊。


五、部署前準備
1、配置文件存放位置比較
以下僅是個人看法,有更好的方式,希望告知。
這裡涉及到一個問題,在 Jenkins 中,我們的 Jenkinsfile 腳本存放在哪比較方便,這裡本人想到三種:
- 1、新建 Git 項目,專門存放不同的 jenkinsfile 腳本,Jenkins 創建任務時候指定腳本存放的 Git 地址;
- 2、放到各個項目中,當在執行 Jenkins 任務時候讀取 Git項目,從中檢測 jenkinsfile 腳本從而執行;
- 3、每個腳本都放置到 Jenkins 每個任務的配置中,每次都執行配置中設置的腳本;
比較三者:
- 第1種方式方便統一管理,一改動git上的配置,jenkins 任務的流水線腳本都會跟著變化;
- 第2種方式可以針對每個項目單獨設置,更靈活,就是不方便統一管理,維護需要各個項目組;
- 第3種方式需要每次都新建項目時候在配置中設置腳本,比較費力不方便維護,不太推薦;
2、設置配置文件到項目中
這裡需要將將一些配置文件存入項目源碼中,用於在執行流水線中讀取對應的配置參數,比如:
- SpringBoot源碼: 用於測試的 helloworld 的SpringBoot項目。
- Dockerfile: 用於 Docker 編譯鏡像的文件,比如打包的基礎鏡像等等。
- values.yaml: 用於 Helm 啟動的chart的配置文件,裡面設置了一些chart的配置資訊,告知該如何啟動應用程式。
項目 Github 地址:https://github.com/my-dlq/springboot-helloworld

(1)、Dockerfile
FROM registry.cn-shanghai.aliyuncs.com/mydlq/openjdk:8u201-jdk-alpine3.9 VOLUME /tmp ADD target/*.jar app.jar RUN sh -c 'touch /app.jar' ENV JAVA_OPTS="-Xmx512M -Xms256M -Xss256k -Duser.timezone=Asia/Shanghai" ENV APP_OPTS="" ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar $APP_OPTS" ]
(2)、values.yaml
kind: Deployment image: pullPolicy: "Always" replicas: 1 resources: limits: memory: 512Mi cpu: 1000m requests: memory: 256Mi cpu: 500m #***java && app 環境變數設置 env: - name: "JAVA_OPTS" value: "-Xmx512M -Xms355M -Xss256k -Duser.timezone=Asia/Shanghai" - name: "APP_OPTS" value: "" envFrom: #- configMapRef: # name: env-config service: type: NodePort #Service type設置 (可以設置為ClusterIP、NodePort、None) labels: svcEndpoints: actuator annotations: {} ports: - name: server port: 8080 targetPort: 8080 protocol: TCP nodePort: 30080 - name: management port: 8081 targetPort: 8081 protocol: TCP nodePort: 30081
3、測試運行環境是否可用
這裡先寫一個簡單的腳本,用於測試各個環境是否都能用,例如slave鏡像的git命令能否執行、maven鏡像的mvn命令是否能用等等。
這裡可以用 container(『docker』) 方式,來引用 kubernetes 插件中設置的容器,利用各個容器不同的客戶端功能,來執行對應的命令。
將之前創建的任務配置中的 pipeline 腳本改成下面:
def label = "jnlp-agent" podTemplate(label: label,cloud: 'kubernetes' ){ node (label) { stage('Git階段'){ echo "1、開始拉取程式碼" sh "git version" } stage('Maven階段'){ container('maven') { echo "2、開始Maven編譯、推送到本地庫" sh "mvn -version" } } stage('Docker階段'){ container('docker') { echo "3、開始讀取Maven pom變數,並執行Docker編譯、推送、刪除" sh "docker version" } } stage('Helm階段'){ container('helm-kubectl') { echo "4、開始檢測Kubectl環境,測試執行Helm部署,與執行部署" sh "helm version" } } } }
jenkins slave 容器中默認集成 git 客戶端,該整體流水線執行就在 Jenkins slave 容器中,任務默認在 Jenkins Slave 執行,所以不需要設置容器名稱。
然後執行查看日誌,日誌內容如下:
Running on jnlp-agent-g7qk5 in /home/jenkins/workspace/k8s-test [Pipeline] { [Pipeline] stage [Pipeline] { (Git階段) [Pipeline] echo 1、開始拉取程式碼 [Pipeline] sh + git version git version 2.11.0 [Pipeline] } [Pipeline] // stage [Pipeline] stage [Pipeline] { (Maven階段) [Pipeline] container [Pipeline] { [Pipeline] echo 2、開始Maven編譯、推送到本地庫 [Pipeline] sh + mvn -version Apache Maven 3.6.0 (97c98ec64a1fdfee7767ce5ffb20918da4f719f3; 2018-10-24T18:41:47Z) Maven home: /usr/share/maven Java version: 1.8.0_201, vendor: Oracle Corporation, runtime: /usr/lib/jvm/java-1.8-openjdk/jre Default locale: en_US, platform encoding: UTF-8 OS name: "linux", version: "3.10.0-957.1.3.el7.x86_64", arch: "amd64", family: "unix" [Pipeline] } [Pipeline] // container [Pipeline] } [Pipeline] // stage [Pipeline] stage [Pipeline] { (Docker階段) [Pipeline] container [Pipeline] { [Pipeline] echo 3、開始讀取Maven pom變數,並執行Docker編譯、推送、刪除 [Pipeline] sh + docker version Client: Version: 18.06.2-ce API version: 1.38 Go version: go1.10.4 Git commit: 6d37f41 Built: Sun Feb 10 03:43:40 2019 OS/Arch: linux/amd64 Experimental: false Server: Docker Engine - Community Engine: Version: 18.09.3 API version: 1.39 (minimum version 1.12) Go version: go1.10.8 Git commit: 774a1f4 Built: Thu Feb 28 06:02:24 2019 OS/Arch: linux/amd64 Experimental: false [Pipeline] } [Pipeline] // container [Pipeline] } [Pipeline] // stage [Pipeline] stage [Pipeline] { (Helm階段) [Pipeline] container [Pipeline] { [Pipeline] echo 4、開始檢測Kubectl環境,測試執行Helm部署,與執行部署 [Pipeline] sh + helm version Client: &version.Version{SemVer:"v2.13.1", GitCommit:"618447cbf203d147601b4b9bd7f8c37a5d39fbb4", GitTreeState:"clean"} Server: &version.Version{SemVer:"v2.13.1", GitCommit:"79d07943b03aea2b76c12644b4b54733bc5958d6", GitTreeState:"clean"} [Pipeline] // podTemplate [Pipeline] End of Pipeline Finished: SUCCESS
最後看見執行狀態為 SUCCESS 則證明環境可用,否則有問題,請檢測問題所在。
六、開始寫 Pipeline 腳本
這裡進行分階段性的腳本編寫,然後一步步測試,最後合併在一起。這裡新建一個名稱為 k8s-pipeline 的任務,然後在配置項腳本框匯總輸入 Pipleline 腳本。
1、Git 拉取
這裡拉取本人 Github 上的一個簡單的 SpringBoot Demo 項目進行實踐。
Groovy腳本
def label = "jnlp-agent" podTemplate(label: label,cloud: 'kubernetes' ){ node (label) { stage('Git階段'){ echo "Git 階段" git branch: "master" ,changelog: true , url: "https://github.com/my-dlq/springboot-helloworld.git" } } }
查看執行日誌
...... Running on jnlp-agent-dhr1h in /home/jenkins/workspace/k8s-pipeline [Pipeline] { [Pipeline] stage [Pipeline] { (Git階段) [Pipeline] echo 1、開始拉取程式碼 [Pipeline] sh + git clone https://github.com/my-dlq/springboot-helloworld.git Cloning into 'springboot-helloworld'... [Pipeline] sh + ls -l total 0 drwxr-xr-x 4 root root 79 Apr 28 07:00 springboot-helloworld [Pipeline] } ...... Finished: SUCCESS
可以通過控制台輸出的日誌看到,已經拉取成功。繼續進行下一步,Maven 階段。
2、Maven 編譯
這裡將進行 Maven 編譯,將 Java 源碼編譯成一個 Jar 項目,方便後續打包進入 Docker 鏡像。( Maven 中也可以進行單元測試,由於某些原因,這裡不進行闡述,可以自己執行測試命令進行測試 )
Groovy腳本
def label = "jnlp-agent" podTemplate(label: label,cloud: 'kubernetes' ){ node (label) { stage('Git階段'){ echo "Git 階段" git branch: "master" ,changelog: true , url: "https://github.com/my-dlq/springboot-helloworld.git" } stage('Maven階段'){ container('maven') { //這裡引用上面設置的全局的 settings.xml 文件,根據其ID將其引入並創建該文件 configFileProvider([configFile(fileId: "75884c5a-4ec2-4dc0-8d87-58b6b1636f8a", targetLocation: "settings.xml")]){ sh "mvn clean install -Dmaven.test.skip=true --settings settings.xml" } } } } }
查看執行日誌
...... [INFO] --- maven-clean-plugin:3.1.0:clean (default-clean) @ springboot-helloword --- [INFO] [INFO] --- maven-resources-plugin:3.1.0:resources (default-resources) @ springboot-helloword --- [INFO] Using 'UTF-8' encoding to copy filtered resources. [INFO] Copying 1 resource [INFO] Copying 0 resource [INFO] [INFO] --- maven-compiler-plugin:3.8.0:compile (default-compile) @ springboot-helloword --- [INFO] Changes detected - recompiling the module! [INFO] Compiling 2 source files to /home/jenkins/workspace/k8s-pipeline/target/classes [INFO] [INFO] --- maven-resources-plugin:3.1.0:testResources (default-testResources) @ springboot-helloword --- [INFO] Not copying test resources [INFO] [INFO] --- maven-compiler-plugin:3.8.0:testCompile (default-testCompile) @ springboot-helloword --- [INFO] Not compiling test sources [INFO] [INFO] --- maven-surefire-plugin:2.22.1:test (default-test) @ springboot-helloword --- [INFO] Tests are skipped. [INFO] [INFO] --- maven-jar-plugin:3.1.1:jar (default-jar) @ springboot-helloword --- [INFO] Building jar: /home/jenkins/workspace/k8s-pipeline/target/springboot-helloword-0.0.1.jar [INFO] [INFO] --- spring-boot-maven-plugin:2.1.4.RELEASE:repackage (repackage) @ springboot-helloword --- [INFO] Replacing main artifact with repackaged archive [INFO] [INFO] --- maven-install-plugin:2.5.2:install (default-install) @ springboot-helloword --- [INFO] Installing /home/jenkins/workspace/k8s-pipeline/target/springboot-helloword-0.0.1.jar to /root/.m2/repository/club/mydlq/springboot-helloword/0.0.1/springboot-helloword-0.0.1.jar [INFO] Installing /home/jenkins/workspace/k8s-pipeline/pom.xml to /root/.m2/repository/club/mydlq/springboot-helloword/0.0.1/springboot-helloword-0.0.1.pom [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 7.989 s [INFO] Finished at: 2019-04-28T09:38:03Z [INFO] ------------------------------------------------------------------------ ...... [Pipeline] // podTemplate [Pipeline] End of Pipeline Finished: SUCCESS
3、Docker 編譯
Groovy腳本
def label = "jnlp-agent" podTemplate(label: label,cloud: 'kubernetes' ){ node (label) { stage('Git階段'){ echo "Git 階段" git branch: "master" ,changelog: true , url: "https://github.com/my-dlq/springboot-helloworld.git" } stage('Maven階段'){ echo "Maven 階段" container('maven') { //這裡引用上面設置的全局的 settings.xml 文件,根據其ID將其引入並創建該文件 configFileProvider([configFile(fileId: "75884c5a-4ec2-4dc0-8d87-58b6b1636f8a", targetLocation: "settings.xml")]){ sh "mvn clean install -Dmaven.test.skip=true --settings settings.xml" } } } stage('Docker階段'){ echo "Docker 階段" container('docker') { // 讀取pom參數 echo "讀取 pom.xml 參數" pom = readMavenPom file: './pom.xml' // 設置鏡像倉庫地址 hub = "registry.cn-shanghai.aliyuncs.com" // 設置倉庫項目名 project_name = "mydlq" echo "編譯 Docker 鏡像" docker.withRegistry("http://${hub}", "ffb3b544-108e-4851-b747-b8a00bfe7ee0") { echo "構建鏡像" // 設置推送到aliyun倉庫的mydlq項目下,並用pom裡面設置的項目名與版本號打標籤 def customImage = docker.build("${hub}/${project_name}/${pom.artifactId}:${pom.version}") echo "推送鏡像" customImage.push() echo "刪除鏡像" sh "docker rmi ${hub}/${project_name}/${pom.artifactId}:${pom.version}" } } } } }
查看執行日誌
編譯 Docker 鏡像 [Pipeline] withEnv [Pipeline] { [Pipeline] withDockerRegistry Executing shell script inside container [docker] of pod [jnlp-agent-v6f1f] Executing command: "docker" "login" "-u" "3******7@qq.com" "-p" ******** "http://registry.cn-shanghai.aliyuncs.com" /home/jenkins/workspace/k8s-pipeline2@tmp/b52e213b-a730-4120-b004-decd8e16b246/config.json. Configure a credential helper to remove this warning. See https://docs.docker.com/engine/reference/commandline/login/#credentials-store Login Succeeded [Pipeline] { [Pipeline] echo 構建鏡像 [Pipeline] sh + docker build -t registry.cn-shanghai.aliyuncs.com/mydlq/springboot-helloword:0.0.1 . Sending build context to Docker daemon 18.37MB Step 1/7 : FROM registry.cn-shanghai.aliyuncs.com/mydlq/openjdk:8u201-jdk-alpine3.9 ---> 3675b9f543c5 Step 2/7 : VOLUME /tmp ---> Running in 7fc4af80e6ce Removing intermediate container 7fc4af80e6ce ---> 4e4224d3b50b Step 3/7 : ADD target/*.jar app.jar ---> 0c24118d522f Step 4/7 : RUN sh -c 'touch /app.jar' ---> Running in 8836cb91e1ca Removing intermediate container 8836cb91e1ca ---> 389e604851b6 Step 5/7 : ENV JAVA_OPTS="-Xmx512M -Xms256M -Xss256k -Duser.timezone=Asia/Shanghai" ---> Running in 5126902b1e6b Removing intermediate container 5126902b1e6b ---> 055ad2b9c49d Step 6/7 : ENV APP_OPTS="" ---> Running in cf8ea4b61eea Removing intermediate container cf8ea4b61eea ---> 07dd4fdda44a Step 7/7 : ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar $APP_OPTS" ] ---> Running in 93c4d0d859e1 Removing intermediate container 93c4d0d859e1 ---> d29e092f2c17 Successfully built d29e092f2c17 Successfully tagged registry.cn-shanghai.aliyuncs.com/mydlq/springboot-helloword:0.0.1 [Pipeline] dockerFingerprintFrom [Pipeline] echo 推送鏡像 [Pipeline] sh + docker tag registry.cn-shanghai.aliyuncs.com/mydlq/springboot-helloword:0.0.1 registry.cn-shanghai.aliyuncs.com/mydlq/springboot-helloword:0.0.1 [Pipeline] sh + docker push registry.cn-shanghai.aliyuncs.com/mydlq/springboot-helloword:0.0.1 The push refers to repository [registry.cn-shanghai.aliyuncs.com/mydlq/springboot-helloword] 8d45ad1172aa: Preparing aba126d6a94c: Preparing a464c54f93a9: Mounted from mydlq/openjdk dee6aef5c2b6: Mounted from mydlq/openjdk aba126d6a94c: Pushed 8d45ad1172aa: Pushed 0.0.1: digest: sha256:2c661931a3c08a1cad1562ec4936c68f06b4b3ffcec5de14c390ae793cf5b53b size: 1371 [Pipeline] echo 刪除鏡像 [Pipeline] sh + docker rmi registry.cn-shanghai.aliyuncs.com/mydlq/springboot-helloword:0.0.1 Untagged: registry.cn-shanghai.aliyuncs.com/mydlq/springboot-helloword:0.0.1 Untagged: registry.cn-shanghai.aliyuncs.com/mydlq/springboot-helloword@sha256:2c661931a3c08a1cad1562ec4936c68f06b4b3ffcec5de14c390ae793cf5b53b Deleted: sha256:d29e092f2c175a3662af0175e62462238583330ba7d46b84d89134056ba14027 Deleted: sha256:07dd4fdda44a12d7749e5e7f60b1943c83e6d0a3da2e4f279ce4d53f3b04f27e ...... Finished: SUCCESS
4、Helm 啟動應用
創建Helm執行方法
這裡提前創建好執行 helm 的方法,將其簡單封裝一下用於執行流水線時候,調用此方法,執行對應的 Helm 操作。
- 方法名:helmDeploy()
- 可配參數:
參數 |
描述 |
---|---|
– init: |
是否為執行 helm 初始化 |
– url: |
初始化 chart 倉庫地址 |
– dry: |
是否為嘗試部署 |
– name: |
部署的應用 Release 名 |
– namespace: |
應用啟動到哪個Namespace |
– image: |
鏡像名 |
– tag: |
鏡像標籤 |
– template: |
選用的chart模板 |
// 執行Helm的方法 def helmDeploy(Map args) { // Helm 初始化 if(args.init){ sh "helm init --client-only --stable-repo-url ${args.url}" } // Helm 嘗試部署 else if (args.dry_run) { println "嘗試 Helm 部署,驗證是否能正常部署" sh "helm upgrade --install ${args.name} --namespace ${args.namespace} -f values.yaml --set ${args.repository},${args.tag} stable/${args.template} --dry-run --debug" } // Helm 正式部署 else { println "正式 Helm 部署" sh "helm upgrade --install ${args.name} --namespace ${args.namespace} -f values.yaml --set ${args.repository},${args.tag} stable/${args.template}" } } // 方法調用 stage() { echo "Helm 初始化 http://chart.mydlq.club" helmDeploy(init: true ,url: "Helm 倉庫地址"); echo "Helm 嘗試執行部署" helmDeploy(init: false ,dry: true ,name: "應用名" ,namespace: "應用啟動的Namespace" ,image: "鏡像名",tag: "鏡像標籤" ,template: "選用的chart模板") echo "Helm 正式執行部署" helmDeploy(init: false ,dry: false ,name: "應用名" ,namespace: "應用啟動的Namespace" ,image: "鏡像名",tag: "鏡像標籤" ,template: "選用的chart模板") }
完整Groovy腳本
def label = "jnlp-agent" // 執行Helm的方法 def helmDeploy(Map args) { if(args.init){ println "Helm 初始化" sh "helm init --client-only --stable-repo-url ${args.url}" } else if (args.dry_run) { println "嘗試 Helm 部署,驗證是否能正常部署" sh "helm upgrade --install ${args.name} --namespace ${args.namespace} ${args.values} --set ${args.image},${args.tag} stable/${args.template} --dry-run --debug" } else { println "正式 Helm 部署" sh "helm upgrade --install ${args.name} --namespace ${args.namespace} ${args.values} --set ${args.image},${args.tag} stable/${args.template}" } } // jenkins slave 執行流水線任務 podTemplate(label: label,cloud: 'kubernetes' ){ node (label) { stage('Git階段'){ echo "Git 階段" git branch: "master" ,changelog: true , url: "https://github.com/my-dlq/springboot-helloworld.git" } stage('Maven階段'){ echo "Maven 階段" container('maven') { //這裡引用上面設置的全局的 settings.xml 文件,根據其ID將其引入並創建該文件 configFileProvider([configFile(fileId: "75884c5a-4ec2-4dc0-8d87-58b6b1636f8a", targetLocation: "settings.xml")]){ sh "mvn clean install -Dmaven.test.skip=true --settings settings.xml" } } } stage('Docker階段'){ echo "Docker 階段" container('docker') { // 讀取pom參數 echo "讀取 pom.xml 參數" pom = readMavenPom file: './pom.xml' // 設置鏡像倉庫地址 hub = "registry.cn-shanghai.aliyuncs.com" // 設置倉庫項目名 project_name = "mydlq" echo "編譯 Docker 鏡像" docker.withRegistry("http://${hub}", "ffb3b544-108e-4851-b747-b8a00bfe7ee0") { echo "構建鏡像" // 設置推送到aliyun倉庫的mydlq項目下,並用pom裡面設置的項目名與版本號打標籤 def customImage = docker.build("${hub}/${project_name}/${pom.artifactId}:${pom.version}") echo "推送鏡像" customImage.push() echo "刪除鏡像" sh "docker rmi ${hub}/${project_name}/${pom.artifactId}:${pom.version}" } } } stage('Helm階段'){ container('helm-kubectl') { withKubeConfig([credentialsId: "8510eda6-e1c7-4535-81af-17626b9575f7",serverUrl: "https://kubernetes.default.svc.cluster.local"]) { // 設置參數 image = "image.repository=${hub}/${project_name}/${pom.artifactId}" tag = "image.tag=${pom.version}" template = "spring-boot" repo_url = "http://chart.mydlq.club" app_name = "${pom.artifactId}" // 檢測是否存在yaml文件 def values = "" if (fileExists('values.yaml')) { values = "-f values.yaml" } // 執行 Helm 方法 echo "Helm 初始化" helmDeploy(init: true ,url: "${repo_url}"); echo "Helm 執行部署測試" helmDeploy(init: false ,dry_run: true ,name: "${app_name}" ,namespace: "mydlqcloud" ,image: "${image}" ,tag: "${tag}" , values: "${values}" ,template: "${template}") echo "Helm 執行正式部署" helmDeploy(init: false ,dry_run: false ,name: "${app_name}" ,namespace: "mydlqcloud",image: "${image}" ,tag: "${tag}" , values: "${values}" ,template: "${template}") } } } } }
查看執行日誌
..... Executing shell script inside container [helm-kubectl] of pod [jnlp-agent-r3c8h] Executing command: "kubectl" "config" "set-cluster" "k8s" "--server=https://kubernetes.default.svc.cluster.local" "--insecure-skip-tls-verify=true" exit Cluster "k8s" set. Executing shell script inside container [helm-kubectl] of pod [jnlp-agent-r3c8h] Executing command: "kubectl" "config" "set-credentials" "cluster-admin" ******** Switched to context "k8s". [Pipeline] { [Pipeline] fileExists [Pipeline] echo Helm 初始化 [Pipeline] echo Helm 初始化 [Pipeline] sh + helm init --client-only --stable-repo-url http://chart.mydlq.club Creating /root/.helm/repository/repositories.yaml Adding stable repo with URL: http://chart.mydlq.club Adding local repo with URL: http://127.0.0.1:8879/charts $HELM_HOME has been configured at /root/.helm. Not installing Tiller due to 'client-only' flag having been set Happy Helming! [Pipeline] echo Helm 執行部署測試 [Pipeline] echo 嘗試 Helm 部署,驗證是否能正常部署 [Pipeline] sh + helm upgrade --install springboot-helloworld --namespace mydlqcloud -f values.yaml --set 'image.repository=registry.cn-shanghai.aliyuncs.com/mydlq/springboot-helloworld,image.tag=0.0.1' stable/spring-boot --dry-run --debug [debug] Created tunnel using local port: '37001' [debug] SERVER: "127.0.0.1:37001" [debug] Fetched stable/spring-boot to /root/.helm/cache/archive/spring-boot-1.0.4.tgz Release "springboot-helloworld" does not exist. Installing it now. [debug] CHART PATH: /root/.helm/cache/archive/spring-boot-1.0.4.tgz [Pipeline] echo Helm 執行正式部署 [Pipeline] echo 正式 Helm 部署 [Pipeline] sh + helm upgrade --install springboot-helloworld --namespace mydlqcloud -f values.yaml --set 'image.repository=registry.cn-shanghai.aliyuncs.com/mydlq/springboot-helloworld,image.tag=0.0.1' stable/spring-boot Release "springboot-helloworld" does not exist. Installing it now. NAME: springboot-helloworld LAST DEPLOYED: Mon Apr 29 07:31:39 2019 NAMESPACE: mydlqcloud STATUS: DEPLOYED RESOURCES: ==> v1/Pod(related) NAME READY STATUS RESTARTS AGE springboot-helloworld-7cd66cf74d-vfjr6 0/1 ContainerCreating 0 0s ==> v1/Service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE springboot-helloworld NodePort 10.10.87.61 <none> 8080:30080/TCP,8081:30081/TCP 0s ==> v1beta1/Deployment NAME READY UP-TO-DATE AVAILABLE AGE springboot-helloworld 0/1 1 0 0s ...... Finished: SUCCESS
5、測試介面
上面的 Helm步驟執行完成後,就可以進行簡單測試了,其中此項目引用的chart是一個簡單的 SpringBoot 項目,其中用 NodePort 方式暴露了兩個埠,30080 & 30081,分別對應8080、8081倆個埠,切提供了一個 Hello World 介面為「/hello」,所以我們這裡訪問一下這個介面地址:
http://192.168.2.11:30080/hello

七、完善 Pipeline 腳本
1、設置超時時間
設置任務超時時間,如果在規定時間內任務沒有完成,則進行失敗操作。
格式:
timeout(time: 20, unit: 'SECONDS') { // 流水線程式碼 }
例子:
設置超時時間為 60s 來讓 Jenkins Slave 節點執行任務。
def label = "jnlp-agent" timeout(time: 60, unit: 'SECONDS') { podTemplate(label: label,cloud: 'kubernetes' ){ node (label) { stage('Git階段'){ echo "Git 階段" } stage('Maven階段'){ echo "Maven 階段" } stage('Docker階段'){ echo "Docker 階段" } stage('Helm階段'){ echo "Helm 階段" } } } }
2、設置郵箱通知
(1)、設置郵箱開啟 POP3/SMTP/IMAP 設置
這裡用的是 163 郵箱

(2)、安裝 Email Extension Template 插件
這裡安裝插件「Email Extension Template」用於設置郵件模板。

(3)、配置系統默認郵件參數
系統管理->系統設置:
配置「Jenkins Location」和「Extended E-mail Notification」,其中系統管理員郵件地址一定要和「User Name」值一致。
Jenkins Location 設置:
參數名稱 |
描述 |
---|---|
– Jenkins URL: |
Jenkins 地址,用於發送郵件時寫入內容之中 |
– 系統管理員郵件地址: |
郵件伺服器賬戶 |

Extended E-mail Notification 設置:
參數名稱 |
描述 |
---|---|
– SMTP server: |
smtp 郵箱服務的地址 |
– Default user E-mail suffix: |
郵件伺服器後綴 |
– User Name: |
郵件伺服器賬戶 |
– Password: |
郵件伺服器 SMTP 授權碼 |
– Default Content Type: |
設置郵件文本格式 |
– Enable Debug Mode: |
啟用 Debug 模式 |

(4)、創建流水線項目
創建一個流水線項目,用於寫 Pipeline 腳本測試郵件發送,並配置 Pipeline 腳本,這裡寫一個簡單的 Pipeline 腳本,調用 emailext 方法執行發送郵件。

腳本內容:
def label = "jnlp-agent" podTemplate(label: label,cloud: 'kubernetes' ){ node (label) { stage('Git階段'){ echo "Git 階段" } stage('Maven階段'){ echo "Maven 階段" } stage('Docker階段'){ echo "Docker 階段" } stage('Helm階段'){ echo "Helm 階段" } stage('email'){ echo "測試發送郵件" emailext(subject: '任務執行失敗',to: '324******47@qq.com',body: '''測試郵件內容...''') } } }
(5)、運行項目查看日誌
查看日誌,看是否執行發送操作以及運行狀況。
Started by user admin Running in Durability level: PERFORMANCE_OPTIMIZED [Pipeline] Start of Pipeline [Pipeline] node Running on Jenkins in /var/jenkins_home/workspace/email-test [Pipeline] { [Pipeline] stage [Pipeline] { (email) [Pipeline] echo 測試發送郵件 [Pipeline] emailext Sending email to: 32*****47@qq.com [Pipeline] } [Pipeline] // stage [Pipeline] } [Pipeline] // node [Pipeline] End of Pipeline Finished: SUCCESS
(6)、查看郵件
查看郵件,可以看到已經收到設置郵箱發來的郵件資訊。

3、判斷成功失敗來發送郵件
(1)、流水線過程判斷成功失敗
這裡加 try、catch、finally 進行流水線,黨執行 finally 時候,進行判斷此任務執行到此是否成功構建,如果成功,則發送成功郵件通知。如果失敗,則發送失敗郵件通知。
try{ def label = "jnlp-agent" podTemplate(label: label,cloud: 'kubernetes' ){ node (label) { stage('Git階段'){ echo "Git 階段" } stage('Maven階段'){ echo "Maven 階段" } stage('Docker階段'){ echo "Docker 階段" } stage('Helm階段'){ echo "Helm 階段" } } } }catch(Exception e) { currentBuild.result = "FAILURE" }finally { // 獲取執行狀態 def currResult = currentBuild.result ?: 'SUCCESS' // 判斷執行任務狀態,根據不同狀態發送郵件 stage('email'){ if (currResult == 'SUCCESS') { echo "發送成功郵件" emailext(subject: '任務執行成功',to: '32*****7@qq.com',body: '''任務已經成功構建完成...''') }else { echo "發送失敗郵件" emailext(subject: '任務執行失敗',to: '32*****7@qq.com',body: '''任務執行失敗構建失敗...''') } } }
(2)、測試成功失敗執行時發送郵件
成功:
讓其正常成功跑完流程後發送郵件。

失敗:
模擬故意執行錯誤發送郵件。

4、將腳本放入到項目中
將腳本放入項目之中,方便後續調用時直接設置項目所在的Git地址即可。

八、完整程式碼
完整程式碼如下:
// 執行Helm的方法 def helmDeploy(Map args) { if(args.init){ println "Helm 初始化" sh "helm init --client-only --stable-repo-url ${args.url}" } else if (args.dry_run) { println "嘗試 Helm 部署,驗證是否能正常部署" sh "helm upgrade --install ${args.name} --namespace ${args.namespace} ${args.values} --set ${args.image},${args.tag} stable/${args.template} --dry-run --debug" } else { println "正式 Helm 部署" sh "helm upgrade --install ${args.name} --namespace ${args.namespace} ${args.values} --set ${args.image},${args.tag} stable/${args.template}" } } // jenkins slave 執行流水線任務 timeout(time: 600, unit: 'SECONDS') { try{ def label = "jnlp-agent" podTemplate(label: label,cloud: 'kubernetes' ){ node (label) { stage('Git階段'){ echo "Git 階段" git branch: "master" ,changelog: true , url: "https://github.com/my-dlq/springboot-helloworld.git" } stage('Maven階段'){ echo "Maven 階段" container('maven') { //這裡引用上面設置的全局的 settings.xml 文件,根據其ID將其引入並創建該文件 configFileProvider([configFile(fileId: "75884c5a-4ec2-4dc0-8d87-58b6b1636f8a", targetLocation: "settings.xml")]){ sh "mvn clean install -Dmaven.test.skip=true --settings settings.xml" } } } stage('Docker階段'){ echo "Docker 階段" container('docker') { // 讀取pom參數 echo "讀取 pom.xml 參數" pom = readMavenPom file: './pom.xml' // 設置鏡像倉庫地址 hub = "registry.cn-shanghai.aliyuncs.com" // 設置倉庫項目名 project_name = "mydlq" echo "編譯 Docker 鏡像" docker.withRegistry("http://${hub}", "ffb3b544-108e-4851-b747-b8a00bfe7ee0") { echo "構建鏡像" // 設置推送到aliyun倉庫的mydlq項目下,並用pom裡面設置的項目名與版本號打標籤 def customImage = docker.build("${hub}/${project_name}/${pom.artifactId}:${pom.version}") echo "推送鏡像" customImage.push() echo "刪除鏡像" sh "docker rmi ${hub}/${project_name}/${pom.artifactId}:${pom.version}" } } } stage('Helm階段'){ container('helm-kubectl') { withKubeConfig([credentialsId: "8510eda6-e1c7-4535-81af-17626b9575f7",serverUrl: "https://kubernetes.default.svc.cluster.local"]) { // 設置參數 image = "image.repository=${hub}/${project_name}/${pom.artifactId}" tag = "image.tag=${pom.version}" template = "spring-boot" repo_url = "http://chart.mydlq.club" app_name = "${pom.artifactId}" // 檢測是否存在yaml文件 def values = "" if (fileExists('values.yaml')) { values = "-f values.yaml" } // 執行 Helm 方法 echo "Helm 初始化" helmDeploy(init: true ,url: "${repo_url}"); echo "Helm 執行部署測試" helmDeploy(init: false ,dry_run: true ,name: "${app_name}" ,namespace: "mydlqcloud" ,image: "${image}" ,tag: "${tag}" , values: "${values}" ,template: "${template}") echo "Helm 執行正式部署" helmDeploy(init: false ,dry_run: false ,name: "${app_name}" ,namespace: "mydlqcloud",image: "${image}" ,tag: "${tag}" , values: "${values}" ,template: "${template}") } } } } } }catch(Exception e) { currentBuild.result = "FAILURE" }finally { // 獲取執行狀態 def currResult = currentBuild.result ?: 'SUCCESS' // 判斷執行任務狀態,根據不同狀態發送郵件 stage('email'){ if (currResult == 'SUCCESS') { echo "發送成功郵件" emailext(subject: '任務執行成功',to: '32******7@qq.com',body: '''任務已經成功構建完成...''') }else { echo "發送失敗郵件" emailext(subject: '任務執行失敗',to: '32******7@qq.com',body: '''任務執行失敗構建失敗...''') } } } }