# 09 Docker 应⽤的 CICD

# 1. CI/CD 实现⽅式

# 1.1 传统环境的 CI/CD

CI:开发⼈员 -> 提交代码 ->Gitlab 仓库 ->Jenkins/CI 抓取代码 -> 漏洞扫描 -> 编译 --> 推送 nexus 仓库 --> 部署⾄测试环境;

CD:jenkins 拉取 Nexus 仓库代码 --> 部署⽣产环境;

1.jpg

# 1.2 Docker 实现 CI/CD

CI:开发⼈员 -> 提交代码 ->gitlab 仓库 ->Jenkins/CI 抓取代码 -> 漏洞扫描 -> 编译 -> 构建镜像 -> 推送 Harbor-> 部署应⽤⾄ Docker 测试环境;

CD:Jenkins/CD-> 获取 Harbor 仓库对应项⽬镜像 -> 部署应⽤⾄ Docker ⽣产环境;

2.jpg

# 2. 部署服务组件

# 2.1 服务地址规划
⻆⾊名称系统IP 地址部署⽅式
gitlabCentos 7.6192.168.40.110容器部署
jenkinsCentos 7.6192.168.40.120容器部署
nexusCentos 7.6192.168.40.121容器部署
sonarqubeCentos 7.6192.168.40.130容器部署
harborCentos 7.6192.168.40.134
docker-testCentos 7.6192.168.40.1818888
docker-prodCentos 7.6192.168.40.18280

1 、所有节点部署 docker

[root@harbor ~]# yum install -y yum-utils device-mapper-persistent-data lvm2
[root@harbor ~]# curl -o /etc/yum.repos.d/docker-ce.repo  https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
[root@harbor ~]# yum list docker-ce --showduplicates |sort -r 
[root@harbor ~]# yum install docker-ce docker-compose -y

2、配置 Docker 加速

[root@harbor ~]# sudo mkdir -p /etc/docker
[root@harbor ~]# sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": [
      "https://docker.credclouds.com",
      "https://k8s.credclouds.com",
      "https://quay.credclouds.com",
      "https://gcr.credclouds.com",
      "https://k8s-gcr.credclouds.com",
      "https://ghcr.credclouds.com",
      "https://do.nark.eu.org",
      "https://docker.m.daocloud.io",
      "https://docker.nju.edu.cn",
      "https://docker.mirrors.sjtug.sjtu.edu.cn",
      "https://docker.1panel.live",
      "https://docker.rainbond.cc"
  ], 
  "exec-opts": ["native.cgroupdriver=systemd"] 
}
EOF
[root@harbor ~]# systemctl enable docker --now

3、安装 Harbor

[root@harbor ~]# cd /soft/
[root@harbor ~]# wget https://github.com/goharbor/harbor/releases/download/v2.6.1/harbor-offline-installer-v2.6.1.tgz
[root@harbor soft]# tar xf harbor-offline-installer-v2.6.1.tgz
[root@harbor soft]# cd harbor
[root@harbor harbor]# vim harbor.yml
hostname: harbor.hmallleasing.com
...
#https:
#  # https port for harbor, default is 443
#  port: 443
#  # The path of cert and key files for nginx
#  certificate: /your/certificate/path
#  private_key: /your/private/key/path
...
harbor_admin_password: Harbor12345
[root@harbor harbor]#  ./install.sh

4、推送镜像至 Harbor

[root@harbor harbor]# docker tag beae173ccac6 harbor.hmallleasing.com/ops/busybox.v1
[root@harbor harbor]# docker push harbor.hmallleasing.com/ops/busybox.v1
[root@harbor harbor]# docker login harbor.hmallleasing.com
[root@harbor harbor]# docker push harbor.hmallleasing.com/ops/busybox.v1

5、Harbor 停止与启动

#停用 Harbor
[root@harbor harbor]# pwd
/soft/harbor
[root@harbor harbor]# docker-compose stop
 #启动 Harbor
[root@harbor harbor]# docker-compose up -d
[root@harbor harbor]# docker-compose start
# 2.2 部署 Gitlab

1、部署 gitlab

docker run -d --name gitlab \
-p 80:80 -p 9022:22 \
--restart always \
--privileged=true \
--hostname "gitlab.hmallleasing.com" \
--add-host jenkins.hmallleasing.com:192.168.40.120 \
-v /data/gitlab/etc:/etc/gitlab \
-v /data/gitlab/log:/var/log/gitlab \
-v /data/gitlab/opt:/var/opt/gitlab \
registry.cn-hangzhou.aliyuncs.com/old_xu/gitlab-ce:16.10.0-ce.0

2、获取 gitlab 的密码

[root@gitlab ~]# cat /data/gitlab/etc/initial_root_password
Password: b//+rFJv06YO83ANK1MRLvFj9ygC1dvqq2Lr4DjNj6Y=

3、准备项⽬代码

[root@gitlab ~]# wget http://file.oldxu.net/jenkins/springboot-devops-demo-jar-java17.tar.gz
[root@gitlab ~]# tar xf springboot-devops-demo-jar-java17.tar.gz

4、为项⽬源代码注⼊ Dockerfile ⽂件

[root@gitlab springboot-devops-demo-jar-java17]# cat Dockerfile
FROM openjdk:17-jdk-alpine
COPY ./target/*.jar /oldxu-jar.jar
EXPOSE 8080
ENTRYPOINT ["sh","-c","java -jar /oldxu-jar.jar --server.port=8080"]

5、提交代码⾄ gitlab 服务器

git config --global user.name "Xu Yong"
git config --global user.email "373370405@qq.com"
git init
git add .
git commit -m "first"
git remote add origin http://gitlab.hmallleasing.com/root/springboot-devops-demo-jar.git
git push origin master

6、检查 gitlab 仓库的代码

1.jpg

# 2.3 部署 Jenkins

因为 Jenkins 需要调⽤ Docker 构建镜像,但默认的 Jenkins 没有内置 docker 命令,也⽆法访问宿主机的 Docker,因此我们需要对 Jenkins 进⾏如下配置:

  • 1、挂在宿主机 docker 命令⾄ jenkins 容器内;
  • 2、挂载宿主机的 docker.sock ⽂件,确保 jenkins 容器能通过宿主机的 docker 进⾏镜像的构建;
  • 3、设置 jenkins 的时区,确保获取的时间是准确的;
  • 4、由于 gitlab、harbor 等服务都需要通过域名进⾏访问,因此可以通过 --add-host 明确指定对应的解析;
  • 5、jenkins 的数据⽬录需要挂载到宿主机的某个⽬录存储;

1、部署 Jenkins

docker run -d --name jenkins \
--hostname "jenkins.hmallleasing.com" \
--restart=always \
-p 8080:8080 \
-p 50000:50000 \
-u root \
--privileged=true \
-e TZ=Asia/Shanghai \
-v /etc/localtime:/etc/localtime:ro \
-v jenkins_home:/var/jenkins_home \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /usr/bin/docker:/usr/bin/docker \
--add-host gitlab.hmallleasing.com:192.168.40.110 \
--add-host nexus.hmallleasing.com:192.168.40.121 \
--add-host sonar.hmallleasing.com:192.168.40.130 \
--add-host harbor.hmallleasing.com:192.168.40.134 \
jenkins/jenkins:2.528.2-lts-jdk17
# nerdctl
nerdctl run -d --name jenkins \
--hostname "jenkins.oldxu.net" \
--restart=always \
-p 8080:8080 \
-p 50000:50000 \
-u root \
--privileged=true \
-e TZ=Asia/Shanghai \
-v /etc/localtime:/etc/localtime:ro \
-v jenkins_home:/var/jenkins_home \
-v /usr/local/bin/nerdctl:/usr/local/bin/nerdctl \
-v /run/containerd/containerd.sock:/run/containerd/containerd.sock \
-v /usr/local/bin/buildctl:/usr/local/bin/buildctl \
-v /run/buildkit/buildkitd.sock:/run/buildkit/buildkitd.sock \
--add-host gitlab.hmallleasing.com:192.168.40.110 \
--add-host nexus.hmallleasing.com:192.168.40.121 \
--add-host sonar.hmallleasing.com:192.168.40.130 \
--add-host harbor.hmallleasing.com:192.168.40.34 \
jenkins/jenkins:2.528.2-lts-jdk17

2、获取 Jenkins 的初始密码

[root@jenkins ~]#  cat /var/lib/docker/volumes/jenkins_home/_data/secrets/initialAdminPassword
90da771dfbdb4b0db4125137fef409e8

3、安装 jenkins 插件

  • 1、安装 docker、Docker Pipeline 插件,⽤于调⽤ docker 容器;
  • 2、安装 pipeline、Pipeline Stage View、Blue Ocean,⽤于执⾏流⽔线语法
  • 3、安装 git、gitlab 插件
  • 4、安装 Active Choices 插件
  • 5、安装 Localization: Chinese 中⽂插件

# 3. 获取代码

# 3.1 创建凭据

点击系统管理 ->Manage Credentials-> 全局凭据,创建⽤户访问 gitlab 的凭据,填写⽤户名、密码、ID 和描述

1.jpg

# 3.2 编写 Stage

创建流水线项目 devops_demo_docker-CI

pipeline {
    agent any
	
	//将workspace工作目录赋予给WORK_SPACES变量,后续stage能正常找到工作目录
	environment {
        Git_Id = "gitlab-root-token"
        Git_Url = "http://gitlab.hmallleasing.com/root/springboot-devops-demo-jar.git"
		WORK_SPACES = "${WORKSPACE}"
		Harbor_Url = "harbor.hmallleasing.com"
		Pro = "app" //Harbor镜像存储的目录
		ImageName = "${Harbor_Url}/${Pro}/springboot" //镜像的完整名称
	}
    stages {
		stage('获取代码'){
			steps {
				git branch: 'master', credentialsId: "${Git_Id }", url: "${Git_Url}"
				sh 'pwd && ls -l'
			}
		}		
    }
}
# 3.3 检查结果

代码被获取到 /var/jenkins_home/workspace ⼯作路径下

2.jpg

# 4. 编译代码

由于 Jenkins 是通过 Docker 运⾏的,因此 Jenkins 容器内并不包含 Maven 相关的命令。如果需要在 Jenkins 中编译代码,可以选择如下两种⽅法:

1、在宿主机上安装 Maven,并将 Maven 安装⽬录挂载到 Jenkins 容器内。这样,Jenkins 容器可以访问并使⽤宿主机上的 Maven 命令。

2、让 jenkins 在编译时运⾏⼀个 Maven 的容器作为构建环境。这样,每次运⾏构建任务时,Jenkins 会⾃动启动⼀个 Maven 容器来执⾏项⽬编译。

# 4.1 maven 环境

1、下载 maven 镜像

[root@docker-node1 ~]# docker pull maven:3.8.5-openjdk-17-slim

2、准备 maven 配置⽂件,可以在配置⽂件中注⼊阿⾥云加速地址,或是 Nexus 制品库地址(避免私有依赖的 jar 包⽆法获取)

[root@docker-node1 ~]# wget -O /etc/maven_settings.xml https://linux.oldxu.net/settings_docker.xml
# 配置 nexus 仓库认证
<servers>
    <server>
        <id>maven-central</id>
        <username>admin</username>
        <password>talent</password>
    </server>
    <server>
        <id>nexus-snapshots</id>
        <username>admin</username>
        <password>talent</password>
        </server>
    <server>
        <id>nexus-releases</id>
        <username>admin</username>
        <password>talent</password>
    </server>
</servers>
# 配置 nexus 仓库地址
<mirrors>
    <mirror>
        <id>maven-central</id>
        <mirrorOf>*</mirrorOf>
        <url>http://nexus.hmallleasing.com/repository/maven-central/</url>
    </mirror>
    <mirror>
        <id>nexus-snapshots</id>
        <mirrorOf>*</mirrorOf>
        <url>http://nexus.hmallleasing.com/repository/maven-snapshots/</url>
    </mirror>
    <mirror>
        <id>nexus-releases</id>
        <mirrorOf>*</mirrorOf>
        <url>http://nexus.hmallleasing.com/repository/maven-releases/</url>
    </mirror>
</mirrors>
# 4.2 编写 Stage
pipeline {
    agent any
	
	//将workspace工作目录赋予给WORK_SPACES变量,后续stage能正常找到工作目录
	environment {
        Git_Id = "gitlab-root-token"
        Git_Url = "http://gitlab.hmallleasing.com/root/springboot-devops-demo-jar.git"
		WORK_SPACES = "${WORKSPACE}"
		Harbor_Url = "harbor.hmallleasing.com"
		Pro = "app" //Harbor镜像存储的目录
		ImageName = "${Harbor_Url}/${Pro}/springboot" //镜像的完整名称
	}
    stages {
		stage('获取代码'){
			steps {
				git branch: 'master', credentialsId: "${Git_Id }", url: "${Git_Url}"
				sh 'pwd && ls -l'
			}
		}
		
		stage('编译代码') {
			agent {
				docker {
					image 'maven:3.8.5-openjdk-17-slim'
					args '-v $HOME/.m2:/root/.m2 -v /etc/maven_settings.xml:/usr/share/maven/conf/settings.xml'
					//当链接的镜像是私有仓库时,我们需要传递私有仓库的地址和认证信息
					//registryUrl "https://${Url}"
					//registryCredentialsId 'harbor-auth'
				}
			}
			steps {
				sh 'cd ${WORK_SPACES} && mvn package -Dmaven.test.skip=true'
				sh 'cd ${WORK_SPACES} && ls -l && ls -l target/'
			}
		}			
    }
}
# 4.3 检查结果

3.jpg

# 5. 构建镜像

镜像构建通过镜像名称 + 镜像 Tag 组成,所以我们可以先定义⼀个阶段完成镜像 Tag 的⽣成;

# 5.1 定义镜像 Tag

1、创建新的 Stage 来定义镜像 Tag 相关内容

pipeline {
    agent any
	
	//将workspace工作目录赋予给WORK_SPACES变量,后续stage能正常找到工作目录
	environment {
        Git_Id = "gitlab-root-token"
        Git_Url = "http://gitlab.hmallleasing.com/root/springboot-devops-demo-jar.git"
		WORK_SPACES = "${WORKSPACE}"
		Harbor_Url = "harbor.hmallleasing.com"
		Pro = "app" //Harbor镜像存储的目录
		ImageName = "${Harbor_Url}/${Pro}/springboot" //镜像的完整名称
	}
    stages {
		stage('获取代码'){
			steps {
				git branch: 'master', credentialsId: "${Git_Id }", url: "${Git_Url}"
				sh 'pwd && ls -l'
			}
		}
		
		stage('编译代码') {
			agent {
				docker {
					image 'maven:3.8.5-openjdk-17-slim'
					args '-v $HOME/.m2:/root/.m2 -v /etc/maven_settings.xml:/usr/share/maven/conf/settings.xml'
					//当链接的镜像是私有仓库时,我们需要传递私有仓库的地址和认证信息
					//registryUrl "https://${Url}"
					//registryCredentialsId 'harbor-auth'
				}
			}
			steps {
				sh 'cd ${WORK_SPACES} && mvn package -Dmaven.test.skip=true'
				sh 'cd ${WORK_SPACES} && ls -l && ls -l target/'
			}
		}
		
		stage('生成成镜像Tag'){
			steps {
				script {
					//镜像本次的CommitID
					env.COMMITID = sh(returnStdout: true, script: "git log -n1 --pretty=format:'%h'").trim()
					//镜像构建的时间
					env.BuildTime = sh(returnStdout: true, script: "date +%Y%m%d_%H%M%S").trim()
					//镜像Tag
					env.ImageTag = COMMITID + "_" + BuildTime 
				}
				sh 'echo ${ImageTag}'
			}
		}
		
    }
}

2、检查镜像 Tag 标签格式 commit + 年⽉⽇ + 时分秒

4.jpg

# 5.2 编写 Stage
pipeline {
    agent any
	
	//将workspace工作目录赋予给WORK_SPACES变量,后续stage能正常找到工作目录
	environment {
        Git_Id = "gitlab-root-token"
        Git_Url = "http://gitlab.hmallleasing.com/root/springboot-devops-demo-jar.git"
		WORK_SPACES = "${WORKSPACE}"
		Harbor_Url = "harbor.hmallleasing.com"
		Pro = "app" //Harbor镜像存储的目录
		ImageName = "${Harbor_Url}/${Pro}/springboot" //镜像的完整名称
	}
    stages {
		stage('获取代码'){
			steps {
				git branch: 'master', credentialsId: "${Git_Id }", url: "${Git_Url}"
				sh 'pwd && ls -l'
			}
		}
		
		stage('编译代码') {
			agent {
				docker {
					image 'maven:3.8.5-openjdk-17-slim'
					args '-v $HOME/.m2:/root/.m2 -v /etc/maven_settings.xml:/usr/share/maven/conf/settings.xml'
					//当链接的镜像是私有仓库时,我们需要传递私有仓库的地址和认证信息
					//registryUrl "https://${Url}"
					//registryCredentialsId 'harbor-auth'
				}
			}
			steps {
				sh 'cd ${WORK_SPACES} && mvn package -Dmaven.test.skip=true'
				sh 'cd ${WORK_SPACES} && ls -l && ls -l target/'
			}
		}
		
		stage('生成成镜像Tag'){
			steps {
				script {
					//镜像本次的CommitID
					env.COMMITID = sh(returnStdout: true, script: "git log -n1 --pretty=format:'%h'").trim()
					//镜像构建的时间
					env.BuildTime = sh(returnStdout: true, script: "date +%Y%m%d_%H%M%S").trim()
					//镜像Tag
					env.ImageTag = COMMITID + "_" + BuildTime 
				}
				sh 'echo ${ImageTag}'
			}
		}
		
		stage('制作镜像'){
			steps {
				sh 'cd ${WORK_SPACES} && docker build -t ${ImageName}:${ImageTag} .'
			}
		}
		
    }
}
# 5.3 检查结果
[root@jenkins ~]# docker images|grep harbor.hmallleasing.com
harbor.hmallleasing.com/app/springboot             aeacd46_20251130_165729   aa41b44e5aaf   7 minutes ago   345MB

# 6. 推送镜像⾄仓库

# 6.1 创建凭据

由于推送镜像⾄ Harbor 私有仓库,需要身份认证,为此我们可以创建⼀个凭据,存储 Harbor 对应的⽤户名以及密码,⽽后在 Pipeline 中进⾏使⽤。

点击系统管理 ->Manage Credentials-> 全局凭据

5.jpg

# 6.2 编写 Stage
pipeline {
    agent any
	
	//将workspace工作目录赋予给WORK_SPACES变量,后续stage能正常找到工作目录
	environment {
        Git_Id = "gitlab-root-token"
        Git_Url = "http://gitlab.hmallleasing.com/root/springboot-devops-demo-jar.git"
		WORK_SPACES = "${WORKSPACE}"
		Harbor_Url = "harbor.hmallleasing.com"
		Pro = "app" //Harbor镜像存储的目录
		ImageName = "${Harbor_Url}/${Pro}/springboot" //镜像的完整名称
        HARBOR_ID = "harbor-auth"
	}
    stages {
		stage('获取代码'){
			steps {
				git branch: 'master', credentialsId: "${Git_Id }", url: "${Git_Url}"
				sh 'pwd && ls -l'
			}
		}
		
		stage('编译代码') {
			agent {
				docker {
					image 'maven:3.8.5-openjdk-17-slim'
					args '-v $HOME/.m2:/root/.m2 -v /etc/maven_settings.xml:/usr/share/maven/conf/settings.xml'
					//当链接的镜像是私有仓库时,我们需要传递私有仓库的地址和认证信息
					//registryUrl "https://${Url}"
					//registryCredentialsId 'harbor-auth'
				}
			}
			steps {
				sh 'cd ${WORK_SPACES} && mvn package -Dmaven.test.skip=true'
				sh 'cd ${WORK_SPACES} && ls -l && ls -l target/'
			}
		}
		
		stage('生成成镜像Tag'){
			steps {
				script {
					//镜像本次的CommitID
					env.COMMITID = sh(returnStdout: true, script: "git log -n1 --pretty=format:'%h'").trim()
					//镜像构建的时间
					env.BuildTime = sh(returnStdout: true, script: "date +%Y%m%d_%H%M%S").trim()
					//镜像Tag
					env.ImageTag = COMMITID + "_" + BuildTime 
				}
				sh 'echo ${ImageTag}'
			}
		}
		
		stage('制作镜像'){
			steps {
				sh 'cd ${WORK_SPACES} && docker build -t ${ImageName}:${ImageTag} .'
			}
		}
		
		stage('推送镜像至Harbor'){
			steps {
                withCredentials([usernamePassword(credentialsId: "${HARBOR_ID}", passwordVariable: 'HARBOR_PASSWORD', usernameVariable: 'HARBOR_USER')]) {
                    //登陆harbor
                    sh 'echo "${HARBOR_PASSWORD}" | docker login ${Harbor_Url} -u "${HARBOR_USER}" --password-stdin'
					sh 'docker push ${ImageName}:${ImageTag}'
				}
			}
		}
		
    }
}
# 6.3 检查结果

1.jpg

# 7. 交付应⽤⾄ Docker

由于 Jenkins 容器内封装了 Docker 命令,并且挂载了宿主机的 socket ⽂件,我们可以直接在 Jenkins 任务中使⽤容器内的 Docker 命令来构建和运⾏容器。

# 7.1 编写 Stage

1、编写 pipeline 流水线

pipeline {
    agent any
	
	//将workspace工作目录赋予给WORK_SPACES变量,后续stage能正常找到工作目录
	environment {
        Git_Id = "gitlab-root-token"
        Git_Url = "http://gitlab.hmallleasing.com/root/springboot-devops-demo-jar.git"
		WORK_SPACES = "${WORKSPACE}"
		Harbor_Url = "harbor.hmallleasing.com"
		Pro = "app" //Harbor镜像存储的目录
		ImageName = "${Harbor_Url}/${Pro}/springboot" //镜像的完整名称
        HARBOR_ID = "harbor-auth"
	}
    stages {
		stage('获取代码'){
			steps {
				git branch: 'master', credentialsId: "${Git_Id }", url: "${Git_Url}"
				sh 'pwd && ls -l'
			}
		}
		
		stage('编译代码') {
			agent {
				docker {
					image 'maven:3.8.5-openjdk-17-slim'
					args '-v $HOME/.m2:/root/.m2 -v /etc/maven_settings.xml:/usr/share/maven/conf/settings.xml'
					//当链接的镜像是私有仓库时,我们需要传递私有仓库的地址和认证信息
					//registryUrl "https://${Url}"
					//registryCredentialsId 'harbor-auth'
				}
			}
			steps {
				sh 'cd ${WORK_SPACES} && mvn package -Dmaven.test.skip=true'
				sh 'cd ${WORK_SPACES} && ls -l && ls -l target/'
			}
		}
		
		stage('生成成镜像Tag'){
			steps {
				script {
					//镜像本次的CommitID
					env.COMMITID = sh(returnStdout: true, script: "git log -n1 --pretty=format:'%h'").trim()
					//镜像构建的时间
					env.BuildTime = sh(returnStdout: true, script: "date +%Y%m%d_%H%M%S").trim()
					//镜像Tag
					env.ImageTag = COMMITID + "_" + BuildTime 
				}
				sh 'echo ${ImageTag}'
			}
		}
		
		stage('制作镜像'){
			steps {
				sh 'cd ${WORK_SPACES} && docker build -t ${ImageName}:${ImageTag} .'
			}
		}
		
		stage('推送镜像至Harbor'){
			steps {
                withCredentials([usernamePassword(credentialsId: "${HARBOR_ID}", passwordVariable: 'HARBOR_PASSWORD', usernameVariable: 'HARBOR_USER')]) {
                    //登陆harbor
                    sh 'echo "${HARBOR_PASSWORD}" | docker login ${Harbor_Url} -u "${HARBOR_USER}" --password-stdin'
					sh 'docker push ${ImageName}:${ImageTag}'
				}
			}
		}
		
		stage('交付镜像测试测试环境') {
			steps {
				sh "export DOCKER_HOST=tcp://192.168.40.181:2375 && \
					docker rm -f spring_test && \
					docker run -d --name spring_test -p8888:8080 ${ImageName}:${ImageTag}"
			}
		}
			
    }
}

2、在测试服务器上,开启 Docker 的远程访问功能;

[root@docker_test ~]# cat /lib/systemd/system/docker.service
...
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock -H tcp://0.0.0.0:2375
...

3、使⽤方法

export DOCKER_HOST=tcp://10.0.0.182:2375
docker run ....
# 7.2 检查结果

1、检查容器的运⾏结果

[root@docker_test ~]# docker ps
CONTAINER ID   IMAGE                                                            COMMAND                  CREATED              STATUS              PORTS                                       NAMES
f1333491e731   harbor.hmallleasing.com/app/springboot:aeacd46_20251130_182807   "sh -c 'java -jar /o…"   About a minute ago   Up About a minute   0.0.0.0:8888->8080/tcp, :::8888->8080/tcp   spring_test

2、访问测试

2.jpg

# 8. 实现⾃动化 CI

⾃动化 CI,就是通过 webhook 来实现,所谓 webhook 就是当开发⼀提交代码,则⾃定对项⽬进⾏构建操作;

# 8.1 配置 Jenkins

1、点击 jenkins 中对应的项⽬,找到构建触发器 -->Build when a change ispushed to GitLab;

2.jpg

2、找到⾼级,然后找到 Secret token,点击 Generate ⽣成 token

3.jpg

# 8.2 配置 Gitlab

1、配置 gitlab , 菜单 -> 管理员 -> 设置 -> ⽹络 -> 出站请求 -> 允许 webhooks 集成对本地⽹络的请求 (勾选)

4.jpg

2、找到对应要实现⾃动化发布的项⽬, 点击设置 -->webhook--> 添加新的 webhook

  • URL--> 通知 jenkins 的哪个项⽬地址
  • 令牌 --> jenkins 针对项⽬⽣成的 token 令牌
  • 触发来源 --> 推送事件

5.jpg

# 8.3 验证结果

6.jpg

# 9. 实现 CD 阶段

CI 与 CD 两个流⽔线

  • CI:拉取代码 -> 漏洞检测 -> 代码编译 -> 镜像制作 -> 推送私有仓库 -> 交付测试环境
  • CD:获取 Harbor 仓库对应项⽬镜像的 tags-> 部署⾄⽣产环境
# 9.1 CD 实现思路
  • 1、获取 Harbor 对应项⽬的 tag;
  • 2、编写 Stage 拼接完整的镜像名称;
  • 3、调⽤ docker 命令在远程⽣产服务器上运⾏对应的容器实例;

注意:获取镜像 Tag 可以使⽤ 级联变量 ⽅式实现,因为镜像可能存储在不同的 Harbor 或不同的项⽬路径中,写死后期修改麻烦;

  • 1、使⽤ Active Choices Parameter 定义 Harbor 仓库地址(变量名:Harbor_Url);
  • 2、使⽤ Active Choices Parameter 定义 Harbor 项⽬名称(变量名:Harbor_Pro);
  • 3、使⽤ Active Choices Parameter 定义 Harbor 镜像名称(变量名:Image_Name);
  • 4、使⽤ Active Choices Reactive Parameter 引⽤并参考此前变量,最终获取镜像 Tag(变量名:Image_tags)

需要安装: Active Choices 插件才可以实现;

# 9.2 命令获取镜像 Tag

1、创建流水线项目 devops_demo_docker-CD

[root@jenkins ~]# curl -s -u admin:talent -H 'Content-Type: application/json' -X GET https://harbor.hmallleasing.com/v2/app/springboot/tags/list| sed -r 's#(\{.*\[)(.*)(.*\]\})#\2#g' | xargs -d "," -n1|xargs -n1
aeacd46_20251130_180212
aeacd46_20251130_180702
aeacd46_20251130_181934
aeacd46_20251130_182658
aeacd46_20251130_182807
# 9.3 创建级联变量

1、添加⼀个 Active Choices Parameter 的参数化构建,创建 Harbor_Url 变量,变量值 return ["harbor.hmallleasing.com","harbor-dev..hmallleasing.com","harbor-prod..hmallleasing.com"] ,然后点击 Approve script

3.jpg

2、添加⼀个 Active Choices Parameter 的参数化构建,创建 Harbor_Pro 变量,变量值 return ["app","ops","base","library"] ,然后点击 Approve script

4.jpg

3、添加⼀个 Active Choices Parameter 的参数化构建,创建 Image_Name 变量,变量值 return ["springboot","nginx","tomcat"] ,然后点击 Approve script

5.jpg

4、添加⼀个 Active Choices Reactive Parameter 的参数化构建

变量名为:Image_tags

变量值:此前获取 tags 的脚本

除此外,它还需要级联之前的变量 Harbor_Url,Harbor_Pro,Image_Name

def get_tag = [ "bash","-c","curl -s -u admin:talent -H 'Content-Type: application/json' -X GET https://${Harbor_Url}/v2/${Harbor_Pro}/${Image_Name}/tags/list | sed -r 's#(\\{.*\\[)(.*)(.*\\]\\})#\\2#g'| xargs -d ',' -n1|xargs -n1 | sort -t '_' -k2 -k3 -nr|head -n5"]
return get_tag.execute().text.tokenize("\n")

6.jpg

7.jpg

5、验证结果(根据选择的不同,Image_Tags 也会列出不同的结果)

8.jpg

# 9.4 编写 Stage

1、编写 stage

pipeline {
	agent any
 
	//将传参的内容设定为变量
	environment {
		Full_Images="${Harbor_Url}/${Harbor_Pro}/${Image_Name}:${Image_tags}"
	}
 
	stages {
		stage('获取完整镜像名称') {
			steps {
				sh 'echo 镜像名称: ${Full_Images}'
			}
		}
		
		stage('交付应用至Docker') {
			steps {
				//这个地址可以通过传参的方式设定
				sh "export DOCKER_HOST=tcp://192.168.40.182:2375 && \
				docker rm -f springboot_prod && \
				docker run -d -p 80:8080 --name springboot_prod ${Full_Images}"
			}
		}
	}
}

2、在测试服务器上,开启 Docker 的远程访问功能;

[root@docker_prod ~]# cat /lib/systemd/system/docker.service
...
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock -H tcp://0.0.0.0:2375
...

3、使⽤方法

[root@docker_prod ~]# systemctl daemon-reload
[root@docker_prod ~]# systemctl restart docker
export DOCKER_HOST=tcp://10.0.0.182:2375
docker run ....

4、部署后检查结果

[root@docker_prod ~]# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.40.134 harbor.hmallleasing.com

9.jpg

# 10. 完整 Pipeline

# 10.1 Pipeline-CI
pipeline {
    agent any
	
	//将workspace工作目录赋予给WORK_SPACES变量,后续stage能正常找到工作目录
	environment {
        Git_Id = "gitlab-root-token"
        Git_Url = "http://gitlab.hmallleasing.com/root/springboot-devops-demo-jar.git"
		WORK_SPACES = "${WORKSPACE}"
		Harbor_Url = "harbor.hmallleasing.com"
		Pro = "app" //Harbor镜像存储的目录
		ImageName = "${Harbor_Url}/${Pro}/springboot" //镜像的完整名称
        HARBOR_ID = "harbor-auth"
	}
    stages {
		stage('获取代码'){
			steps {
				git branch: 'master', credentialsId: "${Git_Id }", url: "${Git_Url}"
				sh 'pwd && ls -l'
			}
		}
		
		stage('编译代码') {
			agent {
				docker {
					image 'maven:3.8.5-openjdk-17-slim'
					args '-v $HOME/.m2:/root/.m2 -v /etc/maven_settings.xml:/usr/share/maven/conf/settings.xml'
					//当链接的镜像是私有仓库时,我们需要传递私有仓库的地址和认证信息
					//registryUrl "https://${Url}"
					//registryCredentialsId 'harbor-auth'
				}
			}
			steps {
				sh 'cd ${WORK_SPACES} && mvn package -Dmaven.test.skip=true'
				sh 'cd ${WORK_SPACES} && ls -l && ls -l target/'
			}
		}
		
		stage('生成成镜像Tag'){
			steps {
				script {
					//镜像本次的CommitID
					env.COMMITID = sh(returnStdout: true, script: "git log -n1 --pretty=format:'%h'").trim()
					//镜像构建的时间
					env.BuildTime = sh(returnStdout: true, script: "date +%Y%m%d_%H%M%S").trim()
					//镜像Tag
					env.ImageTag = COMMITID + "_" + BuildTime 
				}
				sh 'echo ${ImageTag}'
			}
		}
		
		stage('制作镜像'){
			steps {
				sh 'cd ${WORK_SPACES} && docker build -t ${ImageName}:${ImageTag} .'
			}
		}
		
		stage('推送镜像至Harbor'){
			steps {
                withCredentials([usernamePassword(credentialsId: "${HARBOR_ID}", passwordVariable: 'HARBOR_PASSWORD', usernameVariable: 'HARBOR_USER')]) {
                    //登陆harbor
                    sh 'echo "${HARBOR_PASSWORD}" | docker login ${Harbor_Url} -u "${HARBOR_USER}" --password-stdin'
					sh 'docker push ${ImageName}:${ImageTag}'
				}
			}
		}
		
		stage('交付镜像测试测试环境') {
			steps {
				sh "export DOCKER_HOST=tcp://192.168.40.181:2375 && \
					docker rm -f spring_test && \
					docker run -d --name spring_test -p8888:8080 ${ImageName}:${ImageTag}"
			}
		}
			
    }
}
# 10.2 Pipeline-CD
pipeline {
	agent any
 
	//将传参的内容设定为变量
	environment {
		Full_Images="${Harbor_Url}/${Harbor_Pro}/${Image_Name}:${Image_tags}"
	}
 
	stages {
		stage('获取完整镜像名称') {
			steps {
				sh 'echo 镜像名称: ${Full_Images}'
			}
		}
		
		stage('交付应用至Docker') {
			steps {
				//这个地址可以通过传参的方式设定
				sh "export DOCKER_HOST=tcp://192.168.40.182:2375 && \
				docker rm -f springboot_prod && \
				docker run -d -p 80:8080 --name springboot_prod ${Full_Images}"
			}
		}
	}
}
此文章已被阅读次数:正在加载...更新于

请我喝[茶]~( ̄▽ ̄)~*

Xu Yong 微信支付

微信支付

Xu Yong 支付宝

支付宝