1. 程式人生 > >Kubernetes筆記(三):Gitlab+Jenkins Pipeline+Docker+k8s+Helm自動化部署實踐(乾貨分享!)

Kubernetes筆記(三):Gitlab+Jenkins Pipeline+Docker+k8s+Helm自動化部署實踐(乾貨分享!)

通過前面兩篇文章,我們已經有了一個“嗷嗷待哺”的K8s叢集環境,也對相關的概念與元件有了一個基本瞭解(前期對概念有個印象即可,因為只有實踐了才能對其有深入理解,所謂“紙上得來終覺淺,絕知此事要躬行”),本文從實踐角度介紹如何結合我們常用的Gitlab與Jenkins,通過K8s來實現專案的自動化部署,示例將包括基於SpringBoot的服務端專案與基於Vue.js的Web專案。 本文涉及到的工具與技術包括: - Gitlab —— 常用的原始碼管理系統 - Jenkins, Jenkins Pipeline —— 常用的自動化構建、部署工具,Pipeline以流水線的方式將構建、部署的各個步驟組織起來 - Docker,Dockerfile —— 容器引擎,所有應用最終都要以Docker容器執行,Dockerfile是Docker映象定義檔案 - Kubernetes —— Google開源的容器編排管理系統 - Helm —— Kubernetes的包管理工具,類似Linux的yum,apt,或Node的npm等包管理工具,能將Kubernetes中的應用及相關依賴服務以包(Chart)的形式組織管理 環境背景: 1. 已使用Gitlab做原始碼管理,原始碼按不同的環境建立了develop(對應開發環境),pre-release(對應測試環境),master(對應生產環境)分支 2. 已搭建了Jenkins服務 3. 已有Docker Registry服務,用於Docker映象儲存(基於Docker Registry或Harbor自建,或使用雲服務,本文使用阿里雲容器映象服務) 4. 已搭建了K8s叢集 預期效果: 1. 分環境部署應用,開發環境、測試環境、生產環境分開來,部署在同一叢集的不同namespace,或不同叢集中(比如開發測試部署在本地叢集的不同namespace中,生產環境部署在雲端叢集) 2. 配置儘可能通用化,只需要通過修改少量配置檔案的少量配置屬性,就能完成新專案的自動化部署配置 3. 開發測試環境在push程式碼時自動觸發構建與部署,生產環境在master分支上新增版本tag並且push tag後觸發自動部署 4. 整體互動流程如下圖 ![jenkins-cicd](https://img2020.cnblogs.com/other/632381/202005/632381-20200512093659093-1245205963.png) ## 專案配置檔案 首先我們需要在專案的根路徑中新增一些必要的配置檔案,如下圖所示 ![springboot-ci-structure](https://img2020.cnblogs.com/other/632381/202005/632381-20200512093659586-736117565.png) 包括: 1. Dockerfile檔案,用於構建Docker映象的檔案(參考 [Docker筆記(十一):Dockerfile詳解與最佳實踐](http://blog.jboost.cn/docker-11.html)) 2. Helm相關配置檔案,Helm是Kubernetes的包管理工具,可以將應用部署相關的Deployment,Service,Ingress等打包進行釋出與管理(Helm的具體介紹我們後面再補充) 3. Jenkinsfile檔案,Jenkins的pipeline定義檔案,定義了各個階段需執行的任務 ### Dockerfile 在專案根目錄中新增一個Dockerfile檔案(檔名就叫Dockerfile),定義如何構建Docker映象,以Spring Boot專案為例, ``` FROM frolvlad/alpine-java:jdk8-slim #在build映象時可以通過 --build-args profile=xxx 進行修改 ARG profile ENV SPRING_PROFILES_ACTIVE=${profile} #專案的埠 EXPOSE 8000 WORKDIR /mnt #修改時區 RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories \ && apk add --no-cache tzdata \ && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ && echo "Asia/Shanghai" > /etc/timezone \ && apk del tzdata \ && rm -rf /var/cache/apk/* /tmp/* /var/tmp/* $HOME/.cache COPY ./target/your-project-name-1.0-SNAPSHOT.jar ./app.jar ENTRYPOINT ["java", "-jar", "/mnt/app.jar"] ``` 將SPRING_PROFILES_ACTIVE通過引數profile暴露出來,在構建的時候可以通過 --build-args profile=xxx 來進行動態設定,以滿足不同環境的映象構建要求。 > SPRING_PROFILES_ACTIVE本可以在Docker容器啟動時通過`docker run -e SPRING_PROFILES_ACTIVE=xxx`來設定,因這裡使用Helm進行部署不直接通過`docker run`執行,因此通過ARG在映象構建時指定 ### Helm配置檔案 Helm是Kubernetes的包管理工具,將應用部署相關的Deployment,Service,Ingress等打包進行釋出與管理(可以像Docker映象一樣儲存於倉庫中)。如上圖中Helm的配置檔案包括: ```shell helm - chart包的目錄名 ├── templates - k8s配置模版目錄 │ ├── deployment.yaml - Deployment配置模板,定義如何部署Pod │ ├── _helpers.tpl - 以下劃線開頭的檔案,helm視為公共庫定義檔案,用於定義通用的子模版、函式、變數等 │ ├── ingress.yaml - Ingress配置模板,定義外部如何訪問Pod提供的服務,類似於Nginx的域名路徑配置 │ ├── NOTES.txt - chart包的幫助資訊檔案,執行helm install命令成功後會輸出這個檔案的內容 │ └── service.yaml - Service配置模板,配置訪問Pod的服務抽象,有NodePort與ClusterIp等 |── values.yaml - chart包的引數配置檔案,各模版檔案可以引用這裡的引數 ├── Chart.yaml - chart定義,可以定義chart的名字,版本號等資訊 ├── charts - 依賴的子包目錄,裡面可以包含多個依賴的chart包,一般不存在依賴,我這裡將其刪除了 ``` 我們可以在Chart.yaml中定義每個專案的chart名稱(類似安裝包名),如 ```yaml apiVersion: v2 name: your-chart-name description: A Helm chart for Kubernetes type: application version: 1.0.0 appVersion: 1.16.0 ``` 在values.yaml中定義模板檔案中需要用到的變數,如 ```yaml #部署Pod的副本數,即執行多少個容器 replicaCount: 1 #容器映象配置 image: repository: registry.cn-hangzhou.aliyuncs.com/demo/demo pullPolicy: Always # Overrides the image tag whose default is the chart version. tag: "dev" #映象倉庫訪問憑證 imagePullSecrets: - name: aliyun-registry-secret #覆蓋啟動容器名稱 nameOverride: "" fullnameOverride: "" #容器的埠暴露及環境變數配置 container: port: 8000 env: [] #ServiceAccount,預設不建立 serviceAccount: # Specifies whether a service account should be created create: false # Annotations to add to the service account annotations: {} name: "" podAnnotations: {} podSecurityContext: {} # fsGroup: 2000 securityContext: {} # capabilities: # drop: # - ALL # readOnlyRootFilesystem: true # runAsNonRoot: true # runAsUser: 1000 #使用NodePort的service,預設為ClusterIp service: type: NodePort port: 8000 #外部訪問Ingress配置,需要配置hosts部分 ingress: enabled: true annotations: {} # kubernetes.io/ingress.class: nginx # kubernetes.io/tls-acme: "true" hosts: - host: demo.com paths: ["/demo"] tls: [] # - secretName: chart-example-tls # hosts: # - chart-example.local #.... 省略了其它預設引數配置 ``` 這裡在預設生成的基礎上添加了container部分,可以在這裡指定容器的埠號而不用去改模板檔案(讓模板檔案在各個專案通用,通常不需要做更改),同時新增env的配置,可以在helm部署時往容器裡傳入環境變數。將Service type從預設的ClusterIp改為了NodePort。部署同類型的不同專案時,只需要根據專案情況配置Chart.yaml與values.yaml兩個檔案的少量配置項,templates目錄下的模板檔案可直接複用。 部署時需要在K8s環境中從Docker映象倉庫拉取映象,因此需要在K8s中建立映象倉庫訪問憑證(imagePullSecrets) ```shell # 登入Docker Registry生成/root/.docker/config.json檔案 sudo docker login --username=your-username registry.cn-shenzhen.aliyuncs.com # 建立namespace develop(我這裡是根據專案的環境分支名稱建立namespace) kubectl create namespace develop # 在namespace develop中建立一個secret kubectl create secret generic aliyun-registry-secret --from-file=.dockerconfigjson=/root/.docker/config.json --type=kubernetes.io/dockerconfigjson --namespace=develop ``` ### Jenkinsfile Jenkinsfile是Jenkins pipeline配置檔案,遵循Groovy語法,對於Spring Boot專案的構建部署, 編寫Jenkinsfile指令碼檔案如下, ```groovy image_tag = "default" //定一個全域性變數,儲存Docker映象的tag(版本) pipeline { agent any environment { GIT_REPO = "${env.gitlabSourceRepoName}" //從Jenkins Gitlab外掛中獲取Git專案的名稱 GIT_BRANCH = "${env.gitlabTargetBranch}" //專案的分支 GIT_TAG = sh(returnStdout: true,script: 'git describe --tags --always').trim() //commit id或tag名稱 DOCKER_REGISTER_CREDS = credentials('aliyun-docker-repo-creds') //docker registry憑證 KUBE_CONFIG_LOCAL = credentials('local-k8s-kube-config') //開發測試環境的kube憑證 KUBE_CONFIG_PROD = "" //credentials('prod-k8s-kube-config') //生產環境的kube憑證 DOCKER_REGISTRY = "registry.cn-hangzhou.aliyuncs.com" //Docker倉庫地址 DOCKER_NAMESPACE = "your-namespace" //名稱空間 DOCKER_IMAGE = "${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/${GIT_REPO}" //Docker映象地址 INGRESS_HOST_DEV = "dev.your-site.com" //開發環境的域名 INGRESS_HOST_TEST = "test.your-site.com" //測試環境的域名 INGRESS_HOST_PROD = "prod.your-site.com" //生產環境的域名 } parameters { string(name: 'ingress_path', defaultValue: '/your-path', description: '服務上下文路徑') string(name: 'replica_count', defaultValue: '1', description: '容器副本數量') } stages { stage('Code Analyze') { agent any steps { echo "1. 程式碼靜態檢查" } } stage('Maven Build') { agent { docker { image 'maven:3-jdk-8-alpine' args '-v $HOME/.m2:/root/.m2' } } steps { echo "2. 程式碼編譯打包" sh 'mvn clean package -Dfile.encoding=UTF-8 -DskipTests=true' } } stage('Docker Build') { agent any steps { echo "3. 構建Docker映象" echo "映象地址: ${DOCKER_IMAGE}" //登入Docker倉庫 sh "sudo docker login -u ${DOCKER_REGISTER_CREDS_USR} -p ${DOCKER_REGISTER_CREDS_PSW} ${DOCKER_REGISTRY}" script { def profile = "dev" if (env.gitlabTargetBranch == "develop") { image_tag = "dev." + env.GIT_TAG } else if (env.gitlabTargetBranch == "pre-release") { image_tag = "test." + env.GIT_TAG profile = "test" } else if (env.gitlabTargetBranch == "master"){ // master分支則直接使用Tag image_tag = env.GIT_TAG profile = "prod" } //通過--build-arg將profile進行設定,以區分不同環境進行映象構建 sh "docker build --build-arg profile=${profile} -t ${DOCKER_IMAGE}:${image_tag} ." sh "sudo docker push ${DOCKER_IMAGE}:${image_tag}" sh "docker rmi ${DOCKER_IMAGE}:${image_tag}" } } } stage('Helm Deploy') { agent { docker { image 'lwolf/helm-kubectl-docker' args '-u root:root' } } steps { echo "4. 部署到K8s" sh "mkdir -p /root/.kube" script { def kube_config = env.KUBE_CONFIG_LOCAL def ingress_host = env.INGRESS_HOST_DEV if (env.gitlabTargetBranch == "pre-release") { ingress_host = env.INGRESS_HOST_TEST } else if (env.gitlabTargetBranch == "master"){ ingress_host = env.INGRESS_HOST_PROD kube_config = env.KUBE_CONFIG_PROD } sh "echo ${kube_config} | base64 -d > /root/.kube/config" //根據不同環境將服務部署到不同的namespace下,這裡使用分支名稱 sh "helm upgrade -i --namespace=${env.gitlabTargetBranch} --set replicaCount=${params.replica_count} --set image.repository=${DOCKER_IMAGE} --set image.tag=${image_tag} --set nameOverride=${GIT_REPO} --set ingress.hosts[0].host=${ingress_host} --set ingress.hosts[0].paths={${params.ingress_path}} ${GIT_REPO} ./helm/" } } } } } ``` Jenkinsfile定義了整個自動化構建部署的流程: 1. Code Analyze,可以使用SonarQube之類的靜態程式碼分析工具完成程式碼檢查,這裡先忽略 2. Maven Build,啟動一個Maven的Docker容器來完成專案的maven構建打包,掛載maven本地倉庫目錄到宿主機,避免每次都需要重新下載依賴包 3. Docker Build,構建Docker映象,並推送到映象倉庫,不同環境的映象通過tag區分,開發環境使用dev.commitId的形式,如dev.88f5822,測試環境使用test.commitId,生產環境可以將webhook事件設定為tag push event,直接使用tag名稱 4. Helm Deploy,使用helm完成新專案的部署,或已有專案的升級,不同環境使用不同的引數配置,如訪問域名,K8s叢集的訪問憑證kube_config等 ## Jenkins配置 ### Jenkins任務配置 在Jenkins中建立一個pipeline的任務,如圖 ![jenkins-pipeline-pro](https://img2020.cnblogs.com/other/632381/202005/632381-20200512093700032-2002054337.png) 配置構建觸發器,將目標分支設定為develop分支,生成一個token,如圖 ![jenkins-pipeline-config1](https://img2020.cnblogs.com/other/632381/202005/632381-20200512093700648-1924084613.png) 記下這裡的“GitLab webhook URL”及token值,在Gitlab配置中使用。 配置流水線,選擇“Pipeline script from SCM”從專案原始碼中獲取pipeline指令碼檔案,配置專案Git地址,拉取原始碼憑證等,如圖 ![jenkins-pipeline-config2.png](https://img2020.cnblogs.com/other/632381/202005/632381-20200512093701040-520628246.png) 儲存即完成了專案開發環境的Jenkins配置。測試環境只需將對應的分支修改為pre-release即可 ### Jenkins憑據配置 在Jenkinsfile檔案中,我們使用到了兩個訪問憑證——Docker Registry憑證與本地K8s的kube憑證, ```shell DOCKER_REGISTER_CREDS = credentials('aliyun-docker-repo-creds') //docker registry憑證 KUBE_CONFIG_LOCAL = credentials('local-k8s-kube-config') //開發測試環境的kube憑證 ``` 這兩個憑證需要在Jenkins中建立。 新增Docker Registry登入憑證,在Jenkins 憑據頁面,新增一個使用者名稱密碼型別的憑據,如圖 ![jenkins-cred](https://img2020.cnblogs.com/other/632381/202005/632381-20200512093701509-1165893843.png) ![jenkins-cred2](https://img2020.cnblogs.com/other/632381/202005/632381-20200512093701911-1623876891.png) 新增K8s叢集的訪問憑證,在master節點上將/root/.kube/config檔案內容進行base64編碼, ```shell base64 /root/.kube/config > kube-config-base64.txt cat kube-config-base64.txt ``` 使用編碼後的內容在Jenkins中建立一個Secret text型別的憑據,如圖 ![jenkins-cred3](https://img2020.cnblogs.com/other/632381/202005/632381-20200512093702213-1902664908.png) 在Secret文字框中輸入base64編碼後的內容。 ## Gitlab配置 在Gitlab專案的 Settings - Integrations 頁面配置一個webhook,在URL與Secret Token中填入前面Jenkins觸發器部分的“GitLab webhook URL”及token值,選中“Push events”作為觸發事件,如圖 ![gitlab-webhook-config](https://img2020.cnblogs.com/other/632381/202005/632381-20200512093702723-1699224277.png) 開發、測試環境選擇“Push events”則在開發人員push程式碼,或merge程式碼到develop,pre-release分支時,就會觸發開發或測試環境的Jenkins pipeline任務完成自動化構建;生產環境選擇“Tag push events”,在往master分支push tag時觸發自動化構建。如圖為pipeline構建檢視 ![jenkins-build](https://img2020.cnblogs.com/blog/632381/202005/632381-20200512095800290-664365933.png) ## 總結 本文介紹使用Gitlab+Jenkins Pipeline+Docker+Kubernetes+Helm來實現Spring Boot專案的自動化部署,只要稍加修改即可應用於其它基於Spring Boot的專案(具體修改的地方在原始碼的Readme檔案中說明)。 本文涉及的所有配置檔案(包括基於Spring Boot的服務端專案與基於Vue.js的Web專案)可在原始碼專案中獲取(原始碼地址獲取辦法:關注公眾號“半路雨歌”,首頁輸入“k8sops”即可)。 原文地址:http://blog.jboost.cn/k8s3-cd.html --- 作者:雨歌 歡迎關注作者微信公眾號:半路雨歌,一起學習成長 ![微信公眾號](https://img2020.cnblogs.com/other/632381/202005/632381-20200512093703534-1198697972.jpg)