KEEL · 龙骨 · A CURRICULUM FOR THE AI ERA

03 · 把自己的东西装进去 — keel 龙骨

前两章装的是我们的程序。这一章把它换成你自己的: 先看清「你的东西」在交付包里占哪一块,再知道怎么把它重新打进去。

前两章装的是我们的程序。这一章把它换成你自己的:
先看清「你的东西」在交付包里占哪一块,再知道怎么把它重新打进去。

一、包里的重量,几乎不在你的代码上

一个应用镜像拆开看是这样的:

那一层 大小 是什么
基础镜像 python:3.12-slim 179MB Python 运行时本身,你改不动它
依赖层(venv) 28.2MB requirements.txt 装出来的东西(venv 是一个独立的依赖目录,跟系统 Python 隔开)
应用代码层 61.4kB 你自己的程序
合计(board:1.0.0) 215MB 上面三层叠出来的镜像(层就是一层层叠上去的目录快照)

一行话记住:你的代码 61.4kB,它的运行环境 215MB——交付的重量不在你的代码上。
所以「换掉自己的东西」这件事,你要动的是两个文件位置,而不是层层重写:

app/board/            你的程序住的地方(谁在包里的名字就叫这个目录)
app/requirements.txt  你的依赖(没有第三方依赖就留空)
Dockerfile            决定上面那几层怎么叠(一般不用改)

二、把它重新打成一个交付包

改完你的程序,在有外网的那一侧跑这条:

./deploy/build-bundle.sh 1.0.0

预期输出(节选,五段一路向下):

==> 构建 board:1.0.0
==> 确保数据库镜像在本地:postgres:16-alpine
==> 组装交付包
==> 计算制品哈希
==> 打包
==> 完成:.../board-delivery-1.0.0.tar.gz(160M)

包内容:
  MANIFEST.sha256
  compose.yml
  env.example
  images/board__1.0.0.tar
  images/postgres__16-alpine.tar
  install.sh
  ...

看两件事就够:最后那一行给出了包的大小,而包内容里两只镜像 tar 都在。
最常漏的就是第二只——postgres__16-alpine.tar。应用镜像打得再干净,
客户机器上没有 postgres,compose 一样会去 pull,然后在内网里挂到超时。

⚠️ 这只包必须先导进去,现场才可能起得来。 镜像没导进去时,
compose 的默认行为是去仓库取,于是你会得到一段几十秒的等待加一句看起来像网络问题的报错;
加上 --pull never 之后,同一个场景变成立刻报错:

Error response from daemon: No such image: postgres:16-alpine      (退出码 1)

一句即时的 No such image,好过一句迟到的 failed to resolve reference。

三、最容易看错的那个点:版本号有两个来源

build-bundle.sh 会先做一件事——把代码里的版本号和命令行给的版本号对齐,不一致就当场停下:

[失败] 版本号不一致:代码里是 1.0.0,命令行给的是 1.1.0。
         先改 app/board/__init__.py 的 __version__,再打包 —— 否则制品与自述对不上。

这条检查存在的理由,是私有化交付里最容易犯的一类错:参数是给人看的,代码里的那个才是给机器看的。
你改了代码、忘了改代码里的版本号,打出来的包会顶着旧版本号流转,
到了现场谁也说不清「这台机器上到底是哪一版」。

顺带一个改代码时的手感问题:Dockerfile 把 requirements.txt 单独 COPY 一次、再 COPY 代码,
于是第二次构建(没动依赖)会看到

#6 CACHED ... #12 CACHED
#13 DONE 0.1s

改代码不会触发重装依赖,因为依赖那一层的输入没变。这不是巧合,是刻意把两层分开的收益。

四、它不是什么

读完你能做什么

下一步:你已经会打包、会落地了。最后一章换个方向——
先说清什么情况下别做私有化,再告诉你往下该去哪。

进入 keel 阅读