记录时间:2026-10-26 环境:Debian 13 / Jenkins Inbound Agent / systemd / 1Panel + OpenResty / 私有 CA。
目标:在 Debian 13 构建节点上以 root 身份运行 Jenkins Inbound Agent,通过 HTTPS WebSocket 连接 Jenkins Controller,由 systemd 管理开机自启、断线重启和日志,并记录私有根证书、文件目录及反向代理的排错过程。
脱敏声明:文中 jenkins.example.com、192.168.50.10、192.168.50.20、Lab Root CA 均为示例值。Linux-Agent-D13 是演示节点名;实际使用时应替换为自己 Jenkins 上创建的节点名称。不会披露 Agent Secret 或内部 CA 真实私钥。
验证边界:这次现场已确认:原 CA 安装文件为 0 字节,修复后
curl不再出现原来的证书验证错误;后续出现的curl (23)是下载目录不存在,systemd 的200/CHDIR是工作目录不存在。对话尚未提供最终Connected日志,因此本文把 Agent 上线和 Pipeline 构建列为“最终需验收”,不冒充实测完成。
一、部署架构与端口
Debian 13 构建节点 Linux-Agent-D13
└── HTTPS / WSS :443(主动出站)
└── 1Panel / OpenResty 反向代理
192.168.50.10:443
└── Jenkins Controller
192.168.50.20:8080
私有 PKI(示例):
Lab Root CA → Lab Issuing CA → jenkins.example.com
| 服务 / 端口 | 网络方向 | 是否需要 |
|---|---|---|
| HTTPS / TCP 443 | Agent → HTTPS 反向代理 | 必须 |
| Jenkins 后端 TCP 8080 | 反向代理 → Controller | 仅内网代理侧需要 |
| 传统 TCP Inbound Agent 端口(常见 50000) | Agent → Controller | WebSocket 模式不需要 |
| SSH / TCP 22 | 运维终端 → Agent | 按需限制来源 |
WebSocket 的主要优势是不需要为 Inbound Agent 再开放单独的 TCP 端口;代理必须支持 HTTP/1.1 协议升级。不要仅凭浏览器能打开 Jenkins 首页就认定 WebSocket 已正常。
二、先在 Jenkins 后台创建节点
- 在 Manage Jenkins → Nodes 中新建节点,例如
Linux-Agent-D13,类型选择 Permanent Agent。 - Remote root directory 设为
/data/jenkins,标签可设为Linux-Agent-D13。 - Launch method 选择 Launch agent by connecting it to the controller(入站方式)。
- 在节点页面获取 Jenkins 生成的 Agent Secret。不要把它放进 Shell 历史、公开仓库或博客。
三、Debian 13 安装 Java 与 Agent JAR
1. 安装 Java 运行时
apt update
apt install -y openjdk-21-jre-headless \
curl ca-certificates ca-certificates-java
java -version
command -v java
示例使用 Java 21。Jenkins Controller 与 Remoting Agent 都受 Jenkins Java 支持策略约束,部署前应核对当前 Controller 版本的最低 Java 要求;构建项目所需 JDK 可以另行配置,不必与 Agent 进程使用的 Java 版本相同。
2. 确认数据盘并创建目录
# 如果 /data 应该是独立磁盘或 NFS,先确认挂载
findmnt /data || true
ls -ld /data 2>/dev/null || true
# 确认 /data 确实位于预期文件系统后创建
install -d -m 0755 /opt/jenkins-agent
install -d -m 0755 /data/jenkins
install -d -m 0700 /etc/jenkins-agent
注意:如果 /data 原本应挂载数据盘或 NFS,切勿在其未挂载的情况下直接开始构建,否则工作空间可能落到系统盘。
3. 从 Controller 下载匹配版本的 agent.jar
curl -fSL \
https://jenkins.example.com/jnlpJars/agent.jar \
-o /opt/jenkins-agent/agent.jar
test -s /opt/jenkins-agent/agent.jar
chmod 644 /opt/jenkins-agent/agent.jar
ls -lh /opt/jenkins-agent/agent.jar
curl -o 不会自动创建父目录;若 /opt/jenkins-agent 不存在,就会报 curl: (23) client returned ERROR on write。
四、私有根证书信任与 curl (60) 排查
本例 Jenkins 的 TLS 证书由私有 CA 颁发。Debian 必须信任正确的根证书,并由服务端正确提供叶证书及中间 CA 证书。遇到 curl: (60) SSL certificate problem: self-signed certificate in certificate chain,不要直接通过 -k 长期跳过校验。
1. 检查根 CA 文件是否真的有效
file /usr/local/share/ca-certificates/lab-root-ca.crt
ls -lh /usr/local/share/ca-certificates/lab-root-ca.crt
openssl x509 \
-in /usr/local/share/ca-certificates/lab-root-ca.crt \
-noout -subject -issuer -fingerprint -sha256 \
-ext basicConstraints
此次现场根证书文件虽然存在,系统也有对应的 /etc/ssl/certs/ 软链接,但 file 实际输出 empty、大小 0 字节,所以 OpenSSL 报 Could not find certificate。文件名和软链接存在不代表证书内容有效。
2. 用可信来源的正确 PEM 根证书替换空文件
先从可信任的 CA 发布渠道取得并核对根证书指纹,再安装。以下示例假设已经在 /root/lab-root-ca.pem 保存并验证了 PEM 编码根证书;不要复制未经验证的证书到系统信任库。
openssl x509 -in /root/lab-root-ca.pem \
-noout -subject -issuer -fingerprint -sha256
# 如果原目标文件确认为 0 字节,先备份
test ! -e /usr/local/share/ca-certificates/lab-root-ca.crt \
|| cp -a /usr/local/share/ca-certificates/lab-root-ca.crt \
/root/lab-root-ca.crt.backup
install -m 0644 /root/lab-root-ca.pem \
/usr/local/share/ca-certificates/lab-root-ca.crt
update-ca-certificates --fresh
如果源证书是 DER 编码,需要先使用 openssl x509 -inform DER -in source.cer -out /root/lab-root-ca.pem 转换为 PEM。重新安装后再执行 curl -vI https://jenkins.example.com/。此次问题是空文件,不是已确认的 DER 格式问题。
3. 验证服务器证书链
openssl s_client \
-connect jenkins.example.com:443 \
-servername jenkins.example.com \
-verify_hostname jenkins.example.com \
-CAfile /etc/ssl/certs/ca-certificates.crt \
-showcerts </dev/null 2>&1 | \
grep -E 's:|i:|verify error:|Verify return code:'
curl -vI https://jenkins.example.com/
预期看到 Verify return code: 0 (ok) 且域名匹配。如果缺失中间 CA,应修复 Jenkins HTTPS 前端提供的 fullchain,不应把私钥或不必要的 CA 凭据分发到 Agent。若 curl 已通过而 Java 仍报 PKIX path building failed,再核对 ca-certificates-java 和实际 JVM 的 truststore。
五、保存 Secret 并用 root 运行 systemd
1. 将 Secret 保存到 root 专用文件
nano /etc/jenkins-agent/secret
chown root:root /etc/jenkins-agent/secret
chmod 600 /etc/jenkins-agent/secret
文件内容只保留 Agent Secret 本身,避免将 Secret 拼接进 ExecStart=。Jenkins Remoting 支持 -secret @/path/to/file 从文件读取。
2. 创建 systemd Unit
写入 /etc/systemd/system/jenkins-agent.service,以下是本例选择 root 身份运行的完整配置:
[Unit]
Description=Jenkins Inbound Agent - Linux-Agent-D13
Wants=network-online.target
After=network-online.target
RequiresMountsFor=/data/jenkins
[Service]
Type=simple
User=root
Group=root
WorkingDirectory=/data/jenkins
ExecStart=/usr/bin/java \
-jar /opt/jenkins-agent/agent.jar \
-url https://jenkins.example.com/ \
-secret @/etc/jenkins-agent/secret \
-name Linux-Agent-D13 \
-webSocket \
-workDir /data/jenkins
Restart=always
RestartSec=10
TimeoutStopSec=30
SuccessExitStatus=143
UMask=0077
StandardOutput=journal
StandardError=journal
SyslogIdentifier=jenkins-agent
[Install]
WantedBy=multi-user.target
RequiresMountsFor=/data/jenkins 用于声明挂载依赖,不会自动创建目录。启动之前仍必须确保路径已经存在,并且数据盘已按预期挂载。
3. 启动与查看日志
systemd-analyze verify /etc/systemd/system/jenkins-agent.service
systemctl daemon-reload
systemctl enable --now jenkins-agent
systemctl status jenkins-agent --no-pager -l
journalctl -u jenkins-agent -n 100 --no-pager
journalctl -u jenkins-agent -f
确认 Agent 真正连接成功,应以日志中的连接状态和 Jenkins Nodes 页面显示 Online 为准,Active: active (running) 只能证明进程已启动,不能代替服务端连接验收。
六、1Panel / OpenResty WebSocket 反向代理
现场原配置使用 proxy_set_header Connection $http_connection;,它只是透传客户端的 Connection 请求头,不是最稳妥的 WebSocket 处理方式。推荐在 Nginx http 作用域使用 map:
map $http_upgrade $jenkins_connection_upgrade {
default upgrade;
'' '';
}
然后在 Jenkins 对应的 server 内使用下面的 location(注意上面 map 不能写在 location 里面):
location ^~ / {
proxy_pass http://192.168.50.20:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $jenkins_connection_upgrade;
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-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
proxy_connect_timeout 30s;
proxy_send_timeout 3600s;
proxy_read_timeout 3600s;
proxy_request_buffering off;
}
如果 1Panel 只允许编辑局部 location 而无法配置 http 作用域,可临时使用 proxy_set_header Connection "upgrade"; 替代变量方案;这会对普通 HTTP 请求也设置 upgrade,不如 map 精细。应用后应做 Nginx 语法检查并重新加载配置。不要在 proxy_pass 的上游 URL 末尾随意追加斜杠。
Jenkins 本身的 Jenkins URL 也应与浏览器的 HTTPS 域名一致。若全部 Inbound Agent 均使用 WebSocket,可以在 Jenkins 安全设置中禁用独立 TCP Agent 监听器;如果旧节点仍依赖传统 TCP,则不能直接关闭。
七、故障速查:本次实际遇到的三个错误
| 现象 | 定位结论 | 处理 |
|---|---|---|
curl (60) + self-signed certificate in certificate chain |
本次发现已安装 CA 文件是 0 字节,系统没有有效的可信根证书 | 检查 file、openssl x509,从可信来源重新安装 PEM 并刷新 CA |
curl (23) + Failed to open the file ... No such file or directory |
Agent JAR 的目标父目录不存在 | 先创建 /opt/jenkins-agent,再重新下载 |
status=200/CHDIR |
systemd 在启动 Java 前无法进入 WorkingDirectory=/data/jenkins |
确认 /data 挂载并创建目录,重启服务 |
额外排错方向
WebSocket 400/403:核查节点名称、Secret、Jenkins 认证要求,以及代理是否正确传递Upgrade与Connection。PKIX path building failed:通常是 Java 信任链问题;需检查使用的 JDK/JRE 和 CA truststore。200/CHDIR且已写了ExecStartPre=mkdir:仍可能失败,因为进入工作目录发生在执行服务命令之前;应在服务启动前创建目录。- Agent 反复退出:用
journalctl -u jenkins-agent检查实际错误;不要仅依赖自动重启掩盖问题。
八、连接验收、构建权限与后续维护
1. 验收清单
curl -vI的 TLS 校验通过,没有使用-k。test -s /opt/jenkins-agent/agent.jar成功,Java 可正常执行。findmnt /data与实际磁盘规划一致,/data/jenkins存在。systemctl status jenkins-agent显示进程运行,并且日志出现成功连接信息。- Jenkins Nodes 页面中节点为 Online,实际运行一次构建。
2. 验证构建用户
pipeline {
agent { label 'Linux-Agent-D13' }
stages {
stage('Verify Agent') {
steps {
sh '''
whoami
id
java -version
pwd
'''
}
}
}
}
安全提醒:本示例按照现场需求使用 root 身份运行,意味着该节点执行的 Pipeline Shell 命令具有宿主机 root 权限。只应将它分配给经过审核的可信任务,严格控制 Jenkins 用户的配置、脚本修改权限及外部 PR 的执行范围。更普遍的生产建议是使用最小权限专用用户。
3. Jenkins 升级后更新 agent.jar
curl -fSL \
https://jenkins.example.com/jnlpJars/agent.jar \
-o /opt/jenkins-agent/agent.jar.new
test -s /opt/jenkins-agent/agent.jar.new
systemctl stop jenkins-agent
mv /opt/jenkins-agent/agent.jar.new \
/opt/jenkins-agent/agent.jar
chmod 644 /opt/jenkins-agent/agent.jar
systemctl start jenkins-agent
journalctl -u jenkins-agent -n 50 --no-pager
此示例不在每次 systemd 启动时实时下载 JAR,以免 Controller 短暂不可用时阻碍本地服务启动。
参考资料
- Jenkins 官方:Java 支持策略
- Jenkins Remoting:Inbound Agent、WebSocket 与 @secret-file
- Jenkins 官方:Nginx 反向代理
- 妖の森旧文:记一次 Jenkins 升级后的 Agent 修复
本文将已确认的本机错误与可复用配置分开记录。示例命令需要按照自己的 Jenkins URL、节点名称、CA 指纹和磁盘挂载方式调整,未经验证的步骤不应视为已经在现场执行成功。

Comments NOTHING