經過前面兩篇文章,咱們已經有了一個「嗷嗷待哺」的K8s集羣環境,也對相關的概念與組件有了一個基本瞭解(前期對概念有個印象便可,由於只有實踐了才能對其有深刻理解,所謂「紙上得來終覺淺,絕知此事要躬行」),本文從實踐角度介紹如何結合咱們經常使用的Gitlab與Jenkins,經過K8s來實現項目的自動化部署,示例將包括基於SpringBoot的服務端項目與基於Vue.js的Web項目。html
本文涉及到的工具與技術包括:java
環境背景:linux
預期效果:nginx
首先咱們須要在項目的根路徑中添加一些必要的配置文件,以下圖所示git
包括:web
在項目根目錄中添加一個Dockerfile文件(文件名就叫Dockerfile),定義如何構建Docker鏡像,以Spring Boot項目爲例,spring
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 來進行動態設定,以知足不一樣環境的鏡像構建要求。docker
SPRING_PROFILES_ACTIVE本能夠在Docker容器啓動時經過
docker run -e SPRING_PROFILES_ACTIVE=xxx
來設定,因這裏使用Helm進行部署不直接經過docker run
運行,所以經過ARG在鏡像構建時指定shell
Helm是Kubernetes的包管理工具,將應用部署相關的Deployment,Service,Ingress等打包進行發佈與管理(能夠像Docker鏡像同樣存儲於倉庫中)。如上圖中Helm的配置文件包括:npm
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名稱(相似安裝包名),如
apiVersion: v2 name: your-chart-name description: A Helm chart for Kubernetes type: application version: 1.0.0 appVersion: 1.16.0
在values.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)
# 登陸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是Jenkins pipeline配置文件,遵循Groovy語法,對於Spring Boot項目的構建部署, 編寫Jenkinsfile腳本文件以下,
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定義了整個自動化構建部署的流程:
在Jenkins中建立一個pipeline的任務,如圖
配置構建觸發器,將目標分支設置爲develop分支,生成一個token,如圖
記下這裏的「GitLab webhook URL」及token值,在Gitlab配置中使用。
配置流水線,選擇「Pipeline script from SCM」從項目源碼中獲取pipeline腳本文件,配置項目Git地址,拉取源碼憑證等,如圖
保存即完成了項目開發環境的Jenkins配置。測試環境只需將對應的分支修改成pre-release便可
在Jenkinsfile文件中,咱們使用到了兩個訪問憑證——Docker Registry憑證與本地K8s的kube憑證,
DOCKER_REGISTER_CREDS = credentials('aliyun-docker-repo-creds') //docker registry憑證 KUBE_CONFIG_LOCAL = credentials('local-k8s-kube-config') //開發測試環境的kube憑證
這兩個憑證須要在Jenkins中建立。
添加Docker Registry登陸憑證,在Jenkins 憑據頁面,添加一個用戶名密碼類型的憑據,如圖
添加K8s集羣的訪問憑證,在master節點上將/root/.kube/config文件內容進行base64編碼,
base64 /root/.kube/config > kube-config-base64.txt cat kube-config-base64.txt
使用編碼後的內容在Jenkins中建立一個Secret text類型的憑據,如圖
在Secret文本框中輸入base64編碼後的內容。
在Gitlab項目的 Settings - Integrations 頁面配置一個webhook,在URL與Secret Token中填入前面Jenkins觸發器部分的「GitLab webhook URL」及token值,選中「Push events」做爲觸發事件,如圖
開發、測試環境選擇「Push events」則在開發人員push代碼,或merge代碼到develop,pre-release分支時,就會觸發開發或測試環境的Jenkins pipeline任務完成自動化構建;生產環境選擇「Tag push events」,在往master分支push tag時觸發自動化構建。如圖爲pipeline構建視圖
本文介紹使用Gitlab+Jenkins Pipeline+Docker+Kubernetes+Helm來實現Spring Boot項目的自動化部署,只要稍加修改便可應用於其它基於Spring Boot的項目(具體修改的地方在源碼的Readme文件中說明)。
本文涉及的全部配置文件(包括基於Spring Boot的服務端項目與基於Vue.js的Web項目)可在源碼項目中獲取(源碼地址獲取辦法:關注公衆號「半路雨歌」,首頁輸入「k8sops」便可)。
原文地址:http://blog.jboost.cn/k8s3-cd.html
做者:雨歌
歡迎關注做者微信公衆號:半路雨歌,一塊兒學習成長