本文主要示範如何在阿裡雲Kubernetes服務上快速搭建jenkins持續內建環境,并基于提供的示例應用快速完成應用源碼編譯、鏡像建構和推送以及應用部署的流水線。
先決條件:
阿裡雲Kubernetes叢集。
建議: 建議使用者先按照以下文檔安裝部署ack-jenkins應用, 然後成功運作建構任務示例demo-pipeline, 再依照此建構任務示例改造自己的建構任務配置。
1. 快速部署ack-jenkins
容器服務-Kubernetes -> 市場 -> 應用目錄 -> ack-jenkins:
點選 參數 菜單修改 AdminPassword 字段, 選擇Kubernetes叢集、填寫命名空間和釋出名稱并點選 建立:
通路jenkins服務并登陸:
ps:如未設定登陸密碼,則可在部署完畢後使用如下命名檢視:
$ printf $(kubectl get secret --namespace ci jenkins-ci-jenkins -o jsonpath="{.data.jenkins-admin-password}" | base64 --decode);echo
2. 配置 Kubernetes Cloud
2.1 配置 Kubernetes Cloud
Jenkins使用Kubernetes Plugin連接配接Kubernetes叢集并動态生成和釋放slave pod,關于slave pod的模闆配置需要在 系統管理 -> 系統配置 -> 雲 中進行配置
- 名稱:kubernetes;
- Kubernetes 位址: https://kubernetes.default.svc.cluster.local:443 ;jenkins 系統安裝在目前叢集中,可以使用内部服務端點通路叢集的 API Server
-
Kubernetes 命名空間:jenkins;動态 slave pod 會在命名空間 jenkins 下生成和銷毀
點選
按鈕驗證連接配接無誤:測試
- Jenkins 位址: http://ack-jenkins-default:8080 ;slave pod連接配接jenkins master使用的服務端點
- Jenkins 通道:ack-jenkins-default-agent:50000;slave pod使用jnlp連接配接jenkins master
- Pod Templates -> 名稱: slave-pipeline; slave pod名稱
- Pod Templates -> 命名空間:jenkins
- Pod Templates -> 标簽清單:slave-pipeline;jenkins 建構任務通過此标簽選擇使用哪個模闆生成slave pod
- Pod Templates -> 容器清單 -> jnlp;用于jnlp連接配接jenkins master
- Pod Templates -> 容器清單 -> kaniko;用于建構和推送容器鏡像
- Pod Templates -> 容器清單 -> maven:用于maven建構和打包應用
- Pod Templates -> 容器清單 -> kubectl:用于kubectl指令行部署應用
-
Pod Templates -> 環境變量;為kaniko設定DOCKER_CONFIG指定docker.json檔案路徑
建立jenkins-docker-cfg secret用于鏡像倉庫權限設定
本示例中使用了阿裡雲鏡像服務提供的杭州區域鏡像倉庫:
$ docker login -u xxx -p xxx registry-vpc.cn-beijing.aliyuncs.com
Login Succeeded
$ kubectl create secret generic jenkins-docker-cfg -n ci --from-file=/root/.docker/config.json
配置slave pod啟動時挂載 secret jenkins-docker-cfg:
如果需要設定maven緩存目錄,可以挂載hostPath volume 或者共享存儲卷到/root/.m2/repository:
如果需要更改 settings.xml,先建立 ConfigMap:
kubectl create configmap maven-config --from-file=settings.xml -n jenkins
再配置挂載到slave pod:
如果需要部署應用到本叢集,可以在slave pod中使用ServiceAccount通過kubectl部署,配置ServiceAccount:
2.2 建立建構任務
建立建構任務cicd-pipeline,拉取源碼項目
https://github.com/haoshuwei/jenkins-demo.git,分支master;maven打包建構,kaniko建構和推送容器鏡像;kubectl部署到本叢集。
在本示例中,需要打包容器鏡像為:registry-vpc.cn-beijing.aliyuncs.com/haoshuwei24/application-demo:20200915
則配置建構參數如下:
建構任務配置如下:
執行建構:
可以在
中檢視Jenkinsfile内容。
3 連接配接外部叢集
連接配接外部叢集需要建立和使用證書
示例:建立k8sCertAuth證書
在此步驟中,我們需要先建立和配置一個名稱為
k8sCertAuth
的叢集證書, 點選 憑證 添加證書:
儲存。
3. 詳解建構任務示例demo-pipeline
demo-pipeline
建構任務完成的持續內建/持續傳遞任務為:拉取源碼、建構應用war包、打包和推送容器鏡像以及部署應用到k8s叢集。示例源碼項目位址為
https://github.com/AliyunContainerService/jenkins-demo.git
, 整個建構流程會按照示例項目中
Jenkinsfile
聲明的内容執行建構。
3.1 demo-pipeline的任務配置
demo-pipeline設定了4個建構參數,分别為
origin_repo
repo
image_tag
和
branch
,
branch
用于指定本次建構拉取應用源碼倉庫的哪個分支, 另外3個變量用于建構和推送應用容器鏡像的位址,例如示例應用的容器鏡像要推送到
registry.cn-hangzhou.aliyuncs.com/ack-cicd/ack-jenkins-demo:latest
, 則
origin_repo
為
registry.cn-hangzhou.aliyuncs.com/ack-cicd
,
repo
ack-cicd
image_tag
latest
。
建構任務中配置從Git倉庫拉取源碼并指定Jenkinsfile路徑:
3.2 Jenkinsfile解析
我們在示例源碼項目的Jenkinsfile
jenkins-demo/Jenkinsfile中定義了以下内容:
pipeline{
// 定義groovy腳本中使用的環境變量
environment{
// 将建構任務中的建構參數轉換為環境變量
IMAGE_TAG = sh(returnStdout: true,script: 'echo $image_tag').trim()
ORIGIN_REPO = sh(returnStdout: true,script: 'echo $origin_repo').trim()
REPO = sh(returnStdout: true,script: 'echo $repo').trim()
BRANCH = sh(returnStdout: true,script: 'echo $branch').trim()
}
// 定義本次建構使用哪個标簽的建構環境,本示例中為 “slave-pipeline”
agent{
node{
label 'slave-pipeline'
}
}
// "stages"定義項目建構的多個子產品,可以添加多個 “stage”, 可以多個 “stage” 串行或者并行執行
stages{
// 定義第一個stage, 完成克隆源碼的任務
stage('Git'){
steps{
git branch: '${BRANCH}', credentialsId: '', url: 'https://github.com/AliyunContainerService/jenkins-demo.git'
}
}
// 添加第二個stage, 運作源碼打包指令
stage('Package'){
steps{
container("maven") {
sh "mvn package -B -DskipTests"
}
}
}
// 添加第三個stage, 運作容器鏡像建構和推送指令, 用到了environment中定義的groovy環境變量
stage('Image Build And Publish'){
steps{
container("kaniko") {
sh "kaniko -f `pwd`/Dockerfile -c `pwd` --destination=${ORIGIN_REPO}/${REPO}:${IMAGE_TAG} --skip-tls-verify"
}
}
}
// 添加第四個stage, 部署應用到指定k8s叢集
stage('Deploy to Kubernetes') {
parallel {
steps {
container('kubectl') {
step([$class: 'KubernetesDeploy', authMethod: 'certs', apiServerUrl: 'https://kubernetes.default.svc.cluster.local:443', credentialsId:'k8sCertAuth', config: 'deployment.yaml',variableState: 'ORIGIN_REPO,REPO,IMAGE_TAG'])
}
}
}
}
}
}
3.2.1 environment{}
代碼塊
environment{}
用于擷取建構參數并轉換為Jenkinsfile中使用的環境變量。
3.2.2 agent{}
agent{}
用于指定本次建構使用的建構節點标簽為
slave-pipeline
。 進入 系統管理 -> 系統設定 -> 雲 可以看到Kubernetes雲的配置, 此配置用于定義如何動态建立建構節點:
我們可以從上圖的Kubernetes雲配置中看到, 當我們執行建構任務時,jenkins會動态連接配接本叢集的api server url:
https://kubernetes.default.svc.cluster.local:443
在
jenkins
命名空間下建立 slave pod
slave-pipeline-xxx
, 此slave pod包含4個containers, 分别為用于slave連接配接master的
jnlp
、用于maven編譯打包java源碼的
maven
、用于容器鏡像建構和部署的
kaniko
以及用于部署k8s應用的
kubectl
當使用kaniko容器進行容器鏡像建構和推送時, 我們需要擷取鏡像推送到倉庫的權限, 這個權限是以k8s secret的方式挂載到slave-pipeline-xxx的pod裡的,環境變量
DOCKER_CONFIG
則定義了kaniko擷取鏡像倉庫推送權限檔案的預設路徑。 在此我們需要建立名為
jenkins-docker-cfg
的secret:
在一台linux機器上執行以下指令:
$ docker login -u xxx -p xxx registry.cn-hangzhou.aliyuncs.com
$ kubectl create secret generic jenkins-docker-cfg -n ci --from-file=/root/.docker/config.json
3.2.3 stages{}
stages{}
stages{}
代碼塊定義了多個stage,分别完成持續內建/持續部署過程中的不同步驟。
3.2.3.1 源碼拉取
源碼倉庫中一般包含
Jenkinsfile
Dockerfile
deployment.yaml
等檔案
stage('Git'){
steps{
git branch: '${BRANCH}', credentialsId: '', url: 'https://github.com/AliyunContainerService/jenkins-demo.git'
}
}
3.2.3.2 maven編譯和打包源碼
maven編譯的時候,使用者通常會想使用緩存功能, maven緩存需要使用NAS共享存儲,建立nas volume并挂載到Kubernetes雲的slave-pipeline模闆上, 類似jenkins-docker-cfg的挂載。
stage('Package'){
steps{
container("maven") {
sh "mvn package -B -DskipTests"
}
}
}
3.2.3.3 kaniko建構和推送鏡像
kaniko工具建構容器鏡像可以不依賴docker daemon程序,在使用者态空間完成鏡像建構和推送, 更安全可靠。
stage('Image Build And Publish'){
steps{
container("kaniko") {
sh "kaniko -f `pwd`/Dockerfile -c `pwd` --destination=${ORIGIN_REPO}/${REPO}:${IMAGE_TAG} --skip-tls-verify"
}
}
}
3.2.3.4 kubectl部署k8s應用
stage('Deploy to Kubernetes') {
steps {
container('kubectl') {
step([$class: 'KubernetesDeploy', authMethod: 'certs', apiServerUrl: 'https://kubernetes.default.svc.cluster.local:443', credentialsId:'k8sCertAuth', config: 'deployment.yaml',variableState: 'ORIGIN_REPO,REPO,IMAGE_TAG'])
}
}
}
k8sCertAuth
variableState: 'ORIGIN_REPO,REPO,IMAGE_TAG'
字段聲明需要替換
deployment.yaml
檔案中的哪些環境變量。
4. 系統配置詳解
4.1 建構環境配置說明
(1)kubernetes叢集動态配置設定建構pod的配置;
(2)slave-pipeline 使用了4個container分别完成流水線中各個stage的建構,Kubernetes Pod Templates配置
container jnlp:
container kaniko:
container kubectl:
container maven:
(3) 使用到的建構鏡像:
- jnlp 用于建構節點jnlp連接配接master:
jenkinsci/jnlp-slave:3.35-5
- maven 用于mvn打包建構:
maven:3.6.2-jdk-14
- kaniko 用于鏡像建構和推送:
registry.cn-hangzhou.aliyuncs.com/acs/kaniko:v0.14.0
- kubectl 用于kubectl部署應用:
(4) kaniko配置鏡像倉庫權限:registry.cn-hangzhou.aliyuncs.com/acs/kubectl:1.14.8
4.2 示例項目說明
(1)示例項目中使用的源碼倉庫
https://github.com/AliyunContainerService/jenkins-demo.git
(2)kaniko 建構和推送docker鏡像說明
kaniko可以不依賴docker daemon并在使用者空間執行完成Dockerfile中的每一行指令,最終完成docker鏡像的建構和推送。
kaniko -f `pwd`/Dockerfile -c `pwd` --destination=${origin_repo}/${repo}:${image_tag}
(3)部署應用到Kubernetes叢集插件說明
插件配置如下:
對應的Pipeline文法為:
step([$class: 'KubernetesDeploy', authMethod: 'certs', apiServerUrl: 'https://kubernetes.default.svc.cluster.local:443', credentialsId:'k8sCertAuth', config: 'deployment.yaml',variableState: 'ORIGIN_REPO,REPO,IMAGE_TAG'])
聲明的變量ORIGIN_REPO,REPO,IMAGE_TAG可在建構執行時把deployment.yaml檔案中對應的變量值替換為實際值:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: jenkins-java-demo
spec:
replicas: 2
selector:
matchLabels:
app: jenkins-java-demo
template:
metadata:
labels:
app: jenkins-java-demo
spec:
containers:
- name: jenkins-java-demo
image: ${ORIGIN_REPO}/${REPO}:${IMAGE_TAG}
imagePullPolicy: Always
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: jenkins-java-demo
spec:
ports:
- port: 80
targetPort: 8080
name: jenkins-java-demo
selector:
app: jenkins-java-demo
type: LoadBalancer
(4)Jenkinsfile說明
pipeline{
// 定義groovy腳本中使用的環境變量
environment{
// 本示例中使用DEPLOY_TO_K8S變量來決定把應用部署到哪套容器叢集環境中,如“Production Environment”, “Staging001 Environment”等
IMAGE_TAG = sh(returnStdout: true,script: 'echo $image_tag').trim()
ORIGIN_REPO = sh(returnStdout: true,script: 'echo $origin_repo').trim()
REPO = sh(returnStdout: true,script: 'echo $repo').trim()
BRANCH = sh(returnStdout: true,script: 'echo $branch').trim()
}
// 定義本次建構使用哪個标簽的建構環境,本示例中為 “slave-pipeline”
agent{
node{
label 'slave-pipeline'
}
}
// "stages"定義項目建構的多個子產品,可以添加多個 “stage”, 可以多個 “stage” 串行或者并行執行
stages{
// 定義第一個stage, 完成克隆源碼的任務
stage('Git'){
steps{
git branch: '${BRANCH}', credentialsId: '', url: 'https://github.com/AliyunContainerService/jenkins-demo.git'
}
}
// 添加第二個stage, 運作源碼打包指令
stage('Package'){
steps{
container("maven") {
sh "mvn package -B -DskipTests"
}
}
}
// 添加第四個stage, 運作容器鏡像建構和推送指令, 用到了environment中定義的groovy環境變量
stage('Image Build And Publish'){
steps{
container("kaniko") {
sh "kaniko -f `pwd`/Dockerfile -c `pwd` --destination=${ORIGIN_REPO}/${REPO}:${IMAGE_TAG}"
}
}
}
stage('Deploy to Kubernetes') {
parallel {
stage('Deploy to Production Environment') {
when {
expression {
"$BRANCH" == "master"
}
}
steps {
container('kubectl') {
step([$class: 'KubernetesDeploy', authMethod: 'certs', apiServerUrl: 'https://kubernetes.default.svc.cluster.local:443', credentialsId:'k8sCertAuth', config: 'deployment.yaml',variableState: 'ORIGIN_REPO,REPO,IMAGE_TAG'])
}
}
}
stage('Deploy to Staging001 Environment') {
when {
expression {
"$BRANCH" == "latest"
}
}
steps {
container('kubectl') {
step([$class: 'KubernetesDeploy', authMethod: 'certs', apiServerUrl: 'https://kubernetes.default.svc.cluster.local:443', credentialsId:'k8sCertAuth', config: 'deployment.yaml',variableState: 'ORIGIN_REPO,REPO,IMAGE_TAG'])
}
}
}
}
}
}
}
了解更多阿裡雲容器服務内容,請通路
https://www.aliyun.com/product/containerservice了解更多kaniko内容請參考:
https://github.com/GoogleContainerTools/kaniko