Jenkins Docker 安装方式:容器化部署实践
Docker 安装是 Jenkins 最便捷的部署方式之一,无需在宿主机配置 Java 环境,一条 docker run 命令即可启动完整的 CI/CD 服务。但官方文档中的示例命令存在若干需要调整的细节,本文将逐层解析并提供生产可用的完整配置。
镜像选择:jenkins/jenkins vs jenkinsci/blueocean
Docker Hub 上有两个常见的 Jenkins 镜像:
| 镜像 | 说明 | 推荐度 |
|---|---|---|
jenkins/jenkins:lts | 官方 LTS 镜像,体积约 400-500 MB,仅包含 Jenkins 核心 | 推荐 |
jenkinsci/blueocean | 预装 Blue Ocean 插件的镜像,体积更大 | 不推荐(Blue Ocean 已停更) |
推荐使用 jenkins/jenkins:lts,理由如下:
- 镜像体积更小,下载更快
- Blue Ocean 项目已进入维护模式,不再积极开发
- 可以按需在 Jenkins 管理界面中安装插件,保持环境精简
官方命令的问题分析
Jenkins 官方文档提供的 Docker 安装命令大致如下:
docker run \
--rm \
-p 8080:8080 \
-v jenkins_home:/var/jenkins_home \
-v /var/run/docker.sock:/var/run/docker.sock \
jenkinsci/blueocean
bash
这条命令存在三个需要关注的问题:
问题一:--rm 参数不适合生产环境
--rm 会在容器停止时自动删除容器,这意味着所有未持久化的配置和数据都会丢失。生产环境应替换为 --restart=always 或 --restart=on-failure,配合 -d 后台运行。
问题二:blueocean 镜像不必要
如前所述,jenkinsci/blueocean 镜像体积大且 Blue Ocean 已停止更新,应替换为 jenkins/jenkins:lts。
问题三:docker.sock 映射的局限性
docker.sock 是 Docker 守护进程的 Unix Socket 文件,映射它的目的是让 Jenkins 容器内部能够操作宿主机的 Docker。这在理论上可以实现"Docker 中运行 Docker"(DinD),但实际上仅映射 socket 文件是不够的——容器内还需要安装 Docker 客户端才能与 socket 通信。jenkins/jenkins:lts 镜像并未预装 Docker CLI。
推荐的 Docker 运行命令
# 基础版:快速启动 Jenkins
docker run -d \
--name jenkins \
--restart=always \
-p 8080:8080 \
-p 50000:50000 \
-v jenkins_home:/var/jenkins_home \
jenkins/jenkins:lts
bash
参数说明:
| 参数 | 作用 |
|---|---|
-d | 后台运行 |
--name jenkins | 容器命名,方便后续管理 |
--restart=always | 容器异常退出时自动重启 |
-p 8080:8080 | Web 界面端口 |
-p 50000:50000 | Agent 通信端口(Master/Agent 架构需要) |
-v jenkins_home:/var/jenkins_home | 数据持久化,使用 Docker Named Volume |
启动后验证:
# 检查容器运行状态
docker ps | grep jenkins
# 查看初始化日志,获取管理员密码
docker logs jenkins 2>&1 | grep -A 2 "password"
# 或直接从容器文件中读取
docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword
bash
浏览器访问 http://<服务器IP>:8080,输入初始密码即可进入初始化向导。
Docker Compose 完整配置
对于更规范的部署,推荐使用 Docker Compose 进行管理:
# docker-compose.yml
# 适用于 Docker Compose V2(2025+)
# Jenkins LTS + 数据持久化 + 自动重启
services:
jenkins:
image: jenkins/jenkins:lts
container_name: jenkins
restart: always
privileged: false
ports:
- "8080:8080" # Web UI
- "50000:50000" # Agent 通信
volumes:
- jenkins_home:/var/jenkins_home
# 如需在 Jenkins 中操作宿主机 Docker,取消下行注释
# - /var/run/docker.sock:/var/run/docker.sock
environment:
- JENKINS_OPTS=--httpPort=8080
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/login"]
interval: 30s
timeout: 10s
retries: 5
start_period: 60s
deploy:
resources:
limits:
memory: 2G
cpus: "2.0"
reservations:
memory: 512M
cpus: "0.5"
volumes:
jenkins_home:
driver: local
yaml
使用 Docker Compose 管理服务:
# 启动 Jenkins
docker compose up -d
# 查看运行状态
docker compose ps
# 查看日志
docker compose logs -f jenkins
# 获取初始密码
docker compose exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword
# 停止服务
docker compose down
# 停止并删除数据卷(谨慎操作)
docker compose down -v
bash
自定义端口与 HTTPS 配置
如果 8080 端口已被占用,可以在 Docker Compose 中修改端口映射:
# docker-compose.yml(端口自定义版)
services:
jenkins:
image: jenkins/jenkins:lts
container_name: jenkins
restart: always
ports:
- "10050:8080" # 将宿主机 10050 映射到容器 8080
- "50000:50000"
volumes:
- jenkins_home:/var/jenkins_home
yaml
如果需要 HTTPS 支持,推荐使用 Nginx 反向代理而非在 Jenkins 容器内配置证书:
# docker-compose.yml(Nginx 反向代理版)
services:
jenkins:
image: jenkins/jenkins:lts
container_name: jenkins
restart: always
ports:
- "50000:50000"
volumes:
- jenkins_home:/var/jenkins_home
networks:
- jenkins-net
nginx:
image: nginx:alpine
container_name: jenkins-nginx
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/jenkins.conf:ro
- ./certs:/etc/nginx/certs:ro
depends_on:
- jenkins
networks:
- jenkins-net
networks:
jenkins-net:
driver: bridge
volumes:
jenkins_home:
driver: local
yaml
对应的 Nginx 配置文件:
# nginx.conf
server {
listen 80;
server_name jenkins.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
server_name jenkins.example.com;
ssl_certificate /etc/nginx/certs/jenkins.crt;
ssl_certificate_key /etc/nginx/certs/jenkins.key;
location / {
proxy_pass http://jenkins:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 90;
}
}
nginx
数据持久化详解
Jenkins 的所有配置、任务、插件和构建历史都存储在 /var/jenkins_home 目录下。使用 Docker Volume 确保数据不随容器销毁而丢失。
# 查看 Docker Volume 位置
docker volume inspect jenkins_home
# 备份 Jenkins 数据
docker run --rm -v jenkins_home:/source -v $(pwd):/backup \
alpine tar czf /backup/jenkins_backup_$(date +%Y%m%d).tar.gz -C /source .
# 恢复数据
docker run --rm -v jenkins_home:/target -v $(pwd):/backup \
alpine tar xzf /backup/jenkins_backup_YYYYMMDD.tar.gz -C /target
bash
也可以将 volume 直接映射到宿主机目录,方便直接访问:
volumes:
- /data/jenkins:/var/jenkins_home
yaml
注意:映射宿主机目录时需要确保目录权限正确,Jenkins 容器内进程以 UID 1000 运行。
docker.sock 映射的深入理解
docker.sock 是 Docker 守护进程的 Unix Socket 文件,用于进程间通信(IPC)。将其映射到 Jenkins 容器中的目的是实现 Docker in Docker(DinD),即让 Jenkins 在流水线中能够动态创建构建环境。
然而,这种做法存在以下问题:
- 安全性风险:容器获得宿主机 Docker 的完全控制权,等同于 root 权限
- 需要额外配置:仅映射 socket 文件不够,容器内还需安装 Docker CLI
- 架构复杂:Docker 嵌套增加了故障排查的难度
推荐的替代方案:
| 方案 | 说明 | 适用场景 |
|---|---|---|
| Jenkins 直装 + Docker | 在宿主机直装 Jenkins,直接使用宿主机 Docker | 单机生产环境 |
| Jenkins Docker + 独立 Agent | Jenkins 在 Docker 中运行,Docker 环境部署在另一台机器上作为 Agent | 安全性要求高的环境 |
| Jenkins Docker + socket 映射 | 映射 docker.sock 并安装 Docker CLI(下节详解) | 测试/开发环境 |
Troubleshooting 常见问题
问题一:容器内无法使用 docker 命令
docker exec -it jenkins docker ps
# bash: docker: command not found
bash
原因:jenkins/jenkins:lts 镜像未预装 Docker CLI,仅映射 docker.sock 无法直接使用。
解决方案(详见下一节):
- 自定义 Dockerfile 安装 Docker CLI
- 或使用 Jenkins Agent 节点运行 Docker 命令
问题二:容器启动后立即退出
docker ps -a | grep jenkins
# STATUS: Exited (1) 5 seconds ago
bash
排查步骤:
# 查看退出日志
docker logs jenkins
# 常见原因:
# 1. 端口映射冲突 -> 修改 -p 参数
# 2. Volume 权限问题 -> 检查宿主机目录权限
# 3. 内存不足 -> 增加 deploy.resources.limits.memory
bash
问题三:Jenkins 响应缓慢
优化方向:
# 在 docker-compose.yml 中增加资源限制
deploy:
resources:
limits:
memory: 4G # 增加内存
cpus: "4.0" # 增加 CPU
# 配置 JVM 参数
environment:
- JAVA_OPTS=-Xmx2g -Xms512m
yaml
问题四:Volume 数据丢失
如果误用 --rm 或执行了 docker compose down -v,数据卷会被删除。确保:
- 使用 named volume 而非匿名卷
- 定期备份 jenkins_home
- 不使用
docker compose down -v(-v会删除 volume)
↑