🐳 使用 Docker 部署 Jenkins 并实现自动化部署 ------ 从零到一的 CI/CD 实践指南
从容器化部署到 Git 触发自动化构建镜像,一篇带你把 Jenkins CI/CD 流水线跑起来(附流程图)
📌 文章导览
持续集成与持续部署(CI/CD)已成为现代软件开发不可或缺的环节,而 Jenkins 作为全球最流行的开源自动化服务器,被广泛应用于各类自动化任务中。结合 Docker 的轻量化和可移植性,我们可以快速搭建一个高效、灵活且易于扩展的自动化部署环境。
本文涵盖:
✅ Docker 环境准备与镜像选择
✅ Jenkins 容器部署(单容器 + 权限配置)
✅ 初始化与插件安装
✅ 核心重点 :让 Jenkins 容器内调用 Docker(两种方案对比)
✅ 凭证管理(Git + 容器仓库)
✅ Jenkins Pipeline 声明式流水线编写(构建 → 推送 → 部署)
✅ 完整流程图 + 架构图(Mermaid 格式)
✅ 生产环境优化建议与常见问题排查
🧱 一、前置条件
| 项目 | 要求 |
|---|---|
| 操作系统 | Linux(Ubuntu 20.04+ / CentOS 7+ / Debian) |
| Docker | 版本 ≥ 20.10(含 docker compose) |
| 硬件配置 | 建议 2 核 4G 以上(Jenkins 运行需一定资源) |
| 代码仓库 | GitHub / GitLab / Gitee(任意 Git 仓库即可) |
| Docker Registry | Docker Hub / 私有 Harbor / 阿里云容器镜像服务 |
⚠️ 本文基于 Docker 官方 LTS 镜像
jenkins/jenkins:lts,此版本兼容性最佳,适合生产部署。
🗺 二、端到端自动化部署流程总览
开发者提交代码到 Git 仓库
Git Webhook 触发 Jenkins
Jenkins 拉取最新代码
Pipeline:代码编译构建
Pipeline:Docker 镜像打包
Pipeline:登录容器镜像仓库
Push 镜像到仓库
部署到目标服务器/环境
完成自动化部署
🔄 核心闭环:代码提交 → Webhook 触发 → Jenkins 构建 → 镜像推送 → 自动部署,全程无需人工干预。
🐳 三、Docker 环境下部署 Jenkins
3.1 拉取 Jenkins 官方镜像
我们选择 Jenkins 长期支持(LTS)版本,它不仅稳定,而且是目前生产环境的主力选择:
bash
docker pull jenkins/jenkins:lts
若拉取较慢,可配置国内镜像加速源或使用阿里云加速器。
3.2 创建数据持久化目录
bash
mkdir -p /data/jenkins_home
chown -R 1000:1000 /data/jenkins_home
💡 设置权限为
1000:1000是因为 Jenkins 容器内的默认运行用户jenkins的 UID 正是 1000,确保容器能够正常读写该目录,避免使用777(生产环境权限应最小化)。
3.3 启动 Jenkins 容器(基础版)
bash
docker run -d \
--name jenkins \
--restart always \
-p 8080:8080 \
-p 50000:50000 \
-v /data/jenkins_home:/var/jenkins_home \
-e TZ="Asia/Shanghai" \
jenkins/jenkins:lts
关键参数说明:
| 参数 | 作用 |
|---|---|
-p 8080:8080 |
Web 管理界面访问端口 |
-p 50000:50000 |
Jenkins Master-Slave 通信端口 |
-v |
Jenkins 数据持久化 |
-e TZ="Asia/Shanghai" |
设置容器时区 |
--restart always |
保障容器异常退出后自动拉起 |
此时一个基础的 Jenkins 容器已经运行起来。但是请注意:这个容器内无法执行 Docker 命令 ------流水线中的 docker build 等指令会直接报错。接下来我们要解决这个核心问题。
🔑 四、核心配置:让 Jenkins 能调用 Docker
✅ 这是本教程的核心重点。Jenkins 要构建 Docker 镜像,就必须具备 Docker 能力。
4.1 两种技术路线对比
| 对比维度 | 方案 A:挂载 Docker Socket | 方案 B:Docker-in-Docker (DinD) |
|---|---|---|
| 原理 | Jenkins 容器直接调用宿主机 Docker | 在 Jenkins 旁启动一个独立的 Docker 守护进程容器 |
| 容器大小 | 轻量 | 稍重 |
| 隔离性 | 较低(同宿主共享 Docker 引擎) | 高(独立的 Docker 进程) |
| 适用阶段 | 测试/预发布环境 | 生产环境(隔离要求高时) |
| 安全风险 | 容器获得宿主机 root-like 权限 | --privileged 运行但存在独立隔离 |
| 官方示例 | 较为常见 | 官方推荐 DinD 保持环境纯净 |
4.2 方案 A:挂载 Socket(推荐入门)
bash
docker run -d \
--name jenkins \
--restart always \
-p 8080:8080 \
-p 50000:50000 \
-v /data/jenkins_home:/var/jenkins_home \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /usr/bin/docker:/usr/bin/docker \
-e TZ="Asia/Shanghai" \
jenkins/jenkins:lts
核心选项:
-v /var/run/docker.sock:/var/run/docker.sock:让 Jenkins 容器与宿主机 共享 Docker 守护进程-v /usr/bin/docker:/usr/bin/docker:让容器内的dockerCLI 可执行- 在容器内验证是否成功:
docker exec jenkins docker ps
⚠️ 安全提示:该方案相当于将宿主机的 Docker 完全"借"给 Jenkins 容器,务必确保 Jenkins 流水线中不能执行恶意命令。
4.3 方案 B:Docker-in-Docker(DinD,更隔离)
适用场景:K8s 环境、高安全性 CI/CD 集群、多租户场景
步骤一:创建专用 Docker 网络
bash
docker network create jenkins
步骤二:启动 DinD 守护进程容器
bash
docker run --name jenkins-docker \
--rm --detach \
--privileged \
--network jenkins \
--network-alias docker \
-e DOCKER_TLS_CERTDIR=/certs \
-v jenkins-docker-certs:/certs/client \
-p 2376:2376 \
docker:dind \
--storage-driver overlay2
步骤三:启动 Jenkins 并连接到 DinD
bash
docker run --name jenkins-blueocean \
--restart=on-failure \
--detach \
--network jenkins \
-e DOCKER_HOST=tcp://docker:2376 \
-e DOCKER_CERT_PATH=/certs/client \
-e DOCKER_TLS_VERIFY=1 \
-p 8080:8080 \
-v jenkins-data:/var/jenkins_home \
-v jenkins-docker-certs:/certs/client:ro \
jenkins/jenkins:lts
关键解释:
--network-alias docker:为 DinD 容器设置了固定的网络别名,Jenkins 可通过tcp://docker:2376与之通信DOCKER_TLS_VERIFY=1:启用 TLS 加密验证:ro:只读挂载证书,进一步增强安全性
🌐 五、Jenkins 初始化与插件配置
5.1 获取初始解锁密码
bash
docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword
执行后会打印一长串字符串,复制备用。
5.2 Web 初始化流程
- 浏览器访问
http://<服务器IP>:8080 - 粘贴解锁密码进入安装向导
- 选择 "安装推荐插件"(后续可按需增删)
- 创建管理员用户并完成基础设置
5.3 必装插件清单
安装完成之后,建议手动补充以下插件(路径:Dashboard → Manage Jenkins → Plugins → Available plugins):
| 插件名称 | 用途 |
|---|---|
| Git Plugin | 从 Git 仓库拉取代码 |
| Pipeline | Jenkins 核心流水线支持 |
| Blue Ocean(可选) | 可视化流水线界面,提升排错效率 |
| Docker Pipeline | 提供 Jenkinsfile 中的 docker.build(), docker.withRegistry() 语法 |
| Credentials Binding | 安全管理凭证信息(避免在脚本中硬编码) |
| GitHub Integration / Gitee | 实现 Git 触发 Webhook |
💡 安装 Docker Pipeline 后,在流水线中即可调用丰富的 Docker 操作语法。
🔐 六、凭证管理(核心安全配置)
6.1 凭证类型与使用场景
| 凭证类型 | 典型使用场景 |
|---|---|
Username with password |
登录 Docker Hub / 私有镜像仓库 |
SSH Username with private key |
Jenkins 免密拉取 Git 仓库(推荐 GitHub 部署密钥) |
Secret text |
API Token(如 GitHub Personal Access Token) |
Certificate |
PKCS#12 证书文件 |
在 Jenkins 中配置凭证的位置:Dashboard → 系统管理 → 凭证 → 系统 → 全局凭据 → 添加凭据。
6.2 添加 Git 拉取凭证(SSH 方式)
💡 这是确保 Jenkins 能拉取私有仓库代码的必备操作。
在 Jenkins 服务器上生成 SSH 密钥对
bash
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
生成的文件:
~/.ssh/id_rsa------ 私钥(内容粘贴到 Jenkins)~/.ssh/id_rsa.pub------ 公钥(添加到 GitHub/GitLab 账户的 SSH Keys)
将公钥添加到代码仓库
- GitHub:Settings → SSH and GPG keys → New SSH key
- GitLab:Preferences → SSH Keys
在 Jenkins 中添加私钥
添加凭证时选择 SSH Username with private key:
- Username :填写代码仓库账户(如
git或 GitHub 用户名) - Private Key → Enter directly → 粘贴
id_rsa私钥的完整内容 (包含-----BEGIN OPENSSH PRIVATE KEY-----头尾) - 输入可选的 Passphrase(若生成密钥时设置了密码短语)
6.3 添加容器镜像仓库凭证
选择 Username with password 类型:
| 注册表类型 | Username | Password | 凭证 ID 示例 |
|---|---|---|---|
| Docker Hub | Docker Hub 用户名 | Docker Hub 密码/Access Token | dockerhub-creds |
| 阿里云容器镜像 | 阿里云账号名 | 镜像仓库访问密码 | aliyun-acr-creds |
📌 关键提示 :为凭证填写一个有意义的 ID (如
dockerhub-credentials、git-ssh-key),因为在 Jenkinsfile 中要引用这个 ID,而不是直接硬编码用户名密码。
Pipeline 中的凭证引用示例
groovy
withCredentials([usernamePassword(
credentialsId: 'dockerhub-credentials',
usernameVariable: 'DOCKER_USER',
passwordVariable: 'DOCKER_PASS'
)]) {
sh 'echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin'
}
📄 七、Jenkinsfile 自动化部署流水线
7.1 流程图速览
检出代码
Maven/NPM 构建
Build Docker 镜像
Login Registry
Push 镜像
部署镜像
docker run 或 remote deploy
通知/清理
7.2 完整声明式 Pipeline 模板
在代码仓库的根目录下创建一个 Jenkinsfile(以 Spring Boot Java 应用为例,实际可灵活适配):
groovy
pipeline {
agent any
environment {
// ==================== 请根据实际修改此处 ====================
DOCKER_REGISTRY = 'your-registry.cn-hangzhou.cr.aliyuncs.com' // 镜像仓库地址
IMAGE_NAME = 'your-namespace/your-app' // 镜像名称
// ==========================================================
}
stages {
// Stage 1: 从 Git 仓库拉取最新代码
stage('Checkout') {
steps {
checkout scm
script {
// 生成唯一的镜像标签: 构建号-提交哈希前7位(保证可追溯)
SHORT_COMMIT = sh(script: 'git rev-parse --short HEAD', returnStdout: true).trim()
TAG = "${env.BUILD_NUMBER}-${SHORT_COMMIT}"
echo "镜像标签: ${TAG}"
}
}
}
// Stage 2: 【构建】根据项目类型调整
stage('Build') {
steps {
// Java/Maven 项目示例
sh 'mvn clean package -DskipTests'
// Node.js 示例: sh 'npm install && npm run build'
}
}
// Stage 3: 【打包】基于项目 Dockerfile 构建镜像(利用宿主机 Docker)
stage('Build Docker Image') {
steps {
script {
// 方案 A(挂载 socket)语法:
docker.build("${DOCKER_REGISTRY}/${IMAGE_NAME}:${TAG}")
// 方案 B(DinD)语法:
// docker.withServer('tcp://docker:2376', 'docker-certs') {
// docker.build("${DOCKER_REGISTRY}/${IMAGE_NAME}:${TAG}")
// }
}
}
}
// Stage 4: 【推送】登录私有镜像仓库并推送镜像
stage('Push to Registry') {
steps {
script {
// 使用之前配置的 Docker Hub / 私有仓库凭证
docker.withRegistry("https://${DOCKER_REGISTRY}", 'dockerhub-credentials') {
docker.image("${DOCKER_REGISTRY}/${IMAGE_NAME}:${TAG}").push()
// 可选: 为 latest 标签也推送一次
docker.image("${DOCKER_REGISTRY}/${IMAGE_NAME}:${TAG}").push('latest')
}
}
echo "✅ 镜像推送完成: ${DOCKER_REGISTRY}/${IMAGE_NAME}:${TAG}"
}
}
// Stage 5: 【部署】在目标服务器上拉取并运行新版本容器
stage('Deploy to Server') {
steps {
script {
// 方式一: 如果部署在本地宿主机(直接 docker run 更新)
sh """
docker pull ${DOCKER_REGISTRY}/${IMAGE_NAME}:${TAG}
docker stop my-app || true
docker rm my-app || true
docker run -d --name my-app -p 80:8080 ${DOCKER_REGISTRY}/${IMAGE_NAME}:${TAG}
"""
// 方式二: 如果部署在远程服务器(通过 SSH 执行命令)
// 需要安装 SSH Pipeline Steps 插件并配置远程主机凭证
// sshagent(['remote-server-ssh-key']) {
// sh "scp ... && ssh user@remote-server 'docker pull ... && docker run ...'"
// }
}
}
}
}
post {
// 每个阶段结束后自动清理工作空间残留
always {
cleanWs()
}
success {
echo "🎉 流水线执行成功!镜像已推送并部署。"
}
failure {
echo "❌ 流水线执行失败,请检查日志。"
}
}
}
7.3 关键代码解读
| 代码块 | 作用 |
|---|---|
checkout scm |
自动拉取触发此次构建的 Git 仓库对应分支的最新代码 |
SHORT_COMMIT + BUILD_NUMBER |
组合镜像标签,保证唯一性且易于回溯提交记录 |
docker.build(...) |
Docker Pipeline 插件提供的原生镜像构建命令,底层调用 docker build |
docker.withRegistry |
安全登录镜像仓库并推送,全程无需暴露明文密码 |
post -> always/cleanWs |
Jenkins Pipeline 的"最终执行块",保证每次构建完毕清理残留文件,避免磁盘占满 |
🚀 八、多分支流水线配置(推荐生产环境)
8.1 多分支流水线的价值
多分支 Pipeline 是 Jenkins 提供的一种高级功能,它可以自动扫描 Git 仓库中所有分支,并为每个分支独立创建和触发对应的流水线。这意味着:
- 开发分支(
develop)自动构建并部署到测试环境 - 主干分支(
main/master)自动构建并部署到预发环境 - 发布分支(
release/*)自动构建并部署到生产环境 - 无需为每个分支单独配置 Jenkins Job
8.2 配置步骤
- 在 Jenkins 首页点击 新建任务
- 输入项目名称,选择 Multibranch Pipeline(多分支流水线)
- 在 Branch Sources 中添加 Git 仓库 URL 并选择之前配置的 SSH 凭证
- 在 Build Configuration 中指定 Jenkinsfile 的路径(默认为仓库根目录的
Jenkinsfile) - 点击 保存,Jenkins 将自动扫描全部分支并启动构建
Git 仓库
独立构建
独立构建
独立构建
main 分支
develop 分支
feature/xxx 分支
Jenkins 多分支扫描
main 流水线 → 生产部署
develop 流水线 → 测试环境部署
feature 分支 → 快速验证
🏗 九、整体架构图(UML 部署图)
渲染错误: Mermaid 渲染失败: Lexical error on line 8. Unrecognized text. ...e
配置 + 插件 + 构建历史] end -----------------------^
📐 此图展示了 Jenkins Master 与宿主机 Docker Engine、代码仓库、镜像仓库、以及部署目标之间的关联关系。
🔧 十、常见问题排查与优化建议
10.1 容器内无法执行 docker 命令
现象 :构建阶段报 docker: command not found 或 Cannot connect to the Docker daemon。
解决方案:
- 确认启动容器时已挂载
/var/run/docker.sock和/usr/bin/docker - 进入容器验证:
docker exec jenkins docker ps - 检查宿主机 Docker 是否正常运行:
systemctl status docker
10.2 Webhook Git 推送不触发构建
现象:代码已 push 到仓库,但 Jenkins 未自动触发构建。
解决方案:
- 确认 Jenkins 能够被外网/Git仓库访问(检查安全组或防火墙是否放行 8080 端口)
- GitHub 仓库的 Settings → Webhooks 中添加 Payload URL:
http://<jenkins-公网IP>:8080/github-webhook/ - GitLab 的 Webhook URL 为:
http://<jenkins-公网IP>:8080/project/<项目名> - Jenkins 全局安全配置中勾选 "GitHub 自动触发" 相关选项
10.3 Docker socket 权限不足
现象 :Got permission denied while trying to connect to the Docker daemon socket
解决方案:
bash
# 查看 /var/run/docker.sock 的属主
ls -l /var/run/docker.sock
# 将 Jenkins 容器用户加入 docker 组
sudo usermod -aG docker $USER
newgrp docker
# 或修改 socket 权限(不推荐生产环境)
sudo chmod 666 /var/run/docker.sock
10.4 Jenkins 频繁崩溃或内存不足
现象 :容器 502 无法访问,日志显示 OutOfMemoryError。
解决方案:
- 启动容器时增加资源限制:
--memory="4g" --memory-swap="4g" - 在
docker run参数中加入--shm-size=256m - 定期清理构建历史:系统管理 → 构建记录管理器 → 配置规则
10.5 生产环境安全强化清单
| 类别 | 推荐实践 |
|---|---|
| 网络隔离 | Jenkins 置于专用 Docker 网络,不与其他服务混用 |
| HTTPS | 前置 Nginx/Traefik 反向代理并启用 Let's Encrypt 自动签发证书 |
| 权限最小化 | 避免容器以 root 运行:--user 1000:1000 |
| 凭证轮换 | 镜像仓库 Token 每 90 天轮换一次,Git SSH Key 与个人用户严格区分 |
| 数据备份 | 每日凌晨自动备份 /data/jenkins_home:tar -zcvf jenkins_backup_$(date +%Y%m%d).tar.gz /data/jenkins_home |
| 日志轮转 | 配置 Jenkins 系统日志保留 30 天,避免磁盘写满 |
📝 十一、总结
本文系统地介绍了使用 Docker 部署 Jenkins 并实现自动化部署的完整路径:
| 章节 | 核心收获 |
|---|---|
| 三、部署 Jenkins | 掌握 docker run 和 docker-compose 两种容器化部署方式 |
| 四、Docker 集成 | 理解 挂载 socket 与 Docker-in-Docker 两种方案的原理与适用场景,并根据安全要求做出正确选择 |
| 六、凭证管理 | 学会安全存储 Git 和镜像仓库的登录凭据,避免密钥泄露 |
| 七、Jenkinsfile | 能够独立编写声明式 Pipeline,完成从代码拉取到镜像推送再到部署的自动化闭环 |
| 八、多分支 | 掌握多分支流水线的配置,让不同分支的 CI/CD 策略独立运行 |
完成本教程后,你可以将这套 CI/CD 流水线快速集成到团队的 Spring Boot、Node.js、Python 等项目之中,实现"一次提交,自动构建,一键部署"的 DevOps 全流程。
📎 十二、快速命令参考集
bash
# 1. 启动 Jenkins(挂载 socket 方案)
docker run -d --name jenkins --restart always \
-p 8080:8080 -p 50000:50000 \
-v /data/jenkins_home:/var/jenkins_home \
-v /var/run/docker.sock:/var/run/docker.sock \
-e TZ=Asia/Shanghai \
jenkins/jenkins:lts
# 2. 获取解锁密码
docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword
# 3. 进入容器调试
docker exec -it jenkins bash
# 4. 查看实时日志
docker logs -f jenkins
# 5. 重启 Jenkins
docker restart jenkins
# 6. 停止并删除容器(保留数据在 /data/jenkins_home)
docker stop jenkins && docker rm jenkins
# 7. 升级 Jenkins(先保留数据目录,再用新镜像启动)
docker pull jenkins/jenkins:lts
docker stop jenkins && docker rm jenkins
docker run ... # 使用相同的挂载目录重新启动