Jenkins 是目前使用最广泛的持续集成与持续交付工具之一。相比传统的直接安装在服务器上的方式,容器化部署具有明显的优势:环境隔离、版本可控、迁移方便、升级回滚简单。本文将完整介绍如何用 Docker 部署和配置一套可用于生产环境的 Jenkins 服务。

一、为什么选择容器化部署 Jenkins
传统的 Jenkins 安装通常是在一台物理机或虚拟机上安装 JDK,下载 Jenkins 的 war 包或者通过 yum、apt 安装。这种方式最大的问题是环境耦合:服务器上安装了多少插件、改了多少配置文件,往往只有运维人员自己清楚。一旦服务器故障或者需要迁移,重建环境的成本非常高。
容器化部署把 Jenkins 的运行环境打包成一个镜像,镜像里包含了固定版本的 JDK 和 Jenkins 主程序。迁移时只需要导出镜像和数据卷,在新的机器上导入即可恢复。升级时拉取新版本的镜像重建容器,如果出问题,直接切回旧版本镜像就能回滚,整个过程只需要几条命令。
另外,容器化之后可以很方便地与 Kubernetes 等编排系统结合,实现 Jenkins Agent 的动态伸缩,构建任务多的时候自动扩容,空闲时自动回收资源,这是传统部署方式很难做到的。
二、部署前的准备与镜像选择
部署之前需要确认宿主机已经安装好 Docker。可以通过以下命令检查:
docker --version docker info
如果输出正常,说明 Docker 环境可用。接下来是镜像的选择,官方推荐使用 LTS(长期支持)版本的镜像 jenkins/jenkins:lts,这个版本每 12 周发布一次,稳定性经过了充分验证。如果需要最新的功能,可以选择 jenkins/jenkins:latest,但生产环境不建议使用。
需要注意的一点是:jenkins/jenkins 镜像默认只包含 Jenkins 本身,并没有安装 Docker 客户端。如果你希望 Jenkins 在流水线里直接构建 Docker 镜像,需要额外处理,这个会在后面讲到。
拉取镜像的命令如下:
docker pull jenkins/jenkins:lts
三、启动容器与数据持久化
Jenkins 的所有配置、插件、任务数据都存放在容器内的 /var/jenkins_home 目录下,如果不做持久化挂载,容器一旦删除,所有数据都会丢失。因此启动容器时必须配置数据卷。
一个生产可用的启动命令如下:
docker run -d \ --name jenkins \ --restart=always \ -p 8080:8080 \ -p 50000:50000 \ -v /data/jenkins_home:/var/jenkins_home \ -e TZ=Asia/Shanghai \ -e JAVA_OPTS="-Duser.timezone=Asia/Shanghai -Xms512m -Xmx2048m" \ jenkins/jenkins:lts
逐个参数解释一下。--restart=always 保证容器在异常退出或宿主机重启后自动拉起。-p 8080:8080 映射 Web 界面端口,-p 50000:50000 映射 Agent 通信端口,这个端口是 Jenkins master 和 slave 节点通信用的,如果不使用分布式构建可以不映射。
-v /data/jenkins_home:/var/jenkins_home 是关键,把容器内的数据目录挂载到宿主机。挂载后宿主机目录的属主需要是 UID 为 1000 的用户,否则容器启动时会因为权限问题失败。可以用这个命令修复权限:
mkdir -p /data/jenkins_home chown -R 1000:1000 /data/jenkins_home
环境变量 TZ 和 JAVA_OPTS 用来解决时区问题。Jenkins 默认使用 UTC 时区,构建日志的时间戳会和北京时间相差 8 小时,设置之后显示就正常了。-Xmx2048m 限制堆内存最大 2G,根据宿主机配置调整即可。
四、初始化配置与插件管理
容器启动后,通过浏览器访问 http://服务器IP:8080 进入初始化页面。系统会要求输入管理员密码,这个密码在容器日志或者初始密码文件中可以找到:
docker logs jenkins # 或者 cat /data/jenkins_home/secrets/initialAdminPassword
输入密码后选择安装推荐插件,Jenkins 会自动安装 Git、Pipeline、Credentials 等常用插件。由于默认的插件源在国外,下载速度可能很慢,建议提前修改为国内镜像源。编辑挂载目录下的文件:
sed -i 's/https:\/\/updates.jenkins.io\/download/https:\/\/mirrors.tuna.tsinghua.edu.cn\/jenkins/g' /data/jenkins_home/updates/default.json sed -i 's/http:\/\/www.google.com/https:\/\/www.baidu.com/g' /data/jenkins_home/updates/default.json docker restart jenkins
插件安装完成后再创建一个管理员账号,进入系统即可。常用的必装插件包括 Git Parameter(分支参数选择)、Pipeline(流水线核心)、Role-based Authorization Strategy(权限管理)以及 Blue Ocean(可视化流水线界面),可以在系统管理、插件管理页面手动安装。
五、让 Jenkins 在容器内构建 Docker 镜像
这是容器化部署 Jenkins 最常见的痛点。因为 Jenkins 镜像内没有 Docker 环境,流水线里执行 docker build 会直接报错。主流的解决方案有两种。
第一种是挂载宿主机的 Docker Socket,让容器内的进程直接与宿主机的 Docker 守护进程通信:
docker run -d \ --name jenkins \ -p 8080:8080 -p 50000:50000 \ -v /data/jenkins_home:/var/jenkins_home \ -v /var/run/docker.sock:/var/run/docker.sock \ -v $(which docker):/usr/bin/docker \ -e TZ=Asia/Shanghai \ jenkins/jenkins:lts
这种方式配置简单,但存在安全风险:挂载了 docker.sock 的容器相当于拿到了宿主机的 root 权限。此外容器内的 jenkins 用户没有访问 socket 的权限,需要额外执行 usermod -aG docker jenkins 或者使用特权模式。为了简化,很多团队会基于官方镜像自己构建一个包含 Docker CLI 的镜像:
FROM jenkins/jenkins:lts
USER root
RUN apt-get update && \
apt-get install -y docker.io && \
rm -rf /var/lib/apt/lists/*
USER jenkins第二种方案是使用 Docker-in-Docker(DinD),也就是在 Jenkins 容器里再跑一个完整的 Docker 守护进程。这种方式隔离性更好,但嵌套容器开销大、存储驱动容易出问题,一般只在需要完全隔离构建环境的场景下使用。对于大多数团队,DooD(Docker outside of Docker)也就是第一种方案已经够用。
六、常见问题排查
部署完成后,日常使用中可能会遇到几个典型问题。第一个是启动后一直提示 Jenkins 正在启动,很长时间进不去。这通常是因为 JVM 内存不足或者插件下载超时导致的,可以查看 /data/jenkins_home/logs 目录下的日志定位原因,必要时增大堆内存或切换插件源。
第二个是流水线拉取 Git 仓库超时。如果仓库在内网,需要确认容器网络模式。默认的 bridge 网络一般没问题,但如果 Git 服务器需要通过宿主机的 VPN 访问,可以考虑使用 --network host 让容器直接共享宿主机网络栈,不过这样会牺牲端口隔离性。
第三个是构建日志中文乱码,一般是编码问题,可以在 JAVA_OPTS 中追加 -Dfile.encoding=UTF-8,同时确保流水线脚本中执行的相关命令使用 UTF-8 输出。遇到更复杂的问题时,docker logs -f jenkins 配合系统管理页面中的系统日志,基本能覆盖绝大多数排查场景。