Laminar 1.4 完整部署与实战指南

Laminar 1.4 轻量级自托管 CI 的完整认知篇:单进程守护架构、job 脚本约定与目录布局、contexts 并发模型、能力边界、与 Jenkins / Gitea Actions 的选型对比,附 Ubuntu 24.04 实测最小验证路径。

LaminarLaminar持续集成CIC++自动化运维2026/10/27

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.42026-08-16当前最新正式版;手册与打包脚本改进、日志颜色/超链接修复、日志自动滚动、FreeBSD 下 Clang 构建支持
1.32023-05-30上一稳定版
1.22022-01-28
1.12021-08-04
1.02020-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 由 laminard fork 出一个 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 为例,真实链路是:

  1. laminarc 通过 RPC 发送 run 请求(默认抽象 unix socket unix-abstract:laminar);
  2. laminard 为 job 分配递增的构建号,写入 SQLite 的 builds 表,进入内存队列;
  3. 调度器按 context(并发上下文)寻找空闲执行槽位;没有空闲槽位就一直排队;
  4. 拿到槽位后 fork leader,leader 在工作目录里依次执行 hooks.init(仅当 workspace 首次创建)、cfg/before、$JOB.before、$JOB.run、$JOB.after、cfg/after;
  5. 脚本的 stdout/stderr 被管道捕获,边写 SQLite 边通过 SSE 推给浏览器;
  6. 结束后写 completedAt、result(success / failed / aborted),更新 archive/$JOB/latest 软链;
  7. 按 LAMINAR_KEEP_RUNDIRS 清理旧的 run 目录,并再次尝试调度队列中的其他 run。

四、功能特性

4.1 Job 脚本约定(核心能力)

一个 job 至少是一个可执行的 $JOB.run。围绕它,Laminar 支持一整套「约定优于配置」的扩展文件:

文件作用实测要点
$JOB.run主脚本,必须有执行位退出码非 0 → 构建失败
$JOB.initworkspace 首次创建时执行一次适合 git clone 初始化
$JOB.before / $JOB.after主脚本前后执行after 里可用 $RESULT 判断结果
$JOB.env本 job 的环境变量不做 shell 展开,FOO="bar" 会带引号
$JOB.confTIMEOUT=、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 文件,隐式的 default context 就不再自动创建。此时未声明 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.4JenkinsGitea ActionsWoodpecker CIBuildbot
实现语言C++ / Cap'n ProtoJavaGo(act_runner)GoPython
部署形态单二进制 + systemdWAR/容器 + 多插件与 Gitea 同栈 + RunnerServer + AgentMaster + Worker
流水线定义shell 脚本Groovy / JenkinsfileGitHub Actions YAMLYAMLPython 配置
内置 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)

  1. GitHub Releases API repos/ohwgiles/laminar/releases/latest 返回 tag_name: 1.4、published_at: 2026-08-16T22:45:58Z;
  2. releases.atom 列出 1.4 / 1.3 / 1.2 / 1.1 / 1.0 五个正式版本;
  3. shields.io 的 release 徽章返回 v1.4,stars 徽章返回 358;
  4. 官方站点 https://laminar.ohwg.net/ 主页与 /docs.html 用户手册可访问(手册中示例仍写 1.3,属文档滞后,不影响版本判定);
  5. 发行包矩阵与 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 说明:官方未发布可直接运行的镜像,强行拼一个社区镜像会与「官方部署路径」不符。

九、参考链接