Jenkins Agent 以 systemd 方式启动时 conda 找不到命令的排查与解决

背景

在将 Jenkins Agent 从手动命令行启动迁移到 systemd 服务管理后,执行测试任务时出现了如下报错:

1
2
3
4
5
6
16:21:40  + echo '=== Conda Environment Setup ==='
16:21:40 === Conda Environment Setup ===
16:21:40 ++ conda shell.bash hook
16:21:40 + eval ''
16:21:40 + conda activate vltenv
16:21:40 /home/autotest/vlt/workspace/vlt_python_aws_linux@tmp/durable-2cb89764/script.sh.copy:行4: conda: 未找到命令

奇怪的是,在节点机器上,不管是 root 用户还是 autotest 用户,conda 都是存在的。使用命令行直接启动 agent 时任务执行完全正常,一旦改用 systemctl start jenkins-agent.service 启动,就必然报错。


根本原因

这是一个经典的 systemd 服务环境变量缺失问题。

通过命令行手动执行 java -jar agent.jar ... 时,shell 会加载用户的 .bash_profile.bashrc,其中包含 conda 的初始化脚本(即 conda init 写入的那段代码),PATH 和各类 CONDA_* 环境变量都会被正确设置。

但 systemd 启动服务时,不会加载任何用户 shell 的初始化文件,进程的环境变量只有系统默认的最小集合。conda 的路径不在 PATH 中,CONDA_EXECONDA_PYTHON_EXE 等变量也全部缺失,导致 conda shell.bash hook 虽然能被找到并执行,但返回了空字符串,eval '' 自然什么都没初始化,后续的 conda activate 也就彻底失败了。


顺带出现的另一个问题

任务失败后,Jenkins 并没有直接将构建标记为失败,而是一直打印:

1
2
17:18:13  Still waiting to schedule task
17:18:13 Waiting for next available executor on 'linux_label'

这是因为 agent 因环境异常导致进程崩溃,Jenkins Master 收到的信号是”节点离线”而非”任务正常失败”,因此触发了重新调度逻辑,去寻找同一 label 下其他可用的节点。其他节点已经下线,就会无限等待。

解决这个问题最直接的方式就是修复 conda 环境,让 agent 不再异常退出,任务就会正常返回失败或成功状态,不会再触发重调度。此外,建议在 Pipeline 中加入 timeout 作为兜底:

1
2
3
4
5
6
pipeline {
options {
timeout(time: 30, unit: 'MINUTES')
}
...
}

两种可行的解决方案

方案一:使用 EnvironmentFile 注入完整环境变量

先以 autotest 用户身份导出 conda 相关的环境变量到一个文件:

1
su - autotest -c "env | grep -E '^(CONDA|PATH|_CE|JAVA)'" > /home/autotest/vlt/jenkins_home/agent.env

确认文件内容包含类似如下的条目:

1
2
3
4
PATH=/home/autotest/tools/miniforge3/bin:/home/autotest/tools/miniforge3/condabin:...
CONDA_EXE=/home/autotest/tools/miniforge3/bin/conda
CONDA_PYTHON_EXE=/home/autotest/tools/miniforge3/bin/python
CONDA_ROOT=/home/autotest/tools/miniforge3

然后修改 service 文件,用 EnvironmentFile 替换原来手动写的 Environment= 行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[Unit]
Description=Jenkins Agent Service
After=network.target

[Service]
Type=simple
User=autotest
WorkingDirectory=/home/autotest/vlt/jenkins_home

EnvironmentFile=/home/autotest/vlt/jenkins_home/agent.env

ExecStart=/usr/java/jdk-17.0.5/bin/java -jar /home/autotest/vlt/jenkins_home/agent.jar \
-url http://172.16.xxx.xxx:8081/ \
-secret @/home/autotest/vlt/jenkins_home/secret-file \
-name Linux212.128 \
-webSocket \
-workDir /home/autotest/vlt
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

这种方式适合需要精确控制环境变量、对生产环境有规范管理要求的场景。

方案二:通过 bash login shell 启动(改动最小)

修改 ExecStart,让服务通过 bash 登录模式启动,从而自动加载 .bash_profile 中的完整环境:

1
2
3
4
5
6
7
ExecStart=/bin/bash -l -c 'exec /usr/java/jdk-17.0.5/bin/java \
-jar /home/autotest/vlt/jenkins_home/agent.jar \
-url http://172.16.xxx.xxx:8081/ \
-secret @/home/autotest/vlt/jenkins_home/secret-file \
-name Linux212.128 \
-webSocket \
-workDir /home/autotest/vlt'

-l 参数让 bash 以 login shell 模式运行,会自动 source 用户的 .bash_profile,conda init 自然随之生效。这是改动最小、最直接的方式。


操作验证

两种方案修改完成后,执行以下命令使配置生效:

1
2
systemctl daemon-reload
systemctl restart jenkins-agent.service

通过日志确认服务状态正常:

1
journalctl -u jenkins-agent.service -f

小结

systemd 管理的服务不继承用户 shell 环境,是这类问题最常见的根源。遇到”命令行执行正常,systemd 服务执行报错”的情况,第一反应应该是检查环境变量是否完整,尤其是 PATH 和工具链自身依赖的初始化变量。

对于 conda 这类依赖 shell hook 初始化的工具,仅补充 PATH 往往不够,还需要补全 CONDA_EXECONDA_PYTHON_EXE 等变量,或者直接使用 login shell 方式启动,让工具链自己完成初始化。