快速搭建高可用CI工具:基于K8S的Jenkins Master-Slave架构
主流CI工具对比
以下持续集成工具本人进行使用后做出的一些总结:
不同项目需要根据企业要求选择合适的CI工具,例如:企业中采购了gitlab作为所有项目的代码仓库,可以尝试使用Gitlab CI。如果采购了Github企业版本,使用GitHub Actions也是一个不错的选择。同理,企业云战略选择Azure云,完全可以尝试Azure DevOps一站式解决Code Repository以及CI Pipeline。不要局限于特定的工具,按需求按场景进行调整即可。
Jenkins是老牌的CI工具,这里先以Jenkins为例,为大家快速演示如何搭建企业级的CI工具。
高可用 Jenkins 集群
高负载的情况下,经常出现不可用等情况时,是非常影响项目团队的开发效率和体验的。那么可以尝试结合K8S来搭建高可用的Jenkins集群。
这里可以看到通过启动Slave Pod来执行不同的Task任务,这样我们可以通过Docker Image来自定义任何想要的镜像,如:支持Java语言、Golang语言、Node等等。甚至可以嵌套容器将MySQL、Redis等有状态的中间件使用容器的方式运行,用来满足执行集成测试或者API测试。
同理,目前很多CI工具也可以看到类似的设计,如:Drone CI工具,也是分为了 Drone Server和Drone Runner。
流水线的执行流程
举个🌰
环境准备
Kubernetes 1.26.5
Kubectl
Helm v3.12.1
Text Editor (Yaml)
安装Jenkins Master
官方配置微调
以下是我通过Helm微调后后的Jenkins Master的部署脚本,调整的地方:
1)替换定制化Jenkins镜像:这里替换成我自己定制化的镜像,和官方的区别在于将主要的一些plugins直接打包进了Docker Image中,而不用在Yaml文件中,每次部署都需要重新下载,经常出现网络断开无法下载的情况,非常恶心。
插件列表如下:
git
credentials-binding
configuration-as-code
build-timestamp
build-timeout
htmlpublisher
build-monitor-plugin
kubernetes
2)关闭Agent Flag、调整Storage Class、调整javaOpts等,具体可以查看部署文件。
部署脚本
jenkins-local-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: local-pv-jenkins
labels:
app: local-pv-jenkins
spec:
capacity:
storage: 8Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
storageClassName: local-storage
persistentVolumeReclaimPolicy: Retain
local:
path: /Users/eric/Documents/data/jenkins
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- lima-rancher-desktop # <changeme>
local-jenkins-values.yaml
# https://github.com/jenkinsci/helm-charts/blob/main/charts/jenkins/values.yaml
# helm pull jenkins/jenkins --version 3.0.2 --untar
controller:
componentName: "jenkins-master"
image: "amuguelove/jenkins"
tag: "alpine-plugins-20210425"
imagePullPolicy: "IfNotPresent"
imagePullSecretName:
serviceType: ClusterIP
adminSecret: true
adminUser: "admin"
# adminPassword: "xxx" ## <defaults to random>
admin:
existingSecret: "" # changeme
userKey: jenkins-admin-user
passwordKey: jenkins-admin-password
resources:
requests:
cpu: "50m"
memory: "512Mi"
limits:
cpu: "2000m"
memory: "4096Mi"
javaOpts: >-
-Djenkins.install.runSetupWizard=false
-Duser.timezone=Asia/Shanghai
-Dhudson.slaves.NodeProvisioner.initialDelay=0
-Dhudson.slaves.NodeProvisioner.MARGIN=50
-Dhudson.slaves.NodeProvisioner.MARGIN0=0.85
-DsessionTimeout=1440
-DsessionEviction=43200
installPlugins: []
JCasC:
defaultConfig: true
configScripts: {}
securityRealm: |-
local:
allowsSignup: false
enableCaptcha: false
users:
- id: "${chart-admin-username}"
name: "Jenkins Admin"
password: "${chart-admin-password}"
# Ignored if authorizationStrategy is defined in controller.JCasC.configScripts
authorizationStrategy: |-
loggedInUsersCanDoAnything:
allowAnonymousRead: false
ingress:
enabled: false
agent:
enabled: false
persistence:
enabled: true # changeme
storageClass: "local-storage" # changeme
size: "8Gi"
serviceAccount:
create: true
name: jenkins-master
rbac:
create: true
执行脚本
# 准备
kubectl create namespace jenkins
kubectl apply -f jenkins-local-pv.yaml
# 安装Jenkins
helm upgrade --install \
jenkins-master jenkins/jenkins \
-n jenkins \
-f local-jenkins-values.yaml \
--version 3.3.18
执行结果
Release "jenkins-master" does not exist. Installing it now.
NAME: jenkins-master
LAST DEPLOYED: Sat Mar 23 10:06:23 2024
NAMESPACE: jenkins
STATUS: deployed
REVISION: 1
NOTES:
1. Get your 'admin' user password by running:
kubectl exec --namespace jenkins -it svc/jenkins-master -c jenkins -- /bin/cat /run/secrets/chart-admin-password && echo
2. Get the Jenkins URL to visit by running these commands in the same shell:
echo http://127.0.0.1:8080
kubectl --namespace jenkins port-forward svc/jenkins-master 8080:8080
3. Login with the password from step 1 and the username: admin
4. Configure security realm and authorization strategy
5. Use Jenkins Configuration as Code by specifying configScripts in your values.yaml file, see documentation: http:///configuration-as-code and examples: https://github.com/jenkinsci/configuration-as-code-plugin/tree/master/demos
For more information on running Jenkins on Kubernetes, visit:
https://cloud.google.com/solutions/jenkins-on-container-engine
For more information about Jenkins Configuration as Code, visit:
https://jenkins.io/projects/jcasc/
登陆
### 使用host主机8080端口登陆
kubectl --namespace jenkins port-forward svc/jenkins-master 8080:8080
### 查看admin用户的登陆密码
kubectl exec --namespace jenkins -it svc/jenkins-master -c jenkins -- /bin/cat /run/secrets/chart-admin-password && echo
4nrtG6h2l1n3AE25aZt6th
通过http://localhost:8080
访问Jenkins web服务了。
配置Jenknis Slave
接下来我们就需要来配置 Jenkins,让它能够动态的生成 Slave 的 Pod。
第一步:安装Kubernetes Plugin(定制化镜像中有,可忽略),点击 Manage Jenkins -> Manage Plugins -> Available -> Kubernetes plugin 勾选安装即可。
第二步:填写 Kubernetes Cloud Details,点击 Manage Jenkins —> Configure System —> (拖到最下方)Add a new cloud —> 选择 Kubernetes
这里需要点击Test Connection
去验证是否可以连接你的Kubernetes
集群。
第三步:填写Pod Template和Container Template
注意这里的名称最好都一致,例如我这边都使用jnlp-agent
,之前因为名字不同导致无法完美运行。
第四步:添加卷信息
可以看到这里分别挂载了主机的docker,kubectl等工具,其中Mount path根据实际情况进行调整。
如果碰到没有权限执行/var/run/docker.sock
,可登陆到对应的node节点或者本地机器进行授权:
chmod 666 /var/run/docker.sock
第五步:配置权限
这里填写jenkins-master
,是我们在jenkins master中创建的一个Service Account。如果不填的话,会导致执行kubectl
命令会出现权限不足。
验证
新建一个Freestyle的Item:
填写在Container Template中的Label:
添加一个Build Shell:
echo '=== 测试动态jenkins slave === '
echo '=== 打印docker信息 ==='
docker info
echo '=== 获取pods ==='
kubectl get pods
在Jenkins web中点击立即构建Build Now
,在控制观察kuberntes Pod的状态:
执行完毕后,控制台会有如下输出:
Building remotely on jnlp-agent-l58sk (jnlp-agent) in workspace /home/jenkins/agent/workspace/jnlp-demo
[jnlp-demo] $ /bin/sh -xe /tmp/jenkins4069627081916824161.sh
+ echo === 测试动态jenkins slave ===
=== 测试动态jenkins slave ===
+ echo === 打印docker信息 ===
=== 打印docker信息 ===
+ docker info
Containers: 20
Running: 19
Paused: 0
Stopped: 1
Images: 13
Server Version: 18.06.3-ce
Storage Driver: overlay2
Backing Filesystem: extfs
Supports d_type: true
Native Overlay Diff: true
Logging Driver: json-file
Cgroup Driver: cgroupfs
Plugins:
Volume: local
Network: bridge host macvlan null overlay
Log: awslogs fluentd gcplogs gelf journald json-file logentries splunk syslog
Swarm: inactive
Runtimes: runc
Default Runtime: runc
Init Binary: docker-init
containerd version: e6b3f5632f50dbc4e9cb6288d911bf4f5e95b18e (expected: 468a545b9edcd5932818eb9de8e72413e616e86e)
runc version: f56b4cbeadc407e715d9b2ba49e62185bd81cef4 (expected: a592beb5bc4c4092b1b1bac971afed27687340c5)
init version: fec3683
Security Options:
seccomp
Profile: default
Kernel Version: 4.14.105-19-0008
Operating System: CentOS Linux 7 (Core)
OSType: linux
Architecture: x86_64
CPUs: 2
Total Memory: 3.476GiB
Name: VM_0_12_centos
ID: 5EKE:FP2N:QFR7:BHIV:5LLJ:OUUK:CDV6:PRPI:5TW2:GAKT:JCMH:OFF3
Docker Root Dir: /var/lib/docker
Debug Mode (client): false
Debug Mode (server): false
Registry: https://index.docker.io/v1/
Labels:
Experimental: false
Insecure Registries:
127.0.0.0/8
Registry Mirrors:
https://mirror.ccs.tencentyun.com/
Live Restore Enabled: true
+ echo === 获取pods ===
=== 获取pods ===
+ kubectl get pods
NAME READY STATUS RESTARTS AGE
jenkins-587dc776c6-79hlg 1/1 Running 0 23m
jnlp-agent-l58sk 2/2 Running 0 17s
Finished: SUCCESS
到这里基于Kubernetes的动态Jenkins Slave就搭建完毕了。这里演示了一个简单的demo,如果在项目中有Jenkinsfile
,可以定义node属性,指定使用jnlp-agent
即可使用slave构建你的项目了。
可能碰到的坑与解决思路
Slave Pod执行完任务后,会进行Pod销毁,这时在CI过程中所产生的数据也将丢失,如:流水线过程中的各种报告(Jacoco覆盖率报告、测试报告等),需要将这些有用的数据发布到Master上。
每次执行一次流水线,编译代码时,前后端的pod都会去下载很多依赖,如:Jar,npm文件等,如果每次Slave Pod启动都要重新下载这些文件,那么流水线的执行时长将远比虚拟机部署方式要长,注意这些依赖文件的缓存,每次Slave Pod启动能够访问到这些文件。
云厂商的选择上,PV挂载的远程云磁盘,是否支持在线扩容?