Jenkins + Docker + GitLab 搭建 CI/CD 自动化流水线:从踩坑到成功的完整指南
Jenkins + Docker + GitLab 实现 CI/CD 自动化:从手动部署到自动构建的进化
本文记录了使用 Jenkins + Docker + GitLab 搭建 CI/CD 自动化流水线的完整过程,包括 7 个踩坑点及解决方案。
一、前言
刚学运维的时候,每次改完代码都要手动打包、上传、部署,一套流程下来十几分钟,还容易出错。
后来学了 Docker,部署方便了,但还是要登录服务器手动操作。
直到用了 Jenkins + Docker + GitLab,终于实现了"代码提交即部署"的自动化流程。
最终效果:代码 push 到 GitLab 后,Jenkins 自动拉取代码、构建 Docker 镜像、部署到服务器,全程无需手动操作。

流程图

二、技术选型
| 组件 | 选择 | 原因 |
|---|---|---|
| CI/CD 工具 | Jenkins | 业界主流,插件丰富 |
| 代码托管 | GitLab(本地部署) | 内网访问快,不依赖外网 |
| 容器化 | Docker | 轻量级,部署方便 |
| Web 服务器 | Nginx | 轻量级,性能好 |
为什么选择本地 GitLab 而不是 Gitee?
最初计划用 Gitee,但配置 Webhook 时发现 Gitee 服务器在公网,无法访问内网 IP。所以改用本地部署的 GitLab,同在内网,直接访问。
三、环境准备
3.1 服务器配置
| 项目 | 配置 |
|---|---|
| 操作系统 | Kylin Linux V10 |
| IP 地址 | 192.168.1.5(内网环境) |
| 内存 | 8GB(Jenkins 需要 512MB+,GitLab 需要 4GB+) |
| Docker | 已安装 |
3.2 项目结构
jenkins-cicd/
├── jenkins/
│ └── docker-compose.yml # Jenkins 容器配置
├── gitlab/
│ └── docker-compose.yml # GitLab 容器配置
├── demo-app/
│ ├── Dockerfile # 应用镜像定义
│ ├── index.html # 应用网页
│ └── Jenkinsfile # 流水线定义
└── README.md # 项目说明
四、部署 Jenkins
4.1 编写 docker-compose.yml
version: '3'
services:
jenkins:
image: jenkins/jenkins:lts
container_name: jenkins
restart: always
network_mode: host
volumes:
- jenkins_data:/var/jenkins_home
- /var/run/docker.sock:/var/run/docker.sock
- /usr/bin/docker:/usr/bin/docker
- /usr/lib64/libltdl.so.7:/usr/lib/x86_64-linux-gnu/libltdl.so.7
environment:
- JAVA_OPTS=-Duser.timezone=Asia/Shanghai
volumes:
jenkins_data:
关键配置解释:
| 配置 | 作用 |
|---|---|
network_mode: host | 使用宿主机网络,方便访问其他容器 |
/var/run/docker.sock | 让 Jenkins 能调用宿主机的 Docker |
/usr/bin/docker | 挂载 Docker 二进制文件 |
/usr/lib64/libltdl.so.7 | Docker 依赖库(踩坑后发现必须挂载) |
JAVA_OPTS=-Duser.timezone=Asia/Shanghai | 设置时区为中国时间 |
4.2 启动 Jenkins
cd /root/user/jenkins-cicd
docker-compose -f jenkins/docker-compose.yml up -d
4.3 初始化 Jenkins
- 浏览器访问:
http://192.168.1.5:8080 - 获取初始密码:
docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword - 安装推荐插件
- 创建管理员账号
4.4 安装必要插件
在 Jenkins 首页 → Manage Jenkins → Manage Plugins → Available:
- Docker Pipeline:让 Jenkins Pipeline 能执行 docker 命令
- GitLab Plugin:让 Jenkins 能识别 GitLab 的 Webhook 请求
五、部署 GitLab
5.1 编写 docker-compose.yml
version: '3'
services:
gitlab:
image: gitlab/gitlab-ce:latest
container_name: gitlab
restart: always
hostname: gitlab.local
ports:
- "8929:8929"
- "443:443"
- "2222:22"
volumes:
- ./config:/etc/gitlab
- ./logs:/var/log/gitlab
- ./data:/var/opt/gitlab
environment:
GITLAB_OMNIBUS_CONFIG: |
external_url 'http://192.168.1.5:8929'
gitlab_rails['gitlab_shell_ssh_port'] = 2222
关键配置解释:
| 配置 | 作用 |
|---|---|
8929:8929 | GitLab Web 界面端口 |
2222:22 | SSH 克隆端口(避免和宿主机 SSH 冲突) |
external_url | GitLab 访问地址 |
gitlab_shell_ssh_port | 让 GitLab 页面显示正确的 SSH 地址 |
5.2 启动 GitLab
cd /root/user/jenkins-cicd
docker-compose -f gitlab/docker-compose.yml up -d
注意:GitLab 第一次启动需要 5-10 分钟初始化,耐心等待。
检查是否就绪:
curl -s -o /dev/null -w "%{http_code}" http://192.168.1.5:8929
返回 200 或 302 说明就绪了。
5.3 初始化 GitLab
-
获取初始密码:
docker exec gitlab cat /etc/gitlab/initial_root_password -
浏览器访问:
http://192.168.1.5:8929 -
登录(用户名:
root,密码:刚才获取的密码) -
修改密码(初始密码太长难记)
5.4 创建项目
在 GitLab 页面点击 New project:
| 字段 | 值 |
|---|---|
| Project name | jenkins-cicd-demo |
| Project description | Jenkins CI/CD 演示应用 |
| Visibility Level | Private 或 Internal |

六、创建演示应用
6.1 创建 index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>My App</title>
<style>
body {
font-family: Arial, sans-serif;
text-align: center;
padding: 50px;
background-color: #f0f0f0;
}
h1 { color: #333; }
.version { color: #666; font-size: 18px; }
</style>
</head>
<body>
<h1>Hello from Jenkins CI/CD!</h1>
<p class="version">Version: 3.0</p>
<p>This page is automatically deployed by Jenkins Pipeline.</p>
</body>
</html>
6.2 创建 Dockerfile
FROM nginx:alpine
COPY index.html /usr/share/nginx/html/index.html
RUN chmod 644 /usr/share/nginx/html/index.html
EXPOSE 80
注意:RUN chmod 644 这行很重要!不加会导致 nginx 返回 403 Forbidden。
6.3 创建 Jenkinsfile
pipeline {
agent any
environment {
IMAGE_NAME = "demo-app"
IMAGE_TAG = "${env.BUILD_NUMBER}"
CONTAINER_NAME = "demo-app"
HOST_PORT = "8090"
CONTAINER_PORT = "80"
}
stages {
stage('拉取代码') {
steps {
checkout scm
echo "代码拉取完成"
}
}
stage('构建镜像') {
steps {
echo "开始构建 Docker 镜像..."
sh "docker build -t ${IMAGE_NAME}:${IMAGE_TAG} ."
sh "docker tag ${IMAGE_NAME}:${IMAGE_TAG} ${IMAGE_NAME}:latest"
echo "镜像构建完成:${IMAGE_NAME}:${IMAGE_TAG}"
}
}
stage('部署') {
steps {
echo "开始部署..."
sh "docker rm -f ${CONTAINER_NAME} || true"
sh "docker run -d --name ${CONTAINER_NAME} -p ${HOST_PORT}:${CONTAINER_PORT} ${IMAGE_NAME}:latest"
echo "部署完成"
}
}
stage('验证') {
steps {
echo "开始验证..."
sh "sleep 3"
sh "curl -s http://localhost:${HOST_PORT} | grep 'Hello from Jenkins' || exit 1"
echo "验证通过!"
}
}
}
post {
success {
echo "=========================================="
echo " 流水线执行成功!"
echo " 访问地址:http://192.168.1.5:${HOST_PORT}"
echo "=========================================="
}
failure {
echo "=========================================="
echo " 流水线执行失败,请检查日志"
echo "=========================================="
}
}
}
6.4 推送到 GitLab
cd /root/user/jenkins-cicd/demo-app
git init
git add .
git commit -m "Initial commit: demo app"
git remote add origin http://192.168.1.5:8929/root/jenkins-cicd-demo.git
git push -u origin master
注意:GitLab 默认禁止密码认证,需要使用 Personal Access Token。
生成 Token:
- GitLab → 头像 → Settings → Access Tokens
- Token name:
git-push - Scopes:勾选
api和write_repository - 点击 Create personal access token
- 复制 Token,推送时用 Token 代替密码
七、配置 Jenkins Pipeline
7.1 创建 Pipeline 任务
- Jenkins 首页 → New Item
- 输入任务名:
demo-app-pipeline - 选择 Pipeline
- 点击 OK
7.2 配置 Pipeline
进入任务 → Configure:
Pipeline 配置:
| 字段 | 值 |
|---|---|
| Definition | Pipeline script from SCM |
| SCM | Git |
| Repository URL | http://192.168.1.5:8929/root/jenkins-cicd-demo.git |
| Credentials | gitlab-credentials |
| Branch Specifier | */master |
| Script Path | Jenkinsfile |

凭据配置:
- Jenkins → Manage Jenkins → Manage Credentials
- 点击 Jenkins → Global credentials (unrestricted)
- 点击 Add Credentials
- 填写:
| 字段 | 值 |
|---|---|
| Kind | Username with password |
| Scope | Global |
| Username | root |
| Password | 你的 GitLab 密码 |
| ID | gitlab-credentials |
| Description | GitLab 登录凭据 |
八、配置 Webhook(自动触发构建)
8.1 配置 Jenkins 触发器
- Jenkins → demo-app-pipeline → Configure
- 找到 Build Triggers
- 勾选 Build when a change is pushed to GitLab
- 点击 高级(Advanced)
- 找到 Secret Token
- 点击 Generate 生成 Token
- 复制生成的 Token
8.2 配置 GitLab Webhook
- GitLab → 项目 → Settings → Webhooks
- 填写:
| 字段 | 值 |
|---|---|
| URL | http://192.168.1.5:8080/project/demo-app-pipeline |
| Secret token | 粘贴刚才生成的 Token |
| Trigger | 勾选 Push events |
- 点击 Add webhook

8.3 允许本地网络 Webhook
GitLab 默认会阻止对本地网络的 Webhook 请求,需要修改配置:
- GitLab → Admin Area → Settings → Network
- 找到 Outbound requests
- 勾选 Allow requests to the local network from webhooks and integrations
- 点击 Save changes
九、测试自动构建
9.1 手动触发第一次构建
- Jenkins → demo-app-pipeline → Build Now
- 等待构建完成(1-2 分钟)
9.2 查看构建日志
点击构建编号 → Console Output:

9.3 验证部署结果
浏览器访问:http://192.168.1.5:8090

9.4 测试自动触发
修改 index.html,把版本改成 4.0:
<p class="version">Version: 4.0</p>
推送到 GitLab:
cd /root/user/jenkins-cicd/demo-app
git add .
git commit -m "Update to v4.0"
git push
观察 Jenkins 是否自动开始构建!
十、踩坑记录
10.1 libltdl.so.7 缺失
问题:Jenkins 容器执行 docker build 报错:
docker: error while loading shared libraries: libltdl.so.7: cannot open shared object file: No such file or directory
原因:只挂载了 docker 二进制文件,没有挂载依赖库。
解决:在 docker-compose.yml 添加库文件挂载:
volumes:
- /usr/lib64/libltdl.so.7:/usr/lib/x86_64-linux-gnu/libltdl.so.7
10.2 docker.sock 权限问题
问题:
permission denied while trying to connect to the Docker daemon socket
原因:Jenkins 用户没有权限访问 docker.sock。
解决:在宿主机执行:
chmod 666 /var/run/docker.sock
10.3 index.html 403 Forbidden
问题:部署后访问返回 403 Forbidden。
原因:nginx 以 nginx 用户运行,没有权限读取 index.html。
解决:在 Dockerfile 添加权限设置:
RUN chmod 644 /usr/share/nginx/html/index.html
10.4 Jenkins 容器无法访问 localhost:8090
问题:Jenkins 验证阶段 curl localhost:8090 失败。
原因:Jenkins 容器和 demo-app 容器在不同网络,无法互通。
解决:改用 host 网络模式:
services:
jenkins:
network_mode: host
10.5 Gitee Webhook 无法访问内网
问题:Gitee 无法访问内网 IP 的 Jenkins。
原因:Gitee 服务器在公网,无法访问 192.168.1.5 内网地址。
解决:改用本地 GitLab,同在内网,直接访问。
10.6 GitLab Webhook 返回 403
问题:GitLab 测试 Webhook 返回:
HTTP ERROR 403 anonymous is missing the Job/Build permission
原因:Jenkins 需要验证 Webhook 请求的身份。
解决:
- Jenkins 任务配置中设置 Secret Token(高级选项里)
- GitLab Webhook 配置相同的 Secret Token
10.7 GitLab 推送需要 Token
问题:git push 时提示:
HTTP Basic: Access denied. If a password was provided for Git authentication, the password was incorrect or you're required to use a token instead of a password.
原因:GitLab 默认禁止密码认证,要求使用 Personal Access Token。
解决:
- GitLab → Settings → Access Tokens → 生成 Token
- 推送时用 Token 代替密码
十一、核心概念解析
11.1 Docker Outside of Docker(DooD)
Jenkins 容器通过挂载宿主机的 Docker 来执行构建:
Jenkins 容器内
│
▼
执行 docker build . (用挂载进来的 docker CLI)
│
▼
通过 sock 把命令发给宿主机 daemon
│
▼
宿主机 daemon 解析并执行构建
│
▼
镜像构建好,存在宿主机仓库
│
▼
结果通过 sock 返回给 Jenkins 容器
关键点:
- Docker CLI(容器里)= 遥控器,只发命令
- Docker Daemon(宿主机)= 干活的人,真正执行
- Socket = 电话线,连接 CLI 和 Daemon
11.2 Webhook 认证机制
GitLab Webhook 发送请求
↓
带上 Secret Token
↓
Jenkins 收到请求
↓
验证 Secret Token 是否匹配
↓
├── 匹配 → 执行构建
└── 不匹配 → 403 错误
两个认证方向:
| 方向 | 作用 | 认证方式 |
|---|---|---|
| Jenkins → GitLab | 拉代码、访问 API | GitLab API Token |
| GitLab → Jenkins | 发送 Webhook 通知 | Secret Token |
11.3 构建上下文
Jenkins workspace(/var/jenkins_home/workspace/demo-app-pipeline/)
│
├── Dockerfile
├── index.html
└── Jenkinsfile
│
▼ 执行 Jenkinsfile 里的命令
│
docker build -t demo-app:1 .
│
└── 这个 "." 就是当前目录(Jenkinsfile 所在目录)
Dockerfile 和 index.html 都在这个目录下
所以能正确构建
十二、项目总结
12.1 实现功能
- ✅ 代码 push 到 GitLab 后自动触发构建
- ✅ 自动构建 Docker 镜像
- ✅ 自动部署容器
- ✅ 自动验证部署结果
- ✅ 流程可版本控制(Jenkinsfile)
12.2 技术亮点
- Pipeline as Code:用 Jenkinsfile 定义流水线,代码可版本控制
- Docker Outside of Docker:让 Jenkins 容器调用宿主机 Docker
- Webhook 自动触发:代码推送后自动构建部署
- 本地 GitLab:内网访问快,不依赖外网
十三、参考资料
十四、项目代码
本文基于实际项目编写,所有步骤都在内网环境(192.168.1.5)中验证通过。
由于是内网环境,无法提供在线演示,但所有代码和配置都已详细列出,读者可以按照步骤在自己的环境中复现。
更多推荐




所有评论(0)