CI/CD是什么
CI(持续集成)
持续集成是一种软件开发实践,通过自动化工具对代码进行编译、测试和打包,减少人工干预,提高构建效率。它的核心理念是将代码频繁地集成到共享存储库中,并通过自动化构建和测试流程来验证代码的正确性。这样做可以为开发人员提供即时的反馈,帮助他们快速定位并修复问题,从而加速软件开发周期并提高软件质量。
CD(持续交付)
持续交付是一种软件开发实践,通过自动化工具建立一套自动化的流水线,将应用程序部署到不同的环境中,例如开发环境、测试环境和生产环境等。这种流水线能够自动化地执行构建、测试、部署和发布等步骤,使得软件可以在不同环境中快速、可靠地交付给最终用户。持续交付的目标是确保软件可以随时随地以可靠的方式交付给用户,从而缩短交付周期、降低发布风险。
CI/CD的关系
CI和CD是相互关联的两个概念,持续集成是持续交付的基础,只有实现了持续集成,才能够实现持续交付。持续集成提高了代码的质量和可维护性,为持续交付提供了更好的基础;而持续交付则能够更快地将代码交付给用户,从而促进持续集成的实施。这两者相辅相成,共同推动着软件开发过程的持续改进和交付效率的提升。
CI/CD项目简介
利用 Jenkins、SonarQube、Harbor、Container、Kubernetes技术,搭建一个完整的 CI/CD 管道,模拟实际生产环境项目开发部署流程,实现持续集成、持续交付和持续部署。通过自动化构建、测试、代码质量检查和容器化部署,将开发人员从繁琐的手动操作中解放出来,提高团队的开发效率、软件质量和安全性,实现持续更新迭代和持续部署交付。
CI/CD流程图
流程说明
开发测试阶段
- 开发人员需求确定后,从master分支拉取最新代码,在dev分支完成开发后,将dev分支合并到test分支并推送至gitlab仓库。
- Gitlab监听到test分支代码更新,请求jenkins的webhook地址,触发持续构建和持续部署流程,准备将代码部署至测试环境。
- Jenkins从Gitlab中拉取项目源码,编译并打成jar包,然后调用SonarQube完成代码扫描。
- 扫描完成调用docker或者container打包成容器镜像,并推送至Harbor镜像仓库。
- jenkins修改yaml文件,将资源部署至测试环境。
- Jenkins完成测试环境CICD流程后,将结果邮件通知给开发运维和测试人员。
- 测试人员访问测试环境,功能验证无误后反馈给开发主管,至此开发测试阶段完成。
生产发布阶段
- 开发主管将test分支代码合并至master分支,并推送至gitlab仓库。
- gitlab监听到master分支代码更改,请求jenkins的webhook地址,开始触发生产环境cicd流程,使用k8s滚动更新项目版本。
- jenkins完成cicd流程后自动发送邮件通知,用户访问新版本服务。
项目代码仓库地址
gitee:https://gitee.com/cuiliang0302/sprint_boot_demo
github:https://github.com/cuiliang0302/sprint-boot-demo
Jenkins动态slave介绍
为什么需要动态slave
- **配置管理困难:**不同项目可能使用不同的编程语言、框架或库,这导致了每个Slave的配置环境各不相同。因此,需要动态Slave能够根据不同的项目需求,灵活配置不同的运行环境,从而简化配置管理和维护工作。
- **资源分配不均衡:**在使用静态Slave时,可能会出现某些Slave处于空闲状态,而其他Slave却处于繁忙状态,导致资源分配不均衡。动态Slave可以根据当前任务的需求自动调配资源,使得任务能够在空闲的Slave上尽快执行,从而提高资源利用率和任务执行效率。
- **资源浪费:**静态Slave在没有任务执行时仍然占用着资源,这导致了资源的浪费。而动态Slave能够根据实际需要自动扩容或缩减,当没有任务执行时会释放资源,从而避免了资源的浪费。
动态slave工作流程
正因为上面的这些种种痛点,我们渴望一种更高效更可靠的方式来完成这个 CI/CD 流程,而 Docker虚拟化容器技术能很好的解决这个痛点,又特别是在 Kubernetes 集群环境下面能够更好来解决上面的问题,下图是基于 Kubernetes 搭建 Jenkins 集群的简单示意图:
从图上可以看到 Jenkins Master 和 Jenkins Slave 以 Pod 形式运行在 Kubernetes 集群的 Node 上,Master 运行在其中一个节点,并且将其配置数据存储到一个 Volume 上去,Slave 运行在各个节点上,并且它不是一直处于运行状态,它会按照需求动态的创建并自动删除。
这种方式的工作流程大致为:当 Jenkins Master 接受到 Build 请求时,会根据配置的 Label 动态创建一个运行在 Pod 中的 Jenkins Slave 并注册到 Master 上,当运行完 Job 后,这个 Slave 会被注销并且这个 Pod 也会自动删除,恢复到最初状态。
服务部署
本项目所有服务均运行在k8s集群上,使用nfs共享存储,具体部署配置过程可参考下文,此处不再赘述。
k8s部署
参考文档:https://www.cuiliangblog.cn/detail/section/15287639
nfs共享存储部署
参考文档:https://www.cuiliangblog.cn/detail/section/116191364
container部署
参考文档:https://www.cuiliangblog.cn/detail/section/99861101
harbor部署
参考文档:https://www.cuiliangblog.cn/detail/section/99861101
gitlab部署
参考文档:https://www.cuiliangblog.cn/detail/section/131418586
jenkins部署
参考文档:https://www.cuiliangblog.cn/detail/section/131416735
SonarQube部署
参考文档:https://www.cuiliangblog.cn/detail/section/165547985
项目与权限配置
Harbor配置
创建项目
Harbor的项目分为公开和私有的:
公开项目:所有用户都可以访问,通常存放公共的镜像,默认有一个library公开项目。
私有项目:只有授权用户才可以访问,通常存放项目本身的镜像。 我们可以为微服务项目创建一个新的项目
创建用户
创建一个普通用户cuiliang。
配置项目用户权限
在spring_boot_demo项目中添加普通用户cuiliang,并设置角色为开发者。
权限说明
角色 | 权限 |
---|---|
访客 | 对项目有只读权限 |
开发人员 | 对项目有读写权限 |
维护人员 | 对项目有读写权限、创建webhook权限 |
项目管理员 | 出上述外,还有用户管理等权限 |
上传下载镜像测试
可参考文章https://www.cuiliangblog.cn/detail/section/15189547,此处不再赘述。
gitlab项目权限配置
具体gitlab权限配置参考文档:https://www.cuiliangblog.cn/detail/section/131513569
创建开发组develop,用户cuiliang,项目springboot demo
创建组
管理员用户登录,创建群组,组名称为develop,组权限为私有
创建项目
创建sprint boot demo项目,并指定develop,项目类型为私有
创建用户
创建一个普通用户cuiliang
用户添加到组中
将cuiliang添加到群组develop中,cuiliang角色为Developer
配置分支权限
用户权限验证
使用任意一台机器模拟开发人员拉取代码,完成开发后推送至代码仓库。
拉取仓库代码
[root@tiaoban opt]# git clone https://gitee.com/cuiliang0302/sprint_boot_demo.git
正克隆到 'sprint_boot_demo'...
remote: Enumerating objects: 69, done.
remote: Counting objects: 100% (69/69), done.
remote: Compressing objects: 100% (54/54), done.
remote: Total 69 (delta 15), reused 0 (delta 0), pack-reused 0
接收对象中: 100% (69/69), 73.15 KiB | 1.49 MiB/s, 完成.
处理 delta 中: 100% (15/15), 完成.
[root@tiaoban opt]# cd sprint_boot_demo/
[root@tiaoban sprint_boot_demo]# ls
email.html Jenkinsfile LICENSE mvnw mvnw.cmd pom.xml readme.md sonar-project.properties src test
推送至gitlab仓库
# 修改远程仓库地址
[root@tiaoban sprint_boot_demo]# git remote set-url origin http://gitlab.local.com/develop/sprint_boot_demo.git
[root@tiaoban sprint_boot_demo]# git remote -v
origin http://gitlab.local.com/develop/sprint_boot_demo.git (fetch)
origin http://gitlab.local.com/develop/sprint_boot_demo.git (push)
# 推送代码至gitlab
[root@tiaoban sprint_boot_demo]# git push --set-upstream origin --all
Username for 'http://gitlab.local.com': cuiliang
Password for 'http://[email protected]':
枚举对象中: 55, 完成.
对象计数中: 100% (55/55), 完成.
使用 4 个线程进行压缩
压缩对象中: 100% (34/34), 完成.
写入对象中: 100% (55/55), 71.51 KiB | 71.51 MiB/s, 完成.
总共 55(差异 10),复用 52(差异 9),包复用 0
To http://gitlab.local.com/develop/sprint-boot-demo.git
* [new branch] main -> main
分支 'main' 设置为跟踪 'origin/main'。
查看验证
jenkins配置
插件安装与配置
GitLab插件安装与配置:https://www.cuiliangblog.cn/detail/section/127410630
SonarQube Scanner插件安装与配置:https://www.cuiliangblog.cn/detail/section/165534414
Kubernetes插件安装与配置:https://www.cuiliangblog.cn/detail/section/127230452
Email Extension邮件推送插件安装与配置:https://www.cuiliangblog.cn/detail/section/133029974
Version Number版本号插件安装与配置:https://plugins.jenkins.io/versionnumber/
Content Replace文件内容替换插件安装与配置:https://plugins.jenkins.io/content-replace/
jenkins slave镜像制作
安装完Kubernetes插件后,默认的slave镜像仅包含一些基础功能和软件包,如果需要构建镜像,执行kubectl命令,则需要引入其他container或者自定义slave镜像。
关于镜像构建问题,如果k8s容器运行时为docker,可以直接使用docker in docker方案,启动一个docker:dind容器,通过Docker pipeline插件执行镜像构建与推送操作,具体内容可参考https://www.cuiliangblog.cn/detail/section/166573065。
如果k8s容器运行时为container,则使用nerdctl+buildkitd方案,启动一个buildkit容器,通过nerdctl命令执行镜像构建与推送操作,具体内容可参考
https://www.cuiliangblog.cn/detail/section/167380911。
本次实验以container环境为例,通过nerdctl+buildkitd方案演示如何构建并推送镜像。
构建jenkins-slave镜像
[root@tiaoban jenkins]# cat Dockerfile
FROM jenkins/inbound-agent:latest-jdk17
USER root
COPY kubectl /usr/bin/kubectl
COPY nerdctl /usr/bin/nerdctl
COPY buildctl /usr/bin/buildctl
[root@tiaoban jenkins]#
[root@tiaoban jenkins]# docker build -t harbor.local.com/cicd/jenkins-slave:v1.0 .
测试jenkins-slave镜像构建容器与操作k8s
以下操作在k8s集群master机器,容器运行时为container节点执行测试
# 启动buildkit镜像构建服务
# 挂载/run/containerd/containerd.sock方便container调用buildkitd
# 挂载/var/lib/buildkit,以便于将构建过程中下载的镜像持久化存储,方便下次构建时使用缓存
# 挂载/run/buildkit/目录方便nerctl调用buildkitd
[root@master3 ~]# nerdctl run --name buildkit -d --privileged=true \
-v /run/buildkit/:/run/buildkit/ \
-v /var/lib/buildkit:/var/lib/buildkit \
-v /run/containerd/containerd.sock:/run/containerd/containerd.sock \
moby/buildkit:v0.13.2
[root@master3 ~]# nerdctl ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a8de5299dd84 moby/buildkit:v0.13.2 "buildkitd" 4 seconds ago Up buildkit
# 启动jenkins-slave容器
# 挂载/run/containerd/containerd.sock方便netdctl操作container
# 挂载/run/buildkit/目录方便nerctl调用buildkitd构建镜像
# 挂载/root/.kube/目录方便kubectl工具操作k8s
[root@master3 ~]# nerdctl run --name jenkins-slave -it --privileged=true \
-v /run/buildkit/:/run/buildkit/ \
-v /root/.kube/:/root/.kube/ \
-v /run/containerd/containerd.sock:/run/containerd/containerd.sock \
harbor.local.com/cicd/jenkins-slave:v1.0 bash
# 测试container管理
root@28dcd3a667c9:/home/jenkins# nerdctl ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
28dcd3a667c9 harbor.local.com/cicd/jenkins-slave:v1.0 "/usr/local/bin/jenk…" 11 seconds ago Up jenkins-slave
a8de5299dd84 harbor.local.com/cicd/buildkit:v0.13.2 "buildkitd" 11 minutes ago Up buildkit
# 测试k8s管理
root@28dcd3a667c9:/home/jenkins# kubectl get node
NAME STATUS ROLES AGE VERSION
master1 Ready control-plane 241d v1.27.6
master2 Ready control-plane 241d v1.27.6
master3 Ready control-plane 241d v1.27.6
work1 Ready <none> 241d v1.27.6
work2 Ready <none> 241d v1.27.6
work3 Ready <none> 241d v1.27.6
# 测试镜像构建
root@28dcd3a667c9:/home/jenkins# echo 'FROM busybox' >> Dockerfile
root@28dcd3a667c9:/home/jenkins# echo 'CMD ["echo","hello","container"]' >> Dockerfile
root@28dcd3a667c9:/home/jenkins# cat Dockerfile
FROM busybox
CMD ["echo","hello","container"]
root@28dcd3a667c9:/home/jenkins# nerdctl build -t test:v1 .
root@28dcd3a667c9:/home/jenkins# nerdctl images
REPOSITORY TAG IMAGE ID CREATED PLATFORM SIZE BLOB SIZE
test v1 4943762c7956 7 seconds ago linux/amd64 4.1 MiB 2.1 MiB
harbor.local.com/cicd/buildkit v0.13.2 c3cb08891c15 15 minutes ago linux/amd64 190.3 MiB 87.2 MiB
harbor.local.com/cicd/jenkins-slave v1.0 1d5c5b1572bc 6 minutes ago linux/amd64 384.7 MiB 169.8 MiB
job任务创建与配置
配置webhook构建触发器,当分支代码提交时触发构建,具体配置如下:
流水线选择SCM从代码仓库中获取jenkinsfile,脚本路径填写Jenkinsfile-k8s.groov
效果演示
开发测试阶段
模拟开发人员完成功能开发后提交代码至test分支
[root@tiaoban sprint_boot_demo]# git checkout -b test origin/test
分支 'test' 设置为跟踪 'origin/test'。
切换到一个新分支 'test'
[root@tiaoban sprint_boot_demo]# git branch -a
master
* test
remotes/origin/HEAD -> origin/master
remotes/origin/master
remotes/origin/test
# 修改SpringBoot首页内容为Version:v2
[root@tiaoban sprint_boot_demo]# cat src/main/java/com/example/springbootdemo/HelloWorldController.java
package com.example.springbootdemo;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class HelloWorldController {
@RequestMapping("/")
@ResponseBody
public String hello() {
return "<h1>Hello SpringBoot</h1><p>Version:v2 Env:test</p>";
}
@RequestMapping("/health")
@ResponseBody
public String healthy() {
return "ok";
}
}
[root@tiaoban sprint_boot_demo]# git add .
[root@tiaoban sprint_boot_demo]# git commit -m "test环境更新版本至v2"
[test 68fb576] test环境更新版本至v2
1 file changed, 1 insertion(+), 1 deletion(-)
[root@tiaoban sprint_boot_demo]# git push
枚举对象中: 17, 完成.
对象计数中: 100% (17/17), 完成.
使用 4 个线程进行压缩
压缩对象中: 100% (6/6), 完成.
写入对象中: 100% (9/9), 707 字节 | 707.00 KiB/s, 完成.
总共 9(差异 2),复用 0(差异 0),包复用 0
remote:
remote: To create a merge request for test, visit:
remote: http://gitlab.local.com/develop/sprint_boot_demo/-/merge_requests/new?merge_request%5Bsource_branch%5D=test
remote:
To http://gitlab.local.com/develop/sprint_boot_demo.git
86a166a..68fb576 test -> test
此时查看cicd名称空间下的pod信息,发现已经创建一个名为springbootdemo-275-rf832-h6jkq-630x8的pod,包含3个container,分别是jnlp、maven、buildkitd。
[root@tiaoban ~]# kubectl get pod -n cicd
NAME READY STATUS RESTARTS AGE
gitlab-5997c5cdcd-2rvgz 1/1 Running 14 (100m ago) 15d
jenkins-6df7d6479b-v25rt 1/1 Running 9 (100m ago) 5d13h
sonarqube-postgresql-0 1/1 Running 14 (100m ago) 15d
sonarqube-sonarqube-0 1/1 Running 14 (100m ago) 15d
springbootdemo-275-rf832-h6jkq-630x8 3/3 Running 0 65s
查看jenkins任务信息,已顺利完成了集成部署工作。
并且收到了jenkins自动发出的邮件,内容如下:
查看SonarQube代码扫描信息,未发现异常代码。
查看k8s,已成功创建相关资源。
[root@tiaoban sprint_boot_demo]# kubectl get all -n test
NAME READY STATUS RESTARTS AGE
pod/demo-5d44f794d9-s7jw2 1/1 Running 0 7m38s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/demo ClusterIP 10.111.167.204 <none> 8888/TCP 4d3h
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/demo 1/1 1 1 4d3h
NAME DESIRED CURRENT READY AGE
replicaset.apps/demo-5d44f794d9 1 1 1 7m38s
此时模拟测试人员,访问测试环境域名
至此,开发测试阶段演示完成。
生产发布阶段
接下来演示master分支代码提交后,触发生产环境版本发布流程。
[root@tiaoban sprint_boot_demo]# git branch -a
master
* test
remotes/origin/HEAD -> origin/master
remotes/origin/master
remotes/origin/test
[root@tiaoban sprint_boot_demo]# git checkout master
切换到分支 'master'
您的分支与上游分支 'origin/master' 一致。
[root@tiaoban sprint_boot_demo]# vim src/main/java/com/example/springbootdemo/HelloWorldController.java
[root@tiaoban sprint_boot_demo]# cat src/main/java/com/example/springbootdemo/HelloWorldController.java
package com.example.springbootdemo;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class HelloWorldController {
@RequestMapping("/")
@ResponseBody
public String hello() {
return "<h1>Hello SpringBoot</h1><p>Version:v2 Env:prod</p>";
}
@RequestMapping("/health")
@ResponseBody
public String healthy() {
return "ok";
}
}
[root@tiaoban sprint_boot_demo]# git add .
[root@tiaoban sprint_boot_demo]# git commit -m "生产环境更新版本至v2"
[master 889fc5c] 生产环境更新版本至v2
1 file changed, 1 insertion(+), 1 deletion(-)
[root@tiaoban sprint_boot_demo]# git push
枚举对象中: 17, 完成.
对象计数中: 100% (17/17), 完成.
使用 4 个线程进行压缩
压缩对象中: 100% (6/6), 完成.
写入对象中: 100% (9/9), 719 字节 | 719.00 KiB/s, 完成.
总共 9(差异 2),复用 0(差异 0),包复用 0
To http://gitlab.local.com/develop/sprint_boot_demo.git
600a1b6..889fc5c master -> master
待收到邮件通知后,查看k8s资源,已经在prod名称空间下创建相关资源
[root@tiaoban sprint_boot_demo]# kubectl get all -n prod
NAME READY STATUS RESTARTS AGE
pod/demo-7c57975bd8-7nmnx 1/1 Running 0 41s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/demo ClusterIP 10.97.0.219 <none> 8888/TCP 41s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/demo 1/1 1 1 41s
NAME DESIRED CURRENT READY AGE
replicaset.apps/demo-7c57975bd8 1 1 1 41s
此时访问生产环境域名,服务可以正常访问。
此时查看Harbor仓库镜像信息,其中p开头的为生产环境镜像,t开头的为测试环境镜像。
jenkinsfile
完整的jenkinsfile如下所示,由于每个项目使用的开发语言和版本各不相同,因此建议将jenkinsfile存储在代码仓库随项目一同管理,使用yaml格式可以最大程度的定制slave容器。
pipeline {
agent {
kubernetes {
// 定义要在 Kubernetes 中运行的 Pod 模板
yaml '''
apiVersion: v1
kind: Pod
metadata:
labels:
app: jenkins-slave
spec:
containers:
- name: jnlp
image: harbor.local.com/cicd/jenkins-slave:v1.0
resources:
limits:
memory: "512Mi"
cpu: "500m"
securityContext:
privileged: true
volumeMounts:
- name: buildkit
mountPath: "/run/buildkit/"
- name: containerd
mountPath: "/run/containerd/containerd.sock"
- name: kube-config
mountPath: "/root/.kube/"
readOnly: true
- name: maven
image: harbor.local.com/cicd/maven:3.9.3
resources:
limits:
memory: "512Mi"
cpu: "500m"
command:
- 'sleep'
args:
- '9999'
volumeMounts:
- name: maven-data
mountPath: "/root/.m2"
- name: buildkitd
image: harbor.local.com/cicd/buildkit:v0.13.2
resources:
limits:
memory: "256Mi"
cpu: "500m"
securityContext:
privileged: true
volumeMounts:
- name: buildkit
mountPath: "/run/buildkit/"
- name: buildkit-data
mountPath: "/var/lib/buildkit/"
- name: containerd
mountPath: "/run/containerd/containerd.sock"
volumes:
- name: maven-data
persistentVolumeClaim:
claimName: jenkins-maven
- name: buildkit
hostPath:
path: /run/buildkit/
- name: buildkit-data
hostPath:
path: /var/lib/buildkit/
- name: containerd
hostPath:
path: /run/containerd/containerd.sock
- name: kube-config
secret:
secretName: kube-config
'''
retries 2
}
}
environment {
// 全局变量
HARBOR_CRED = "harbor-cuiliang-password"
IMAGE_NAME = ""
IMAGE_APP = "demo"
branchName = ""
}
stages {
stage('拉取代码') {
environment {
// gitlab仓库信息
GITLAB_CRED = "gitlab-cuiliang-password"
GITLAB_URL = "http://gitlab.cicd.svc/develop/sprint_boot_demo.git"
}
steps {
echo '开始拉取代码'
checkout scmGit(branches: [[name: '*/*']], extensions: [], userRemoteConfigs: [[credentialsId: "${GITLAB_CRED}", url: "${GITLAB_URL}"]])
// 获取当前拉取的分支名称
script {
def branch = env.GIT_BRANCH ?: 'master'
branchName = branch.split('/')[-1]
}
echo '拉取代码完成'
}
}
stage('编译打包') {
steps {
container('maven') {
// 指定使用maven container进行打包
echo '开始编译打包'
sh 'mvn clean package'
echo '编译打包完成'
}
}
}
stage('代码审查') {
environment {
// SonarQube信息
SONARQUBE_SCANNER = "SonarQubeScanner"
SONARQUBE_SERVER = "SonarQubeServer"
}
steps {
echo '开始代码审查'
script {
def scannerHome = tool "${SONARQUBE_SCANNER}"
withSonarQubeEnv("${SONARQUBE_SERVER}") {
sh "${scannerHome}/bin/sonar-scanner"
}
}
echo '代码审查完成'
}
}
stage('构建镜像') {
environment {
// harbor仓库信息
HARBOR_URL = "harbor.local.com"
HARBOR_PROJECT = "spring_boot_demo"
// 镜像标签
IMAGE_TAG = ''
// 镜像名称
IMAGE_NAME = ''
}
steps {
echo '开始构建镜像'
script {
if (branchName == 'master') {
IMAGE_TAG = VersionNumber versionPrefix: 'p', versionNumberString: '${BUILD_DATE_FORMATTED, "yyMMdd"}.${BUILDS_TODAY}'
} else if (branchName == 'test') {
IMAGE_TAG = VersionNumber versionPrefix: 't', versionNumberString: '${BUILD_DATE_FORMATTED, "yyMMdd"}.${BUILDS_TODAY}'
} else {
error("Unsupported branch: ${params.BRANCH}")
}
IMAGE_NAME = "${HARBOR_URL}/${HARBOR_PROJECT}/${IMAGE_APP}:${IMAGE_TAG}"
sh "nerdctl build --insecure-registry -t ${IMAGE_NAME} . "
}
echo '构建镜像完成'
echo '开始推送镜像'
// 获取harbor账号密码
withCredentials([usernamePassword(credentialsId: "${HARBOR_CRED}", passwordVariable: 'HARBOR_PASSWORD', usernameVariable: 'HARBOR_USERNAME')]) {
// 登录Harbor仓库
sh """nerdctl login --insecure-registry ${HARBOR_URL} -u ${HARBOR_USERNAME} -p ${HARBOR_PASSWORD}
nerdctl push --insecure-registry ${IMAGE_NAME}"""
}
echo '推送镜像完成'
echo '开始删除镜像'
script {
sh "nerdctl rmi -f ${IMAGE_NAME}"
}
echo '删除镜像完成'
}
}
stage('项目部署') {
environment {
// 资源清单名称
YAML_NAME = "k8s.yaml"
}
steps {
echo '开始修改资源清单'
script {
if (branchName == 'master' ) {
NAME_SPACE = 'prod'
DOMAIN_NAME = 'demo.local.com'
} else if (branchName == 'test') {
NAME_SPACE = 'test'
DOMAIN_NAME = 'demo.test.com'
} else {
error("Unsupported branch: ${params.BRANCH}")
}
}
// 使用Content Replace插件进行k8s资源清单内容替换
contentReplace(configs: [fileContentReplaceConfig(configs: [fileContentReplaceItemConfig(replace: "${IMAGE_NAME}", search: 'IMAGE_NAME'),
fileContentReplaceItemConfig(replace: "${NAME_SPACE}", search: 'NAME_SPACE'),
fileContentReplaceItemConfig(replace: "${DOMAIN_NAME}", search: 'DOMAIN_NAME')],
fileEncoding: 'UTF-8',
filePath: "${YAML_NAME}",
lineSeparator: 'Unix')])
echo '修改资源清单完成'
sh "cat ${YAML_NAME}"
echo '开始部署资源清单'
sh "kubectl apply -f ${YAML_NAME}"
echo '部署资源清单完成'
}
}
}
post {
always {
echo '开始发送邮件通知'
emailext(subject: '构建通知:${PROJECT_NAME} - Build # ${BUILD_NUMBER} - ${BUILD_STATUS}!',
body: '${FILE,path="email.html"}',
to: '[email protected]')
echo '邮件通知发送完成'
}
}
}
查看更多
微信公众号
微信公众号同步更新,欢迎关注微信公众号《崔亮的博客》第一时间获取最近文章。
博客网站
崔亮的博客-专注devops自动化运维,传播优秀it运维技术文章。更多原创运维开发相关文章,欢迎访问https://www.cuiliangblog.cn