采用Jenkins in Kubernetes方式,可以在Kubernetes集群上动态运行Jenkins build agent。这样可以使得Jenkins build agent相互隔离,并实现自动扩容。
本文中的Jenkins Master部署在Kubernetes集群外,如果Jenkins Master部署在Kubernetes集群内,配置方法会稍有不同。
官方文档:
参考文档:
Configuring Certificates for Jenkins Kubernetes Plugin 0.12
Set Up a Jenkins CI/CD Pipeline with Kubernetes
初试 Jenkins 使用 Kubernetes Plugin 完成持续构建与发布
在Jenkins上,打开Manage Jenkins / Configure Global Security
找到Agents,在TCP port for JNLP agents选项选择Random (如果需要打开防火墙,则建议选择一个固定的端口)
点开Agent protocols,确保勾选了 Java Web Start Agent Protocol/4
如果不开启JNLP访问,Jenkins无法和Kubernetes交互
检查并安装Kubernetes插件
Kubernetes插件的详细说明参见:
https://github.com/jenkinsci/kubernetes-plugin
在Kubernetes上为Jenkins构建创建专有Namespace:
kubectl create namespace devops
也可以使用默认的default
namespace。
在Kubernetes上为Jenkins构建创建有Cluster Admin权限的Service Account jenkins
:
kubectl create clusterrolebinding jenkins --clusterrole cluster-admin --serviceaccount=devops:jenkins
或者根据YAML文件创建jenkins
Service Account:
wget https://raw.githubusercontent.com/cookcodeblog/OneDayDevOps/master/devops_in_k8s/jenkins/jenkins-slave-service-account.yml
kubectl apply -f jenkins-slave-service-account.yml
指定了Namespace为devops
的jenkins-slave-service-account.yml
内容:
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: jenkins
namespace: devops
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: jenkins
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: jenkins
namespace: devops
如果Jenkins Master运行在Kubernetes上可跳过此步
在Kubernetes Master上,打开~/.kube/config
文件,复制certificate-authority-data
的内容,运行以下命令生成Kubernetes server certificate key,并保存在ca.crt
文件中:
echo "" | base64 -d > ca.crt
如果Jenkins Master运行在Kubernetes上可跳过此步
在Kubernetes Master上,打开~/.kube/config
文件:
# 复制client-certificate-data的内容,运行以下命令生成client.crt
echo "" | base64 -d > client.crt
# 复制client-key-data的内容,运行以下命令生成client.key
echo "" | base64 -d > client.key
# 根据前面步骤生成的ca.crt, client.crt和client.key来生成PKCS12格式的cert.pfx
# 以下命令运行时,需要输入4位以上的密码
openssl pkcs12 -export -out cert.pfx -inkey client.key -in client.crt -certfile ca.crt
将生成的Kubernetes Client P12 Certificate Filecert.pfx
复制到Jenkins Master服务器上,比如复制到/var/lib/jenkins/kubernetes_cert/cert.pfx
。
如果Jenkins Master运行在Kubernetes上可跳过此步
在Jenkins上增加一个Global的Credential,选择类型为Certificate
选择“From a PKCS#12 file on Jenkins master”,输入上面生成的cert.pfx
文件在Jenkins Master服务器上的路径,比如/var/lib/jenkins/kubernetes_cert/cert.pfx
。
输入生成cert.pfx
文件时输入的密码
输入一个有意义的ID,比如kubernetes-cluster
输入有意义的Description
在Jenkins上,打开Manage Jenkins / Configure System
找到Cloud ,点击Add a new cloud
选择 Kubernetes
输入一个有意义的Name,比如kubernetes
输入Kubernetes URL:
kubectl cluster-info
获得Kubernetes Master API Server URLhttps://..svc.
,比如https://kubernetes.default.svc.cluster.local
,或短名称https://kubernetes.default
复制上面步骤生成的ca.crt
文件内容到Kubernetes server certificate key (如果Jenkins Master运行在Kubernetes上可跳过此步)
输入上一步创建的Kubernetes Namespace,比如devops
选择上一步配置好的Kubernetes Credential (如果Jenkins Master运行在Kubernetes上可跳过此步)
点击“Test Connection"按钮测试Jenkins是否可以成功连接Kubernetes。
输入Jenkins URL:
确保Pod Retention选择了Never,这样每次Jenkins构建结束后(无论成功和失败)都会销毁Pod,达到动态创建和运行Jenkins Build Agent的目的。
创建一个Jenkins Pipeline来测试Jenkins是否可以动态地在Kubernetes上创建和运行Build Agent。
一个使用了Maven、Docker和Kubectl的Jenkins Pipeline示例:
def label = "k8s-slave-${UUID.randomUUID().toString()}"
podTemplate(label: label, containers: [
containerTemplate(
name: 'jnlp',
image: 'jenkins/jnlp-slave:3.27-1-alpine',
alwaysPullImage: false,
privileged: true,
args: '${computer.jnlpmac} ${computer.name}'),
containerTemplate(name: 'maven', image: 'maven:3.6-jdk-8-alpine', command: 'cat', ttyEnabled: true, privileged: true),
containerTemplate(name: 'docker', image: 'docker:18.06', command: 'cat', ttyEnabled: true, privileged: true),
containerTemplate(name: 'kubectl', image: 'lachlanevenson/k8s-kubectl:v1.8.15', command: 'cat', ttyEnabled: true, privileged: true),
],
namespace: 'devops',serviceAccount: 'jenkins',automountServiceAccountToken: 'true',
volumes: [
hostPathVolume(hostPath: '/var/run/docker.sock', mountPath: '/var/run/docker.sock'),
hostPathVolume(hostPath: '/root/.m2', mountPath: '/root/.m2')
]) {
node(label) {
stage('Test Docker') {
container('docker') {
sh 'docker version'
}
}
stage('Test Maven') {
container('maven') {
sh 'mvn -v'
}
}
stage('Test Kubernetes') {
container('kubectl') {
sh 'kubectl get pods --all-namespaces'
}
}
}
}
参见:
https://raw.githubusercontent.com/cookcodeblog/OneDayDevOps/master/devops_in_k8s/jenkins/pipeline/maven_docker_k8s.groovy
说明:
jnlp
、maven
、docker
和k8s-kubectl
Container Templatejnlp
Container Template,但是这里还是用一个明确定义的jnlp
Container Template去覆盖缺省的jnlp
Container Template (这样可以指定jnlp
镜像的版本和镜像仓库的地址),因此须要命名为jnlp
,并带上args: '${computer.jnlpmac} ${computer.name}'
jnlp
容器用来和Jenkins Master通讯,用maven
容器来运行Maven构建,用docker
容器来运行Docker镜像打包和推送镜像,用k8s-kubectl
容器来部署Kubernetes应用devops
,使用了devops
Namespace下有Cluster Admin权限的Service Account jenkins
/var/run/docker.sock
使得可以运行Docker,挂载了/root/.m2
来做Maven本地缓存jnlp
容器,其它容器须使用command: 'cat', ttyEnabled: true
来保证容器一直保持运行状态Q: kubectl not found
A: 检查Pod Template中是否包含了k8s-kubectl
的Container Template,再检查kubectl
命令是否在kubectl
容器下执行的。
Q: Error from server (Forbidden): pods is forbidden: User “system:serviceaccount:devops:jenkins” cannot list pods at the cluster scope
A: 一般是Kubernetes RBAC权限问题;先检查是否使用了正确的Namespace,再检查是否使用了正确的Service Account,再检查该Service Account是否有Cluster Admin权限。
Q: 运行Docker命令失败
A: 检查是否挂载了/var/run/docker.sock
Q: 运行Maven构建时每次都要重新拉jar包,导致构建很慢
A: 检查是否挂载了/root/.m2
Maven本地缓存
Troubleshooting参考:
https://issues.jenkins-ci.org/browse/JENKINS-33419
https://stackoverflow.com/questions/48827345/kubernetes-jenkins-integration
https://jenkins.io/blog/2018/09/14/kubernetes-and-secret-agents/
https://stackoverflow.com/questions/52202159/jenkins-kubenetes-how-to-use-kubectl-in-kubernetes-plugin
https://devopscube.com/kubernetes-api-access-service-account/