容器 21:停止、重启和日志分别由谁负责
03解释了 PID 1 的信号与回收,18区分 task 与 content。现在讨论停止与恢复:`stop` 是请求进程退出的流程,`restart` 是另一次启动动作,日志是 FD 输出经采集路径形成的证据;三个动作不自动互相保证。 时间窗口需要观测 通常运行时先发终止信号,留宽限期等待程序自己处理、回收子进程并结束;超时后可强制终止。应用若安装 handler 却不结束在途请求,或 shell 没有转发信号,即使运行时遵守流程也可能直到超时。比较宽限期要保存发送时刻、程序收到的事件、最后一条业务响应、实际退出原因,单看 shell 的 137 不能推出是超时强制杀还是内存 OOM。 日志链也有不同持有者:程序写 stdout/stderr FD、shim/daemon 依配置收集、日志驱动再存储或转发。程序未 flush 缓冲、进程强制终止或采集器重启都可能造成末尾日志缺口;不能以运行时提示“容器 restarted”证明请求、日志和写盘数据已经恢复。daemon 的重启行为依运行时、shim、配置及版本而异,必须在专用实验 daemon 里验证,禁止重启共享宿主服务。 正...
容器 20:rootless 和容器内非 root 有什么不同
在05,内侧 root 被映射为外侧 UID 1001,说明“程序显示 UID 0”不足以推出宿主特权。另一种常被混写的配置是 Dockerfile USER 65532:它指定应用在容器内的身份,并没有规定 daemon 本身用哪个宿主 UID 工作。rootless 模式则涉及 daemon 和容器进程的宿主权限前提,资源与网络能力也随委派和实现改变。 三个身份分别记账 先记录 daemon 宿主身份、容器外侧 UID 映射与应用内侧 UID,再问低端口绑定、设备访问、overlay/snapshotter、cgroup v2 控制器和网络转发在所选 rootless 实现中如何实现。不应把“daemon 非 root”和“应用 USER 非 root”合并成一项。rootless 不会凭空委派宿主 cgroup 子树,也不是应用任意设备访问的许可证。不同运行时和 Linux 版本对辅助工具、存储与网络的要求不同;适用范围必须限定到实测版本。 正常实验在专用 VM 用同一探针分别以 rootful daemon + USER 65532 与 rootless daemon +...
容器 19:一次 run 在哪一步变成 exec
“docker run 成功了”最多说明某条客户端请求获得了响应,不保证 probe-app 能够接收真实业务请求。先把13 的镜像制品、18 的 content/task和17 的 create/start沿时间摆成一条链,再讨论不同故障落在哪一层。 一次请求的可能路径 用户端请求 daemon 创建或启动容器;daemon 对镜像引用做解析或拉取,准备快照与运行配置;下游运行时创建对应 Linux 进程并执行入口。是否通过 containerd、shim、哪一种 OCI runtime 由本机版本与配置决定,不能给出未经版本验证的唯一函数调用栈。进程 exec 成功之后,应用还可能解析参数失败、只监听 loopback 或尚未就绪。把“客户端返回”、“daemon 对象创建”、“宿主进程执行”、“HTTP /ready=200”各自作为检查点,才能说清哪个阶段失败。 正常案例要求同一次构建的 image digest 贯穿 docker inspect、runtime ID、宿主 PID、namespace inode 和请求日志,按时间排序保存不同组件的...
容器 18:containerd 管的是镜像、任务还是进程
17 的 OCI runtime关注单个 bundle 的 create/start/delete;更上一层还要保存镜像内容、展开快照、管理任务生命周期。containerd 的 content store、snapshotter、container metadata、task 和 shim 并不是五个同义词。它们各自关联不同对象,故一个动作“删镜像”与“停 task”不应被写成相同效果。 内容引用与进程引用分开 按 containerd 的 Runtime V2 文档,运行时 shim 承担与任务相关的运行时集成,OCI runtime 可用于创建实际进程。content store 保存内容寻址的字节;snapshotter 为运行准备文件系统视图;task 对应运行中的进程生命周期。containerd 的 namespace 是其 API/元数据隔离维度,不能拿它代替 Linux namespace inode,更不能把它当 Kubernetes Namespace 的定义。一个镜像引用可以消失而正在运行的进程仍存在;停止任务也不会自动清空 registry 的原始镜像。...
容器 17:OCI runtime 的 create 与 start 分别做什么
10 的镜像布局适于存储、传输与校验;OCI runtime 读取的入口却是 bundle,核心是 `config.json` 和 rootfs。把 manifest 文件直接交给 runc 并不能从镜像层自动得到已经展开的 rootfs。前篇09构造了局部的进程启动链路,这篇以规范而非教学实现重画边界。 生命周期为何拆成两段 OCI Runtime Specification v1.3.0 定义了 create、start、state、kill、delete 的状态与条件。create 必须按照 config.json 构造执行环境,但此时不执行用户指定程序;state 的 created 带有 Linux PID,却不等于应用开始响应。start 才使用户程序开始执行;程序退出后进入 stopped,满足删除前提后 delete 撤销运行时创建的资源。runtime 对象 ID、容器进程 PID、bundle 的绝对路径分别是三种字段;别用 ID 当 PID。即使 running,也仍需实际请求 /ready 才能讨论服务可用性。 bundle 与镜像不是两种叫法 OCI i...
容器 16:数据写到了哪一层,重建后还在吗
11的 upperdir 描述可写视图,但应用不一定向它写数据。如果 `/data` 是 bind mount、volume 或 tmpfs,同一路径的写入会落在不同的生命周期范围。只看到容器内文件并不能回答“删掉容器后数据是否还在”。 位置决定删除后的命运 容器可写层一般属于该容器对象;删掉并重新创建相同镜像的新容器,不应期待该层的修改自动出现在新容器中。bind mount 让宿主指定目录可见,数据的所有者和备份边界仍属于该外部目录;volume 有独立于单个容器的生命周期,删除容器不等于删除 volume。tmpfs 不把写入落到同一磁盘持久层,容器重建后内容不能作为持久状态。具体默认快照实现、匿名 volume 清理规则和挂载传播需以所选运行时与执行命令核对,不对所有系统给绝对保证。 同一个容器内路径背后可能是四种对象 应用对 /tmp/containers-state/state.txt 打开文件时,路径首先按该进程的根与 mount namespace 解析。如果没有更具体的挂载覆盖,写入可能落在运行时为该容器准备的可写快照;若运行参数将宿主自有目录 bind 到相同...
容器 15:同一镜像名称怎样指向多种 CPU 制品
同一 tag 不能让 x86 指令在 ARM CPU 上原生执行。10 的 index能够把 platform 描述符指向不同的 manifest:选择具体平台时,客户端取得对应的 config/layer 并执行该平台的程序。平台选择成功只是内容匹配;不保证内核 ABI、库或应用本身都可运行。 把构建和运行拆开 在本机生成两套平台元数据并不证明拥有两套机器码。原生构建、交叉编译与仿真构建分别对构建时使用的 CPU/用户空间提出不同条件;通过 QEMU user-mode 仿真运行的性能不能当作原生 ARM 性能。对解释型应用也不能因为 probe_app.py 是同一份文本就断言镜像完全相同:最终基础镜像中的 Python 二进制、库和平台标记仍可能不同。index manifest、每平台 manifest digest 和实际内核及 CPU 架构应一起保存。 一个名字会解析到哪个对象 OCI image-spec v1.1.1 的 image index 是高于单平台 manifest 的对象,其 manifests 数组包含描述符;每条 descriptor 可以带 pl...
容器 14:构建缓存为什么不能等同于可复现性
13用同一探针说明构建图;下一步要区分三件事:某步骤缓存命中、最终产物字节是否相同、构建中使用的凭据有没有进入制品。这三种结论不能互相代替。 输入改变与缓存边界 在 BuildKit 中,一个步骤的缓存依赖指令、输入内容及相关构建状态。更改被 COPY 的源码通常会影响后续依赖节点;修改上下文中一个被 .dockerignore 排除的文件不应改变对应 COPY 的输入。命中缓存只是重用已有结果,不证明两台构建机独立构建会得到字节相同的 tar、时间戳或上游包;要谈可复现还要固定基础镜像 digest、依赖、平台、环境与产物字节。 同一行 Dockerfile 也可能有多种输入 缓存检查先确定“这个步骤依赖了什么”:COPY probe_app.py 会消费构建 context 中经过忽略规则筛选后的脚本字节,后续以它为输入的步骤才能从新内容出发。更改仅存在于另一个目录、没有被 Dockerfile 步骤读取的文件,即使它就在本机工作区,也不能由“文件修改时间变了”推出对应缓存必须失效。反过来,基础 FROM 指向可变 tag 而解析到新 digest,即使应用源码没有变化,后续...
容器 13:Dockerfile 怎样变成可运行制品
12解决把已存在的镜像字节送到别处;构建器还得决定“哪些文件进入镜像,哪些步骤依赖哪些输入”。Dockerfile 是构建输入的一部分,不是 OCI runtime 的 `config.json`,更不是运行后宿主 PID 的清单。 构建上下文与阶段边界 COPY 只能按构建上下文和忽略规则读取输入;在仓库根构建时不检查 .dockerignore,可能无意把实验日志或其他任务的文件送给构建器。这里的独立源码包提供 probe_app.py、.dockerignore 及三种 Dockerfile:单阶段 Dockerfile.single 将脚本复制到 Python 基础镜像;Dockerfile.multi 在 check 阶段运行 py_compile,最终阶段只复制脚本。两者仍依赖相同 Python runtime;多阶段在这个例子里不应被宣称必然更小或更快。BuildKit 对步骤形成依赖图,跳过与目标无关的步骤要结合构建输出与目标阶段判断。 构建上下文不是当前 shell 目录的同义词 执行 docker build -f examples/containers/Do...
容器 12:镜像拉取是下载还是解包
本地已知镜像的index、manifest 与 blob,接下来问另一台机器如何得到相同字节。Distribution Specification 约定 registry API;它不等于 Linux 的 mount、容器进程的 exec,也不保证客户端已把层成功解压成可运行的 rootfs。 引用解析和内容获取分开 客户端把名字/tag 交给 registry,商议清单媒体类型,读取 manifest 后根据 descriptor 的 digest 与 size 请求 config 和 layer blob。digest 绑定字节,tag 可重新指向另一 manifest;“我拉到了 tag”不能证明和上一次拉到的内容相同。Blob 下载成功仍需校验摘要、解压层、建立本机快照才能供运行时使用。认证失败应定位到 HTTP 挑战、凭据与仓库权限,缺失 blob 则应定位对应 digest 和 HTTP 返回,而非笼统说“网络不通”。不要将镜像 registry 的身份验证和镜像签名混为一谈。 引用如何落到确定的 manifest OCI Distribution Specifica...











