KEEL · 龙骨 · A CURRICULUM FOR THE AI ERA

02 · 跑起第一个交付包 — keel 龙骨

上一章说的「自带的包」,这一章给它一个实物。装它只有一条命令,就在包的根目录下敲:

上一章说的「自带的包」,这一章给它一个实物。装它只有一条命令,就在包的根目录下敲:

./install.sh

前置:手边要有一只解开的交付包。它由课程配套项目打出来(怎么打的在第 3 章);
这一章你要做的是把它跑起来,以及看懂它一路打出来的每一行。
会一点命令行就够,不需要会写代码。

一、它会一路打出什么

命令跑完,屏幕上是这样一串(节选,逐段都对得上):

==> 前置检查
==>    docker 29.8.2 / 可用磁盘 58376932KB
==> 校验制品完整性
==>    全部制品校验通过
==> 导入镜像(不访问外网)
==>    docker load < board__1.0.0.tar
Loaded image: board:1.0.0
==>    docker load < postgres__16-alpine.tar
Loaded image: postgres:16-alpine
==> 生成现场配置
==>    已写入 .../.env(权限 600),数据库密码为现场随机生成
==> 启动
 Container board-db-1 Started
 Container board-db-1 Healthy
 Container board-migrate-1 Exited
 Container board-web-1 Started
 Container board-worker-1 Started
==> 等待 web 就绪
==>    web 已就绪
==> 自检
  制品校验                       通过
  db                                 running / healthy
  web                                running / healthy
  worker                             running / healthy
  GET /healthz                       HTTP 200
  GET /readyz                        HTTP 200
  结构版本                       2

这份输出把「成功的形状」写得非常清楚。看最后一屏自检就够做判断:
三个服务 running,两个探针 200,结构版本是一个数字 2。
(两个探针问的是两个问题:/healthz 回答「进程还活着吗」,/readyz 回答「它真能干活吗」;
结构版本是数据库结构当前在第几版,迁移一次就往前走一版。)

它像什么?像一个不联网的安装程序:带着自己要用的每一个零件上门,装完自己验一遍。
真实形态就是上面那几行——docker load 读的是包里的 board__1.0.0.tar,
没有一步去网上取东西。

二、这五步分别在做什么

顺序不能换,每一步失败时现场状态都不一样:

步 它在做什么 失败了说明什么
1 前置检查 确认 docker 在、daemon 活着、磁盘够 这台机器还装不了,一个容器都还没建
2 校验制品 用 MANIFEST.sha256 逐个比对哈希 包在搬运途中被改坏/搬家不完整,不要继续装
3 导入镜像 docker load 把两只镜像 tar 灌进本机 制品没进来,后面 compose 会去 pull(见下一章)
4 生成现场配置 照 env.example 生成 .env,密码现场随机生 密码只在现场产生,不来自我们、也不进任何制品
5 起栈并自检 起数据库 → 迁移 → web/worker,然后自检 半启动状态,得看自检哪条是 bad

第 3 步里有一行值得单独记住: Container board-migrate-1 Exited。
它不是错误——migrate 是一次性进程,跑完库结构就该退出,它的成功形态就是「退出码 0 且不再运行」。
把它当成故障,是现场最常见的一次误判。

三、两种一定会撞上的报错

① 少了配置就启动。 交付包解开了、.env 还没生成时直接 docker compose up:

error while interpolating services.db.environment.POSTGRES_DB: required variable BOARD_DB_NAME is missing a value
error while interpolating services.migrate.image: required variable BOARD_IMAGE is missing a value
...(每个用到的地方各报一条)

注意它的时机:在解析期就失败了,一个容器都没创建。 这是刻意的设计——
宁可在起任何东西之前死,也不要起了一半再死。

② 包被改过。 交付包里的文件动过一处,校验那一关当场拦下:

env.example: FAILED
sha256sum: WARNING: 1 computed checksum did NOT match
[失败] 制品校验失败:镜像包与 MANIFEST 不一致。**不要继续安装** —— 用原始介质重新拷贝一次。

中止发生在任何有副作用的动作之前:镜像没被导入、配置没被写、容器没被起。

四、它不是什么

读完你能做什么

下一步:包跑起来了,但它装的是我们的程序。下一章把它换成你自己的东西——
一条打包命令,加一个最容易看错的点。

进入 keel 阅读