29 KiB
all.sh 使用与实现说明
本文件记录 all.sh 的功能、使用方法和内部实现细节,便于后续维护与扩展。
1. 概述
all.sh 是 XCE 项目的统一管理脚本,提供两个能力:
- 构建:以
nix develop+meson构建开发版 / 生产版 / 清理构建产物,并输出编译耗时统计。 - 服务管理:启动 / 停止 / 重启 / 查看状态四个核心程序:
xce_controller(controller,控制器,默认端口 9990)xce_scheduler(scheduler,调度器,默认连接 localhost:9990)xcecd(worker,工作节点守护进程,需要 root)xce_apiserver(apiserver,REST API,默认 HTTP 端口 8080,连接 controller)
所有日志统一输出到 logs/,PID 统一记录在 run/。
2. 快速使用
# ---------- 构建 ----------
./all.sh build -c # 彻底清理构建产物(删除 build/ 与 build-prod/)
./all.sh build -d # 开发构建(debug + ASan,带编译耗时统计)
./all.sh build -p # 生产构建(release,无 sanitizer,带编译耗时统计)
# ---------- 集群配置 ----------
./all.sh config deploy # 构建+部署 cluster.bin(从 src/configs)并重启 controller
./all.sh config backup # 备份所有服务配置到 ~/all_configs
# ---------- 服务管理 ----------
./all.sh start # 启动全部服务(controller scheduler worker apiserver)
./all.sh start controller scheduler # 只启动指定服务
./all.sh stop [svc...] # 停止指定服务,缺省为全部
./all.sh restart [svc...] # 重启指定服务,缺省为全部
./all.sh status [svc...] # 查看状态(名称 / 状态 / PID / 二进制 / 日志),缺省为全部
服务清单(svc 可取值):controller / scheduler / worker / apiserver。
3. 配置区(customize 区)
脚本顶部 # customize begin ... # customize end 是可定制区域:
| 变量 | 说明 |
|---|---|
g_code_base |
项目根目录,默认 /home/jsxing/allwork/git/myxce |
g_services |
服务注册表:`名称 |
g_deploy_dir |
统一部署根目录,默认 ${g_code_base}/deploy(所有配置/logs/run 都收在这里) |
g_conf_dir |
生成配置目录,默认 ${g_deploy_dir}/conf(worker.yaml 等) |
g_run_dir |
PID/证书/数据目录,默认 ${g_deploy_dir}/run |
g_log_dir |
日志目录,默认 ${g_deploy_dir}/logs |
g_loglevel |
全局日志级别:info / debug / debug2 / debug3,默认 info |
g_worker_bin_dir |
作业 worker 二进制(xcecd_worker)安装目录,默认 /opt/xce/bin;必须选一个所有作业执行用户都能到达的位置(仓库路径在 /home/<user> 下通常 750,其他用户无法进入,作业会以 exit 126 立即退出,见 9.4)。由 svc_prepare_worker 经 sudo 安装、svc_env_worker 以 XCE_SERVER_DIR 注入 |
3.1 日志级别 g_loglevel
一个开关控制所有组件的日志详细度。修改后 restart 对应服务 即生效(配置会在启动前自动同步):
g_loglevel |
controller | scheduler | worker (xcecd) | apiserver |
|---|---|---|---|---|
info(默认) |
无 flag | 无 flag | worker.yaml log_level: "info" |
logger.yaml level: "info" |
debug |
-v |
-v |
log_level: "debug" |
level: "debug" |
debug2 |
-vv |
-vv |
log_level: "debug2" |
level: "debug"(zap 无 debug2) |
debug3 |
-vvv |
-vvv |
log_level: "debug3" |
level: "debug" |
实现要点:
- controller / scheduler:
svc_args_*通过$(_verbose_flags)注入-v/-vv/-vvv(argp--verbose,verbose_to_loglevel()映射到 debug/debug2/debug3)。 - worker:
svc_prepare_worker把$(_worker_loglevel)写进${g_conf_dir}/worker.yaml的log_level字段。注意 xcecd 传了-c后会忽略-l/-p/-m,日志级别/目录完全由 worker.yaml 决定;文件已存在时也会sed同步级别。 - apiserver:
svc_prepare_apiserver把$(_apiserver_loglevel)写进<topdir>/config/logger.yaml的level(zap 只支持debug/info/warn/error/fatal,故 debug2/3 映射为debug);文件已存在时同样同步。
服务注册表
g_services=(
"controller|${g_code_base}/src/build/controller/xce_controller"
"scheduler|${g_code_base}/src/build/scheduler/xce_scheduler"
"worker|${g_code_base}/src/build/worker/xcecd"
"apiserver|${g_code_base}/src/build/frontend/apiserver/xce_apiserver"
)
新增服务只需在此追加一行 "名字|二进制路径",并补上对应的 svc_args_<名字> 函数(见下)。
4. 构建实现
构建统一使用:
nix develop --extra-experimental-features "nix-command flakes" \
--command bash -c "meson setup <build目录> <参数> && meson compile -C <build目录>"
注意:
meson setup和meson compile必须都跑在nix develop环境内,因此被放进同一个--command bash -c。
4.1 build -d(开发版)
- 构建目录:
src/build/ - 参数:
--buildtype=debug -Db_sanitize=address(调试符号 + AddressSanitizer) - 输出:
output is in ${src_dir}/build
4.2 build -p(生产版)
- 构建目录:
src/build-prod/ - 参数:
--buildtype=release(开启优化,无 sanitizer) - 输出:
output is in ${src_dir}/build-prod
4.3 build -c(清理)
- 循环删除
src/build/与src/build-prod/两个目录,彻底清空 meson 产物。
4.4 编译耗时统计
用 date +%s 在 meson compile 前后各取一次时间戳,结束时打印:
Build finished in 0m 56s (56s total)
4.5 构建耗时经验值
-d/-p:增量编译,只有 xceadmin 每次固定重跑(约 11s)+ 首次/源码变更时全量。初次全量约 60s。-c:彻底清理后需要完整重建。
4.6 config deploy(部署集群配置)
./all.sh config deploy
一条命令完成:
- 定位
xceadmin(依次查找src/build、src/build-prod的 venv、PATH)。 - 在
nix develop环境中用xceadmin config build src/configs -o ...构建cluster.bin(schema 校验会输出若干 "field ... ignored" 警告,属正常)。 - 按 sha256 前 12 位生成 tag,部署到
run/controller/ctrl-1/conf/cluster.bin.<tag>,写入conf/current。 - 重启 controller 加载配置(日志见
Primary config loaded: tag=...,ctrlstatus显示Cluster: <name>)。
为什么要部署:无 cluster.bin 时 controller 处于冷启动无配置状态,worker join 会被拒绝并返回 EINVAL(
find_matched_hostconfig因cluster_config == NULL返回 NULL,见 8 节)。清空重建(build -c)或删掉run/后必须重新config deploy。
4.7 config backup(备份所有服务配置)
./all.sh config backup
把全部服务的运行时配置备份到 ~/all_configs/(保留目录结构,覆盖式备份):
~/all_configs/
├── controller/certs/ # TLS 证书 xce.crt / xce.key
├── controller/conf/ # cluster.bin.* + current
├── worker/worker.yaml # /opt/xce/conf/worker.yaml(sudo 读取)
├── apiserver/ # app.yaml / auth.yaml / logger.yaml / rbac.yaml
└── scheduler/README.txt # 说明:scheduler 无独立配置文件
缺失的文件会被跳过并提示(例如未启动过 apiserver 时无其配置目录)。
5. 服务管理实现
服务管理采用「注册表 + 每服务钩子函数」的可扩展设计。
5.1 每服务启动参数函数 svc_args_<name>
svc_start 通过 svc_args_${name} 动态调用,返回该服务的启动参数。
| 服务 | 参数 |
|---|---|
svc_args_controller |
--myname ctrl-1 --peers ctrl-1=localhost:9990 --workdir <deploy>/run/controller --certdir <deploy>/run/certs --logfile <deploy>/logs/controller.log --vcpus 4 [<g_loglevel>] |
svc_args_scheduler |
-l <deploy>/logs/scheduler.log [<g_loglevel>] |
svc_args_worker |
-c <deploy>/conf/worker.yaml -n localhost01(xcecd 传 -c 后忽略 -l/-p/-m,日志位置/级别由 worker.yaml 决定) |
svc_args_apiserver |
-d <deploy>/run/apiserver |
表中
[<g_loglevel>]表示按g_loglevel注入的-v/-vv/-vvv(见 3.1 节)。
另外还有每服务环境变量钩子 svc_env_<name>(可选):svc_start 在 sudo 启动分支里用 sudo -n env <值> bash -c ... 注入到进程环境。
| 钩子 | 注入的环境变量 | 说明 |
|---|---|---|
svc_env_worker |
XCE_SERVER_DIR=${g_worker_bin_dir} |
让 xcecd 用安装在 ${g_worker_bin_dir}(默认 /opt/xce/bin)的 xcecd_worker 启动作业 worker,而不是仓库路径;否则作业会以 exit 126 立即退出(见 9.4) |
5.2 启动前准备钩子 svc_prepare_<name>
svc_start 在启动前检查 svc_prepare_${name} 是否存在,存在则先执行(如失败则中止启动)。
-
svc_prepare_controller:确保自签名 TLS 证书存在。若<deploy>/run/certs/xce.crt或xce.key缺失,用openssl生成(RSA 2048、10 年有效期)。controller 缺少证书会启动失败(TLS certificate not found)。 -
svc_prepare_worker:生成${g_conf_dir}/worker.yaml(management_list/controller_port/log_level/log_dir),其中log_level跟随g_loglevel、log_dir指向${g_log_dir}。文件已存在时也会sed同步log_level,因此修改g_loglevel后重启 worker 即生效。- 另外会把
src/build/worker/xcecd_worker用sudo install -m 0755安装到${g_worker_bin_dir}(默认/opt/xce/bin),源更新才重装(幂等)。作业 worker 由 systemd 以作业执行用户身份运行,必须放在所有人可访问的目录(仓库路径在/home/<user>下通常 750,其他用户无法进入 → exit 126,见 9.4)。
- 另外会把
-
svc_prepare_apiserver:确保<deploy>/run/apiserver/config/下的四个配置文件齐全(缺失才生成,不会覆盖已有配置):logger.yaml:日志配置(相对outputPath会落在<topdir>/logs/),level跟随g_loglevel(zap 只支持 debug/info/warn/error,debug2/3 映射为 debug);已存在时也会同步level。rbac.yaml:权限配置;优先复制仓库样例src/frontend/apiserver/config/rbac.yaml,否则生成最小空配置。auth.yaml:认证配置;优先复制仓库样例src/frontend/apiserver/config/auth.yaml(config_file提供者,默认 admin/admin),否则生成最小单用户配置。app.yaml:主配置(app/security/authentication/rate_limit/whitelist/file_upload/cache为 schema 必填),生成默认值并将backend.address/port指向 controller(localhost:9990)。
这四份文件缺一不可,Go 二进制启动时会按顺序校验(缺失即报错退出)。
5.3 sudo 支持 svc_needs_sudo
function svc_needs_sudo() {
case "$1" in
worker) return 0 ;; # 需要 root
*) return 1 ;;
esac
}
xcecd 校验 geteuid() != 0 直接退出(FATAL: xcecd should be run as root),因此 worker 必须 root 运行。
启动(sudo 分支)
sudo -n env ${envp} bash -c "echo \$\$ > '${pidfile}'; exec nohup '${bin}' ${args}" \
>>"${log}" 2>&1 < /dev/null &
envp 来自可选的 svc_env_<name> 钩子(如 worker 注入 XCE_SERVER_DIR=${g_worker_bin_dir},见 5.1);用 env 显式设置,避免受 sudo env_reset 影响。
设计要点:
- 内层 bash 先把自己的 PID 写入 pid 文件,再
exec进二进制。exec不改变 PID,因此 pid 文件记录的是真实的 root 进程 PID(而不是 sudo 的 PID)。 nohup防护 SIGHUP,避免终端关闭时被杀。sudo -n非交互式:要求 sudo 免密(NOPASSWD)或已有缓存凭证(默认缓存 15 分钟),避免后台进程挂起等密码。
状态检测与停止(root 进程)
普通用户对 root 进程执行 kill/kill -0 会返回 EPERM(失败),因此对 root 服务统一走 sudo:
_svc_alive:sudo -n kill -0 <pid>检测存活。_svc_kill:sudo -n kill [-9] <pid>发信号。
5.4 启动流程 svc_start
- 解析二进制路径,检查二进制存在(
-x)。 - 防重复启动:
_svc_pid已检测到存活 PID 则直接返回。 - 创建
run/、logs/目录。 - 执行
svc_prepare_<name>(若存在)。 - 执行
svc_check_<name>预检(若存在,如 controller 需要已部署 cluster.bin)。 - 按是否 sudo 分支后台启动,写 pid 文件,
disown:- sudo 分支先取
svc_env_<name>注入的环境变量(若存在),再sudo -n env ... bash -c ...。 - 非 sudo 分支
setsid nohup <bin> <args>后台启动。
- sudo 分支先取
sleep 1后通过_svc_pid校验:存活则报started (pid ...);否则报FAILED to start并清理 pid 文件。
5.5 停止流程 svc_stop
- 从 pid 文件读 PID;若无,用
pgrep -f <二进制名>兜底。 - 发送 SIGTERM(root 服务走 sudo)。
- 最多等待 3 秒(10 次 × 0.3s),仍存活则 SIGKILL。
- 清理 pid 文件,报
stopped。
实测:worker 在 SIGTERM 后 3 秒内不退出,会走 SIGKILL 兜底,属预期行为。
5.6 状态显示 svc_status
SERVICE STATE PID
controller running 200831
binary: /home/jsxing/allwork/git/xce/src/build/controller/xce_controller
log: /home/jsxing/allwork/git/xce/logs/controller.log
last: <日志最后一行>
STATE由_svc_pid(pid 文件 + 存活校验)决定,stale pid 文件会被自动清理。- 显示日志文件路径及其最后一行,便于快速排查。
5.7 参数解析与分发
入口处的 case 把子命令分发给各 cmd_* 函数:
case "${1:-}" in
build) shift; cmd_build "$@" ;;
config) shift; cmd_config "$@" ;;
start) shift; cmd_start "$@" ;;
stop) shift; cmd_stop "$@" ;;
restart) shift; cmd_restart "$@" ;;
status) shift; cmd_status "$@" ;;
*) echo "Usage: $0 {build|config|start|stop|restart|status} [args]" && exit 1 ;;
esac
服务列表解析 _svc_resolve
把命令行参数解析成服务名列表:缺省返回全部注册服务(_svc_names);未知服务名会报错并提示合法值(错误写到 stderr,避免被命令替换吞掉返回值)。
批量分发 _cmd_svc_foreach
start/stop/restart 三个子命令都经由它按服务逐个执行;status 不走这里(见下)。
function _cmd_svc_foreach() {
local action="$1"; shift # start | stop | restart
local targets rc=0
targets="$(_svc_resolve "$@")" || exit 1
local name
for name in $targets; do
if ! "svc_${action}" "$name"; then
rc=1
# fail-fast for start/restart: one bad service (e.g. controller
# without cluster.bin) should stop the whole launch so the user
# sees the precheck hint (./all.sh config deploy) immediately.
case "$action" in
start|restart) break ;;
esac
fi
done
return "$rc"
}
function cmd_start() { _cmd_svc_foreach start "$@"; }
function cmd_stop() { _cmd_svc_foreach stop "$@"; }
function cmd_restart() { _cmd_svc_foreach restart "$@"; }
要点:
- 动态调用:
"svc_${action}" "$name"即svc_start/svc_stop/svc_restart,天然支持一次操作多个服务(如./all.sh start controller scheduler)。 - fail-fast:
start/restart遇到某个服务失败立即break(如 controller 未部署 cluster.bin 时svc_check_controller失败),让用户马上看到./all.sh config deploy的提示;stop不打断,尽力停止其余服务。 - 退出码:任何服务失败都累计
rc=1,脚本整体返回非 0。 status不走 foreach:cmd_status自行循环调用svc_status,先打印SERVICE/STATE/PID表头。config deploy不走 foreach:config_deploy内部直接svc_restart controller。
6. 配置文件与运行时目录
所有配置、日志、运行时状态统一在
${g_deploy_dir}(deploy/)下,不再引用系统目录或旧仓库路径。
6.1 controller
- 数据目录:
<deploy>/run/controller/ctrl-1/(data/WAL、conf/配置),由 controller 自动创建。 - TLS 证书:
<deploy>/run/certs/,由svc_prepare_controller自动生成。 - 日志:
<deploy>/logs/controller.log。
6.2 worker 配置 ${g_conf_dir}/worker.yaml
xcecd 的默认配置路径是 DEFAULT_CONFIG_PATH(/opt/xce/conf/worker.yaml),但 all.sh 始终用 -c ${g_conf_dir}/worker.yaml 显式指定,由 svc_prepare_worker 自动生成:
---
workerconf:
management_list: ["localhost"]
controller_port: 9990
log_level: "info"
log_dir: "/home/jsxing/allwork/git/myxce/deploy/logs"
- 样例参考:
src/worker/conf/worker.yaml。 - 必填字段:
controller_port(>0)、log_dir(非空)、management_list。 - 注意:传了
-c后,-l/-p/-m参数会被忽略(main.c行为),因此log_dir/log_level必须写在配置里;log_level跟随g_loglevel(见 3.1)。
6.3 apiserver
- top 目录:
<deploy>/run/apiserver/,启动参数-d <topdir>(必填)。 - 配置文件:
<deploy>/run/apiserver/config/下需有app.yaml、logger.yaml、rbac.yaml、auth.yaml(由svc_prepare_apiserver自动生成,见 5.2)。 - 后端连接:
app.yaml的backend段配置 controller 地址/端口(localhost:9990);若后端未配置则运行在 stub 模式。 - HTTP 端口:
app.yaml的app.http_port(默认 8080)。 - 日志:写在自己目录
<deploy>/run/apiserver/logs/apiserver.log(由 logger.yaml 决定),<deploy>/logs/apiserver.log仅收集启动期输出。 - 默认账号:
admin/admin(来自auth.yaml样例)。
7. xcecli 使用(登录与查询)
xcecli 是访问 apiserver(REST API,端口 8080)的命令行客户端,构建产物在 src/build/frontend/xcecli/xcecli(root 命令名为 xce)。
7.1 登录
src/build/frontend/xcecli/xcecli login -u admin -p admin --url http://localhost:8080
| 参数 | 必填 | 说明 |
|---|---|---|
-u, --username |
✅ | 用户名 |
-p, --password |
可选 | 密码;不填则交互式提示输入(不回显) |
--url |
✅ | API Server 地址,http://IP:8080 或 https://IP:8443 |
- 成功输出
login successfully,令牌(JWT)保存到~/.xce/config.json。 - 默认账号定义在
run/apiserver/config/auth.yaml:admin/admin、rshen/Letmein(cluster_admin)、user1/user1、user2/user2(basic_user)。
7.2 登录后的常用命令
xce login # 登录
xce logout # 退出(清除该服务器登录信息)
xce list # 列出已登录的 API Server
xce id # 查询集群信息(无集群配置时返回:no cluster found)
xce hosts list # 查询主机(无主机时返回:no host found)
xce jobs ... # 作业管理
xce queues ... # 队列管理
xce users ... # 用户管理
xce roles ... # 角色管理
xce permissions # 权限管理
xce resources # 资源管理
注:单节点冷启动未配置
cluster.bin时,集群/主机等查询返回"无数据"属预期行为,认证链路本身已验证正常。
8. xce_test 使用(直连 controller 的测试客户端)
xce_test 是直接连接 xce_controller(端口 9990,走 TLS 协议)的测试客户端,绕过 apiserver,直接读写集群状态。构建产物在 src/build/controller/xce_test(与 xce_controller 一同构建)。
与
xcecli的区别:xcecli走 apiserver 的 HTTP REST API(8080);xce_test直连 controller 本身(9990),是 HA soak 测试(/xce-test-ha、run-ha-soak.sh)的客户端驱动。
jsxing@test01:~$ xce_test
xce>client
xce/client>
xce/client>workerlist
HostName Status HostType HostModel CPUfactor MaxCPU MaxMem MaxSwap MaxTmp NDisks
test01 UNAVAIL unknown unknown 0.000000 0 0.000000 0.000000 0.000000 0
h1 UNAVAIL unknown unknown 0.000000 0 0.000000 0.000000 0.000000 0
8.1 用法(stdin 命令脚本)
printf 'set masters ctrl-1=localhost:9990
set myname probe
client
wait-primary 3000
ctrlstatus
quit
' | ASAN_OPTIONS=detect_leaks=0 src/build/controller/xce_test
实测输出:
wait-primary: primary 'ctrl-1' serving after 48 ms
Primary: ctrl-1
term: 4
committedLsn: 0
appliedLsn: 0
ASAN_OPTIONS=detect_leaks=0是必需的:debug/ASAN 构建在退出时会报无害的内存泄漏,污染退出码(但不影响命令输出)。
8.2 常用设置
| 命令 | 说明 |
|---|---|
set masters ctrl-1=localhost:9990 |
controller 候选列表(支持 HA failover,多个用逗号分隔,格式同 --peers) |
set myname probe |
客户端标识 |
set secret ... |
认证密钥(默认 keepSecret) |
8.3 命令结构(三个上下文)
| 上下文 | 进入命令 | 可用命令 |
|---|---|---|
| client(客户端) | client |
wait-primary、ctrlstatus、jobsub/jobrun/jobkill/jobckpt/jobinfo/joblist、workerlist、reslist/resval、qctrl、hello、expect-job(s)、shutdown、taskrun、waldump/walcompare/snapverify/snapdiff |
| worker(模拟工作节点) | worker |
join、jstart/jfinish/jfail、resupdate |
| scheduler(模拟调度器) | scheduler |
join、getconf、setpend |
注:单节点冷启动未配置
cluster.bin时,workerlist/joblist/reslist等返回空属预期行为;连接与查询链路本身已验证正常。
9. 修复的问题记录
9.1 controller 在 scheduler 加入时 ASan 崩溃(已修复)
现象:单节点冷启动(无 cluster.bin 配置)时启动 scheduler,controller 立即崩溃:
AddressSanitizer: SEGV on unknown address 0x000000000038
#0 xcesched_push_configs ../controller/sched.c:172
#1 on_scheduler_join ../controller/sched.c:667
#2 handle_scheduler_join ../controller/join.c:165
根因:冷启动时 c->myconf->cluster_config 为 NULL,xcesched_push_configs 无条件解引用(空指针 + 偏移 0x38)。同文件 worker.c 的 find_matched_hostconfig 有防护,唯独 sched.c 没有。
修复(src/controller/sched.c):
if (c->myconf == NULL || c->myconf->cluster_config == NULL) {
WARN("%s: cluster_config not loaded yet, skipping config push", __func__);
return 0;
}
配置未就绪时跳过推送、不崩溃,scheduler 之后重新加入时会收到配置。
验证:修复后 controller 日志出现 cluster_config not loaded yet, skipping config push,scheduler 成功接入,进程稳定。
9.2 单节点冷启动时序(正常行为)
controller 持久化了 term 后重启,会先进入 CTRL_STANDBY:
- 启动时
load_primary_config找不到配置 → "will fetch from primary after election" - 进入
CTRL_STANDBY,等待 primary - 约 10s liveness 超时 → 触发 PreVote failover
- PreVote 单节点立即胜出 → 进入真实选举 →
CTRL_PRIMARY(term+1) - 此期间 scheduler 的 role-2 join 会被拒绝(
standby has no primary yet),scheduler 自动重试,成为 primary 后即接入
因此启动顺序无关紧要:scheduler / worker 连不上会重试,controller 竞选完成后自然接入。
9.3 worker join 返回 EINVAL(无集群配置)
现象:xce_test worker 上下文执行 join 报 xce_join: Invalid argument。
根因:与 9.1 同一个根因(冷启动无 cluster.bin)。handle_worker_join(join.c)先调 find_matched_hostconfig 找匹配主机配置,而该函数(worker.c)在 c->myconf->cluster_config == NULL 时返回 NULL 并设 errno = EINVAL,controller 据此回复 -EINVAL。
修复:部署集群配置后重启 controller——即 ./all.sh config deploy。修复后 join 输出 host join ok,workerlist(client 上下文)可见 h1 已注册。
注意:
workerlist/joblist/reslist等查询命令只在 client 上下文可用,worker 上下文里没有这些命令(正常命令集划分)。
9.4 作业可调度但运行时立即退出(exit 126,Permission denied)
现象:作业能被调度(jobs list 可见),但一运行就结束。xcecd.log 显示:
Worker 188071 exited with code 126
systemd journal 给出真正原因:
sh[188071]: /bin/sh: 1: exec: /home/jsxing/allwork/git/myxce/src/build/worker/xcecd_worker: Permission denied
systemd[1]: xcecd-worker-1-0-...service: Main process exited, code=exited, status=126/n/a
根因:每个作业的 worker(xcecd_worker)由 systemd 临时单元以作业执行用户(如 admin)身份启动(User=<execuser>)。而 xcecd 通过 XCE_SERVER_DIR(未设置时按自身二进制路径推断)去找 xcecd_worker,推断结果是仓库路径 /home/jsxing/allwork/...。该路径的父目录 /home/jsxing 权限为 750(仅属主可进入),admin 无法穿越,于是 exec 失败 → Permission denied → sh 退出码 126。namei -l 可确认:
drwxr-x--- jsxing jsxing home/jsxing <- 其他用户无法进入
-rwxr-xr-x jsxing jsxing .../xcecd_worker
相关背景:
controller_connector.c里 bug-1507 的注释只处理了"执行用户不存在"(systemd 退出码 217 EXIT_USER),未覆盖"二进制路径不可达"(126)这一种情况。
修复(all.sh):
- 新增 customize 变量
g_worker_bin_dir="/opt/xce/bin"(所有作业用户都能到达的目录,XCE 标准布局)。 svc_prepare_worker用sudo install -m 0755把xcecd_worker装到${g_worker_bin_dir}(源更新才重装,幂等)。- 新增
svc_env_worker:启动 worker 时注入XCE_SERVER_DIR=${g_worker_bin_dir}(svc_start对 sudo 服务通过sudo -n env ...注入),使dbus_start_worker使用安装好的二进制而不是仓库路径。
验证:./all.sh restart worker 后:
/opt/xce/bin/xcecd_worker存在且admin可执行(sudo -u admin /opt/xce/bin/xcecd_worker -h正常)。xcecd进程环境含XCE_SERVER_DIR=/opt/xce/bin。xcecli jobs submit sleep 60状态保持RUN直到跑满 60s,controller 日志on_job_finish ... exit code 0干净结束。
说明:worker 日志目录不是问题——
xcecd启动时会chmod(logdir, 0777)并预创建xcecd_worker.log(0666),执行用户可写。
9.5 作业无法调度
修改 src/configs/*.yaml 后必须重新 ./all.sh config deploy —— cluster.bin 是编译产物,不会自动跟随源文件。
10. 使用注意事项
- sudo:worker 需要 root。要求
sudo -n可用(NOPASSWD 或缓存凭证);长期使用建议配置/etc/sudoers的 NOPASSWD。 - 构建环境:构建需
nix develop环境(提供 meson、openssl 等工具)。 - 日志查看:服务启动失败时,
status会显示日志最后一行,完整日志在logs/<服务名>.log。 - PID 文件:
run/<服务名>.pid;stale pid 文件会被自动清理。 - 测试服务管理:如需测试 start/stop 机制,建议用临时副本 + 无害二进制(如
/bin/sleep+ 自定svc_args_<测试名>),不要污染真实服务。
11. 目录结构速查
all.sh # 主脚本
all_README.md # 本文档
src/build/ # 开发版构建产物
controller/xce_controller
controller/xce_test
scheduler/xce_scheduler
worker/xcecd
frontend/apiserver/xce_apiserver
frontend/xcecli/xcecli
src/build-prod/ # 生产版构建产物
deploy/ # 统一部署根目录(${g_deploy_dir})
conf/ # 生成配置
worker.yaml # worker 配置(svc_prepare_worker 生成,log_dir/log_level 跟随 all.sh)
run/ # PID 文件 / 证书 / 数据
controller.pid # PID 文件
scheduler.pid
worker.pid
apiserver.pid
certs/ # TLS 证书(自动生成)
controller/ # controller 数据目录
ctrl-1/conf/ # cluster.bin.* + current(config deploy 生成)
ctrl-1/data/ # WAL / snap / ha-term
apiserver/ # apiserver top 目录
config/ # app.yaml / logger.yaml / rbac.yaml / auth.yaml(自动生成)
logs/ # apiserver 自身日志
logs/ # 服务日志
controller.log
scheduler.log
worker.log
apiserver.log
~/.xce/config.json # xcecli 登录令牌(JWT,login 后生成)
旧路径
/opt/xce/conf/worker.yaml(需 root 写入)已不再被 all.sh 引用;worker 配置统一在deploy/conf/worker.yaml。
作业 worker 二进制例外:
xcecd_worker被 systemd 以作业执行用户身份运行,必须装在所有人可访问的位置(默认/opt/xce/bin/xcecd_worker,见g_worker_bin_dir与 9.4)。
xcecli cases
jobs
xcecli jobs submit -J 'A[1-10]' sleep 222
xcecli jobs list
xcecli hosts list
xcecli jobs submit sleep 100