代码推到 GitHub 后还要 SSH 登录服务器、手工执行 git pull、安装依赖、跑测试和重启服务,这套流程项目少时还能凑合。项目一多,最常见的问题不是“不会部署”,而是某一步被忘了:测试没跑、环境变量拿错、失败版本覆盖线上、谁操作的也查不到。
Jenkins 解决的是这条自动化链路。它能在收到 GitHub 推送后拉取代码,按仓库里的 Jenkinsfile 执行构建、测试、制品归档和部署,并保存每次运行的日志。不过 Jenkins 也能执行任意构建脚本,所以部署方式不能只追求“先跑起来”。Controller、构建 Agent、凭据和 Docker 权限必须从第一天就分开。
本文以 Jenkins 2.568.2 LTS、Java 21、Docker Compose、Caddy 和 Ubuntu 24.04 为基准,搭建一个适合小团队的 Jenkins CI/CD 环境。你会完成 Controller 部署、HTTPS、初始加固、GitHub Webhook、Pipeline、WebSocket Agent、备份恢复、升级和常见故障排查。版本信息按 2026 年 9 月 Jenkins 官方下载页核对;实际安装时仍应先看最新 LTS 与安全公告。
Jenkins 把中心服务称为 Controller。它保存配置、凭据、任务和构建记录,负责排队并把任务派给 Agent。默认安装允许在内置节点上执行构建,这对体验功能方便,但不适合长期使用。
构建脚本来自仓库,能执行 Shell、下载依赖、运行测试,甚至调用 Docker。只要它在 Controller 上运行,就可能读取 $JENKINS_HOME、插件配置和凭据相关文件。Jenkins 官方建议把 Controller 的执行器数量设为 0,所有构建交给 Agent。
本文使用下面的结构:
GitHub Push / Pull Request
|
| HTTPS Webhook
v
Caddy :443
|
| 127.0.0.1:8080
v
Jenkins Controller
仅编排,不执行构建
^
| WebSocket over HTTPS
|
Jenkins Build Agent
拉代码、测试、构建
WebSocket Agent 主动连接 Controller,只使用现有的 HTTPS 443,因此不需要再把 Jenkins 的 TCP Agent 端口 50000 暴露到公网。
Jenkins 的优势是插件多、部署位置自由、能连接内网环境,也能管理不同系统和架构的 Agent。代价是 Controller、插件、凭据、备份和升级都要自己维护。
| 方案 | 优势 | 主要代价 | 更适合 |
|---|---|---|---|
| Jenkins | 自托管自由度高,插件与 Agent 生态成熟 | 需要维护 Controller、插件和安全策略 | 多环境、内网构建、已有 Jenkins 经验 |
| GitHub Actions | 与 GitHub 集成直接,托管 Runner 开箱即用 | 深度依赖 GitHub,复杂自托管仍要运维 | GitHub 项目与标准流水线 |
| GitLab CI | 与 GitLab 仓库、权限和制品一体化 | 自建 GitLab 本身资源占用较高 | 已经使用 GitLab CE 的团队 |
如果代码都在 GitHub,流程也不复杂,GitHub Actions 自托管 Runner通常更省管理时间。需要自建代码平台、Issue、Registry 和 CI 一体化,可以看GitLab CE 与 Runner 部署教程。已经有多套内部系统、跨平台构建或大量 Jenkins Job 时,自建 Jenkins 更合适。
Jenkins 官方 Docker 安装文档给小团队的建议是 4GB 以上内存、50GB 以上磁盘。这只是 Controller 与轻量使用的起点,真正吃资源的是编译、测试、Docker 镜像和制品。
| 场景 | Controller 建议 | Agent 建议 |
|---|---|---|
| 学习和个人项目 | 2 vCPU、4GB 内存、50GB SSD | 可暂时同机,但用独立容器和用户 |
| 3–10 人小团队 | 2–4 vCPU、4–8GB 内存、80GB SSD | 单独 4 vCPU、8GB 内存起步 |
| Java/Android/大型前端构建 | Controller 保持稳定即可 | 8 vCPU、16GB 以上,按构建峰值扩容 |
| 多架构或高并发 | 独立 Controller | 多个带标签的临时 Agent |
如果 Controller 和 Agent 放在同一台 4GB VPS 上,编译时很容易把管理界面挤到 OOM。预算有限时至少设置容器内存限制,并把 Agent 执行器设为 1。更稳妥的方式是 Controller 使用一台小而稳定的 VPS,Agent 使用另一台可随时重建的机器。
磁盘也别只看 Jenkins 安装包。下面这些内容都会持续增长:
$JENKINS_HOME中的配置、插件和构建记录;- Agent 的 workspace、依赖缓存和临时文件;
- Docker 镜像、BuildKit 缓存和容器层;
- 归档制品、测试报告和控制台日志;
- 本地备份。
准备独立域名,例如 ci.example.com,把 A 记录指向 Controller VPS。没有正确配置 IPv6 时不要添加 AAAA 记录。
Controller 只需要:
| 端口 | 用途 | 建议 |
|---|---|---|
22/TCP | SSH 管理 | 限制管理 IP,使用密钥登录 |
80/TCP | Caddy 证书验证和跳转 | 对公网开放 |
443/TCP | Jenkins Web、Webhook、WebSocket Agent | 对需要的来源开放 |
8080/TCP | Jenkins 容器 HTTP | 只绑定 127.0.0.1 |
50000/TCP | 传统 Inbound Agent | 本文不用,不开放 |
GitHub.com 要向 Jenkins 投递 Webhook,443 必须能从公网访问。如果 Jenkins 只服务内网,可以不用 GitHub Webhook,改用受控隧道、GitHub App 或定时 SCM Poll;不要为了一个回调把整个管理后台无保护地暴露出去。
遇到“容器正常但域名打不开”,按端口、防火墙、安全组与监听地址排查指南从本机端口向外检查,不要一上来关闭 UFW。
下面以 Ubuntu 24.04 为例。Docker 建议按 Docker 官方仓库安装;已有 Docker 环境不要再运行来源不明的一键脚本。
sudo apt update
sudo apt full-upgrade -y
sudo apt install -y ca-certificates curl caddy openssl jq
docker --version
docker compose version
sudo systemctl enable --now caddy
生产环境还要处理日志轮转、资源限制、健康检查和回滚,可结合Docker Compose 生产配置清单检查。
sudo install -d -m 750 -o "$USER" -g "$USER" /opt/jenkins
cd /opt/jenkins
本文使用 Docker Named Volume 保存 $JENKINS_HOME。官方 Jenkins 镜像中的服务用户 UID 通常是 1000,Named Volume 能减少宿主机 Bind Mount 常见的权限问题。
创建 /opt/jenkins/compose.yaml:
services:
jenkins:
image: jenkins/jenkins:2.568.2-lts-jdk21
container_name: jenkins-controller
restart: unless-stopped
environment:
JAVA_OPTS: >-
-Djava.awt.headless=true
-Djenkins.install.runSetupWizard=true
JENKINS_OPTS: >-
--httpPort=8080
ports:
- "127.0.0.1:8080:8080"
volumes:
- jenkins_home:/var/jenkins_home
mem_limit: 3g
cpus: 2.0
healthcheck:
test: ["CMD-SHELL", "curl -fsS http://127.0.0.1:8080/login >/dev/null || exit 1"]
interval: 30s
timeout: 10s
retries: 10
start_period: 90s
logging:
driver: json-file
options:
max-size: "10m"
max-file: "5"
volumes:
jenkins_home:
name: jenkins_home
这里故意没有映射 50000,也没有把 /var/run/docker.sock 挂进 Controller。把 Docker Socket 交给 Controller,相当于让能控制构建的人获得宿主机高权限;后面会把构建放进独立 Agent。
mem_limit: 3g 适合 4GB 以上、只运行 Controller 的 VPS 起步。如果同机还有数据库或其他应用,应根据实际内存重新分配,不要照抄数字后让系统长期使用 Swap。
先检查最终配置:
cd /opt/jenkins
docker compose config --quiet
docker compose pull
docker compose up -d
docker compose ps
首次启动要解压 Jenkins、初始化目录并加载插件元数据,健康检查可能需要一两分钟。查看日志:
docker compose logs -f --tail=200 jenkins
出现 Jenkins 已完成启动的信息后按 Ctrl+C 退出日志。确认端口只绑定回环地址:
curl -fsSI http://127.0.0.1:8080/login
ss -lnt | grep ':8080'
预期监听地址是 127.0.0.1:8080,而不是 0.0.0.0:8080。
创建或编辑 /etc/caddy/Caddyfile:
ci.example.com {
encode zstd gzip
reverse_proxy 127.0.0.1:8080
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains"
X-Content-Type-Options "nosniff"
Referrer-Policy "strict-origin-when-cross-origin"
}
}
Caddy 默认会传递 Host,并设置 X-Forwarded-For、X-Forwarded-Proto 和 X-Forwarded-Host。这正是 Jenkins 生成正确 HTTPS 跳转与回调地址需要的信息。
检查并重载:
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy
sudo systemctl status caddy --no-pager
curl -fsSI https://ci.example.com/login
Jenkins 放在独立子域名根路径最省事。若一定要使用 https://example.com/jenkins/,Controller 与反向代理必须使用完全相同的 Context Path,并为 Jenkins 增加 --prefix=/jenkins。只在 Caddy 里改路径而不改 Jenkins,常见结果就是静态资源 404、跳转到错误 URL,或后台提示反向代理损坏。更多反代配置可参考Caddy 自动 HTTPS 教程。
首次访问 https://ci.example.com/ 会看到 Unlock Jenkins 页面。读取一次性密码:
cd /opt/jenkins
docker compose exec jenkins \
cat /var/jenkins_home/secrets/initialAdminPassword
粘贴密码后安装建议插件并创建新的个人管理员。不要长期使用共享 admin,也不要把初始密码保存到团队聊天里。
初始向导完成后,进入 Manage Jenkins → System,把 Jenkins URL 设置为:
https://ci.example.com/
这个地址会用于 Webhook、邮件和页面链接。它必须与用户实际访问的 HTTPS 地址一致,否则后台可能提示 It appears that your reverse proxy setup is broken。
进入:
Manage Jenkins → Nodes → Built-In Node → Configure
把 Number of executors 设为 0 并保存。此时没有 Agent 的任务会停在队列里,这是正常现象。先完成 Agent 配置,再开始正式构建。
进入 Manage Jenkins → Security:
- 保持认证开启,不允许匿名用户拥有管理或 Job 配置权限;
- 小团队也不要使用
Logged-in users can do anything作为长期授权策略; - 安装并配置 Matrix Authorization Strategy 时,从最小权限开始;
- 只有少数管理员拥有
Overall/Administer; - 限制
Credentials/Create和 Job 配置权限; - 保持 Agent → Controller Access Control,不要为了旧插件报错而关闭。
如果管理后台必须公开访问,可在 Caddy 前增加 VPN、身份代理或固定 IP 限制。Webhook 与管理界面共用域名时,限制规则要单独测试,避免把 /github-webhook/ 一起拦掉。
插件越多,依赖与安全更新越复杂。起步通常只需要:
- Pipeline;
- Git;
- GitHub 或 GitHub Branch Source;
- Credentials Binding;
- Matrix Authorization Strategy;
- 如果 Agent 中要使用 Docker Pipeline,再安装 Docker Pipeline。
安装后进入 Manage Jenkins → Plugins 检查更新与安全警告。不要为了复制一篇旧教程,安装多年没人维护的插件。
Agent 可以放在另一台 VPS,也可以暂时放在 Controller 同一台宿主机的独立容器中。单机方式不能提供高可用,但至少让构建进程无法直接读取 jenkins_home。
在 Jenkins 中进入:
Manage Jenkins → Nodes → New Node
创建 Permanent Agent,例如:
- Node name:
linux-agent-01 - Number of executors:
1 - Remote root directory:
/home/jenkins/agent - Labels:
linux docker-small - Usage:Only build jobs with label expressions matching this node
- Launch method:Launch agent by connecting it to the controller
- WebSocket:开启
保存后 Jenkins 会显示连接命令与 Agent Secret。Secret 相当于 Agent 登录凭据,不要写进 Git。
在 /opt/jenkins 创建 .agent.env:
cd /opt/jenkins
umask 077
cat > .agent.env <<'EOF'
JENKINS_URL=https://ci.example.com/
JENKINS_AGENT_NAME=linux-agent-01
JENKINS_AGENT_SECRET=把这里替换为节点页面显示的真实Secret
EOF
chmod 600 .agent.env
在 compose.yaml 的 services 下增加 Agent。这里使用官方 JDK 21 Inbound Agent 镜像:
agent:
image: jenkins/inbound-agent:jdk21
container_name: jenkins-agent-01
restart: unless-stopped
env_file:
- .agent.env
command:
- -url
- ${JENKINS_URL}
- -secret
- ${JENKINS_AGENT_SECRET}
- -name
- ${JENKINS_AGENT_NAME}
- -webSocket
- -workDir
- /home/jenkins/agent
volumes:
- jenkins_agent_work:/home/jenkins/agent
mem_limit: 3g
cpus: 2.0
logging:
driver: json-file
options:
max-size: "10m"
max-file: "5"
同时在文件底部 volumes 增加:
jenkins_agent_work:
name: jenkins_agent_work
这里有一个容易忽略的问题:Compose 默认只读取 .env 做 ${变量} 展开,env_file 只负责把变量放进容器。最简单的做法是把 command 改成固定的非敏感 URL 和节点名,并仅从 .env 展开 Secret;或者启动时显式指定:
cd /opt/jenkins
docker compose --env-file .agent.env config --quiet
docker compose --env-file .agent.env up -d agent
docker compose logs --tail=100 agent
在 Jenkins 节点页面确认状态为 Connected。WebSocket 连接通过 https://ci.example.com/,不需要 50000。
jenkins/inbound-agent:jdk21 主要提供 Agent 运行环境,并不会自动包含 Node.js、Maven、Go、Android SDK 或 Docker CLI。项目需要什么工具,就构建自己的 Agent 镜像并固定版本。例如:
FROM jenkins/inbound-agent:jdk21
USER root
RUN apt-get update \
&& apt-get install -y --no-install-recommends git curl make \
&& rm -rf /var/lib/apt/lists/*
USER jenkins
不要让 Agent 永久以 root 运行。每次修改镜像都应进入代码审查,并为基础镜像记录 tag 或 digest。
很多教程会把下面这一行加到 Agent:
- /var/run/docker.sock:/var/run/docker.sock
这样确实能立刻执行 docker build,但拥有 Docker Socket 的构建基本等同于拥有宿主机 root 权限。只要有人能修改 Jenkinsfile,就可能挂载宿主机根目录、读取其他容器密钥或控制 Controller。
更稳妥的选择包括:
- 使用独立、可随时重建的 Agent VPS,且不放其他业务;
- 使用短生命周期虚拟机或 Kubernetes Pod Agent;
- 使用受 TLS 保护的远程 BuildKit/Docker Builder;
- 对不可信 Pull Request 使用完全隔离且没有生产凭据的 Agent;
- 把生产部署与普通测试拆成不同 Agent、标签和凭据域。
如果只是个人项目并接受同机 Docker Socket 风险,也应明确这台 Agent 是一次性构建机,不运行数据库、密码管理器或其他关键服务。
Jenkins 凭据不应写在 Pipeline 脚本或 Job 参数中。进入:
Manage Jenkins → Credentials
按用途选择:
- 拉取私有仓库:Deploy Key 或最小权限 GitHub App;
- 调用 GitHub API:GitHub App 或最小权限 Fine-grained PAT;
- 部署服务器:独立 SSH Key;
- Registry:专用机器人账号或短期 Token。
凭据尽量放到具体 Folder 范围,不要全部做成 Global。Jenkins 官方建议从“默认没有权限”开始,只给确实需要的项目和人员开放。
Pipeline 通过 Credentials ID 引用秘密,不读取真实值。例如:
withCredentials([string(credentialsId: 'registry-token', variable: 'TOKEN')]) {
sh '''
set +x
./scripts/publish.sh "$TOKEN"
'''
}
Groovy 中使用三单引号,让 Shell 在运行时展开变量,减少秘密被 Groovy 插值后出现在进程参数或日志中的风险。即便 Jenkins 会尝试掩码,也不能把不可信 Pipeline 与生产凭据放在同一权限范围。
安装 GitHub 插件后,Webhook 常用地址是:
https://ci.example.com/github-webhook/
末尾斜杠不要漏。进入 GitHub 仓库:
Settings → Webhooks → Add webhook
填写:
- Payload URL:
https://ci.example.com/github-webhook/ - Content type:
application/json - Secret:生成一段独立随机值,并在 Jenkins 对应 GitHub Server/Webhook 配置中使用同一值
- Events:只选择任务需要的 Push、Pull request 等事件
- Active:开启
生成 Secret:
openssl rand -hex 32
不要复用管理员密码、GitHub PAT 或 Agent Secret。Webhook Secret 用来验证请求来自持有该秘密的一方,不应赋予 GitHub API 权限。
Job 中启用 GitHub hook trigger for GITScm polling;Multibranch Pipeline 则按 GitHub Branch Source 的配置让插件扫描分支和 Pull Request。
在 GitHub Webhook 页面点击 Recent Deliveries,查看:
- HTTP 状态码;
- Request Headers 中的事件类型与 Delivery ID;
- Response 内容;
- 是否触发了对应 Jenkins Job。
收到 HTTP 200 只代表端点接收请求,不代表 Pipeline 一定运行。还要检查仓库 URL、分支规则、Job Trigger 和 Jenkins 系统日志。
推荐把 Pipeline 写进仓库根目录的 Jenkinsfile,而不是只保存在 Jenkins 网页里。这样构建流程能跟随代码评审和版本历史。
下面是一个不会直接碰生产服务器的最小示例:
pipeline {
agent { label 'linux' }
options {
timestamps()
disableConcurrentBuilds()
buildDiscarder(logRotator(numToKeepStr: '30'))
timeout(time: 20, unit: 'MINUTES')
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Environment') {
steps {
sh '''
set -eu
uname -a
git --version
'''
}
}
stage('Test') {
steps {
sh '''
set -eu
./scripts/ci-test.sh
'''
}
}
stage('Package') {
steps {
sh '''
set -eu
./scripts/ci-package.sh
'''
}
}
}
post {
always {
junit allowEmptyResults: true, testResults: 'reports/**/*.xml'
archiveArtifacts allowEmptyArchive: true, artifacts: 'dist/**'
}
cleanup {
deleteDir()
}
}
}
把具体语言和框架命令放进仓库的 scripts/ci-test.sh 与 scripts/ci-package.sh,本地和 Jenkins 都能运行,排障比把几十行 Shell 塞进 Groovy 更容易。
创建 Pipeline 或 Multibranch Pipeline,选择 Pipeline script from SCM,填写仓库与 Jenkinsfile 路径。首次运行时确认任务显示在 linux-agent-01,而不是 Built-In Node。
测试和打包通过后,很多人会直接:
ssh root@production "cd /app && git pull && docker compose up -d"
这条命令简单,但把 root 私钥交给构建环境,回滚和审计也很弱。更好的最低标准是:
- 创建只负责部署的系统用户,不允许密码登录;
- SSH Key 只放在具体 Folder 的 Jenkins Credential;
- 限制部署用户能执行的命令和目录;
- 先构建带不可变版本号或 digest 的制品;
- 部署明确版本,而不是线上
git pull; - 健康检查失败时自动停止并回滚;
- 生产部署需要审批或只允许受保护分支触发。
不要让来自外部贡献者的 Pull Request 接触部署凭据。Pull Request 测试 Agent 与生产部署 Agent 应使用不同标签、网络和 Credential Scope。
Jenkins Job 可以通过 buildDiscarder 保留有限数量的构建记录,但 Docker 缓存和 Agent Workspace 仍会增长。
每周检查:
df -h
docker system df
docker volume ls
docker compose logs --tail=100 jenkins
不要在不确认用途时执行 docker system prune -a --volumes。它可能删除其他项目仍需要的镜像、缓存和未挂载 Volume。建议:
- 每个 Job 设置构建保留数量;
- 大制品上传到对象存储或 Artifact Repository;
- Agent 使用独立磁盘;
- 为 Docker BuildKit 设置可控的缓存清理策略;
- 对
$JENKINS_HOME与 Agent Workspace 分别告警; - 不把本机备份长期堆在同一块系统盘。
Jenkins 配置、插件、Job 与构建记录主要在 $JENKINS_HOME,本文对应 Named Volume jenkins_home。一致性要求高时,官方更推荐文件系统或存储快照。小团队也可以在维护窗口停止 Controller 后制作归档。
创建目录:
sudo install -d -m 700 -o "$USER" -g "$USER" /opt/jenkins/backups
sudo install -d -m 700 -o "$USER" -g "$USER" /opt/jenkins/controller-key
先进入 Quiet Down,等待当前构建结束:
Manage Jenkins → Prepare for Shutdown
停止 Controller,再归档 Volume:
cd /opt/jenkins
docker compose stop jenkins
docker run --rm \
-v jenkins_home:/data:ro \
-v /opt/jenkins/backups:/backup \
alpine:3.22 \
tar --exclude='./workspace' \
--exclude='./caches' \
--exclude='./secrets/master.key' \
-czf "/backup/jenkins-home-$(date +%F-%H%M%S).tar.gz" \
-C /data .
docker compose start jenkins
docker compose ps
Jenkins 官方特别提醒:master.key 不要和常规备份放在一起。拿到常规备份与 Controller Key 的人可以解密 Jenkins 保存的凭据。把 Key 单独复制到另一套受严格控制的加密存储:
docker run --rm \
-v jenkins_home:/data:ro \
-v /opt/jenkins/controller-key:/key \
alpine:3.22 \
sh -c 'umask 077; cp /data/secrets/master.key /key/master.key'
chmod 600 /opt/jenkins/controller-key/master.key
复制到离线密码库或独立加密存储后,不要把 Controller Key 与常规归档上传到同一 Bucket、同一凭据域。常规备份本身也包含敏感配置,必须加密。
除了 Volume,还应备份:
/opt/jenkins/compose.yaml;- Caddyfile;
- 自定义 Agent Dockerfile;
- 插件清单与 Jenkins 镜像版本;
.agent.env的加密副本或重新注册 Agent 的流程;- 恢复步骤和最近一次演练结果。
备份完成后同步到异机或对象存储。具体的 restic、rclone、保留策略和恢复演练可参考VPS 备份恢复演练指南。
不要等 Controller 磁盘坏了才第一次解压备份。至少每季度在隔离环境演练:
- 创建空 Volume;
- 解压
jenkins-home-*.tar.gz; - 从独立安全位置恢复
secrets/master.key; - 使用与备份一致的 Jenkins 镜像和插件版本启动;
- 登录并检查 Job、Folder、Credential 元数据和节点;
- 用测试仓库运行一条没有生产权限的 Pipeline;
- 验证 Webhook、Agent 和制品归档;
- 记录恢复时间目标与缺失文件。
恢复时不要顺手升级 Jenkins 和所有插件。灾难恢复的目标是先恢复原状态;版本升级应是另一项有备份、有测试的变更。
Jenkins LTS 每隔一段时间会更新基线和修复版本。升级前完成:
- 阅读 LTS Changelog、Upgrade Guide 和安全公告;
- 备份
$JENKINS_HOME与独立 Controller Key; - 导出已安装插件及版本;
- 在备份副本上测试 Controller 启动与关键 Pipeline;
- 检查 Java 版本和插件最低 Jenkins 版本;
- 保留旧镜像与完整回滚步骤。
列出插件:
cd /opt/jenkins
docker compose exec jenkins \
jenkins-plugin-cli --list
确认新版本后修改 compose.yaml 的明确 LTS tag,不要在生产配置里使用无法追踪的 latest。然后:
cd /opt/jenkins
docker compose pull jenkins
docker compose up -d jenkins
docker compose logs -f --tail=200 jenkins
完成登录、Agent 重连、Webhook 与测试 Pipeline 后再清理旧镜像。插件更新也要分批执行;一次更新几十个插件,失败后很难判断是哪项依赖造成。
cd /opt/jenkins
docker compose ps
docker compose logs --tail=200 jenkins
curl -fsSI http://127.0.0.1:8080/login
ss -lnt | grep ':8080'
本机 127.0.0.1:8080 都不通,先处理 Jenkins 容器、内存或 Volume 权限;本机正常再查 Caddy、DNS 和防火墙。
检查 Manage Jenkins → System → Jenkins URL 是否为外部 HTTPS 地址,并确认 Caddy 正常传递 Host 与 X-Forwarded-*。如果使用子路径,Controller --prefix 和 Caddy 路径必须一致。
依次检查:
- GitHub Recent Deliveries 的事件类型;
- Payload URL 是否以
/github-webhook/结尾; - Job 是否启用 GitHub Hook Trigger;
- Job 仓库 URL 是否与 Payload 仓库一致;
- Multibranch 的分支发现规则;
- GitHub 插件和系统日志;
- Queue 中是否因为没有匹配 Agent 而等待。
查看 Agent 日志:
cd /opt/jenkins
docker compose --env-file .agent.env logs --tail=200 agent
curl -fsSI https://ci.example.com/login
常见原因包括 URL 末尾路径不对、Agent Name 或 Secret 已更新、Caddy 未支持正常 WebSocket 连接、证书链不被 Agent 信任,以及系统时间错误。重新创建节点后旧 Secret 会失效,必须同步更新 .agent.env。
确认 Agent 已 Connected,标签与 Jenkinsfile 的 agent { label 'linux' } 一致,并且 Agent 执行器不是 0。Controller 的执行器保持 0 是正确配置,不要为了让队列消失就重新在 Controller 上跑构建。
Inbound Agent 镜像不等于完整构建环境。进入 Agent 检查:
docker compose --env-file .agent.env exec agent sh
java -version
git --version
缺少的工具应写进版本化的 Agent Dockerfile,重新构建镜像;不要每次进入运行中的容器手工安装,因为重建后改动会消失。
docker run --rm -v jenkins_home:/data alpine:3.22 \
sh -c 'ls -ldn /data; find /data -maxdepth 1 -printf "%u:%g %p\n" | head'
官方镜像通常以 UID 1000 运行。不要直接 chmod -R 777,这会让凭据和配置对其他进程可读。先找出是否由 root 运行的备份容器、错误 Bind Mount 或手工复制改变了所有者,再按准确 UID/GID 修复。
df -h
docker system df
docker run --rm -v jenkins_home:/data:ro alpine:3.22 \
du -h -d 2 /data | sort -h | tail -30
重点看 Job 构建记录、归档制品、Workspace、插件缓存和 Agent Docker 层。先确认保留需求再删除,不要把 jenkins_home Volume 当缓存清空。
- Jenkins 使用明确的 LTS 与 JDK 21 镜像 tag;
-
8080只绑定127.0.0.1; - 没有无用途地公开
50000; - Caddy HTTPS、Jenkins URL 与外部域名一致;
- 初始管理员密码已完成替换;
- 内置节点执行器为
0; - 构建只在带明确标签的 Agent 上运行;
- Controller 没有挂载 Docker Socket;
- Agent Secret 与
.agent.env权限受控; - 匿名用户和普通登录用户没有过高权限;
- 插件数量受到控制,安全警告已处理;
- GitHub Webhook 只订阅需要的事件;
- GitHub、Registry 和部署凭据使用最小权限;
- 不可信 Pull Request 无法访问生产凭据;
- Job、制品与 Docker 缓存有保留策略;
-
$JENKINS_HOME有加密异机备份; -
master.key与常规备份分开保存; - 恢复流程已在隔离环境跑通;
- 升级前会检查 LTS Upgrade Guide 与插件兼容性。
Jenkins 官方为小团队建议 4GB 以上内存和 50GB 以上磁盘。只让 Controller 负责编排时,2–4 vCPU、4GB 内存可以作为起点;编译、测试和 Docker 构建应在独立 Agent 上运行。若 Controller 与 Agent 同机,建议至少 8GB,并限制 Agent 并发。
不需要。Inbound Agent 可以使用 WebSocket 通过 Jenkins 现有 HTTPS 地址连接,此时不必启用额外 TCP Agent 端口。本文就是这种方式。只有明确使用传统 TCP Agent 协议时,才需要评估固定端口和防火墙规则。
技术上可以,但风险很高。能控制 Docker Socket 的进程通常可以获得宿主机 root 级能力,而 Controller 还保存配置与凭据。至少应把 Docker 构建放到独立 Agent;生产环境优先使用一次性 Agent、独立构建 VPS 或远程 Builder。
GitHub 插件的常用地址是 https://你的Jenkins域名/github-webhook/,注意末尾斜杠。GitHub 返回 200 后还要确认事件类型、仓库匹配、Job Trigger、分支规则和 Agent 队列,不能只看 HTTP 状态。
还不够。常规 $JENKINS_HOME 备份要加密并异机保存,secrets/master.key 应按官方建议单独存放在不同的安全域。还要保留 Compose、Caddyfile、自定义 Agent 镜像、插件版本和恢复步骤,并实际进行恢复演练。
代码都在 GitHub、流水线较标准时,GitHub Actions 通常最省维护。已经自建 GitLab 时优先使用 GitLab CI。需要连接内网、多系统 Agent、复杂插件或迁移已有 Job 时,Jenkins 的灵活性更有价值,但要接受插件、安全和备份的持续运维成本。
- Jenkins 下载与 LTS 版本
- Jenkins 官方 Docker 安装
- Jenkins Controller 隔离
- Jenkins Agent 使用指南
- Jenkins 网络端口与 WebSocket Agent
- Jenkins GitHub 插件与 Webhook
- Jenkins 凭据安全
- Jenkins 备份与恢复
- Jenkins 反向代理排障
Jenkins 真正的上线标准不是首页能打开,而是 Controller 不执行构建、Agent 权限可控、Webhook 能准确触发、凭据不会进入日志、备份可以恢复,升级失败也能回到原状态。先用一个没有生产权限的测试仓库跑通提交、测试、制品和失败通知,再接入正式部署,比一上来给 Jenkins root 密钥安全得多。
