將基礎的Kubernetes cluster建置之後,最多的用法就是建置CI/CD管道(pipeline),完整的將開發與部署的整個流程串連起來。
本篇將說明如何建立一個基本的Jenkins,並且在架構上,將Jenkins Agent以動態的方式在有需要的方式進行產生,執行結束後又會自動將Agent Pod刪除,不會造成系統內有一大堆沒有作用的Pod耗用有限的資源。
最後也在Jenkins Web UI上建立一個最基本的Pipeline,簡單驗證本篇建立出來的Jenkins engine是可以正常運行。
照慣例,本篇章節如下:
傳統的Jenkins 一對多的架構會有以下問題:
所以透過運行Jenkins master/slave在k8s cluster之內,可以很好的處理上述問題。
做法:Master 將設定資料存儲到volume內,Slave運行在各個節點上,且不是一直運行,是有需要才動態產生,結束時自動刪除。
這種方式解決了:
實際部署的方式有二種:
#---------------------------------------------------
# S2-1. create namespace
#---------------------------------------------------
[master]# kubectl create ns jenkinspoc
#---------------------------------------------------
# S2-2. create service account
#---------------------------------------------------
[master]# vim jenkinspoc-sa.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: jenkins-admin
namespace: jenkinspoc
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: jenkins
namespace: jenkinspoc
labels:
"app.kubernetes.io/name": 'jenkins'
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["create","delete","get","list","patch","update","watch"]
- apiGroups: [""]
resources: ["pods/exec"]
verbs: ["create","delete","get","list","patch","update","watch"]
- apiGroups: [""]
resources: ["pods/log"]
verbs: ["get","list","watch"]
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: jenkins-role-binding
namespace: jenkinspoc
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: jenkins
subjects:
- kind: ServiceAccount
name: jenkins-admin
namespace: jenkinspoc
[master]# kubectl create -f jennkinspoc-sa.yaml -n jenkinspoc
serviceaccount/jenkins-admin created
role.rbac.authorization.k8s.io/jenkins created
rolebinding.rbac.authorization.k8s.io/jenkins-role-binding created
[master]# kubectl get sa -n jenkinspoc
#---------------------------------------------------
# S2-3. 建立給master存放資料的volume
#---------------------------------------------------
[master]# vim jenkinspoc-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: jenkins-pv-claim
spec:
storageClassName: managed-nfs-storage
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
[master]# kubectl create -f jenkinspoc-pvc.yaml -n jenkinspoc
[master]# kubectl get pvc -n jenkinspoc
#---------------------------------------------------
# S2-4. 部署Jenkins master
#---------------------------------------------------
[master]# vim jenkinspoc-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: jenkins-deployment
spec:
replicas: 1
selector:
matchLabels:
app: jenkins
template:
metadata:
labels:
app: jenkins
spec:
serviceAccountName: jenkins-admin
securityContext:
fsGroup: 1000
runAsUser: 1000
containers:
- name: jenkins
image: jenkins/jenkins:lts
resources:
limits:
memory: "2Gi"
cpu: "1000m"
requests:
memory: "500Mi"
cpu: "500m"
ports:
- name: httpport
containerPort: 8080
- name: jnlpport
containerPort: 50000
livenessProbe:
httpGet:
path: "/login"
port: 8080
initialDelaySeconds: 90
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 5
readinessProbe:
httpGet:
path: "/login"
port: 8080
initialDelaySeconds: 60
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
volumeMounts:
- name: jenkins-data
mountPath: /var/jenkins_home
volumes:
- name: jenkins-data
persistentVolumeClaim:
claimName: jenkins-pv-claim
[master]# kubectl create -f jenkinspoc-deploy.yaml -n jenkinspoc
[master]# kubectl get pod -n jenkinspoc
#---------------------------------------------------
# S2-5. create svc
#---------------------------------------------------
[master]# vim jenkinspoc-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: jenkins-service
annotations:
prometheus.io/scrape: 'true'
prometheus.io/path: /
prometheus.io/port: '8080'
spec:
selector:
app: jenkins
type: NodePort
ports:
- name: httpport
port: 8080
targetPort: 8080
nodePort: 32003
- name: jnlpport
port: 50000
targetPort: 50000
[master]# kubectl create -f jenkinspoc-svc.yaml -n jenkinspoc
[master]# kubectl get all
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/jenkins-service NodePort 10.109.168.87 <none> 8080:32003/TCP,50000:30779/TCP 3sf
#---------------------------------------------------
# S2-6. Obtain admin passowrd
#---------------------------------------------------
[master]# kubectl get pod -n jenkinspoc
[master]# kubectl exec jenkins-deployment-bdf778654-br6mv -n jenkinspoc -- cat /var/jenkins_home/secrets/initialAdminPassword
b1a8117e9a364353855ebfe03b3308be
http://worker01.test.example.poc:32003
admin/P@ssw0rd
#---------------------------------------------
# S3-1. Install Suggestion plugin
#---------------------------------------------
#---------------------------------------------
# S3-2. Install kubernetes plugin
#---------------------------------------------
Manage Jenkins > Plugins > Available plugins > "Kubernetes" > Install
#---------------------------------------------
# S4-1. create cloud
#---------------------------------------------
Manage Jenkins > Clouds > New cloud > Name: K8s-pipeline (勾選Kubernetes) > Create
#---------------------------------------------
# S4-2. create credentials
# 因為全部都部署在同一座k8s,透過ServiceAccount的授權
#---------------------------------------------
Kubernetes URL : 免填
Certification key: 免填
Kubernetes namespace: jenkinspoc
=> test connection
#---------------------------------------------
# S4-3. Jenkins URL
#---------------------------------------------
[syntax] http://<service-name>.<namespace>.svc.cluster.local:8080
Jenkins URL: http://jenkins-service.jenknspoc.svc.cluster.local:8080
=> SAVE
#---------------------------------------------
# S4-4. Pod template
#---------------------------------------------
Name: jenkins-agent
namespace: jenkinspoc
Labels: jenkinsagent <== Job會套用這個Label來找到要用那個Pod配置來建立
Containers:
(如果可以連到DockerHub,可以使用預設的jenkins/inbound-agent)
(以下使用自訂的jnlp,並且覆蓋預設值)
(1) Name: jnlp
(2) Image: jenkins/inbound-agent:latest
(3) 移除"Sleep" , "99999" <== 不然會改寫預設的Entrypoint
(4) Service Account : jenkins-admin
#---------------------------------------------
# S5-1. 建立Job
#---------------------------------------------
Dashboard > New item > name: jenkins-job-1 (free style project) > OK
Label express: jenkinsagent
Build Step > Excute Shell > echo "Test Jenkins JOB Successfully." > SAVE
> Build Now
#---------------------------------------------
# S6-1. 建立Pipeline
#---------------------------------------------
Dashboard > New item > pipeline (name: pipeline-test) > OK
#---------------------------------------------
# S6-2. Pipeline script
#---------------------------------------------
node('jenkinsagent') {
stage('Clone') {
echo "1.Clone Stage"
}
stage('Test') {
echo "2.Test Stage"
}
stage('Build') {
echo "3.Build Stage"
}
stage('Deploy') {
echo "4. Deploy Stage"
}
}
> SAVE > Build Now
基本上許多使用K8S架構的組織,都會配合CI/CD的各種工具來實際運作,本篇所說的Jenkins便是其中一個廣泛使用的工具之一,並且許多工作的技能需求也要求K8S工程師也要負責維護CI/CD Pipeline的順利運作,所以建議學習K8S平台的各位,至少也要掌握一個Pipeline的工具建置、操作、維護的技能。
本篇只演示了CI/CD中的其中一個階段,有興趣的話,可以先了解整個CI/CD完整的循環有那些階段與相關的工具。例如先前提到的Harbor registry就是循環內的另一個階段。
那今天就先到此為止,下篇再見囉 !!
References: