我们今天来实现一个基于 docker 的自动化部署。

- 首先,程序员将本地代码,git push 到远程 GitLab 服务器。
- 然后,Jenkins git pull 代码到 Jenkins 服务器,使用 maven 帮我们打成 jar 包,并用 docker build 成镜像。
- 紧接着 Jenkins 帮我们把镜像 push 到远程 harbor 镜像仓库。
- 最后,测试服务器从 harbor 上 docker pull 拉取镜像,docker run 运行成容器。
一、前置准备
这里需要用到四台服务器,一台安装 GitLab,一台安装 Jenkins,一台安装 harbor,还有一台安装有 docker 的测试服务器。当然,harbor 跟 docker 放在同一台也行,就看每个人手头现有的服务器资源是怎样的。
服务器信息如下:
|---------------|--------------------|----------|--------------------------------------|
| 服务器名 | IP | 配置 | 安装的软件 |
| harbor100 | 192.168.40.100 | 4C4G | harbor |
| gitlab99 | 192.168.40.99 | 8C8G | gitlab |
| jenkins98 | 192.168.40.98 | 4C4G | jenkins\jdk\maven\git\docker |
| test97 | 192.168.40.97 | 2C2G | docker |
- GitLab 安装可以 戳这里👉 SSH下安装GitLab Docker 安装 GitLab
- Jenkins 安装可以 戳这里👉 CentOS7 安装 Jenkins
二、环境准备
1、给 Jenkins 服务器安装 docker
1)配置安装 docker 需要的 repo 源
bash
# 安装 yum-utils 工具以便能使用 yum-config-manager 命令
yum install -y yum-utils
# 添加 repo 源
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# 清理 yum 缓存并重新生成缓存
yum clean all && yum makecache
执行完会在 /etc/yum.repos.d/ 目录下,新增一个 docker-ce.repo 配置文件。

2)安装 docker
bash
yum install -y docker-ce-24.0.6
# 启动 docker 并设置为 开机自启动
systemctl enable docker && systemctl start docker
大家也可以安装自己想要的 docker 版本,不一定需要跟我一样。
3)配置镜像加速
bash
# 编辑
vim /etc/docker/daemon.json
# 修改 harborIP 为你自己的 harborIP
{
"registry-mirrors":["https://docker.lmirror.top","https://docker.m.daocloud.io", "https://hub.uuuadc.top","https://docker.anyhub.us.kg","https://dockerhub.jobcher.com","https://dockerhub.icu","https://docker.ckyl.me","https://docker.awsl9527.cn","https://docker.laoex.link"],
"insecure-registries":["192.168.40.100"]
}
若 Harbor 使用自签名证书或 HTTP 协议,需在
/etc/docker/daemon.json中配置:"insecure-registries":["192.168.40.100"]
4)重启 docker
bash
systemctl daemon-reload && systemctl restart docker
5)查看 docker 状态
bash
# running 就是成功的
systemctl status docker
2、给测试服务器安装 docker
跟 1 一模一样的方式,给测试服务器安装 docker,略。
3、在 harbor 上新建项目

4、Jenkins 服务器推送镜像到 harbor
1)创建一个目录用于构建镜像
bash
[root@jenkins98 ~]# mkdir -p /root/test
[root@jenkins98 ~]# ls
test
2)将项目 jar 包与 Dockerfile 文件存放在该目录下
bash
[root@jenkins98 test]# ls
app.jar Dockerfile
Dockerfile 文件内容如下:
bash
FROM openjdk:17
EXPOSE 8088
WORKDIR /root
ADD app.jar app.jar
ENTRYPOINT ["java","-jar","app.jar"]
FROM:从一个基础镜像开始
EXPOSE:定义容器暴露的服务端口
WORKDIR:WORKDIR 指令用于为后续的 RUN、CMD、ENTRYPOINT、COPY 和 ADD 等指令设置工作目录。启动容器后,默认的登录目录也是 WORKDIR 指定的目录(除非被 CMD 或 ENTRYPOINT 覆盖)。
ADD:将当前目录下的 app.jar 文件,添加到容器工作目录下的 app.jar。也就是容器运行之后,/root 目录下会有一个 app.jar 文件。
ENTRYPOINT:定义容器启动时执行的命令。也就是容器启动后执行
bashjava -jar app.jarENTRYPOINT 用来定义容器核心启动命令(入口点),它不会被 docker run 参数所覆盖,固定容器必须执行的核心程序。
3)构建镜像
bash
docker build -t 192.168.40.100/jenkins-study/my-app:1.0 .
-t:tag 的意思,后面接构建后的镜像TAG。在这里,镜像TAG 为
Harbor IP\] / \[项目名称\] / \[镜像名称\]:\[版本号
.:指定Dockerfile文件位置

查看镜像是否构建成功:
bash
[root@jenkins98 test]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
192.168.40.100/jenkins-study/my-app 1.0 e1c57de530dd 13 seconds ago 489MB
4)测试镜像是否可用
bash
docker run -d -p 8088:8088 --name jenkins-study 192.168.40.100/jenkins-study/my-app:1.0
-d:后台启动
-p:定义宿主机端口与容器端口的映射关系
--name:定义容器的名称
浏览器访问 Jenkins 服务器IP:8088

证明镜像构建没问题
5)推送镜像到 Harbor 服务器
- 登录 Harbor
bash
docker login 192.168.40.100 -u admin -p Harbor12345
-u:Harbor username
-p:Harbor 密码
bash
[root@jenkins98 test]# docker login 192.168.40.100 -u admin -p Harbor12345
WARNING! Using --password via the CLI is insecure. Use --password-stdin.
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
- 推送镜像到 Harbor
bash
docker push 192.168.40.100/jenkins-study/my-app:1.0
bash
[root@jenkins98 test]# docker push 192.168.40.100/jenkins-study/my-app:1.0
The push refers to repository [192.168.40.100/jenkins-study/my-app]
31559b2e79e9: Pushed
5f70bf18a086: Pushed
dc9fa3d8b576: Pushed
27ee19dc88f2: Pushed
c8dd97366670: Pushed
1.0: digest: sha256:ff23bd7a7fc56dcf319c40415f7f662a3096e01148aea0835eae640c95911e9b size: 1372
- Harbor 上查看镜像

5、测试服务器推送镜像到 harbor
跟 4 一模一样的方式,验证从测试服务器推送镜像到 harbor,略。
三、Jenkins CICD
1、将 Dockerfile 拷贝到项目根路径下,并提交到远程 GitLab 仓库

2、新建 Jenkins Pipeline 流水线任务
Dashboard > 新建 Item

3、配置流水线框架
Dashboard > first-pipeline >Configuration
选择 Pipeline script 脚本方式

可以选择的流水线定义有两种:
Pipeline script:自己在下面编辑框内编写流水线脚本。
Pipeline script from SCM:从版本管理系统(如:Git)中拉取已经写好的 Jenkinsfile,实现流水线配置的可版本化。
在右侧下拉选项中,选择 Hello World,以此为基础,编写流水线脚本。

在流水线语法 Tab 页,选择插件或者命令,可以帮助我们以配置的方式,来生成流水线脚本。

4、拉取代码
示例步骤选择 git:Git,填入代码仓库地址,点击添加凭证

填入 gitlab 相关信息,点击添加

下拉选择凭证,点击生成流水线脚本

复制生成的代码,粘贴到流水线编写框中

5、构建 jar 包
构建 jar 包需要使用 maven,而 maven 是一种工具(tools),它的配置在
Dashboard > Manage Jenkins > Tools > Maven 安装里
Maven 我们在 实战1 已经配置过了,所以流水线中我们可以直接拿它的名字 maven386 来使用。
- 添加 "构建 jar 包" 阶段:
bash
pipeline {
agent any
tools {
maven 'maven386' // 引用全局配置的 Maven 名称(需提前在"全局工具配置"中设置)
}
stages {
stage('拉取代码') {
steps {
git credentialsId: 'gitlab', url: 'http://192.168.40.99/study/jenkins-study.git'
echo '拉取成功'
}
}
stage('构建 jar 包') {
steps {
sh 'mvn clean package -DskipTests=true'
echo '构建 jar 包成功'
}
}
}
}
保存【配置】并【立即构建】,可以在控制台输出中查看构建日志
- 制品
构建成功后,默认会在 /root/.jenkins/workspace/jenkins-study/target 目录下,帮我们生成一个 jar 包,说明构建 jar 包成功。
- 移动 jar 包并重命名
为了方便我们后续 docker build,我们移动并重命名一下 jar 包,让它跟 Dockerfile 在一个路径下
bash
pipeline {
agent any
tools {
maven 'maven386' // 引用全局配置的 Maven 名称(需提前在"全局工具配置"中设置)
}
environment {
HARBOR_PROJECT = "jenkins-study" // Harbor 中的项目名
}
stages {
stage('拉取代码') {
steps {
git credentialsId: 'gitlab', url: 'http://192.168.40.99/study/jenkins-study.git'
echo '拉取成功'
}
}
stage('构建 jar 包') {
steps {
sh """
mvn clean package -DskipTests=true
cp ./target/${HARBOR_PROJECT}*.jar ./app.jar
"""
echo '构建 jar 包成功'
}
}
}
}
environment:定义环境变量,environment 后续的模块就可以使用 ${KEY} 来获取 VALUE
""" xxxxx """:定义多行 Shell 命令,不同行 Shell 命令之间具有上下文关系
cp ./target/${HARBOR_PROJECT}*.jar ./app.jar:将 target 目录下的 jar 包拷贝到当前目录并重命名为 app.jar
保存【配置】并【立即构建】后,app.jar 跟 Dockerfile 在同一个路径下了:
6、构建镜像
bash
pipeline {
agent any
tools {
maven 'maven386' // 引用全局配置的 Maven 名称(需提前在"全局工具配置"中设置)
}
environment {
HARBOR_DOMAIN = "192.168.40.100" // Harbor 域名/IP
HARBOR_PROJECT = "jenkins-study" // Harbor 中的项目名
IMAGE_NAME = "my-app" // 镜像名称
IMAGE_TAG = "${BUILD_NUMBER}" // 镜像标签(用 Jenkins 构建号,确保唯一)
FULL_IMAGE = "${HARBOR_DOMAIN}/${HARBOR_PROJECT}/${IMAGE_NAME}:${IMAGE_TAG}" // 完整镜像地址
}
stages {
stage('拉取代码') {
steps {
git credentialsId: 'gitlab', url: 'http://192.168.40.99/study/jenkins-study.git'
echo '拉取成功'
}
}
stage('构建 jar 包') {
steps {
sh """
mvn clean package -DskipTests=true
cp ./target/${HARBOR_PROJECT}*.jar ./app.jar
"""
echo '构建 jar 包成功'
}
}
stage('构建 docker 镜像') {
steps {
// 假设项目根目录有 Dockerfile,执行构建
sh """
echo "开始构建镜像:${FULL_IMAGE}"
docker build -t ${FULL_IMAGE} .
"""
echo '构建 docker 镜像成功'
}
}
}
}
保存【配置】并【立即构建】后,在 jenkins98 服务器查看镜像:
bash
[root@jenkins98 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
192.168.40.100/jenkins-study/my-app 3 b07ccde534f1 47 seconds ago 489MB
7、推送镜像到 Harbor
要推送镜像到 Harbor,就要登录 Harbor,就需要 用户名、密码;用户名密码在 Jenkins 里面叫 凭据。
打开流水线语法 Tab 页,http://192.168.40.98:8080/job/jenkins-study/pipeline-syntax/ 示例步骤选择 withCredentials: Bind credentials to variables,新增
新增一个用户名密码分开的绑定

定义用户名变量、密码变量、添加 Harbor 凭据

填入 Harbor 相关信息后点击添加

选择新建的Harbor 凭据,生成流水线脚本

拷贝流水线脚本到我们的流水线代码中
Groovy
pipeline {
agent any
tools {
maven 'maven386' // 引用全局配置的 Maven 名称(需提前在"全局工具配置"中设置)
}
environment {
HARBOR_DOMAIN = "192.168.40.100" // Harbor 域名/IP
HARBOR_PROJECT = "jenkins-study" // Harbor 中的项目名
IMAGE_NAME = "my-app" // 镜像名称
IMAGE_TAG = "${BUILD_NUMBER}" // 镜像标签(用 Jenkins 构建号,确保唯一)
FULL_IMAGE = "${HARBOR_DOMAIN}/${HARBOR_PROJECT}/${IMAGE_NAME}:${IMAGE_TAG}" // 完整镜像地址
CONTAINER_NAME = "jenkins-study" // 部署的容器名
}
stages {
stage('拉取代码') {
steps {
git credentialsId: 'gitlab', url: 'http://192.168.40.99/study/jenkins-study.git'
echo '拉取成功'
}
}
stage('构建 jar 包') {
steps {
sh """
mvn clean package -DskipTests=true
cp ./target/${HARBOR_PROJECT}*.jar ./app.jar
"""
echo '构建 jar 包成功'
}
}
stage('构建 docker 镜像') {
steps {
// 假设项目根目录有 Dockerfile,执行构建
sh """
echo "开始构建镜像:${FULL_IMAGE}"
docker build -t ${FULL_IMAGE} .
"""
echo '构建 docker 镜像成功'
}
}
stage('推送镜像到 Harbor') {
steps {
script {
// 使用 Harbor 凭证登录并推送
withCredentials([usernamePassword(
credentialsId: 'harbor-creds',
passwordVariable: 'HARBOR_PWD',
usernameVariable: 'HARBOR_USER'
)]) {
sh """
# 登录 Harbor(若为 HTTP,需配置 Docker insecure-registries)
docker login ${HARBOR_DOMAIN} -u ${HARBOR_USER} -p ${HARBOR_PWD}
# 推送镜像
docker push ${FULL_IMAGE}
# 登出(可选)
docker logout ${HARBOR_DOMAIN}
"""
}
}
}
}
}
}
Jenkins 流水线语法 script 脚本的作用:
- 在 Jenkins 流水线(Pipeline)中,
script块是一个非常重要的语法元素,主要作用是在声明式流水线(Declarative Pipeline)中嵌入脚本式流水线(Scripted Pipeline)的语法,从而增强流水线的灵活性和可编程性。- 声明式流水线(以
pipeline { ... }为标志)语法简洁、结构固定,适合定义标准化的流程(如agent、stages、post等固定结构),但对复杂逻辑(如循环、条件判断嵌套、动态生成步骤等)支持有限。script块允许在声明式流水线中插入脚本式语法(类似 Groovy 脚本),实现复杂的流程控制。script块可以在声明式流水线的steps、post、environment等部分中使用,用于在固定结构中插入自定义逻辑。script块是声明式和脚本式流水线的 "桥梁",但应避免过度使用(否则会失去声明式流水线的简洁性)。- 脚本式语法更灵活,但也更易出错(需自己处理异常、流程控制等),建议优先使用声明式的内置语法(如
when条件),仅在必要时用script块扩展。- 上述代码其实也可以不使用
script块,这里只是提供一种补充,让大家知道有这种语法功能。
保存【配置】并【立即构建】后,在 Harbor 服务器查看镜像:

8、部署到 Docker 服务器
我们要想在测试服务器运行 docker 命令,需要安装 SSH Publisher 插件。
1)安装 SSH Publisher 插件
Dashboard > Manage Jenkins > 插件管理 > Available plugins

安装完,返回首页

2)配置测试服务器信息
Dashboard > Manage Jenkins > System,新增测试服务器

- 填上测试服务器信息并保存

- Test Configuration 测试连接

3)生成流水线脚本
在流水线语法 Tab 页

拷贝流水线脚本到我们的流水线代码中
Groovy
pipeline {
agent any
tools {
maven 'maven386' // 引用全局配置的 Maven 名称(需提前在"全局工具配置"中设置)
}
environment {
HARBOR_DOMAIN = "192.168.40.100" // Harbor 域名/IP
HARBOR_PROJECT = "jenkins-study" // Harbor 中的项目名
IMAGE_NAME = "my-app" // 镜像名称
IMAGE_TAG = "${BUILD_NUMBER}" // 镜像标签(用 Jenkins 构建号,确保唯一)
FULL_IMAGE = "${HARBOR_DOMAIN}/${HARBOR_PROJECT}/${IMAGE_NAME}:${IMAGE_TAG}" // 完整镜像地址
CONTAINER_NAME = "jenkins-study" // 部署的容器名
}
stages {
stage('拉取代码') {
steps {
git credentialsId: 'gitlab', url: 'http://192.168.40.99/study/jenkins-study.git'
echo '拉取成功'
}
}
stage('构建 jar 包') {
steps {
sh """
mvn clean package -DskipTests=true
cp ./target/${HARBOR_PROJECT}*.jar ./app.jar
"""
echo '构建 jar 包成功'
}
}
stage('构建 docker 镜像') {
steps {
// 假设项目根目录有 Dockerfile,执行构建
sh """
echo "开始构建镜像:${FULL_IMAGE}"
docker build -t ${FULL_IMAGE} .
"""
echo '构建 docker 镜像成功'
}
}
stage('推送镜像到 Harbor') {
steps {
script {
// 使用 Harbor 凭证登录并推送
withCredentials([usernamePassword(
credentialsId: 'harbor-creds',
passwordVariable: 'HARBOR_PWD',
usernameVariable: 'HARBOR_USER'
)]) {
sh """
# 登录 Harbor(若为 HTTP,需配置 Docker insecure-registries)
docker login ${HARBOR_DOMAIN} -u ${HARBOR_USER} -p ${HARBOR_PWD}
# 推送镜像
docker push ${FULL_IMAGE}
# 登出(可选)
docker logout ${HARBOR_DOMAIN}
"""
}
}
}
}
stage('部署到 Docker 服务器') {
steps {
script {
withCredentials([
usernamePassword(
credentialsId: 'harbor-creds',
passwordVariable: 'HARBOR_PWD',
usernameVariable: 'HARBOR_USER'
)
]) {
// 通过 SSH 连接远程服务器,执行部署命令
sshPublisher(
publishers: [
sshPublisherDesc(
configName: "test97", // 对应 Publish Over SSH 配置的服务器名
transfers: [
sshTransfer(
execCommand: """
# 远程服务器登录 Harbor
docker login ${HARBOR_DOMAIN} -u ${HARBOR_USER} -p ${HARBOR_PWD}
# 停止并删除旧容器(若存在)
if [ \$(docker ps -a -q -f name=${CONTAINER_NAME}) ]; then
docker stop ${CONTAINER_NAME} && docker rm ${CONTAINER_NAME}
fi
# 拉取最新镜像
docker pull ${FULL_IMAGE}
# 启动新容器(根据需求调整端口、环境变量等)
docker run -d --name ${CONTAINER_NAME} -p 8088:8088 ${FULL_IMAGE}
# 登出 Harbor
docker logout ${HARBOR_DOMAIN}
""",
execTimeout: 120000 // 超时时间 2 分钟
)
]
)
]
)
}
}
}
}
}
}
docker ps -a -q -f name=${CONTAINER_NAME}) :
- **docker ps:**显示容器信息
- -a:all,显示所有容器信息,包括正在运行的和已停止的
- -q:quiet,只显示容器ID
- **-f name=${CONTAINER_NAME}):**filter 过滤容器名称=jenkins-study 的容器
整条命令的意思,是过滤出容器名称=jenkins-study 的容器,只返回容器ID
""" if [ \$() ] """ 中 \ 的作用:
\$中的\是转义字符 ,作用是保留$符号本身,防止其被外层的解析器提前解析。- 在多行字符串内部,如果直接写
$(...),外层的解析器(比如 Python 解释器,或生成 shell 脚本的程序)可能会把$当作变量或命令替换的标记,尝试提前解析$(...)的内容,导致不符合预期的结果。- 加上
\转义后,\$会被外层解析器当作普通的$符号处理,最终生成的字符串中会保留$(...)结构。当这段字符串被作为 shell 脚本执行时,shell 会正常解析$(...)作为命令替换(即执行docker ps ...并获取结果)。
保存【配置】并【立即构建】后,在 test97 服务器查看:
bash
[root@test97 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
192.168.40.100/jenkins-study/my-app 5 3e575c84958d 16 hours ago 489MB
[root@test97 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8afe1f6f176b 192.168.40.100/jenkins-study/my-app:5 "java -jar app.jar" 17 seconds ago Up 16 seconds 0.0.0.0:8088->8088/tcp, :::8088->8088/tcp jenkins-study
TAG 都是 5,证明容器正常运行。
题外话:
大家如果容器起不来,可以使用 docker inspect 容器ID 命令来查看容器日志
9、修改业务代码测试
修改业务代码,提交到远程 GitLab,启动流水线构建,在浏览器查看

测试服务器 test97 部署的是最新的代码了

四、总结
大家如果感觉博主写得还可以的话,请点赞加关注吧~~~~
博主写了好几个小时,中间网络有问题,我点击了好多次保存草稿,但是服务器上没有给我保存,造成后面有好几节的内容丢失,博主只能重写。
5555555555555555555555555555555555555555555555555555555555555
