Laminar 1.4 常用 CLI 命令与运维实践
Laminar 1.4 的 laminarc 8 个子命令逐条实测:queue/start/run 的返回时机与退出码、--next 插队、abort 与 job 内 set、错误输出清单,以及 git hook、cron、systemd、备份升级与排障实践。
Laminar 1.4 常用 CLI 命令与运维实践
目标:把
laminarc的 8 个子命令讲透,并给出真实服务器上的退出码、错误输出与排障路径;再进一步,把 Laminar 接进 git hook、cron 与 systemd 的日常运维。文中 20 余条命令回显均来自 Ubuntu 24.04.5 + Laminar 1.4 的实机执行。
一、环境信息
| 项目 | 实测值 |
|---|---|
| 操作系统 | Ubuntu 24.04.5 LTS |
| 主机名 | ci.example.com |
| Laminar 版本 | 1.4-1~upstream-ubuntu2404 |
| 客户端 | /usr/bin/laminarc(108848 字节) |
| 守护进程 | /usr/sbin/laminard |
| RPC 通道 | unix:/var/lib/laminar/laminar.sock |
| 数据目录 | /var/lib/laminar |
| Web | http://127.0.0.1:8080 |
本文所有 laminarc 命令都在导出了 LAMINAR_HOST 的 shell 中执行:
export LAMINAR_HOST=unix:/var/lib/laminar/laminar.sock
如果沿用默认的抽象 socket,则不需要这个变量(连接细节见第二节)。
二、命令总览
2.1 完整帮助
不带参数执行 laminarc 会打印版本与全部子命令:
$ laminarc
laminarc version 1.4-1~upstream-ubuntu2404
Usage: laminarc [-h|--help] COMMAND
-h|--help show this help message
where COMMAND is:
queue JOB_LIST... queues one or more jobs for execution and returns immediately.
start JOB_LIST... queues one or more jobs for execution and blocks until it starts.
run JOB_LIST... queues one or more jobs for execution and blocks until it finishes.
JOB_LIST may be prepended with --next, in this case the job will
be pushed to the front of the queue instead of the end.
set PARAMETER_LIST... sets the given parameters as environment variables in the currently
running job. Fails if run outside of a job context.
abort NAME NUMBER aborts the run identified by NAME and NUMBER.
show-jobs lists all known jobs.
show-queued lists currently queued jobs.
show-running lists currently running jobs.
JOB_LIST is of the form:
[JOB_NAME [PARAMETER_LIST...]]...
PARAMETER_LIST is of the form:
[KEY=VALUE]...
Example:
laminarc start \
nightly-build branch=master type=release \
nightly-build branch=master type=debug
1.4 的客户端只有这 8 个子命令,没有 log、status、cancel 之类的扩展。日志与状态全部走 HTTP,这一点决定了后面的运维方式:控制走 CLI,观测走 HTTP/journal。
2.2 三个触发命令的区别
| 命令 | 行为 | 返回时机 | 典型用途 |
|---|---|---|---|
queue | 入队后立即返回 | 入队完成 | git hook、定时任务 |
start | 入队并等待开始执行 | 任务真正开跑 | 需要拿到运行号后做后置动作 |
run | 入队并等待执行结束 | 构建结束(退出码反映结果) | 手工验证、脚本串联 |
三者都支持 --next 把任务插到队列最前,也都支持一次传多个 job 和 KEY=VALUE 参数。
2.3 客户端连接方式
laminarc 通过 RPC 找守护进程,地址由客户端环境变量 LAMINAR_HOST 指定,未设置时用编译默认值 unix-abstract:laminar。把服务端改成文件 socket 后(见配置篇),客户端必须同步:
$ LAMINAR_HOST=unix:/var/lib/laminar/laminar.sock laminarc show-jobs
env-demo
fail
hello
不匹配时的报错非常直接,且是进程崩溃而非友好提示:
$ laminarc show-jobs
exit=134
terminate called after throwing an instance of 'kj::ExceptionImpl'
what(): kj/async-io-unix.c++:1517: disconnected: connect(): Connection refused; address = unix-abstract:laminar
所以凡是切换过 RPC 地址的机器,都要同步修改所有调用方(cron、git hook、CI 脚本)的环境变量。
三、查询类命令
3.1 show-jobs
列出 cfg/jobs/ 下所有 job 名,一行一个:
$ laminarc show-jobs
env-demo
fail
hello
hooks
init-demo
inspect
setdemo
single-env
slow
timeout-demo
它只反映当前配置目录里的 job 定义,不包含历史。job 文件是热加载的,新增后立刻出现在列表里。
3.2 show-queued
$ laminarc show-queued
队列为空时没有任何输出,退出码 0。有内容时按「job:运行号」逐行打印。
3.3 show-running
$ laminarc show-running
slow:9
同样的「job:运行号」格式,只显示正在执行的 run。它和 show-queued 一起用,就能判断「是没触发、还是触发了但排不上队」。
四、触发类命令
4.1 queue:入队即返回
$ laminarc queue slow
slow:9
输出即「job:本次运行号」。命令本身不等待构建,适合放在 git hook、cron 里。
带参数触发也很常见,参数会注入脚本环境:
$ laminarc queue inspect os=ubuntu arch=x86_64
inspect:13
job 侧用 env | grep 验证参数确实生效:
$ curl -s http://127.0.0.1:8080/log/inspect/13
[laminar] Executing cfg/before
[global before] time=15:18:48 job=inspect
[laminar] Executing cfg/jobs/inspect.run
+ echo 'JOB=inspect RUN=13 CONTEXT=default'
JOB=inspect RUN=13 CONTEXT=default
+ env
+ sort
+ grep -E '^(os|arch|branch)='
arch=x86_64
os=ubuntu
[laminar] Executing cfg/after
[global after] job=inspect run=13 result=success last=success context=default
💡 提示:命令行参数不会覆盖 env 文件里已定义的变量(优先级见配置篇),同名变量只以文件为准。
4.2 start:等到开跑
$ laminarc start inspect
inspect:11
start 会阻塞到该 run 真正拿到执行槽位为止,然后打印运行号返回。适合「拿到运行号后立即轮询日志」的场景。
4.3 run:等到结束
$ laminarc run init-demo
init-demo:1
成功时退出码为 0。失败任务退出码为 3,超时任务同样是 3(但结果字段是 aborted):
$ laminarc run timeout-demo
timeout-demo:2
$ echo $?
3
这是把 Laminar 接进 shell 脚本时最重要的约定:判断结果看退出码,区分失败/超时看日志或 Web 结果字段。
4.4 一次触发多个 job
$ laminarc run inspect hello
inspect:12
hello:10
$ echo $?
0
按参数顺序逐个入队,每行一个运行号;全部成功才返回 0。
4.5 --next:插队
single 上下文只有一个执行槽,slow 每条要跑 60 秒,正好用来观察队列顺序:
$ laminarc queue slow
slow:9
$ laminarc queue slow
slow:10
$ laminarc queue --next slow
slow:11
$ laminarc show-running
slow:9
$ laminarc show-queued
slow:11
slow:10
后入队的 11 排在 10 前面,说明 --next 是插入队首而不是队尾。它不影响正在运行的 9。
4.6 记录触发原因
LAMINAR_REASON 是客户端环境变量,会被写入本次 run 并在 Web 与 SSE 中展示:
$ LAMINAR_REASON="nightly trigger" laminarc queue hello
hello:11
抓 SSE 中的 recent 数组验证:
$ curl -s -N --max-time 2 -H "Accept: text/event-stream" http://127.0.0.1:8080/ | grep -o '{"name":"hello","number":11[^}]*}'
{"name":"hello","number":11,"context":"default","queued":1793114344,"started":1793114344,"completed":1793114344,"result":"success","reason":"nightly trigger"}
把「谁触发的」写进 reason,是排查「这条构建为什么跑」的最低成本手段。
五、控制类命令
5.1 abort:中止运行
语法是 abort NAME NUMBER,两个参数缺一不可:
$ laminarc abort slow 9
$ echo $?
0
中止后队列立刻推进:
$ laminarc show-running
slow:11
$ laminarc show-queued
slow:10
中止不存在的运行时,命令无输出、退出码 2:
$ laminarc abort nosuchjob 1
$ echo $?
2
5.2 set:在 job 内部传值
set 只能在 job 上下文里用,它会通过隐藏管道把变量写回同一个 run 的后续脚本。实测 job:
# cfg/jobs/setdemo.before
#!/bin/bash
laminarc set STAGE=prepare
# cfg/jobs/setdemo.run
#!/bin/bash -ex
echo "STAGE=$STAGE"
运行日志:
[laminar] Executing cfg/jobs/setdemo.before
[laminar] Executing cfg/jobs/setdemo.run
+ echo STAGE=prepare
STAGE=prepare
在普通 shell 里调用则直接失败:
$ laminarc set foo=bar
Must be run from within a laminar job
$ echo $?
1
六、错误输出与退出码
| 场景 | 命令 | 输出 | 退出码 |
|---|---|---|---|
| 未知子命令 | laminarc bogus | Unknown command bogus | 1 |
| 用选项查版本 | laminarc --version | Unknown command --version | 1 |
| 触发不存在的 job | laminarc queue nosuchjob | Failed to queue job 'nosuchjob' | 2 |
| 中止不存在的 run | laminarc abort nosuchjob 1 | 无输出 | 2 |
| job 外调用 set | laminarc set foo=bar | Must be run from within a laminar job | 1 |
| RPC 连不上 | laminarc show-jobs | terminate called ... Connection refused | 134 |
| 构建失败/超时 | laminarc run <job> | 正常打印运行号 | 3 |
守护进程侧没有 --version 选项,传了会先提示再打印版本与用法:
$ /usr/sbin/laminard --version
Unknown argument --version
laminard version 1.4-1~upstream-ubuntu2404
Usage:
-h|--help show this help message
-v enable verbose output
七、日志与观测
7.1 按 run 取日志
$ curl -s http://127.0.0.1:8080/log/hello/12 | head -3
[laminar] Executing cfg/before
[global before] time=15:19:32 job=hello
[laminar] Executing cfg/jobs/hello.run
latest 是特殊运行号,取最近一次:
$ curl -s http://127.0.0.1:8080/log/hello/latest | head -2
[laminar] Executing cfg/before
[global before] time=15:19:32 job=hello
注意:每个 job 是独立编号的,latest 只对存在的 job 有效;job 被删除或该编号不存在时返回 Not Found。
7.2 journald
调度器视角的记录在 journald 里,适合排查「任务为什么没跑」:
$ journalctl -u laminar --no-pager -n 6 -o cat
rpc.cpp:55: info: RPC queue; jobName = hello
laminar.cpp:714: info: Started job; run->name = hello; run->build = 12; ctx->name = default
laminar.cpp:755: info: Run completed; r->name = hello; to_string(r->result) = success
run.cpp:83: info: Run destroyed
laminar.cpp:755: info: Run completed; r->name = slow; to_string(r->result) = success
run.cpp:83: info: Run destroyed
RPC queue 与 Started job 之间的时间差就是排队时长;只有 RPC queue 没有 Started job,基本可以判定是上下文槽位或归属配置问题。
7.3 状态徽章与产物
$ curl -s --compressed http://127.0.0.1:8080/badge/hello.svg | grep -o 'success\|failed'
success
$ curl -s http://127.0.0.1:8080/archive/hello/8/build-time.txt
2026-10-27T14:47:44Z
徽章适合贴到 README,产物链接适合贴到发布记录;两者都不需要登录。
7.4 备份
需要备份的只有三样:配置、历史库、归档。实测打包:
$ tar czf /tmp/laminar-backup.tgz -C /var/lib/laminar cfg archive laminar.sqlite
$ ls -lh /tmp/laminar-backup.tgz
-rw-r--r-- 1 root root 6.9K Oct 27 15:19 /tmp/laminar-backup.tgz
run/ 不需要备份(临时目录与可重建的 workspace),但 laminar.sqlite 与 archive/ 一旦丢失,历史与产物就不可恢复。
八、运维集成实践
8.1 git post-receive hook(实测)
最省事的 SCM 集成方式:在裸仓库里挂一个 hook,推送到位就触发构建。
# /srv/demo.git/hooks/post-receive
#!/bin/bash
export LAMINAR_HOST=unix:/var/lib/laminar/laminar.sock
laminarc queue hello
给执行位后,从工作副本推送,构建号会直接回显在 push 输出里:
$ git push -q origin master
warning: You appear to have cloned an empty repository.
remote: hello:12
$ echo $?
0
remote: hello:12 就是 hook 里 laminarc queue 的输出,说明整条链路(push → hook → RPC → 调度)已经打通。生产环境建议在 hook 里带上分支等信息,例如 laminarc queue build branch=$(git rev-parse --abbrev-ref HEAD)。
8.2 cron 定时触发
Laminar 没有内置定时器,定时任务交给 cron:
# /etc/cron.d/laminar-nightly
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
15 2 * * * root LAMINAR_HOST=unix:/var/lib/laminar/laminar.sock laminarc queue nightly-build reason=scheduled
用 queue 而不是 run,避免 cron 进程被长时间构建占住;触发原因通过参数或 LAMINAR_REASON 写进数据库,方便回溯。
8.3 systemd 日常
$ systemctl status laminar --no-pager
$ sudo systemctl restart laminar
$ sudo systemctl disable --now laminar
restart 前必须确认队列为空——内存队列重启即丢:
$ laminarc show-queued
有输出就先等它跑完,或明确接受丢弃。
8.4 升级与回滚
升级流程(发行包方式):
laminarc show-queued确认没有排队任务;systemctl stop laminar;tar czf备份cfg、archive、laminar.sqlite;- 安装新版本包,
systemctl start laminar; systemctl status laminar、laminarc show-jobs、curl /三项验收。
回滚就是把上一版包重新装上并恢复备份的数据库与归档。跨大版本回滚前必须先恢复 laminar.sqlite,否则旧版二进制可能读不了新结构。
九、排障手册
9.1 laminarc 连接失败并 core dump(退出码 134)
terminate called after throwing an instance of 'kj::ExceptionImpl'
what(): kj/async-io-unix.c++:1517: disconnected: connect(): Connection refused; address = unix-abstract:laminar
按顺序检查:
systemctl is-active laminar是否为active;- 服务端
LAMINAR_BIND_RPC与客户端LAMINAR_HOST是否一致; - 文件 socket 模式下
ls -l权限是否为srw-rw---- laminar laminar,当前用户是否在laminar组。
9.2 服务起不来:socket 权限被拒
fatal: ::bind(sockfd, &addr.generic, addrlen): Permission denied; toString() = unix:/var/run/laminar.sock
/run 归 root,laminar 用户无权建 socket。把 socket 放到 /var/lib/laminar 下,或预建目录并 chown laminar:laminar。
9.3 任务一直排队、日志无报错
先看两处:
$ laminarc show-queued
$ laminarc show-running
- 两者都有内容 → 正常排队,等槽位;
show-queued有、show-running长期为空 → 上下文归属问题,检查cfg/contexts/*.conf是否存在但缺少default,或 job 的CONTEXTS=写错。
9.4 触发成功但 log 返回 Not Found
三个常见原因:
- job 名拼错(区分大小写);
- 运行号不存在(
latest只指向真实存在的最近一次); - 服务刚重启且该次 run 被中断,记录未落库。
9.5 构建结果与预期不符
同时看三个字段:
$ curl -s http://127.0.0.1:8080/log/<job>/latest | tail -3
$ journalctl -u laminar -n 20 -o cat
$ laminarc show-jobs
result=aborted 优先怀疑超时配置($JOB.conf 的 TIMEOUT);result=failed 看脚本退出码与 bash -ex 日志最后一条命令。
十、命令速查表
| 目的 | 命令 | 关键输出/退出码 |
|---|---|---|
| 查看帮助 | laminarc | 8 个子命令清单 |
| 列出 job | laminarc show-jobs | 每行一个 job 名 |
| 看排队 | laminarc show-queued | job:run,空则无输出 |
| 看运行 | laminarc show-running | job:run |
| 入队 | laminarc queue <job> [k=v...] | job:run |
| 插队 | laminarc queue --next <job> | 队列最前 |
| 等开跑 | laminarc start <job> | job:run |
| 等结束 | laminarc run <job> | 退出码 0/3 |
| 中止 | laminarc abort <job> <run> | 0;不存在为 2 |
| job 内传值 | laminarc set K=V | job 外调用退出码 1 |
| 取日志 | curl /log/<job>/<run|latest> | 纯文本 |
| 看徽章 | curl /badge/<job>.svg | SVG |
| 取产物 | curl /archive/<job>/<run>/<file> | 文件内容 |
| 看调度日志 | journalctl -u laminar -o cat | RPC queue / Started job |
| 备份 | tar czf ... cfg archive laminar.sqlite | 归档文件 |
十一、参考链接
- 官方站点:https://laminar.ohwg.net/
- 用户手册(
laminarc命令与目录结构):https://laminar.ohwg.net/docs.html - 源码仓库:https://github.com/ohwgiles/laminar
- 版本发布:https://github.com/ohwgiles/laminar/releases
- 客户端源码(命令语义以这里为准):https://github.com/ohwgiles/laminar/blob/master/src/client.cpp
系列导航:
- 《Laminar 1.4 完整部署与实战指南》 —— 定位、架构、能力边界与选型
- 《Laminar 1.4 安装与首个流水线》 —— 发行包与源码安装、目录语义、首个 job
- 《Laminar 1.4 配置参数详解》 —— 服务端变量、contexts、全局钩子实测