7.1 容器运行机制:镜像、隔离、资源与生命周期
服务和实例不断增加后,靠人工复制目录、设置环境变量和启动进程已经无法保证结果一致。团队需要一种可重复交付进程及其依赖的方法,但“装进容器”是否等于得到一台轻量虚拟机?
本课讨论普通 Linux 容器:其中的进程受 namespaces、cgroups、capabilities 和文件系统视图约束,与承载它的 Linux 系统共享内核。Docker Desktop 中这个 Linux 宿主可能是一台虚拟机;容器与虚拟机可以分层使用。
镜像是不可变内容图
OCI 镜像由 manifest、配置和只读 layers 组成。layer 按内容摘要寻址,可以在镜像之间复用。
下面的运行镜像示例要求构建上下文中已有可执行 app.jar。它只演示打包,不包含该项目的编译步骤。
FROM eclipse-temurin:21-jre
WORKDIR /app
COPY --chown=10001:10001 app.jar /app/app.jar
USER 10001:10001
ENTRYPOINT ["java", "-jar", "/app/app.jar"]示例基础镜像使用 tag 便于阅读,生产构建应固定审核过的基础镜像 digest,发布时也固定最终镜像 digest。以下省略号只是格式占位符,不能直接拉取:
registry.example/atlas@sha256:...需要编译工具时,可用多阶段构建把工具链留在构建阶段;同时控制基础镜像内容、锁定依赖,并记录 SBOM 与签名。签名仍需校验可信签发者及策略,不能只检查“有签名”。删除密钥不会从旧 layer 中抹除它。排除构建上下文中的密钥;必须在构建时使用的凭据可通过 BuildKit secret mount 临时提供,避免写入 ARG、ENV 或输出文件。
Namespace 隔离视图
PID、mount、network、UTS、IPC、user 等 namespace 让进程看到不同的系统资源视图。隔离不是安全边界的全部:内核漏洞、过宽 capability、宿主挂载和特权容器仍可能突破边界。
默认移除不需要的 Linux capabilities,使用 seccomp/AppArmor/SELinux、只读根文件系统和非 root 用户。不能因为进程在容器里就信任任意代码。
Cgroups 计量和限制资源
- 编排层 CPU request 用于调度资源核算,并可映射到 cgroup 的竞争权重;CPU quota/limit 则限制一个周期内可用 CPU 时间,可能触发 throttling;
- cgroup v2 的
memory.max是硬上限,回收仍不能满足分配时可能 OOM kill;memory.high可通过回收和节制分配产生压力,但不能让超额内存像 CPU 时间一样轮流使用; - PID、IO 等也可以受控。
应用需要知道容器可用 CPU/内存,而不是宿主机总量。线程池、JVM heap 和缓存上限要与 cgroup 预算协调,在自身容器预算内留出 native memory、线程栈等空间。Sidecar 通常有独立容器预算,Pod 和节点层面还要计算所有容器的总需求。
Writable layer 是短暂状态
可写层跟随容器对象:Docker 的 stop/start 或 restart 通常保留它,删除并重建容器才会换成新层。Kubernetes 重建容器时也不能依赖旧层中的文件。业务持久数据应写入有明确生命周期的 volume 或外部存储,日志通常输出 stdout/stderr。
Pod 的 emptyDir 可跨容器重启保留,但随 Pod 删除而消失;持久卷的保留又取决于 PVC/PV 与回收策略。容器本地会话、上传文件或队列可能在替换、迁移时丢失,也不会因扩容自动共享。只读根文件系统的应用若需要临时文件,应单独提供容量受限的临时挂载。
PID 1 与优雅终止
容器的主进程需要处理终止信号;会派生子进程时,还需正确回收它们,必要时使用合适的 init 进程。上面的 exec-form ENTRYPOINT 让 Java 直接接收信号,避免多一层未正确转发信号的 shell。以 Kubernetes 正常终止为例:
- 记录 Pod 终止状态,异步更新端点,已有连接和缓存路由不会立刻消失;
- 若配置了
preStop,先执行它,耗时计入终止宽限期; - 向容器主进程发送停止信号,通常为
SIGTERM,具体信号可由运行配置决定; - 应用在剩余宽限期内排空并退出;
- 宽限期耗尽后仍未退出的进程会被强制终止。
应用应停止接新工作、排空在途请求、提交或放弃有明确语义的任务,然后退出。宽限期不是无限处理时间。
镜像启动成功不代表可运行
上线前验证:
- 以非 root 和只读文件系统运行;
- CPU throttling、内存接近上限和 OOM 后的行为;
SIGTERM、连接排空和重复消息;- 基础镜像漏洞、签名和来源;
- 临时目录、证书和时区等运行依赖;
- 宿主架构和内核能力兼容性。
下一课把容器放进 Kubernetes 的声明式控制循环。
参考资料
Docker, Storage drivers
Docker, Build secrets
Kubernetes, Pod termination
Docker, Container security
Linux Kernel, Control Group v2