Jenkins基于k8s的CI/CD教程
1. 背景介绍传统的主机方式部署的 Jenkins 集群在复杂场景中会经常碰到如下问题,如:主 Master 发生单点故障时,会导致整个流程不可用;Slave 的配置环境不一样,需要重复安装和配置维护麻烦,没办法定制差异化的配置管理;资源分配不均衡,有的 Slave 要运行的 job 出现排队等待,而有的 Slave 处于空闲状态;资源有浪费,每台 Slave 可能是实体机或者 VM,当 Slav
1. 背景介绍
传统的主机方式部署的 Jenkins 集群在复杂场景中会经常碰到如下问题,如:
- 主 Master 发生单点故障时,会导致整个流程不可用;
- Slave 的配置环境不一样,需要重复安装和配置维护麻烦,没办法定制差异化的配置管理;
- 资源分配不均衡,有的 Slave 要运行的 job 出现排队等待,而有的 Slave 处于空闲状态;
- 资源有浪费,每台 Slave 可能是实体机或者 VM,当 Slave 处于空闲状态时,也不会完全释放掉资源;
基于k8s编排和Docker容器技术,可以很好的解决上面问题。Jenkins Master 和 Jenkins Slave 以 Docker Container 形式运行在 Kubernetes 集群的 Node 上,Master 运行在其中一个节点,Slave 运行在各个节点上,并且它不是一直处于运行状态,它会按照需求动态的创建并自动删除。最终的方式和传统的是一样的,也是基于jenkins-agent.jar,通过jnlp进行通信,只不过jenkins 的 kubernetes-plugin 插件帮我们把这些操作做完了。通过这样的方式,在需要 Build 的时候创建 Slave Pod 动态伸缩,而且pod可以基于模板定制环境,Build结束后就销毁,合理利用计算资源。
2. k8s中进行jenkins安装
网上很多,可以通过helm或者自己定制配置文件。
3. 插件安装
Pipeline相关,文档语法:
https://www.jenkins.io/zh/doc/book/pipeline/syntax/
Kubernetes Plugin,主要用于创建Slave Pod 并分配给Slave相关流水线工作内容,相关文档:
https://github.com/jenkinsci/kubernetes-plugin
https://www.jenkins.io/doc/pipeline/steps/kubernetes/
Kubernetes Continuous Deploy Plugin,主要用于容器内发布k8s相关资源,相关文档:
https://www.jenkins.io/doc/pipeline/steps/kubernetes-cd/#kubernetesdeploy-deploy-to-kubernetes
4. Jenkins 配置 Kubernetes Plugin
插件安装完毕后,点击 “系统管理” —> “系统设置” —> “Cloud” 进入云集群配置页面 选择 Kubernetes
填写kubernetes 和 Jenkins 配置
- kubernetes apiserver地址,k8s默认安装后是这个
- 点击连接测试,如果连接成功会显示 Connected to Kubernetes 失败会有其他提示
- jenkins的http服务地址,也就是用来登录的那个地址
- Jenkins 通道 jnlp 通信用的。如下图,如果 jenkins 在 k8s 中 service 是 8080 和 50000端口同时暴露的,就不需要额外填写。如果分两个service暴露8080和50000端口(或者端口有改动),则需要填写对应的ip:host
需要注意一点:在使用的时候Pipeline的Pod启动的时候会有Node-Selectors: kubernetes.io/os=linux,所以需要在集群node上面添加kubernetes.io/os=linux的label
5. pipeline 方式
参数化配置
配置的参数可以在pipeline script 中使用 ${params.key} 来使用,如上面就是 ${params.APP_NAME}
流水线脚本,可以参考上面提供的插件文档
def label = "test-pipline"
def HARBOR_HOST = "test.harbor.com"
podTemplate(label: label, cloud: 'kubernetes', imagePullSecrets: ['test-harbor'],
containers: [
containerTemplate(name: 'node', image: 'node:14.16.0-alpine3.10', ttyEnabled: true, command: 'cat'),
containerTemplate(name: 'maven', image: 'test.harbor.com/maven:3.6.3-jdk-8', ttyEnabled: true, command: 'cat'),
containerTemplate(name: 'docker', image: 'docker:20.10.5-git', , ttyEnabled: true, command: 'cat'),
], volumes: [
hostPathVolume(hostPath: '/var/run/docker.sock', mountPath:'/var/run/docker.sock'),
persistentVolumeClaim(claimName: 'jenkins-build-pvc', mountPath: '/root/repo')
], workspaceVolume: persistentVolumeClaimWorkspaceVolume(claimName: 'jenkins-workspace-pvc')
) {
node(label) {
stage('Git Clone') {
git branch: 'master', credentialsId: 'gitlab', url: 'https://test.gitlab.com/devil/test.git'
}
stage('Node Build') {
container('node'){
sh """
cd vue
npm install
npm run build
"""
}
}
stage('Maven Build') {
container('maven'){
sh "mvn package -Dmaven.test.skip=true"
}
}
stage('Docker Build') {
container('docker'){
withCredentials([usernamePassword(credentialsId: "harbor", passwordVariable: 'HARBOR_PWD', usernameVariable: 'HARBOR_USERNAME')]) {
sh "docker login " + HARBOR_HOST + " -u ${HARBOR_USERNAME} -p ${HARBOR_PWD}"
sh "docker build -t " + HARBOR_HOST + "/${params.IMAGE_NAME}:${params.IMAGE_TAG} ."
sh "docker push " + HARBOR_HOST + "/${params.IMAGE_NAME}:${params.IMAGE_TAG}"
}
}
}
stage('Deploy') {
sh "sed -e 's#{IMAGE_URL}#" + HARBOR_HOST + "/${params.IMAGE_NAME}#g;s#{IMAGE_TAG}#${params.IMAGE_TAG}#g;s#{APP_NAME}#${params.APP_NAME}#g;s#{REPLICAS_NUM}#${params.REPLICAS_NUM}#g;s#{HEAP_SIZE}#${params.HEAP_SIZE}#g;s#{DIRECT_MEMORY_SIZE}#${params.DIRECT_MEMORY_SIZE}#g' k8s-deployment-tpl.yaml > k8s-deployment.yml"
sh "cat k8s-deployment.yml"
kubernetesDeploy(configs: 'k8s-deployment.yml', kubeconfigId: "k8s-client")
}
}
}
容器模板相关
- podTemplate:也就是为这个流水线这次作业而创建的pod的模板,可以通过yaml方式,参考文档
- label:pod的名称,在流水线执行过程中,可以在 jenkins 的namespce看到创建了对应的pod
- cloud:对应的cloud,也就是步骤4中配置的
- containers:这个pod中使用了哪些容器
- imagePullSecrets:由于例子中 containers 使用了私有仓库,所以需要docker登录认证,这里需要在jenkins master pod 的namespace创建k8s用于访问私有仓库的secret类型资源,imagePullSecrets数组中对应 k8s 中 secret 名称。通过命令创建
kubectl create secret docker-registry -n jenkins harbor --docker-username=*** --docker-password=**** --docker-email=aaa@gg.com --docker-server=host
- container(‘node’):使用node这个容器,并执行对应操作
- credentialsId:jenkins 的 凭证管理中对应的凭证 id
- kubernetesDeploy:参考上文 Kubernetes Continuous Deploy Plugin 相关文档
- kubeconfigId:jenkins 的 凭证管理中创建的k8s config类型凭证,用于k8s集群访问,通过kubeadmin生成
数据挂载相关
- volumes:需要挂载的文件,这里主要用于maven下载的jar包不重复下载
- workspaceVolume:挂载slave的jenkins工作目录,这里使用pvc的方式,需要在k8s创建pvc
kubeconfig类型凭证,系统管理-> manager Credentials 创建凭证,credentialsId同理,基于需要用Username/Password或者其它类型
上面pipeline最后Deploy指定了项目根目录下k8s-deployment-tpl.yaml,用自定义的构建参数进行文本替换,生成k8s-deployment.yml用于k8s发布插件发布。k8s-deployment-tpl.yaml 模板内容如下:
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: {APP_NAME}
spec:
replicas: {REPLICAS_NUM}
template:
metadata:
name: {APP_NAME}
labels:
app: {APP_NAME}
spec:
containers:
- image: {IMAGE_URL}:{IMAGE_TAG}
name: {APP_NAME}
ports:
- containerPort: 80
volumeMounts:
- name: config
mountPath: /root/config.yaml
subPath: config.yaml
volumes:
- name: config
configMap:
name: test-config
items:
- key: config.yaml
path: config.yaml
---
apiVersion: v1
kind: Service
metadata:
name: {APP_NAME}
spec:
ports:
- port: 80
targetPort: 80
selector:
app: {APP_NAME}
6. 非Pipeline
创建 pod 模板
这里的配置其实和上面pipeline中podTemplate相似,创建完pod模板后,构建一个自由风格的软件项目,在标签中选择刚刚创建的模板,这样就会自动按模板要求进行操作。
更多推荐
所有评论(0)