1. 为什么要用 Docker 部署 Jenkins
先说结论:如果你还在用传统方式往宿主机上装 Jenkins,那大概率是在给自己埋坑。
传统安装 Jenkins 的痛点我太熟悉了。首先是 JDK 依赖问题,Jenkins 新版本对 JDK 版本有硬性要求,比如 Jenkins 2.3xx 之后要求 JDK 8 或 11,而到了 2.4xx 系列,JDK 11 都开始显得吃力,很多插件要求 JDK 17。你服务器上可能还跑着别的 Java 应用,动一下 JDK 版本就是牵一发动全身。其次是安装过程繁琐,下载 WAR 包、配置 Jetty 参数、写 systemd 服务脚本、设置开机自启,每一步都有出错的余地。再就是升级和回滚,Jenkins 升级出问题的情况并不少见,传统方式下回滚基本等于重装。
Docker 方式把这些全部解决了。镜像打包了完整的运行环境,JDK 版本、基础配置全部固化在镜像里,你不需要关心宿主机上的 Java 环境。升级就是换镜像版本重新起容器,出问题就切回旧镜像,整个过程秒级完成。数据通过 volume 挂在宿主机上,想迁移服务器直接把挂载目录打包带走就行,新服务器上一条 docker run 命令就恢复全部配置和构建任务。
不过这里要想清楚一件事:Jenkins 官方镜像 jenkins/jenkins 和 jenkinsci/blueocean 该怎么选?我的建议是,没有特殊情况直接选jenkins/jenkins 的 LTS 版本,标签格式类似于 jenkins/jenkins:lts-jdk17。BlueOcean 镜像内置了 BlueOcean 界面,看着花哨,但实际用下来发现插件兼容性和升级节奏反而麻烦,国内外很多团队已经放弃了 BlueOcean 界面,回归经典 UI。LTS 版本每 12 周发布一次,稳定性优先,插件兼容性也经过更多验证,适合生产环境使用。
还要考虑一个问题:Docker 里的 Jenkins 要不要用 DinD(Docker in Docker)?很多教程会让你在 Jenkins 容器里再装一个 Docker 引擎,或者在容器里挂载 /var/run/docker.sock,让 Jenkins 能直接调用宿主机的 Docker。我个人的经验是,如果你的 Jenkins 主要做代码构建、测试、静态检查这类任务,完全不需要容器内运行 Docker,用 JDK、Maven、Node 这些工具链就够了。只有当你要把构建产物打成 Docker 镜像并推送仓库时,才需要考虑挂载 docker.sock 的方案。而且挂载 docker.sock 存在一定的安全风险,因为容器内拿到了宿主机的 Docker 控制权,等于有了宿主机 root 权限,建议只在信任的构建任务中开启,别让 Jenkins 跑不可信的流水线脚本。
2. 安装前的环境准备
别急着直接拉镜像,先把宿主机环境收拾好,后面能省掉一半的排查时间。
2.1 宿主机 Docker 环境检查
无论你用的是哪台机器,先确认 Docker 本身能不能正常工作。Linux 环境下执行:
docker --version docker info我遇到过很多次,用户报 Jenkins 起不来,结果第一步就发现 Docker 服务根本没启动。Linux 下排查服务状态:
systemctl status docker没启动就启动并设置开机自启:
sudo systemctl start docker sudo systemctl enable docker如果是 Windows 环境,Docker Desktop 的坑就更多了。热词里出现的 virtualizaton support not detected 和 Docker Desktop failed to start 几乎是新人必踩的坎。Docker Desktop 在 Windows 上依赖 WSL2 和硬件虚拟化,这里有两个前置条件要注意:
第一,BIOS 里必须开启虚拟化技术。重启进 BIOS,找到 Intel Virtualization Technology(Intel VT-x)或 AMD SVM Mode,设为 Enabled。开机后可以在任务管理器 -> 性能 -> CPU 里查看虚拟化是否已启用,显示"已启用"就是没问题,显示"已禁用"就得回 BIOS 检查。
第二,WSL2 要装好。管理员身份打开 PowerShell,执行:
wsl --install安装完成后重启系统,把默认版本设为 WSL2:
wsl --set-default-version 2然后打开命令提示符输入 wsl --status 确认发行版状态。Docker Desktop 在设置里勾选 Use the WSL 2 based engine,它就会自动调用 WSL2 后端。如果检测不到虚拟化,Docker Desktop 启动时就会直接报 virtualizaton support not detected,这种一般是 BIOS 设置问题,少数情况是 Windows 的 Hyper-V 功能没启用,可以用:
dism.exe /Online /Enable-Feature /FeatureName:Microsoft-Hyper-V-All这个命令需要在管理员 PowerShell 里执行,装完还要重启。
Windows 下 Docker Desktop 启动后还有个常见报错:Failed to connect to the Docker API at npipe:////./pipe/dockerDesktopLinuxEngine。这个词条出现在热词里完全不意外,九成的原因是 Docker Desktop 正在启动过程中,Linux 后端引擎还没就绪,你在终端里就急着敲 docker 命令了。解决方法是等托盘图标不再转圈、变成稳定状态后再用 docker version 确认连接。如果一直连不上,可以执行:
net stop com.docker.service net start com.docker.service然后重启 Docker Desktop。再不行就检查 Windows 功能里的 Hyper-V、虚拟机平台、适用于 Linux 的 Windows 子系统这三个选项是否都勾上了。
2.2 目录规划与权限问题
Linux 下我习惯把 Jenkins 数据放在 /opt/jenkins_home,Windows 下放在 D:\docker\jenkins_home。目录的权限问题几乎人人都踩过:Jenkins 容器内的用户是 uid 1000,如果你创建目录后没做权限处理,启动容器时会报 Permission denied。
两种处理方式任选:
sudo mkdir -p /opt/jenkins_home sudo chown -R 1000:1000 /opt/jenkins_home或者干脆让容器自己初始化权限:
sudo mkdir -p /opt/jenkins_home sudo chmod 755 /opt/jenkins_home第二种方式我在某些场景下遇到过 SELinux 拦截的问题,如果启动容器时提示 permission denied,建议直接 chown 到 1000 用户。Debian/Ubuntu 系还要看一眼 AppArmor 状态。
这里插一句,别图省事把 Jenkins 数据目录放在 /tmp 或者 root 家目录下面,权限混乱只是小事,真正麻烦的是备份和迁移时你根本不知道哪些文件属于 Jenkins。目录规划从一开始就给 Jenkins 一个专属空间,后面做备份、做清理、做迁移都清晰得多。
2.3 镜像选择与加速器配置
docker pull jenkins/jenkins:lts-jdk17 是我的首选。这里有个小细节,国内网络环境直接拉官方镜像经常会卡住,要么配置 Docker 镜像加速器,要么从其他渠道获取镜像。
加速器配置位置在 /etc/docker/daemon.json(Linux)或 Docker Desktop 的 Settings -> Docker Engine(Windows):
{ "registry-mirrors": [ "https://docker.m.daocloud.io" ] }填好之后重启 Docker 服务:
sudo systemctl daemon-reload sudo systemctl restart docker如果加速器也慢,那就只能慢慢等了,跳过加速器设置直接执行 pull。镜像几百 MB,网速差的时候确实折磨人。拉取完成后可以验证一下:
docker images | grep jenkins到这里环境准备就结束了。整个准备过程看起来琐碎,但这些基本功不做扎实,后面 Jenkins 起来之后各种网络、权限、构建环境问题会一起爆发,到时候排查起来远比现在多花十分钟要痛苦得多。
3. Jenkins 容器安装实操
3.1 命令行方式快速启动
最直接的方式是 docker run。我给出一套自己长期在用的参数组合:
docker run -d \ --name jenkins \ --restart=always \ -p 8080:8080 \ -p 50000:50000 \ -v /opt/jenkins_home:/var/jenkins_home \ -v /var/run/docker.sock:/var/run/docker.sock \ -e TZ=Asia/Shanghai \ jenkins/jenkins:lts-jdk17逐个参数解释一下:
-p 8080:8080:Jenkins Web 服务端口,宿主机 8080 映射到容器 8080。如果 8080 被占了,改成-p 8081:8080,但后面访问地址也要跟着变。-p 50000:50000:Jenkins 与 agent 节点通信的端口。只在你要做分布式构建时才用得上,纯单机使用可以不映射,但留着也不碍事。-v /opt/jenkins_home:/var/jenkins_home:数据持久化,Jenkins 的所有配置、任务、插件、构建记录都在这个目录里。这是整个部署方案的生命线。-v /var/run/docker.sock:/var/run/docker.sock:把宿主机的 Docker 控制权暴露给容器。前面说过,只有需要 Jenkins 构建 Docker 镜像才加这个参数。加了之后,容器内可以用 docker 命令操作宿主机引擎,但前提是容器里得有 docker 客户端二进制,官方镜像不是都带,后面我会单独讲这个问题。-e TZ=Asia/Shanghai:时区设置。不设置的话容器默认 UTC 时间,构建日志和定时任务的时间看着能让人怀疑人生。
启动之后等一两分钟,第一次启动 Jenkins 要初始化目录和配置文件。然后用浏览器访问 http://宿主机IP:8080,你会看到解锁页面,要求输入管理员密码。
密码获取方式:
docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword把输出的一串十六进制字符复制粘贴进输入框即可。这个页面也是新人最容易卡住的地方,注意看的是容器内的路径,不是宿主机路径。
3.2 docker-compose 方式部署
生产环境我更推荐 compose 方式。说句心里话,docker run 只适合临时起一个环境玩玩,真正要长期维护,compose 文件才是正道。它把容器的定义、网络、依赖关系都写进文件里,换机器部署时一条 docker-compose up -d 就完事,不需要回忆当初敲了什么命令。
创建一个目录:
mkdir -p /opt/jenkins && cd /opt/jenkins新建 docker-compose.yml:
version: '3.8' services: jenkins: image: jenkins/jenkins:lts-jdk17 container_name: jenkins restart: always ports: - "8080:8080" - "50000:50000" volumes: - /opt/jenkins_home:/var/jenkins_home - /var/run/docker.sock:/var/run/docker.sock environment: - TZ=Asia/Shanghai启动:
docker-compose up -d看日志:
docker-compose logs -f jenkins这个文件我能直接用一年,不是说没有需要改的时候,而是改动的频率很低。真正需要扩展的时候,通常不是加参数,而是加配套服务。
3.3 配套容器一起编排
在实际使用中,Jenkins 很少是孤零零一个容器,旁边通常站着 SonarQube 做代码质量分析、Nexus 或者 Harbor 做制品仓库、MySQL 做任务数据存储。把这些兄弟服务写进同一个 compose 文件,互相之间用服务名通信,比各自单独起容器然后靠 IP 互相访问要省心得多。
举个例子,我要加一个 Jenkins 构建依赖的 Maven 私服 Nexus:
version: '3.8' services: jenkins: image: jenkins/jenkins:lts-jdk17 container_name: jenkins restart: always ports: - "8080:8080" - "50000:50000" volumes: - /opt/jenkins_home:/var/jenkins_home - /var/run/docker.sock:/var/run/docker.sock environment: - TZ=Asia/Shanghai networks: - devops-net nexus: image: sonatype/nexus3 container_name: nexus restart: always ports: - "8081:8081" volumes: - /opt/nexus-data:/nexus-data networks: - devops-net networks: devops-net: driver: bridge这样 Jenkins 里面配置 Maven 私服地址时,可以直接写 http://nexus:8081,不用去查 IP。Docker 内置 DNS 会自动解析服务名。这是一个很大的效率提升,也减少了很多因为 IP 变化导致的构建失败。
3.4 初始化安装插件的关键选择
第一次访问解锁页面后,Jenkins 会问你要不要安装社区建议的插件。这一步我建议选择 Install suggested plugins,除非你有非常明确的离线安装需求。为什么?因为后面再手动补插件虽然不难,但新版 Jenkins 的插件依赖关系处理得很严格,漏一个依赖插件就会导致功能异常。官方推荐列表是经过兼容性测试的,先装全,之后根据实际需要再精简。
插件安装是最能体现网络状况的环节。在线安装走的是 Jenkins 官方更新中心,国内网络环境下经常超时。安装过程中看到一堆红色的失败提示不要慌,等全部结束后,进入 Manage Jenkins -> Plugins -> Available plugins,点右上角 Check now,同时把国内插件加速站配置上去。
加速配置位置:Manage Jenkins -> Plugins -> Advanced settings。找到 Update Site 输入框,填入国内镜像地址。这里注意不要写带引号的地址,直接填 URL 就行。填完先点 Check now 验证连通性,再点 Submit 保存。这一步做完,后面装插件的速度会有质的提升。
如果加速源也失效了,还有一个兜底方案:在 Jenkins 服务器上配一个代理服务,让updates.jenkins.io的流量走代理。但这种方式在部分公司内网里会碰到证书拦截,反而更麻烦。所以我的建议顺序是:默认源 -> 国内加速源 -> 手工上传 hpi 文件。第三种方式适合完全断网的环境,在 Manage Jenkins -> Plugins -> Advanced settings 里有一个 Upload Plugin 的入口,把提前下载好的 .hpi 文件上传即可。这种方式虽然笨,但绝对有效。
4. 环境配置与工具链部署
到这一步,Jenkins 已经跑起来了,但离真正干活还有距离。构建任务需要 JDK、Maven、Node 这些工具链。这里我说一个很多新手理解偏差的地方:Jenkins 的构建环境,默认是复用容器内的环境,不是宿主机上装什么 Jenkins 就能用什么。
4.1 通过系统配置添加 JDK 和构建工具
进入 Manage Jenkins -> Tools,能看到 JDK、Maven、NodeJS 等配置项。这里有两种玩法:一种是让 Jenkins 自动下载指定版本的工具,另一种是使用容器内已经安装好的工具路径。
我推荐第一种,因为容器是干净的,不保证预装了你要的版本。以 JDK 为例,在 Tools 配置里点 Add JDK,选择 Install automatically,勾选版本比如 JDK 17,Jenkins 会在首次构建时自动下载并配置好。Maven 同理,Add Maven -> Install automatically -> 选择版本。
这里有个非常实用的细节:手动配置 JDK 路径时,容器内的路径和宿主机路径完全无关,这一点必须刻在脑子里。Jenkins 运行在容器内,它看到的文件系统是容器视角的。如果宿主机 JDK 安装在 /usr/lib/jvm/java-17-openjdk-amd64,容器内对应的路径很可能是 /opt/java/openjdk17,因为官方镜像已经把 JDK 放到了自己的标准位置。所以要么使用自动安装,要么用 docker exec 进容器里确认路径,别凭宿主机记忆写容器路径。
工具链配置好之后,构建任务在 Executors 中就可以使用这些工具了。但这里又有一个高频坑:容器内默认用户是 jenkins(uid 1000),它没有 root 权限,很多构建脚本里要执行 sudo 的命令会直接失败。解决方案有两种,一种是在 docker run 时加--user root,让容器以 root 身份运行,这种做法简单粗暴,适合内部测试环境;另一种是给 jenkins 用户配置免密 sudo 权限,需要你自己制作一个继承镜像并在里面安装配置 sudo,麻烦一点但相对安全。对于生产环境我建议用后一种,毕竟 root 跑 Jenkins 一旦被攻击者拿下,容器逃逸的后果不是闹着玩的。
4.2 安装常用构建插件
工具链选完,接下来是插件。我挑几个高频实际场景中绕不开的插件,建议全部装上:
- Credentials Plugin:基于凭据的认证管理,Git 仓库的 SSH 密钥、Docker Hub 的账号密码、服务器 SSH 密码,统一存在这个插件体系里。
- Git Plugin / Git Client Plugin:Git 仓库拉取代码的基础依赖,不装这俩,Pipeline 里的 git checkout 直接歇菜。
- Docker Pipeline Plugin:在流水线里用 docker 命令构建推送镜像的核心插件,与挂载 docker.sock 配合使用就能打通 CI 到镜像仓库的链路。
- Pipeline Utility Steps:提供 readJSON、writeJSON、readYaml 这些文件读取能力,流水线脚本里经常用到。
- Kubernetes Plugin:如果目标是让 Jenkins 动态调度 Kubernetes 上的 Pod 作为构建 agent,这个插件是核心。热词里提到的 jenkins 2.541.3配置kubunertes(大概率是 kubernetes 的拼写错误)指的就是这类场景。Kubernetes 插件配置起来比传统静态 agent 复杂一些,但好处是构建任务按需拉起 Pod,构建完自动销毁,资源利用率高很多。
- Locale Plugin:汉化插件,没有它界面一直是英文,虽然不影响使用,但对于习惯中文界面的用户来说,装了能显著降低操作焦虑。
- Email Extension Plugin:用邮件发送构建结果通知,Jenkins 自带的邮件功能有限,这个扩展插件可以配置富文本模板、附件、多收件人,比默认邮件好使得多。
插件装完了,去 Manage Jenkins -> Plugins -> Installed plugins 里搜一下,勾选你不需要的插件点卸载。别小看这一步,插件越多启动越慢,维护负担越大,还容易触发插件版本冲突。我的原则是:能用系统自带的就不用插件,能用一个插件解决的事绝不用两个。
4.3 凭据管理与安全配置
凭据管理是 Jenkins 使用中最重要也最容易被忽略的部分。我见过太多团队把 Git 密码、服务器 SSH 密码直接写死在 Pipeline 脚本的明文里,这是拿公司的基础设施开玩笑。正确做法是:把凭据存到 Jenkins 的 Credentials 里,在 Pipeline 中用 withCredentials 上下文引用。
添加凭据的位置:Manage Jenkins -> Credentials -> System -> Global credentials (unrestricted) -> Add Credentials。
常见的凭据类型里,Username with password 适合 HTTP Git 仓库账号密码和 SSH 登录密码;SSH Username with private key 适合 Git 服务器的 SSH key;Secret text 适合 API Token 这类字符串。选对类型能省去后面很多认证失败的排查时间。
在 Pipeline 里引用凭据的标准姿势是:
withCredentials([usernamePassword(credentialsId: 'my-git-credentials', usernameVariable: 'GIT_USER', passwordVariable: 'GIT_PASS')]) { sh 'git clone http://$GIT_USER:$GIT_PASS@git.example.com/group/repo.git' }密码不会出现在控制台日志上,Jenkins 会自动遮蔽。这里有个反直觉的点:不要自己去 echo 凭据变量的值去验证是否取到,因为 echo 出来之后 Jenkins 可能无法遮蔽,密码会泄露在构建日志里。
4.4 汉化与界面优化
Locale 插件装好后,在 Manage Jenkins -> Configure System 里找到 Locale 选项,输入 zh_CN,同时勾选 Ignore browser preference and force this language to all users。保存后刷新页面,整个界面就变成中文了。
汉化这事看着简单,但有个小坑:插件装完后要重启 Jenkins 或者等热加载完成,汉化才生效。如果刷新后还是英文,先去 Manage Jenkins -> Plugins 里确认 Locale 插件状态是 Installed and enabled,再检查系统配置里的强制语言是否保存成功。还有一个容易被忽略的问题,有些界面元素是第三方插件自带的,他们自己不做国际化,这部分就算系统语言设成中文它也还是英文,这个属于正常现象,不用纠结。
4.5 容器内的 Jenkins 调用宿主 Docker 的配置
回到前面提到的挂载 docker.sock。挂载做完之后,容器内如果没有 docker 客户端,docker 命令照样跑不了。很多教程在这里断了,没告诉你要在容器内装 docker CLI。
两种处理方式:
第一种,进入容器安装 docker CLI:
docker exec -it jenkins bash curl -fsSL https://get.docker.com -o get-docker.sh sh get-docker.sh装的是完整的 Docker Engine,有点重,但一步到位。
第二种,通过挂载宿主机的 docker CLI 二进制文件到容器内:
docker run -d \ --name jenkins \ -v /usr/bin/docker:/usr/bin/docker \ -v /var/run/docker.sock:/var/run/docker.sock \ jenkins/jenkins:lts-jdk17这种方式省去了容器内安装的步骤,但前提是宿主机 Docker CLI 的版本和容器内依赖的库没有冲突,实际使用中偶尔会遇到动态库找不到的情况。所以我的默认方案还是第一种,完整安装虽然多花一两分钟,但最稳。
配置好之后在 Jenkins 系统管理 -> 节点管理 -> 内置节点 -> 配置里,查看一下环境变量,确认 PATH 里包含 docker 路径。或者直接建一个自由风格任务,构建命令写 docker version,跑一次看输出。这一步能快速验证 docker.sock 是否真正可用。
5. 常见故障排查与实践技巧
5.1 插件下载慢或失败
前面提过加速源的配置,这里再补充一个执行层面更有效的小技巧:如果你配置了加速源还是慢,可以测试一下加速源本身的连通性。在浏览器直接访问http://你的加速源/updates/current/update-center.json,如果能打开但很慢,说明加速源本身也在被限速,换一个。如果打不开,说明地址写错了或者源已经失效。换源后记得在插件管理页面点 Check now,让 Jenkins 重新拉取更新列表。
还有一类情况是下载了插件但激活失败,提示版本依赖冲突。这种多半是更新源的插件索引和你要装的插件版本不匹配。解决思路:优先安装 LTS 版本匹配的插件版本,不要追最新。如果在 Manage Jenkins 里看到某个插件版本显示 unavailable,直接去官方插件站找到对应版本的 hpi 文件,手动上传安装。
5.2 容器内 SSH 报错 Connection is not established
热词里的 jenkins ssh java.lang.illegalstateexception: connection is not established! 是 SSH 插件或者可发布插件跑任务时报的典型错误。我之前排查过几次,总结了三个高频原因:
第一,SSH 服务器本身不支持。确认目标机器上 sshd 服务正常,防火墙 22 端口放通。最简单的验证方式是在 Jenkins 容器里直接执行:
docker exec -it jenkins bash ssh -p 22 root@目标IP如果容器内手写 ssh 都连不上,那问题就出在网络层,跟 Jenkins 没关系。这边要提醒一点:很多云服务器厂商默认安全组不开放 22 端口,或者改了 SSH 端口,你要在容器里先确认用 -p 指定了正确的端口。
第二,凭据配置问题。SSH 服务器配置了密钥登录,但你填的是密码。或者密钥格式不对,服务端不接受。这种排查方法是,在凭据里重新创建 SSH 类型凭据,如果本地有密钥对,直接上传私钥内容,公钥确保已经写到了目标机器的 authorized_keys 里。
第三,首次连接时 known_hosts 主机的指纹确认。Jenkins 插件在非交互模式下遇到陌生主机指纹时,默认不自动接受,如果你之前从来没连过这台目标机器,就会因为 known_hosts 缺失导致连接异常。解决方式有两个:一是先在容器内手动 ssh 登录一次该目标机器,记录指纹;二是在系统配置里给 SSH 站点设置不验证主机密钥。生产环境还是建议把指纹输进去,别图省事关掉验证,Man-in-the-middle 攻击可不是闹着玩的。
5.3 容器启动失败与权限错误
启动容器时最常见的两个报错:
mkdir /var/jenkins_home/.jenkins: permission denied chown: changing ownership of '/var/jenkins_home': Permission denied都是同一个原因:挂载目录的所有者不是 uid 1000。执行:
sudo chown -R 1000:1000 /opt/jenkins_home然后重新启动容器。如果你用的是 compose,先停掉再启动:
docker-compose down docker-compose up -d另一个高频问题是在 Docker Desktop 环境中,挂载目录是 D:\docker\jenkins_home 这种 Windows NTFS 路径,容器内 chown 权限动作经常受限。解决办法是在 Windows 上把 Docker Desktop 的挂载方式改为使用 WSL2 后端后重建容器,或者给目录加上完全控制权限(右键属性 -> 安全 -> 编辑 -> 完全控制)。
5.4 Jenkins 更新中心加速与版本兼容
热词里有一条 jenkins 的插件加速器,其实就是设置 Update Site 的操作,前面已经讲过。这里再补充一条经验:加速源有风险,镜像内容可能滞后于官方源几天甚至几周,所以如果你在插件市场搜索一个刚发布的插件或者新版本搜不到,不要怀疑自己操作失误,先切回官方源试试能不能搜到,能搜到说明加速源同步滞后,沉住气等它同步,或者直接手动上传 hpi。
版本兼容这块,一个最真实的教训:不要在生产环境轻易升级 Jenkins 主版本。每次升级前先去官网看 What's new,了解升级对现有插件的影响。我在生产环境把 Jenkins 从 2.387 升到 2.414 时,有个旧版插件直接不兼容导致构建全部失败,最后把镜像回滚到旧版本才恢复。所以升级 Jenkins 的正确姿势是:备份 /opt/jenkins_home、快照插件清单、升级后立即验证核心流水线。想要稳妥,可以先用 docker tag 给当前镜像打个标签作为快速回滚点。
Jenkins 官方也有升级助手机制,如果第一次升级失败,它会启动回滚。但无论如何,做好备份永远是你最后的保障。
5.5 构建历史与磁盘空间的清理
Jenkins 跑久了,/var/jenkins_home/jobs 下会积累大量构建记录和工作空间文件,体积轻松超过几十个 G。热词里的 jenkins构建清理 指的就是这件事。
清理构建历史的推荐方式是使用 Discard Old Builds 策略。在任务配置 -> General 里打开 Discard old builds,设置保持构建的天数和最大数量。比如保留 7 天或最近 30 次构建,这样旧数据会自动清理。
对于工作空间,可以在任务执行完成后执行自动清理。一种做法是在流水线结束阶段加一步:
stage('Cleanup') { steps { cleanWs() } }cleanWs() 会删掉当前任务的工作空间。额外的磁盘清理还可以用 docker system prune 命令把悬空镜像、停止的容器、无用的网络和数据卷清理掉:
docker system prune -f docker system prune -a注意-a会删除所有没有被容器使用的镜像,执行前确认你真的不需要那些镜像了。我一般只跑不带 -a 的,保留所有镜像避免误删。
5.6 Docker 网络不通的问题
有时 Jenkins 容器能跑起来,但在容器内部无法访问宿主机上的其他服务,比如访问宿主机 3306 端口的 MySQL。这个属于 Docker 网络模式问题,如果你是用 bridge 网络启动容器,容器内访问宿主机可以用特殊域名host.docker.internal,这是 Docker Desktop 提供的宿主机别名。Linux 服务器上默认不一定支持,可以通过--add-host=host.docker.internal:宿主机IP参数手动加一条 hosts 映射:
docker run -d \ --add-host=host.docker.internal:192.168.1.100 \ jenkins/jenkins:lts-jdk17这样容器内访问数据库、Redis 这类宿主机服务时,直接用 host.docker.internal 就行。在 compose 文件里对应的写法是:
extra_hosts: - "host.docker.internal:192.168.1.100"这个技巧在开发环境和测试环境特别实用,少了到处配 IP 的麻烦。
5.7 构建失败时查看日志的姿势
最后分享一个排查思路,这是我在实际支持中给团队反复叮嘱的:构建失败不要只看 Jenkins 的 Console Output 底部几行,直接把整个日志下载下来,然后 grep 关键词。一般我会按照这个顺序排查:
- 查看 Console Output 最后的红色/错误文本,定位大致阶段。
- grep ERROR 和 Exception 关键词,找到异常点。
- 如果和 Docker 有关,看容器日志:docker logs jenkins。
- 如果和网络有关,在容器内 curl 一下目标地址,确认网络通不通。
- 如果和凭据有关,检查凭据 ID 是否存在或是否被误删。
这套流程虽然朴素,但能解决 90% 以上的 Jenkins 故障,至少能让你带着明确的问题去搜索引擎求解,而不是对着报错干瞪眼。
6. 总结与个人实践心得
工作三年维护 Jenkins 下来,我最大的体会是:Docker 化的 Jenkins 真正的威力不在安装那一瞬间,而在于后续的升级、迁移和扩展都非常顺畅。以前用传统方式装 Jenkins,每次升级都如临大敌,现在换镜像重启,五分钟完成。备份也不再依赖什么专门的备份插件,直接把 /opt/jenkins_home 目录打成 tar 包扔到对象存储里,恢复时解压再起一个容器就能继续干活。
还有一个小技巧值得分享:如果你计划长期维护 Jenkins,建议把 /opt/jenkins_home 目录纳入自动化备份任务。我习惯每天凌晨 2 点执行一次备份脚本,把目录打包并保留最近 14 天的版本。这个动作看起来简单,但真正遇到插件事故、误删任务、配置丢失的时候,一条命令恢复全量配置的底气是无可替代的。
最后一个经验:Jenkins 是工具,不是目的。不少人花大量时间折腾 Jenkins 的各种插件、主题、面板,却忽略了真正重要的事情——让代码提交到自动构建、测试、部署的整个链路跑得顺畅可靠。我踩过的最大的坑就是过度追求界面华丽和插件齐全,结果维护成本直线上升。保持最小可用、按需扩展、定期清理,这十二个字是我给所有想要长期玩 Jenkins 的人的忠告。
这套 Docker + Jenkins 的方案我已经在十几台机器上验证过,从单机配置到生产级集群都能稳定运行。照着这篇文章的步骤走下来,你花在安装配置上的时间不会超过半小时,剩下的精力可以真正投入到自动化流水线的设计上。