Laminar 1.4 完整部署与实战指南
Laminar 1.4 轻量级自托管 CI 的完整认知篇:单进程守护架构、job 脚本约定与目录布局、contexts 并发模型、能力边界、与 Jenkins / Gitea Actions 的选型对比,附 Ubuntu 24.04 实测最小验证路径。
Laminar 1.4 完整部署与实战指南
目标:用一篇文档建立对 Laminar 的完整认知——它是什么、由哪些进程和目录构成、能做什么与明确不做什么、与 Jenkins / Gitea Actions 等主流方案怎么选,并给出可落地的最小验证路径。文中版本信息于 2026-10-27 联网核实,最小验证路径与运行回显来自 Ubuntu 24.04.5 服务器上的 Laminar 1.4 实测。
一、环境信息
本文的认知部分依据官方站点、GitHub 仓库与发行包元数据核实;快速开始部分在一台 Ubuntu 24.04.5 服务器上实装 Laminar 1.4 后取得回显。可复现信息如下:
- 操作系统:Ubuntu 24.04.5 LTS(Noble Numbat),x86_64
- 内核:7.0.0-38-generic
- init 系统:systemd 255(255.4-1ubuntu8.17)
- Laminar 版本:1.4-1~upstream-ubuntu2404(上游发行包)
- 依赖库:libcapnp-1.0.1(1.0.1-4)、libsqlite3-0、zlib1g
- 守护进程:
/usr/sbin/laminard(systemd 单元laminar.service) - 命令行客户端:
/usr/bin/laminarc - 配置目录:
/etc/laminar.conf(服务环境变量) - 数据目录:
/var/lib/laminar(cfg / run / archive / laminar.sqlite) - Web 监听:
*:8080(默认) - 运行账户:
laminar(uid/gid 999,安装包自动创建)
💡 说明:本文的版本核实口径是「官方仓库最新正式发布 tag」,不是发行版仓库里的打包版本。写作时点官方最新为 1.4,发布于 2026-08-16。
二、软件概述
2.1 它解决什么问题
Laminar 是一个面向 Linux 的轻量级自托管持续集成(Continuous Integration)服务。它把「一次构建」抽象为一个由 shell 脚本定义的 job:脚本放在 /var/lib/laminar/cfg/jobs/<job>.run,Laminar 负责调度、并发控制、日志采集、结果入库与 Web 展示。
它刻意不做的事情,和它做的事情同样重要:
- 没有「点按钮建流水线」的可视化编辑器,不生成 Jenkinsfile 一类 XML/YAML DSL;
- 不内置 SCM 集成、不内置流水线编排语言、不内置插件市场;
- Web UI 严格只读,因此默认不带登录鉴权也能放在内网或公网展示;
- 触发构建靠外部工具:cron、git hook、webhook 程序,或者直接调用
laminarc。
官方站点用一句话概括其设计哲学:复用 Linux 已有的工具(bash、cron、git hook、ssh),而不是重新发明轮子。
2.2 项目身份
- 项目名:Laminar(二进制为
laminard+laminarc) - 作者/维护者:Oliver Giles(GitHub
ohwgiles) - 许可证:GPLv3
- 官方站点:
https://laminar.ohwg.net/(用户手册在/docs.html) - 源码仓库:
https://github.com/ohwgiles/laminar - 技术栈:C++(C++14 语法)+ Cap'n Proto(RPC 与异步 IO)+ SQLite(构建历史)+ zlib(日志压缩)
- 前端:单页应用,静态资源编译进二进制,通过 Server-Sent Events(SSE)推送状态
⚠️ 注意:任务线索里出现的
laminar.ci域名在核实当天无法完成 TLS 握手,不是官方站点;官方站点与手册均为laminar.ohwg.net。
2.3 版本与活跃度(2026-10-27 核实)
| 版本 | 发布时间 | 说明 |
|---|---|---|
| 1.4 | 2026-08-16 | 当前最新正式版;手册与打包脚本改进、日志颜色/超链接修复、日志自动滚动、FreeBSD 下 Clang 构建支持 |
| 1.3 | 2023-05-30 | 上一稳定版 |
| 1.2 | 2022-01-28 | |
| 1.1 | 2021-08-04 | |
| 1.0 | 2020-12-05 |
- GitHub 星标:358(写作当天取 shields.io 徽章数据)
- 发布节奏:以「年」为单位,属于典型的「够用即停」型基础设施软件
- 预编译包矩阵(1.4):Ubuntu 22.04 / 24.04(amd64)、Debian 13 / 14(amd64、armhf)、Rocky 9 / 10(x86_64)、openSUSE 16(x86_64)
- 官方没有在 Docker Hub 等公共仓库发布可直接
pull的镜像;仓库内仅有docker/Dockerfile用于自行构建一个最小基础镜像(官方说明其只适合作为二次构建的底座)
💡 说明:正因为没有「官方镜像 + 完整运行环境」的一键部署形态,本系列没有写 Docker Compose 部署篇,而是把精力放在服务器直装(发行包 / 源码编译)、参数配置与命令行运维三篇上,避免编造不存在的部署路径。
三、核心架构
3.1 进程模型
Laminar 只有两个可执行文件,职责非常清晰:
+---------------------------+
浏览器 ──HTTP──▶ | laminard(常驻守护进程) |
(只读 UI) | ├─ HTTP 服务 :8080 |
| ├─ RPC 服务(unix socket) |
运维脚本 ──RPC──▶ | ├─ 调度器(contexts) |
(laminarc) | ├─ SQLite(构建历史) |
| └─ fork → leader → 脚本 |
+---------------------------+
- laminard:唯一常驻进程,由 systemd 托管。它同时监听两个端点——HTTP(给浏览器看)与 RPC(给
laminarc发指令)。 - laminarc:纯客户端,把
queue/start/run/abort/set/show-*指令通过 RPC 发给laminard,本身不执行业务逻辑。 - leader 进程:每次 run 由
laminardfork 出一个 leader,leader 再按顺序 fork/exec 各个脚本,并以进程组为单位管理整棵进程树,保证中止任务时能把子进程一并回收。
这种「一个守护进程 + 一个瘦客户端」的模型带来两个直接结果:资源占用极低(实测空载常驻内存约 2 MB),以及不存在 agent/worker 分布式组件。
💡 技术拓展:CI 系统的执行架构常见三类——中心化单机(Laminar、Buildbot master+worker 的极简形态)、控制器+多 Agent(Jenkins、GitLab Runner)、托管式 SaaS Runner(GitHub Actions、Gitea Actions 的 act_runner)。三者的差异在于「构建能力放在哪」:单机模型把构建能力与调度放在同一台机器,配置最简单但没有横向扩展;多 Agent 模型可以横向扩容,但需要维护注册、标签、缓存与网络;SaaS Runner 免运维,但构建环境与数据不在自己手里。Laminar 属于第一类,本文后续所有部署与运维内容都围绕单机形态展开。
3.2 数据与配置布局
Laminar 的全部状态围绕 LAMINAR_HOME(默认 /var/lib/laminar)组织,实测初始目录如下:
/var/lib/laminar
├── cfg/
│ ├── before # 全局前置脚本(每个 job 都执行)
│ ├── after # 全局后置脚本(每个 job 都执行)
│ ├── env # 全局环境变量文件
│ ├── groups.conf # Web 端 Jobs 页签分组
│ ├── scripts/ # 会被加入 PATH 的辅助脚本目录
│ ├── contexts/ # 并发上下文:$CONTEXT.conf / $CONTEXT.env
│ └── jobs/ # job 脚本:$JOB.run / .init / .before / .after / .env / .conf
├── run/ # 运行目录(默认跑完即删,可保留 N 份)
│ └── $JOB/$RUN/ # 本次 run 的工作目录(脚本 CWD)
│ └── $JOB/workspace/ # 跨 run 复用的工作区($WORKSPACE)
├── archive/ # 产物归档(每个 run 一个目录,$ARCHIVE 指向它)
│ └── $JOB/$RUN/
│ └── $JOB/latest -> $RUN # 指向最近一次归档的软链接
└── laminar.sqlite # 构建历史(编号、时间、结果、日志)
四个路径语义要在写脚本前就分清,这是 Laminar 最容易踩坑的地方:
| 路径 / 变量 | 生命周期 | 用途 |
|---|---|---|
$PWD(run 目录) | 本次 run,默认结束后删除 | 编译中间产物、临时文件 |
$WORKSPACE | 跨 run 持久 | 源码仓库缓存、依赖目录 |
$ARCHIVE | 永久 | 需要留存的构建产物 |
laminar.sqlite | 永久 | 构建历史与日志(不存产物) |
3.3 一次 run 的完整链路
以 laminarc run hello 为例,真实链路是:
laminarc通过 RPC 发送run请求(默认抽象 unix socketunix-abstract:laminar);laminard为 job 分配递增的构建号,写入 SQLite 的builds表,进入内存队列;- 调度器按 context(并发上下文)寻找空闲执行槽位;没有空闲槽位就一直排队;
- 拿到槽位后 fork leader,leader 在工作目录里依次执行
hooks.init(仅当 workspace 首次创建)、cfg/before、$JOB.before、$JOB.run、$JOB.after、cfg/after; - 脚本的 stdout/stderr 被管道捕获,边写 SQLite 边通过 SSE 推给浏览器;
- 结束后写
completedAt、result(success / failed / aborted),更新archive/$JOB/latest软链; - 按
LAMINAR_KEEP_RUNDIRS清理旧的 run 目录,并再次尝试调度队列中的其他 run。
四、功能特性
4.1 Job 脚本约定(核心能力)
一个 job 至少是一个可执行的 $JOB.run。围绕它,Laminar 支持一整套「约定优于配置」的扩展文件:
| 文件 | 作用 | 实测要点 |
|---|---|---|
$JOB.run | 主脚本,必须有执行位 | 退出码非 0 → 构建失败 |
$JOB.init | workspace 首次创建时执行一次 | 适合 git clone 初始化 |
$JOB.before / $JOB.after | 主脚本前后执行 | after 里可用 $RESULT 判断结果 |
$JOB.env | 本 job 的环境变量 | 不做 shell 展开,FOO="bar" 会带引号 |
$JOB.conf | TIMEOUT=、DESCRIPTION=、CONTEXTS= | 改完立即生效,无需重启 |
cfg/before / cfg/after | 全局前后置 | 适合统一发通知、统一清理 |
cfg/scripts/ | 加入 PATH 的辅助脚本目录 | 可直接当命令调用 |
4.2 并发控制:Contexts
Laminar 用「上下文(context)」而不是「队列优先级」来做并发治理:一个 context 有固定数量的 executor 槽位,job 通过 JOBS=(context 侧匹配)或 CONTEXTS=(job 侧声明)关联到 context。默认(未定义任何 context 文件时)等价于一个名为 default、容量 6 的 context。
⚠️ 注意:这是实测踩到的坑——只要
cfg/contexts/下出现任意 context 文件,隐式的defaultcontext 就不再自动创建。此时未声明CONTEXTS的 job 会因为匹配不到名为default的 context 而永久排队(show-queued能看到,show-running为空,日志没有任何报错)。解决办法是显式再定义一个contexts/default.conf。
4.3 Web 前端(只读 + SSE)
- 首页、Jobs、单个 Job、单次 Run、Wallboard(大屏)五个视图;
- 状态通过 SSE 推送,前端无需轮询;SSE 首帧就带版本、标题、最近构建、运行中、分组等全部信息;
- 日志可实时流式查看:
GET /log/$JOB/$RUN(latest表示最近一次); - 徽章:
GET /badge/$JOB.svg,可直接嵌入 README; - 无登录、无写操作——「Build Now」按钮是被故意去掉的。
4.4 参数化与任务链
- 参数化:
laminarc queue hello branch=main会把branch=main注入脚本环境; - 任务链:在脚本里直接调用
laminarc run <下游 job>,laminarc会自动带上$JOB/$RUN作为溯源信息; - 跨脚本传值:
laminarc set KEY=VALUE把变量写入同一个 run 的后续脚本; - 触发原因:
LAMINAR_REASON="..."会被存进数据库并在 UI 展示。
4.5 能力边界(选型时最该看的一节)
| 能力 | Laminar 现状 | 影响 |
|---|---|---|
| 用户/权限体系 | 无。Web 只读,RPC 靠 unix socket 权限或网络隔离 | 不能直接暴露公网写接口 |
| 手动触发 | 无 Web 按钮,只能 CLI/RPC | 强制走自动化触发,减少人为操作 |
| SCM 集成 | 无内置,靠 git hook 调 laminarc | 需要自己写 3 行 hook |
| 流水线 DSL | 无,就是 shell | 上手极快,但复杂编排要靠脚本自理 |
| 分布式执行 | 无 agent,单机 | 扩容靠加机器 + 外部调度,或换方案 |
| 高可用 | 单实例 + SQLite | 不适合作为公司级唯一 CI |
| 插件生态 | 无 | 能力边界=你的 shell 能力 |
| 制品存储 | 本地 archive/ 目录 | 长期归档要自己接对象存储或 NFS |
| 队列持久化 | 内存队列,重启即丢 | 重启前要确认没有排队任务 |
五、快速开始(最小可用示例)
以下步骤在一台干净的 Ubuntu 24.04 上从零到「跑通第一个 job」只需几分钟,全部命令与回显为实测。
5.1 安装与启动
wget https://github.com/ohwgiles/laminar/releases/download/1.4/laminar_1.4-1.upstream-ubuntu2404_amd64.deb
sudo apt install ./laminar_1.4-1.upstream-ubuntu2404_amd64.deb
sudo systemctl enable --now laminar
实测安装输出(节选):
The following NEW packages will be installed:
laminar libcapnp-1.0.1
Setting up libcapnp-1.0.1:amd64 (1.0.1-4) ...
Setting up laminar (1.4-1~upstream-ubuntu2404) ...
Creating laminar user with home in /var/lib/laminar
服务状态实测:
systemctl status laminar --no-pager
● laminar.service - Laminar continuous integration service
Loaded: loaded (/usr/lib/systemd/system/laminar.service; enabled; preset: enabled)
Active: active (running)
Main PID: 541 (laminard)
Tasks: 1 (limit: 2932)
Memory: 2.1M (peak: 2.6M)
CPU: 6ms
5.2 写第一个 job
sudo tee /var/lib/laminar/cfg/jobs/hello.run >/dev/null <<'EOF'
#!/bin/bash -ex
echo "Hello from Laminar job=$JOB run=$RUN context=$CONTEXT"
uname -a
echo "workspace=$WORKSPACE"
echo "archive=$ARCHIVE"
date -u +%FT%TZ > "$ARCHIVE/build-time.txt"
EOF
sudo chown laminar:laminar /var/lib/laminar/cfg/jobs/hello.run
sudo chmod +x /var/lib/laminar/cfg/jobs/hello.run
5.3 触发并观察
laminarc show-jobs
laminarc run hello
hello
hello:1
日志通过 HTTP 拉取(实测):
curl -s http://127.0.0.1:8080/log/hello/1
[laminar] Executing cfg/jobs/hello.run
+ echo 'Hello from Laminar job=hello run=1 context=default'
Hello from Laminar job=hello run=1 context=default
+ uname -a
Linux ci.example.com 7.0.0-38-generic #38-Ubuntu SMP PREEMPT_DYNAMIC Fri Sep 4 09:10:14 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux
+ echo workspace=/var/lib/laminar/run/hello/workspace
workspace=/var/lib/laminar/run/hello/workspace
+ echo archive=/var/lib/laminar/archive/hello/1
archive=/var/lib/laminar/archive/hello/1
产物与徽章实测:
curl -s http://127.0.0.1:8080/archive/hello/1/build-time.txt
curl -s http://127.0.0.1:8080/badge/hello.svg | grep -o 'success'
2026-10-27T14:39:09Z
success
到这里,一个具备产物归档、日志流、状态徽章的最小 CI 已经跑通;后续三篇分别展开安装细节、参数配置与命令行运维。
六、适用场景与限制
6.1 适合的场景
- 个人/小团队的 Linux 构建机:几台机器、十几个仓库,用 shell 描述构建步骤,不想维护 Jenkins 插件树;
- 嵌入式/交叉编译:需要真机、串口、特定工具链,脚本里直接
ssh/scp到目标板; - 定时任务与数据管道:把 cron 换成
laminarc+ 日志留存 + Web 看板; - 内网强隔离环境:没有外网、不能跑 SaaS Runner,发行包即可离线安装;
- 只读看板需求:Wallboard 大屏 + 只读 UI,天然适合放在公共区域。
6.2 不适合的场景
- 需要多租户、权限分级、审计的企业级 CI 平台(无用户体系);
- 需要 Kubernetes 原生调度、弹性扩缩容的流水线(无 agent、无 k8s 集成);
- 需要丰富流水线 DSL、可视化编排的团队(只有 shell);
- 需要构建缓存、制品仓库、依赖代理的一体化平台(需外部组件补齐)。
6.3 资源画像
实测空载常驻内存约 2.1 MB、CPU 累计 6 ms,1 核 1 GB 的服务器足以承载十余个 job 的日常调度;主要成本来自 job 自身的编译开销,而非 Laminar 本身。
七、同类选型对比
| 维度 | Laminar 1.4 | Jenkins | Gitea Actions | Woodpecker CI | Buildbot |
|---|---|---|---|---|---|
| 实现语言 | C++ / Cap'n Proto | Java | Go(act_runner) | Go | Python |
| 部署形态 | 单二进制 + systemd | WAR/容器 + 多插件 | 与 Gitea 同栈 + Runner | Server + Agent | Master + Worker |
| 流水线定义 | shell 脚本 | Groovy / Jenkinsfile | GitHub Actions YAML | YAML | Python 配置 |
| 内置 SCM 集成 | 无(靠 git hook) | 有(插件) | 有(原生) | 有 | 有 |
| 用户与权限 | 无 | 完整 | 继承 Gitea | 完整 | 有限 |
| 分布式执行 | 不支持 | 支持 | 支持 | 支持 | 支持 |
| 空载资源占用 | ~2 MB 内存 | 数百 MB 起(JVM) | 数十 MB | 数十 MB | 数十 MB |
| 学习曲线 | 极低(会写 shell 即可) | 高 | 中 | 中 | 中高 |
| 扩展方式 | 任意 CLI 工具 | 1800+ 插件 | Actions 生态 | 插件 | Python |
| 适合规模 | 单机 / 小团队 | 中大型 | Gitea 用户 | 中小团队 | 中小团队 |
关键差异解读:
- 与 Jenkins 的差异:Jenkins 是「平台」,Laminar 是「执行器 + 看板」。Jenkins 需要 JVM 与插件治理,Laminar 只需要一个 300 KB 的 deb;但 Jenkins 的凭据管理、权限、分布式、插件生态,Laminar 一概没有。
- 与 Gitea Actions 的差异:Gitea Actions 的优势是「仓库、Issue、PR、CI 一体」,触发链路天然打通;Laminar 与 SCM 完全解耦,你可以把它接到 Git、SVN、甚至一个 rsync 目录上,代价是触发要自己写。
- 与 Woodpecker/Buildbot 的差异:后两者提供 YAML/Python 的流水线抽象与 Agent;Laminar 把「抽象」全部交还给 shell,因此更简单也更「原始」。
💡 技术拓展:CI 流水线的描述方式主要有四种——脚本式(Laminar、纯 shell)、DSL 式(Jenkinsfile/Groovy)、YAML 声明式(GitHub Actions、GitLab CI、Woodpecker)、代码化配置(Buildbot 的 Python)。脚本式最灵活、零学习成本,但缺少结构化复用与校验;YAML 声明式易复用、易做可视化,但受平台语法限制;DSL 式能力最强、维护成本也最高。Laminar 选择脚本式,是把「流水线即运维脚本」这件事做到极致。
选型建议(如果…则…):
- 如果团队已经在用 Gitea/GitLab,且希望 PR 页面直接看到 CI 状态 → 选 Gitea Actions / GitLab CI;
- 如果构建环境是特殊硬件、需要大量 ssh/串口交互,且团队规模 < 20 人 → Laminar 很合适;
- 如果需要权限分级、审计、弹性扩容 → 不要选 Laminar;
- 如果只是想把「一堆 cron 脚本」变成「有历史记录和看板的定时任务」 → Laminar 是成本最低的答案。
八、版本核实依据与系列导航
8.1 版本核实依据(2026-10-27)
- GitHub Releases API
repos/ohwgiles/laminar/releases/latest返回tag_name: 1.4、published_at: 2026-08-16T22:45:58Z; releases.atom列出 1.4 / 1.3 / 1.2 / 1.1 / 1.0 五个正式版本;- shields.io 的 release 徽章返回
v1.4,stars 徽章返回358; - 官方站点
https://laminar.ohwg.net/主页与/docs.html用户手册可访问(手册中示例仍写 1.3,属文档滞后,不影响版本判定); - 发行包矩阵与 1.4 的 assets 列表一致(ubuntu2204/2404、debian13/14、rocky9/10、opensuse16)。
8.2 本系列篇目
| 篇 | 标题 | 内容定位 |
|---|---|---|
| A1 | 本文 | 定位、架构、能力边界、选型与最小验证 |
| B1 | 《Laminar 1.4 安装与首个流水线》 | 发行包安装与源码编译两条路径、目录结构、systemd 托管、跑通首个 job |
| C1 | 《Laminar 1.4 配置参数详解》 | 服务端变量、job 配置、contexts、全局钩子与实测 |
| D1 | 《Laminar 1.4 常用 CLI 命令与运维实践》 | laminarc 全命令族、systemd/cron/git hook 集成、日志与排障 |
系列共 4 篇。没有 Docker Compose 篇的原因已在 2.3 说明:官方未发布可直接运行的镜像,强行拼一个社区镜像会与「官方部署路径」不符。