本文檔不講解jenkins的基礎用法,主要講解配合k8s的插件,和pipeline的用途和k8s ci/cd的流程。
以及部署在k8s集群內和集群外的注意事項。
1.准備工作
以下在整個CI/CD流程中需要使用到的插件,可能有時候pipeline等插件沒安裝,這里不做記錄。
插件 | 版本 | 用途 |
Kubernetes | 1.18.1 | 1.Kubernetes集群中運行動態代理,簡單來說就是使用改插件運行jenkins slave鏡像 |
Kubernetes Continuous Deploy | 2.1.0 | 1.安裝這個插件之后才能部署創建k8s 類型的里連接密鑰. 然后默認會使用匿名用戶去訪問,所以eks想使用該插件,那么就要開放匿名用戶權限 |
Kubernetes Cli Plugin | 1.7.0 | 1.安裝這個插件后,可以創建又一種類型的k8s密鑰. 2.通過驗證后,可以執行k8s的查詢命令. |
Git Parameter Plug-In | 0.9.11 | 1.可以動態獲取git的分支和tag. |
Dynamic Parameter/Dynamic choice parameter | 0.1.1 | 1.安裝一個 dynamic Parameter,dynamic choice pararmeter會同時安裝. 2.dynamic choice parameter 是動態下拉框. 3.該插件在jenkins新版本中由於安全漏洞已下線,需要單獨去下載 4.插件列表中有3個版本,只有0.1.1能夠使用 |
jenkins | 2.176.2 | jenkins |
2.Pipeline插件
把pipeline放在第二章講解是為了熟悉一些插件的使用方式和怎么使用pipeline,在后面整個構建過程會更方便.
- Pipeline通過特定語法對簡單到復雜的傳輸管道進行建模;
聲明式:遵循與Groovy相同語法。pipeline { }
腳本式:支持Groovy大部分功能,也是非常表達和靈活的工具。node { }
- Jenkins Pipeline的定義被寫入一個文本文件,稱為Jenkinsfile。
pipeline有一點需要注意,和shell腳本執行方式不同,pipeline首先會檢查全部的語法,只有出錯一處就會提示錯誤.
2.1 簡單jenkinsfile案例
以下是一個簡單pipeline的CI/CD流程的案例.
從git拉取代碼 → mvn編譯 → 鏡像打包 → 部署至k8s → 執行kubectl命令
1 // 公共 2 def registry = "registry.ap-northeast-1.aliyuncs.com" 3 def git_address = "http://degitlab.mbgadev.cn/platform/resource.git" 4 5 // 項目 6 def project = "lcm-tw" 7 def app_name = "resource" 8 def image_name = "${registry}/${project}/${app_name}-${branch}:${BUILD_NUMBER}" 9 // 認證 10 def secret_name = "aliyun-hub-jp" 11 def docker_registry_auth = "f0bba775-b2b5-4376-a69e-b30265f21af9" 12 def git_auth = "74c9c446-430d-4398-90b6-e25604a62b0c" 13 def k8s_auth = "f50f35b2-8473-44e0-b750-16b8bed5b76c" 14 15 #重要主體代碼,需要做的操作都在node(){}里面增加stage(){}就可以 16 node(""){ 17 // 第一步 18 stage('拉取代碼'){ 19 checkout([$class: 'GitSCM', branches: [[name: '${branch}']], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_address}"]]]) 20 } 21 // 第二步 22 stage('代碼編譯'){ 23 sh "mvn clean" 24 sh "mvn compile" 25 sh "mvn package -DskipTests" 26 } 27 28 // 第三步 29 stage('構建鏡像'){ 30 withCredentials([usernamePassword(credentialsId: "${docker_registry_auth}", passwordVariable: 'password', usernameVariable: 'username')]) { 31 sh """ 32 33 cp ../config/logback.xml ./ 34 sed -i 's#\$namespace#${namespace}#' logback.xml 35 36 docker login -u ${username} -p '${password}' ${registry} 37 echo ' 38 FROM registry.ap-northeast-1.aliyuncs.com/dena/alpine-oraclejdk8:jst 39 WORKDIR /opt 40 COPY resource-srv/target/*.jar /opt/resource-srv.jar 41 COPY resource-srv/dist/bootstrap.yml /opt/ 42 COPY logback.xml /opt 43 EXPOSE 8080 44 ENTRYPOINT ["java","-Dlogging.config=logback.xml","-Xms1024m","-Xmx1024m","-jar","/opt/resource-srv.jar"] 45 ' > Dockerfile 46 echo ${env} 47 docker build -t ${image_name} . 48 docker push ${image_name} 49 """ 50 } 51 } 52 // 第四步 53 stage('部署到K8S平台'){ 54 sh """ 55 cp -rf ../config/${JOB_NAME}.yml ./ 56 pwd 57 sed -i 's#\$NAMESPACE#${namespace}#' ${JOB_NAME}.yml 58 sed -i 's#\$IMAGE_NAME#${image_name}#' ${JOB_NAME}.yml 59 sed -i 's#\$SECRET_NAME#${secret_name}#' ${JOB_NAME}.yml 60 """ 61 kubernetesDeploy configs: "${JOB_NAME}.yml", kubeconfigId: "${k8s_auth}" 62 } 63 64 //第五步 65 stage('查看pod狀態及最后100行日志'){ 66 67 withKubeConfig(caCertificate: '', contextName: '', credentialsId: '46d2adc5-6c00-4988-a84a-26b83e44c62c', namespace: 'public', serverUrl: 'https://1E73E810A71671022BC2EB1A4F79C434.sk1.ap-northeast-1.eks.amazonaws.com') { 68 sh """ 69 sleep 60; 70 echo '============查看當前pod狀態============'; 71 kubectl get pod -n ${namespace} | grep ${JOB_NAME}; 72 echo '============查看當前deploy狀態============'; 73 kubectl get deploy -n ${namespace} | grep ${JOB_NAME}; 74 for i in `kubectl get pod -n ${namespace} | grep ${JOB_NAME} | awk '{print \$1}' | tail -1`;do echo "----------------------------------------------------------\$i----------------------------------------------------------";kubectl logs --tail 100 \$i -n test ;done 75 """ 76 } 77 } 78 }
2.2 憑據
參照2.1章節,12行為git_auth鑒權id
12. def git_auth = "74c9c446-430d-4398-90b6-e25604a62b0c"
1.點擊jenkins→憑據→憑據(jenkins)憑據→點擊全局憑據→左側欄添加憑據
輸入用戶名和密碼.
由於在創建的時候是看不到密鑰id的,所以在創建完成后重新編輯密鑰可以看到生成的密鑰ID
點擊更新就可以看到了
2.3 Git
17-19為pipeline怎么使用git插件(當然也可以用SVN)
17. stage('拉取代碼'){ 18. checkout([$class: 'GitSCM', branches: [[name: '${branch}']], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_address}"]]]) 19. }
1.找到jenkins pipeline流水線語法
這個鏈接在一個pipeline任務里面才會有
進入pipeline的測試界面
2.演示git pipeline中的使用方式
然后點擊“生成流水線腳本”
由於這里直接生成的腳本參數比較多,還有很多默認值,而且還有比如地址,git密鑰都可以替換成為變量的方式,所以,這里稍作更改.
所以我們可以刪除多余的參數,然后將一些我們輸入的參數替換成為變量.
17. stage('拉取代碼'){ 18. #三個參數,分支,git密鑰,git地址全部替換成為變量 19。 checkout([$class: 'GitSCM', branches: [[name: '${branch}']], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_address}"]]]) 20. }
2.4 Docker login
案例27-50行的過程是鏡像打包,如果設置鏡像打包以及推送至倉庫,那么就需要登錄docker倉庫,那么明文的密碼不夠安全,所以該插件可以將明文包裹成為密文配合在jenkins中使用.
27. // 第三步 28. stage('構建鏡像'){ 29. withCredentials([usernamePassword(credentialsId: "${docker_registry_auth}", passwordVariable: 'password', usernameVariable: 'username')]) { 30. sh """ 31. 32. cp ../config/logback.xml ./ 33. sed -i 's#\$namespace#${namespace}#' logback.xml 34. 35. docker login -u ${username} -p '${password}' ${registry} 36. echo ' 37. FROM registry.ap-northeast-1.aliyuncs.com/dena/alpine-oraclejdk8:jst 38. WORKDIR /opt 39. COPY resource-srv/target/*.jar /opt/resource-srv.jar 40. COPY resource-srv/dist/bootstrap.yml /opt/ 41. COPY logback.xml /opt 42. EXPOSE 8080 43. ENTRYPOINT ["java","-Dlogging.config=logback.xml","-Xms1024m","-Xmx1024m","-jar","/opt/resource-srv.jar"] 44. ' > Dockerfile 45. echo ${env} 46. docker build -t ${image_name} . 47. docker push ${image_name} 48. """ 49. } 50. }
1.創建docker login憑證
添加憑證方式和添加git憑證一樣,添加一個docker的密鑰,這里不需要輸入地址,等會由插件把這個id轉換為變量,那么就是密文了.
2.演示docker login pipeline中的語法
新增一個值,將剛才的保存的docker密鑰轉換成為 username和password2個變量
然后生成流水線語法
3.引用這個2個變量
在代碼的35行引用這2個變量
35. docker login -u ${username} -p '${password}' ${registry}
2.5 Kubernetes(Jenkins slave)
到這里,大多情況,就需要去安裝插件了.
官方插件鏈接: https://github.com/jenkinsci/kubernetes-plugin
本插件是連接k8s集群配合kubernetes Continuous Deploy來使用,使用這個插件才能新增一個雲類型,去配置連接k8s.
1.安裝kubernetes
Jenkins→系統管理→插件管理→可選插件
安裝完成就回出現podTemplate語法和系統管理的新建雲。
語法:
新建雲:
系統管理→系統管理
詳細插件的用法會在jenkins slave篇章講解。
2.6 Kubernetes Continuous Deploy
本插件需要注意,由於該插件會去讀取kubeconfig去訪問k8s集群,如果是eks集群,aws是自行開發的認證插件,所以該插件無法讀取出aws eks的 kubeconfig的用戶,從而去使用匿名用戶訪問。
以下是添加密鑰完成后給的提示,該插件沒有去使用這個密鑰。
所以這里需要開啟匿名的訪問權限,由於匿名用戶訪問沒有安全性,所以建議k8s集群使用防火牆限制訪問。
kubectl create clusterrolebinding cluster-system-anonymous --clusterrole=cluster-admin --user=system:anonymous
案例52-61行是使用此插件推送資源至目標k8s集群,kubernetes去連接k8s,但是推送資源需要本插件,本章核心主要就是59行代碼
51. stage('部署到K8S平台'){ 52. sh """ 53. cp -rf ../config/${JOB_NAME}.yml ./ 54. pwd 55. sed -i 's#\$NAMESPACE#${namespace}#' ${JOB_NAME}.yml 56. sed -i 's#\$IMAGE_NAME#${image_name}#' ${JOB_NAME}.yml 57. sed -i 's#\$SECRET_NAME#${secret_name}#' ${JOB_NAME}.yml 58. """ 59. kubernetesDeploy configs: "${JOB_NAME}.yml", kubeconfigId: "${k8s_auth}" 60. }
1.安裝kubernetes Continuous Deploy
Jenkins→系統管理→插件管理→可選插件
安裝完成后等待重啟
2.輸入k8s憑證
只有安裝了該插件,才會有k8s的憑證類型
該憑證一般在以下路徑
3.演示 Kubernetes continuous deploy在pipiline中的用法
生成流水線流水線腳本
然后我們刪除默認,把值替換成為變量,就有了59行代碼
59. kubernetesDeploy configs: "${JOB_NAME}.yml", kubeconfigId: "${k8s_auth}"
注意:這里只有2個參數,而沒有k8s地址的信息,怎么將該資源推送至k8s集群?
因為在k8s_auth的信息里面已經有了k8s集群的地址信息。
2.6.1 推送失敗
在k8s1.16版本,jenkins以及插件有了很多更新,此時jenkins最新版本為 2.235.5,這時,使用kubernetes插件,一直會有以下報錯.
以下是網絡上的解決辦法(本人未測試)
當jenkins為2.235.5時,jackson2 版本為2.11.2,snakeyaml 為1.26.4,k8s為1.16以上時,那么只有回滾插件版本才能解決。
2.7 Kubernetes Cli
插件官方鏈接: https://github.com/jenkinsci/kubernetes-cli-plugin
這一個插件和上一個不太相同,Kubernetes Continuous deploy的作用是讓插件去調用k8s的api完成工作,該插件是獲取k8s集群的權限,使用命令去訪問k8s集群。
代碼78-89講解了如何使用Cli 執行k8s的命令操作k8s集群,主要是80行代碼
79. stage('查看pod狀態及最后100行日志'){ 80. 81. withKubeConfig(caCertificate: '', contextName: '', credentialsId: '46d2adc5-6c00-4988-a84a-26b83e44c62c', namespace: 'public', serverUrl: 'https://1E73E810A71671022BC2EB1A4F79C434.sk1.ap-northeast-1.eks.amazonaws.com') { 82. sh """ 83. sleep 60; 84. echo '============查看當前pod狀態============'; 85. kubectl get pod -n ${namespace} | grep ${JOB_NAME}; 86. echo '============查看當前deploy狀態============'; 87. kubectl get deploy -n ${namespace} | grep ${JOB_NAME}; 88. for i in `kubectl get pod -n ${namespace} | grep ${JOB_NAME} | awk '{print \$1}' | tail -1`;do echo "----------------------------------------------------------\$i----------------------------------------------------------";kubectl logs --tail 100 \$i -n test ;done 89. """ 90. }
1.安裝kubernetes Cli
Jenkins→系統管理→插件管理→可選插件
2.輸入k8s憑證
只有安裝了該插件,才會有另一種 k8s的憑證類型,該憑據是secret密鑰的類型,安裝這個插件,在pipeline語法生成器的時候選擇密鑰才會顯示出來,或者拷貝別人的代碼然后把密鑰id粘貼過去,這里為了走一個完整流程,所以這里多了一句嘴。
獲取secret密鑰,該密鑰簡單來說就是k8s serveraccount的token,所以會有以下步驟,
以下就是就是創建一個jenkins的server,然后與集群有權限的角色綁定,然后在獲取他的token.
kubectl -n public create serviceaccount jenkins-robot kubectl -n public create clusterrolebinding jenkins-robot-binding --clusterrole=cluster-admin --serviceaccount=public:jenkins-robot kubectl -n public get serviceaccount jenkins-robot -o go-template --template='{{range .secrets}}{{.name}}{{"\n"}}{{end}}' kubectl -n public get secrets jenkins-robot-token-rwvkv -o go-template --template '{{index .data "token"}}' | base64 -d
密鑰名根據實際情況做調整
然后將token復制到jenkins的secret位置
這樣,pipeline語法的時候就有這個密鑰可以選擇了。
3.演示 Kubernetes Cli在pipiline中的用法
然后生成流水線腳本
基本上保留憑據id和serverurl就可以連接至k8s集群了
withKubeConfig(caCertificate: '', contextName: '', credentialsId: '46d2adc5-6c00-4988-a84a-26b83e44c62c', namespace: 'public', serverUrl: 'https://1E73E810A71671022BC2EB1A4F79C434.sk1.ap-northeast-1.eks.amazonaws.com') {}
4.報錯提示
一般出現以上報錯,要么就是密鑰沒有正確,要么就是k8s地址沒有正確,一般這里都是細節問題沒有改好
2.8 Git Parameter Plug-In
該插件不做詳細講解,由於該插件安裝之后是一個多選框,而不是下拉框,所以這里只是記錄一些注意事項。
該插件會有如下不美觀幾個問題。
1. 獲取到的前端顯示為多選框,不美觀.
2. 獲取的分支帶有origin,配合本文檔的pipeline,pipeline需要修改.
3. git認證問題,這里無法輸入密碼,要么在圖形git插件認證后,要么使用pipeline的如下代碼認證:
checkout([$class: 'GitSCM', branches: [[name: '${branch}']], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_address}"]]])
2.9 Dynamic Parameter
2.9.1 插件安裝
1.先下載插件hpi文件到本地
jenkins插件下載地址
http://mirror.xmission.com/jenkins/plugins/
http://updates.jenkins-ci.org/download/plugins/
dynamicparameter下載地址:
http://mirror.xmission.com/jenkins/plugins/dynamicparameter/
依賴插件 role-strategy,同樣參照下面方法上傳插件(經過測試該版本不依賴),可能由於插件不在更新,Jenkins為更新版的緣故,但是改jenkins版本,只有0.1.1能夠使用.
2.手動上傳插件
目錄:jenkins->插件管理->高級->高級->上傳插件
安裝成功后,會顯示藍色的圖標以及完成,如果失敗是紅色的,會顯示詳細信息
然后去jenkins job的配置里,添加參數可以看到新增的Dynamic Parameter插件
2.9.2 插件使用
Dynamic Parameter使用groovy語言
Name中為變量名,Choices Script為腳本內容,最后一個變量值賦給定義的變量
注意:
Dynamic Choice Parameter是一個選擇列表,需要定義的變量是一個列表型
Dynamic Parameter是一個字符串,需要定義的變量是一個字符串
最終展示的Dynamic Choice Parameter和Dynamic Parameter情況如下:
3.Jenkins slave
3.1 簡介之kubernetes的詳細使用
由於jenkins特性的原因,會導致jenkins-slave重新拉取代碼,所以這一篇章只是演示jenkins-slave怎么使用,具體需不需要使用根據實際環境定義.
從上一章可以看到,Jenkins使用pipeline運行任務腳本的主體是node(){},然后在里面執行各個步驟stage('拉取代碼'){ }等,所以一個簡單的pipeline就是以下:
node(''){ stage('pipetest'){ sh """ echo "pipetest" """ } }
在第二章節里面可以看出,這是一個整體pipeline流程,也就是一個cd/cd的流程,但是,所有的一些都是由jenkins master來執行的,如果想只要jenkins master來管理任務分配,其余的去生成一個job的容器去處理任務處理完成銷毀,那么就需要jenkins slave了。
這里就需要用到kubernetes插件,因為有了這個插件才會有語法。
具體如下圖。
由於以上圖形操作使用比較復雜,所以這里,直接使用現有的配置做更改。(調試語法才推薦使用圖形)
1. #注意,這里因為是文本,所以注釋使用#,在pipeline里面注釋使用// 2. #這里的label要與node()這里的對應,不然無法匹配到jenkins slave的任務 3. #cloud算是一些默認配置,namespace最好填寫jenkisn Master所在的namespace,這里隨便哪里都行,只要jenkins-slave能訪問jenkins-master,建議和jenkins master在同一空間下。這里的命名空間就是這個slave將要運行的空間 4. podTemplate(label: 'jenkins-slave', cloud: 'kubernetes', namespace: 'public',containers: [ 5. containerTemplate( 6. #這個name必須是jnlp,不然創建jenkins slave的時候無法匹配到 7. name: 'jnlp', 8. #這是自定義的jenkins slave鏡像,里面安裝了maven git kubectl等工具 9. image: "lizexiong/jenkins-slave-dena-jdk", 10. #總是獲取新鏡像,因為有時候需要自動去更新 11. alwaysPullImage: true, 12. #jenkins slave的資源限制 13. resourceLimitCpu: '500m', resourceLimitMemory: '1024Mi', 14. resourceRequestCpu: '200m', resourceRequestMemory: '218Mi' 15. ), 16. ], 17. volumes: [ 18. #掛在本地docker文件,讓jenkins slave可以執行docker命令 19. hostPathVolume(mountPath: '/var/run/docker.sock', hostPath: '/var/run/docker.sock'), 20. hostPathVolume(mountPath: '/usr/bin/docker', hostPath: '/usr/bin/docker') 21. ], 22. ) 23. 24. 25. #pipeline執行的主體,ci/cd流程就在這里 26. { 27. #這里的名稱一定要與podTemplate里面的label對應上,否則無法關聯到任務 28. node('jenkins-slave'){ 29. stage('測試jenkins slave'){ 30. sh """ 31. echo "jenkins slave結束后銷毀" 32. """ 33. } 34. } 35. }
Jenkins slave通過 Jenkins master的50000端口來通信,所以要開啟這個端口;
在系統管理→全局安全設置→代理
以上是構建jenkins slave的pipeline的基礎框架,但是部署jenkins master分為在集群內和集群外,
所以延伸出2種方式,Jenkins master是部署在集群內還是集群外。以下講解。
(另外,k8s也分很多種雲平台,比如eks,認證是自己開發出來的插件,可能和現在的插件無法完全契合)
3.2 EKS部署
3.2.1 Jenkins master鏡像制作
1. FROM lizexiong/alpine-oraclejdk8:cst 2. #ADD讓tomcat文件夾下的會拷貝到目標目錄,而不是直接把文件夾拷貝過去了 3. ADD tomcat/ /usr/local/tomcat/ 4. RUN rm -rf /usr/local/tomcat/webapps/* 5. RUN apk add git 6. RUN apk add maven 7. RUN apk add libltdl 8. RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime 9. #配置k8s cli插件,可以執行kubectl命令 10. ADD kubectl /bin/ 11. ADD jenkins /usr/local/tomcat/webapps/ROOT/ 12. #自定義了maven 私有倉庫的地址 13. ADD settings.xml /root/.m2/ 14. #WORKDIR /usr/local/tomcat/bin 15. EXPOSE 8080 16. #jenkins slave使用的agent的端口 17. EXPOSE 50000 18. #ENTRYPOINT /usr/local/tomcat/bin/startup.sh && tail -f /usr/local/tomcat/logs/catalina.out 19. ENTRYPOINT ["/usr/local/tomcat/bin/catalina.sh","run"]
3.2.2 Jenkins master K8s yml配置
由於服務都是通用模版,這里也僅貼出開放的一些端口和磁盤掛載的方式
1. ports: 2. - containerPort: 8080 3. #jenkins slave agent的端口 4. - containerPort: 50000 5. volumeMounts: 6. #因為在docker里面,所以做了數據系統分離,使用nfs保存jenkins數據 7. - mountPath: "/opt/jenkins/" 8. name: jenkins-data 9. #以下2個掛載是為了可以執行docker命令 10. - mountPath: /var/run/docker.sock 11. name: docker-sock 12. - mountPath: /usr/bin/docker 13. name: docker-cli 14. volumes: 15. - name: docker-sock 16. hostPath: 17. path: /var/run/docker.sock 18. - name: docker-cli 19. hostPath: 20. path: /usr/bin/docker 21. - name: jenkins-data 22. nfs: 23. path: /nfs/jenkins 24. server: 10.1.50.250
3.2.3 Jenkins slave鏡像制作
因為jenkins slave是動態生成的,用完銷毀,所以這里沒有k8s配置
1. #以下是jenkins slave默認的地址,可以根據這個鏡像構建自建需要的鏡像 2. #http://jenkins-test.mobage.cn/jenkins/jnlpJars/slave.jar 3. FROM lizexiong/alpine-oraclejdk8:cst 4. 5. #這里是一個jenkins的bug,在啟動的時候加入這個參數就能解決報錯,該報錯體現在maven編譯那里 6. ENV JAVA_OPTS="-Dorg.jenkinsci.plugins.durabletask.BourneShellScript.HEARTBEAT_CHECK_INTERVAL=86400" 7. RUN mkdir -p /usr/share/jenkins 8. RUN apk add curl 9. RUN apk add maven 10. RUN apk add git 11. RUN apk add libltdl 12. COPY slave.jar /usr/share/jenkins/slave.jar 13. COPY jenkins-slave /usr/bin/jenkins-slave 14. #不管怎么啟動,經過本人驗證,只有當有任務正常接入的時候jenkins-slave才會正常工作,否則會因為沒有持續輸出導致容器退出失敗 15. RUN chmod +x /usr/bin/jenkins-slave 16. ADD settings.xml /root/.m2/ 17. #配置k8s cli插件,可以執行kubectl命令 18. ADD kubectl /bin/ 19. ENTRYPOINT ["jenkins-slave"]
3.2.4 新建雲
安裝kubernetes之后,在系統管理界面會出現一個新建雲的方式,在podTemplate里面指定的要和這里的雲對應,如果不新建一個雲,那么就會有以下報錯.
所以說,新建雲和podTemplate是聯合使用的.
podTemplate是jenkins-slave的信息。
新建雲是明確k8s地址和jenkins master地址的作用。
1.新建雲,系統管理→系統設置,下拉到最下面.
以下主要是各個地方命名空間的問題,只要把握住,不管是podTempalte里面填寫的命名空間還是新建雲里面填寫的,只要jenkins-slave能連接jenkins-master,那就沒有問題。
3.2.5 Jenkins-slave的訪問權限
在運行job之前,可能是使用的default的serviceaccount,那么這個jenkins-slave無法去調用k8s的資源類型,會有以下報錯。(AWSEKS里面是這樣)
{ "timestamp":"2018-12-05T08:18:31.503+0000", "status":500, "error":"Internal Server Error", "message":"Failure executing: GET at: https://kubernetes.default.svc/api/v1/namespaces/default/services. Message: Forbidden!Configured service account doesn't have access. Service account may have been revoked. services is forbidden: User "system:serviceaccount:default:default" cannot list resource "services" in API group "" in the namespace "default".", "path":"/services"}
那么如果不去指定jenkins-slave的servicesaaccount的話,那么用以下簡單的方法來處理就可以,提升全部用戶為管理員權限(不建議生產環境使用)
kubectl create clusterrolebinding permissive-binding \ --clusterrole=cluster-admin \ --user=admin \ --user=kubelet \ --group=system:serviceaccounts
3.2.6 運行一個job
以上工作完成后,我們運行一個簡單的示例看看是否能正常運行jenkins-slave.
出現以下jenkins-slave就算構建成功了
3.2.7 Jenkins slave報錯解決
Jenkins slave無法連接jenkins master
以下報錯是jenkins在 public空間,但是jenkins slave被部署到了prod空間,slave找不到jenkins master,所以報錯了,解決方式很簡單,只要保證jenkins slave能連接到jenkins master就行了.
3.3 宿主機部署
由於每個雲平台認證插件不太相同,所以一下列出每個平台的對比
3.3.1 宿主機部署推送至EKS
由於宿主機部署jenkins master這里不做講解,而jenkins slave的鏡像制作和pipeline和在k8s集群中完全一樣,所以這里只寫出不同的地方,而不同的地方只有一個。
由於認證方式不同,無法獲取eks的訪問憑證,所以這里直接使用匿名來訪問,並且禁用https的檢查。
這樣的方式可能不太安全,但是截至2019年8月24日 15:47:43 jenkins密鑰插件還不能完美的配置eks集群。
以上這一點不同之處完成,就可以在eks中構建jenkins slave了。
3.3.2 宿主機部署推送至阿里雲
4.持續部署持續交付
本章節講解完全的一套ci/cd流程,包括需要哪些參數,哪些插件都回詳細說明,並且根據對應的更新情況有對應的更新項,具體如以下:
(在下面實例中,不演示jenkins-slave的方式,jenkins-slave參閱第三章)
1.2019年9月18日 發布與回滾.
4.1 發布與回滾
這里使用resource項目來講解,可能代碼中的一些密鑰id發生了改變,但是主體邏輯不變.
1.創建一個”流水線項目”,首先進入參數化構建過程.
發布與回滾需要用到參數化構建過程中3個選項參數,一個運行時參數.(參數值最好不要用“-”,否則pipeline讀取變量時無法識別)
分支參數
Git使用這個的變量來判斷拉取哪個分支的代碼.
命名空間
用這個控制服務部署在k8s的哪個命名空間下
發布與回滾控制參數
這個參數控制是否發布還是回滾
回滾版本號
該參數是一個url,需要自己去獲取其中的版本號,如果status變量是deploy,這個參數可以忽略
2.流水線語法
1. // 公共 2. def registry = "registry.cn-hangzhou.aliyuncs.com" 3. def git_address = "http://degitlab.mbgadev.cn/platform/resource.git" 4. def k8s_address = "https://1E73E810A71671022BC2EB1A4F79C434.sk1.ap-northeast-1.eks.amazonaws.com" 5. 6. // 項目 7. def project = "lcm-tw" 8. def app_name = "resource" 9. def image_name = "${registry}/${project}/${app_name}-${branch}:${BUILD_NUMBER}" 10. 11. 12. // 認證 13. def secret_name = "aliyun-hub-cn" 14. def docker_registry_auth = "6a657490-41e1-44bb-a7ad-5914ccb351b0" 15. def git_auth = "a424997d-4465-4ddf-9462-01ea8f576b06" 16. def k8s_auth = "827816b0-945f-48a2-bdf3-a8f098029f2c" 17. //k8s-cli插件需要使用到的密鑰 18. def k8s_cli = "2b2b9edb-7b71-429e-9909-19de7ffce54e" 19. 20. 21. node(""){ 22. 23. //如果 status 等於deploy,那么才會繼續拉取代碼→ 代碼編譯 → 構建鏡像,如果等於其它的,那么跳過這3個步驟 24. if ( "${status}" == "deploy" ) { 25. 26. // 第一步 27. stage('拉取代碼'){ 28. //如果使用 動態獲取git分支插件,可以在圖形化或者這里來通過git的驗證 29. checkout([$class: 'GitSCM', branches: [[name: '${branch}']], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_address}"]]]) 30. } 31. // 第二步 32. stage('代碼編譯'){ 33. sh "mvn clean" 34. sh "mvn compile" 35. sh "mvn package -DskipTests" 36. } 37. 38. // 第三步 39. stage('構建鏡像'){ //使用pipeline調試可以把密碼id轉換成用戶名和密碼字段 40. withCredentials([usernamePassword(credentialsId: "${docker_registry_auth}", passwordVariable: 'password', usernameVariable: 'username')]) { 41. sh """ 42. 43. #因為這里的日志格式發生了改變,這是java的logback模版,里面新增了一個 環境 的字段 44. cp ../config/logback.xml ./ 45. sed -i 's#\$namespace#${namespace}#' logback.xml 46. 47. docker login -u ${username} -p '${password}' ${registry} 48. echo ' 49. FROM registry.ap-northeast-1.aliyuncs.com/dena/alpine-oraclejdk8:jst 50. WORKDIR /opt 51. COPY resource-srv/target/*.jar /opt/resource-srv.jar 52. COPY resource-srv/dist/bootstrap.yml /opt/ 53. COPY logback.xml /opt 54. EXPOSE 8080 55. ENTRYPOINT ["java","-Dlogging.config=logback.xml","-Xms2048m","-Xmx2048m","-jar","/opt/resource-srv.jar"] 56. ' > Dockerfile 57. echo ${env} 58. docker build -t ${image_name} . 59. docker push ${image_name}1 60. """ 61. } 62. } 63. } //if結束 64. 65. // 第四步 66. stage('部署到K8S平台'){ 67. 68. //如果等於deploy,那么就會走完正常流程 69. if ( "${status}" == "deploy" ) { 70. 71. sh """ 72. cp -rf ../config/${JOB_NAME}.yml ./ 73. sed -i 's#\$NAMESPACE#${namespace}#' ${JOB_NAME}.yml 74. sed -i 's#\$IMAGE_NAME#${image_name}#' ${JOB_NAME}.yml 75. sed -i 's#\$SECRET_NAME#${secret_name}#' ${JOB_NAME}.yml 76. """ 77. 78. }else if ( "${status}" == "rollback" ){ 79. 80. //否則從運行時參數里面過濾出版本號,然后生成新的鏡像名稱讓k8s yaml去替換 81. //因為運行時參數獲取的是一個url,這里把里面的版本號給過濾出來 82. def rollback_version_number = sh(script: "echo `echo ${rollback_version} | awk -F '/' '{print \$6}'`", returnStdout: true).trim() 83. def rollback_image = "${registry}/${project}/${JOB_NAME}-${branch}:${rollback_version_number}" 84. 85. sh """ 86. echo "${rollback_version_number}" 87. echo "${rollback_image}" 88. echo "${status}" 89. cp -rf ../config/${JOB_NAME}.yml ./ 90. sed -i 's#\$NAMESPACE#${namespace}#' ${JOB_NAME}.yml 91. sed -i 's#\$IMAGE_NAME#${rollback_image}#' ${JOB_NAME}.yml 92. sed -i 's#\$SECRET_NAME#${secret_name}#' ${JOB_NAME}.yml 93. """ 94. } //if結束 95. kubernetesDeploy configs: "${JOB_NAME}.yml", kubeconfigId: "${k8s_auth}" 96. } 97. 98. //第五步 99. stage('查看pod狀態'){ 100. //k8s執行命令的插件,這里的密鑰是k8s serveraccount的token值 101. withKubeConfig(caCertificate: '', contextName: '', credentialsId: "${k8s_cli}", namespace: 'public', serverUrl: "${k8s_address}") { 102. sh """ 103. sleep 60; 104. echo '等待60秒' 105. echo '============查看當前pod狀態============'; 106. kubectl get pod -n ${namespace} | grep ${app_name}; 107. echo '============查看當前deploy狀態============'; 108. kubectl get deploy -n ${namespace} | grep ${app_name}; 109. //這里輸出pod的最后100行日志,由於在腳本式里面寫出這行代碼非常困難,所以保留,供后期參考 110. #for i in `kubectl get pod -n ${namespace} | grep ${app_name} | awk '{print \$1}' | tail -1`;do echo "----------------------------------------------------------\$i----------------------------------------------------------";kubectl logs --tail 100 \$i -n ${namespace} ;done 111. """ 112. } 113. } 114. }
3.界面展示
5.Aws case
5.1 關於kubernetes continuous deploy 密鑰
李先生您好,
很高興今天和您通話。在電話中我們探討了 EKS 管理集群的用戶或 IAM 角色,其中需要注意的是當您創建一個 Amazon EKS 集群時,將在集群的 RBAC 配置中自動為創建集群的 IAM 實體用戶或角色(例如,聯合身份用戶)授予 system:masters 權限。要授予其他 AWS 用戶或角色與您的集群進行交互的能力,您必須編輯 Kubernetes 內的 aws-auth ConfigMap [1]. 例如我們添加了 IAM 用戶 arn:aws:iam::858659433780:user/zexiong.li 在 mapUsers 部分。
我們通過屏幕共享查詢了 aws-auth-cm.yaml 和 configmap 並確認您的 EKS 集群配置無誤並且可以正常在跳板實例上操作 kubectl 相關命令。您遇到的主要問題是在利用 Jenkins 做持續部署導入 EKS 集群中時遇到如下報錯:
ERROR: ERROR: io.fabric8.kubernetes.client.KubernetesClientException: Failure executing: GET at: https://1e73e810a71671022bc2eb1a4f79c434.sk1.ap-northeast-1.eks.amazonaws.com/apis/apps/v1/namespaces/test/deployments/lcm-user . Message: Forbidden! User arn:aws:eks:ap-northeast-1:858659433780:cluster/ap-lcm-test-k8sMaster doesn't have permission. deployments.apps "lcm-user" is forbidden: User "system:anonymous" cannot get resource "deployments" in API group "apps" in the namespace "test".
通過細致排查,我們發現您 jenkins 用到的插件 kubernetes-cd-plugin [2] 目前不支持 [3] 我們 EKS 使用的 IAM 授權工具 aws-iam-authenticator [4]. 我們建議您在 Github 社區論壇里繼續跟蹤此問題看插件是否有更新。於此同時您問到是否有其他方法可以繞過報錯達到 EKS 給予用戶 "system:anonymous" get resource "deployments" 的權限。我查了不少相關討論,很多都需要涉及 API server 的改動在 EKS 無法實現因為 api server 屬於控制平面 (control plane),是由我們 EKS 內部團隊在負責。而且這些改動大多被 k8s 社區看做有潛在的安全隱患 [5].
關於使用 EKS 和 Jenkins 的集成來實現持續交付的架構您可以參考我們在電話里提到的一篇中文博客 - 使用 Amazon EKS 和 Jenkins X 持續交付 [6]. 這篇博客從頭到尾有演示步驟以及解釋,和您目前的架構非常相像,也可以繞開上面的插件報錯。另外,Jenkins 及其插件屬於第三方軟件超出了我們技術支持的范疇,不過我們會盡最大努力來幫助您。
希望以上信息對您有所幫助。如需任何其他協助,請隨時聯系我們。
謝謝!
相關文獻:
[1] - https://docs.aws.amazon.com/zh_cn/eks/latest/userguide/add-user-role.html
[2] - https://github.com/jenkinsci/kubernetes-cd-plugin
[3] - https://github.com/jenkinsci/kubernetes-cd-plugin/issues/71
[4] - https://github.com/kubernetes-sigs/aws-iam-authenticator
[5] - https://github.com/kubernetes-sigs/apiserver-builder-alpha/issues/225#issuecomment-501444546
[6] - https://aws.amazon.com/cn/blogs/china/continuous-delivery-eks-jenkins-x/