KEEL · 龙骨 · A CURRICULUM FOR THE AI ERA
02 · 容器 — keel 龙骨
类型:主题概念 · 应用课对应:01 章 §一、06 章 · 信号与优雅停机
类型:主题概念 · 应用课对应:01 章 §一、06 章 · 信号与优雅停机
这一页只回答四件事:它为什么会出现、它是什么、它长什么样、它怎么工作。
一、它为什么会出现
一台机器上要跑十个服务,每个服务自带自己的依赖,而十个服务用的库版本还互相冲突。把它们直接装在同一台机器上,迟早会打起来。
它面对的需求是两件事同时成立:互相隔离,而且不要虚拟机的代价。
虚拟机确实能隔离,但每一台都自带一个内核、都要走一遍引导流程——启动慢、内存占用高;而你现在想跑的是一个进程,不是想模拟一台机器。
二、它是什么
一句话:容器 = 一份只读的镜像 + 一个可写层 + 一个进程。
这个定义里有三处经常被读错:
- 它是一个进程,不是一台"小机器"。 它和虚拟机的根本区别在内核:虚拟机每台自带一个内核,容器共享宿主内核。这一条推出来的结论很硬——容器里不能跑"需要改内核"的东西(自编译驱动、依赖特定内核版本的模块、
sysctl调优)。这不是配置没写对,是模型限制。 - 它是"隔离",不是"模拟"。 进程看到的"独立根目录"和"独占了整台机器",是宿主的文件系统与资源被切出来的一块视图。
- 它不是镜像。 镜像是只读的模板,容器是它跑起来的那一刻。
三、它长什么样
docker run -d --name api -p 3000:3000 myapp:1.7.2
这一行的读法是"在只读镜像之上,加一个可写层,起一个进程":
容器 = [ 镜像:只读的层堆叠 ]
[ 可写层:这次运行产生的一切改动都写在这里 ]
[ 进程:config 里 CMD 指定的那条命令 ]
+ 边界:文件系统 / 进程视图 / 资源视图,都是宿主的切片
两个日常现象,从这张结构直接读出来:
- 容器里
rm掉一个文件,镜像体积一点没变——你删的只是可写层里的一个记录,下面只读层里的文件还在。 - 容器删掉,里面的改动就没了——可写层不持久。这就是"容器无状态"的真实含义:不是"不存数据",而是"可写层不持久"。
四、它怎么工作
从 docker run 到进程跑起来,是四步:
① 解压 把镜像的层按顺序叠加,拼成一个只读的根文件系统
② 加层 在最上面加一个空的可写层,之后所有写入都落在它上面
③ 起进程 按 config 拉起 CMD 指定的进程,并把它放进隔离边界里
④ 收尾 收到停止信号 → 等一段时间 → 没退出就强杀(应用课 06 章整章讲这段时序)
第 ③ 步里"隔离边界"具体由哪些机制实现,本页不展开——那属于内核文档的范围。这一层只需要一个判断:某件事能不能在容器里做,先问一句"它需要动内核吗"。
五、它会带出哪些概念
- 镜像:容器的只读部分。→ 00 页
- 层:可写层是层的特殊形态——它不参与共享、不可复用,随容器一起消失。→ 01 页
- 信号与停机:第 ④ 步的三段时序,以及"你的进程有没有收到那个信号",取决于
CMD写的是哪种形式。→ 应用课 06 章
六、回到应用课
| 应用课里的那句话 | 现在你知道它为什么成立 |
|---|---|
| "容器不是小虚拟机" | 差别只在一条上:内核是共享的。隔离单位是进程,不是硬件 |
| "容器无状态" | 可写层不持久;要留下东西就必须落到卷或外部存储上 |
| "容器停止时,正在处理的请求会不会被切断" | 信号发给容器里的 1 号进程;它有没有把信号转给真正干活的进程,看 CMD 是哪种写法 |
这一页改变了你的哪个判断:听到"要在容器里做某件事"时,你先分流——是要动内核(做不了或要特权),还是只是文件与进程层面的事(正常做)。这一问能挡掉大部分返工。
自测题
- 容器里
rm -rf /app/node_modules之后,镜像体积变了吗?为什么? - "容器里不能装自己编译的驱动"——这是配置没写对,还是模型限制?