容器 E01:gVisor、Kata 和 microVM 改变了哪层边界
主线08指出 Linux namespace 与 seccomp 不能组合成一句“完全安全”。当隔离目标是降低对宿主内核系统调用面的直接依赖时,gVisor 与 Kata Containers 等项目提供不同的执行路径,但它们并非“给普通 runc 加同一个开关”。要先画清应用、系统调用实现、宿主内核或 guest kernel、硬件虚拟化的边界,再讨论兼容与成本。 隔离和兼容都需对象级比较 gVisor 项目文档把它描述成在应用与宿主内核间提供用户态内核接口/拦截路径;Kata 采用轻量 VM 方向的隔离,应用与 guest kernel 交互,宿主看到 VM 资源。实际 shim 与 runtime 组合依版本实现核对,microVM 是否具备硬件虚拟化支持也是实验前提。两者对特定系统调用、文件系统路径、网络 I/O 和启动资源的处理不同,不应拿一条“启动更慢”的口号当证据。 正常案例在独立 VM 上固定同一 probe-app 镜像 digest,分别用 runc、选定版本 gVisor 和 Kata 运行相同 /ready、/state 请求,记录系统调用兼容与资源边界;...
容器 39:CPU weight 在竞争下是否才起作用
独立研究要回答一个可以被实际结果推翻的问题,而不是重新打包35 的性能清单。选用 Linux kernel cgroup v2 官方技术文档中 cpu.weight 的语义作原始来源,提出命题:同一父 cgroup 的两个有界计算负载同时争用相同 CPU 集时,改变其相对 weight 会影响所得 CPU 时间份额;若只有一个任务运行且 CPU 足够空闲,改变 weight 不应当被写成“硬配额”。 将文档语义变成可证伪实验 实验前固定专用 Linux VM、内核/cgroup v2 版本、物理或虚拟 CPU 数、cpuset、频率与可用邻居负载。CPU weight 对照程序中的两个工作进程执行相同的整数运算负载,绑定同一 CPU,并按公共单调时钟窗口起跑;每轮最多两秒,不能用短窗口的单次比例代替稳定结论。各组预设至少十次,交叉调换两个子 cgroup 的 weight 与启动顺序,保存 cpu.stat 前后值、每次工作量、总耗时和退出状态;以相同父组和相同配额条件比较。另运行“只有一个子组工作”的反例组,排除把权重理解成任何时候都有的硬限速。若 CPU 饱和条件不成立,即使...
容器 38:重启了,业务真的恢复了吗
37建立了正常请求链路;故障实验只问这一条链断在何处、多久恢复、有没有损坏或丢失状态。容器重启、Pod 重建和节点恢复不是同一个操作;Pod 出现 Ready 也不等于所有请求成功、数据校验通过。 先界定故障注入范围 仅在专用 kind 集群和自建 namespace 中注入四类故障:单容器内存上限的有界申请、明确终止自建容器进程、只阻断实验 Pod 之间的网络、停掉专门提供的测试节点。每次保留注入前的基线:镜像 digest、Pod UID、容器 ID、PID、网络路径、state 文本哈希与固定负载;注入后记录每次请求开始/结束、HTTP 状态/超时、相应事件及新的对象 ID。先定义恢复为“连续指定次数业务请求成功,且状态校验一致”,再测从第一条失败请求到恢复的时间。节点测试需有宿主控制台/VM 恢复路径,不能停止共享宿主上的 kubelet、daemon 或默认网络。 正常恢复例是一个受控容器被终止后由节点重新启动,新 PID 取得原有卷并在约定窗口内重新响应;边界例是 readiness 重新变绿,但自有测试卷上的状态未满足应用数据条件,因而不能称为完整恢复。内存事件还要...
容器 37:从源码到一次真实服务响应
本系列的每个对象都有不同寿命:源码树变化不等于已部署镜像改变;Pod 重建不等于数据卷重建;HTTP 请求成功也不能证明读写结果符合预期。综合实验必须让一条请求沿同一个 digest、容器 ID、Pod UID、宿主 PID、namespace/cgroup、网络路径和卷挂载回溯到源码,而不是把不同实验的截图接成故事。 只用一份 probe-app 从干净的实验目录取独立源码包并校验 SHA,固定基础镜像 digest,按13构建真实镜像,按12分发到受控 registry,再按26将 digest 部署到自建 kind 节点。通过 Service 访问 /identity 取得进程信息,通过 /state 写入一段最多 4096 字节的合成文本;从 Pod UID 到容器 ID、宿主 PID,查 namespace inode、cgroup、网络地址/路由及卷的 mountinfo,最后在同一卷上重建 Pod 后复读内容。每一步记录输入、时间、原始响应与校验值,不能从“yaml 配好了”跳到“持久化成功”。 正常案例需要请求 JSON 中的 PID 和 UID 与节点对象一一对...
容器 36:同一 digest 怎样交付并安全回退
33 的制品身份和28 的就绪状态属于交付的两个不同条件。单机 Compose 和 Kubernetes 可以消费同一镜像 digest,但不会因此拥有相同的网络、卷、策略或回退语义。应以实际 digest 为连接点,再记录各自的声明配置与请求结果。 回退最容易被误写成一句命令:Compose 将服务重新指向旧镜像,Kubernetes 对 Deployment 执行 rollout undo,于是“服务已经恢复”。但真正的前后状态至少有制品 digest、命令/探针/环境变量、流量入口、卷数据和应用协议五部分。Kubernetes 可能报告上一次 ReplicaSet 已恢复,而 PVC 里仍是坏版本改写过的数据;Compose 也可能健康检查变绿,却让客户端继续访问另一端口。本篇让同一份 probe-app 镜像同时进入两种声明体系,制造配置故障并观察回退,不假装有第二份已构建/签名的坏镜像,也不假装合成文本具备数据库迁移语义。 固定制品,不固定假设 附件中的 `compose.yaml` 只检查 `PROBE_IMAGE` 非空;复跑前还需核验它是实际可拉取的 `repo@...
容器 35:性能差异来自隔离层还是实验条件
“容器性能损耗多少”缺少测量对象和实验条件。应用计算、内存申请、文件系统 copy-up、网络封装都在不同路径上。34只评估启动,不把 steady-state 性能一起平均。本篇先设定可以证伪的对照,再决定哪些数字能归因于容器边界。 把某个程序在宿主上执行十次、在容器里执行十次,得到两列数字后相除,并不能得出“容器比宿主慢 X%”:运行方式可能顺带改变了 CPU 配额、可用核心、进程 UID、文件路径、网络路由与镜像依赖。先用一种不碰磁盘、也不调用网络的短时计算任务建立最小对照;再单独讨论内存、OverlayFS 与网络。每次只改变一个前提,才能知道数字是哪个路径造成的。本篇没有专用 Docker/双 VM,数字一律留空,不用外部基准报告代替自己的实验。 逐项只改变一个条件 同一内核、架构、节点、二进制和输入,先在普通进程跑 bounded_load 的有界计算/内存路径,再在专用容器内跑同样命令;分别记录用户态时间、CPU/内存控制器、可运行 CPU 集与真实的 cgroup 限制。文件系统实验区分宿主相同盘的普通写入、容器可写层首次写入与同一镜像的重复写入;网络实验区分 l...
服务网格 02:Service 地址怎样到达 Pod
orders 这个名字解析成功后,请求到了哪里 前两篇已经建立了本地进程链:loadgen → frontend → orders → inventory-v1 / inventory-v2,并让 orders 调用 external-stub。第 00 篇中的 orders.invalid 在解析阶段失败;本篇换一个问题:即使 orders 的 Service 名称能够解析,哪些条件决定某次 TCP 连接落到哪个 Pod?读者需能区分 DNS 查询、TCP 连接、HTTP 请求和 Pod readiness。 图左侧是调用者看到的稳定入口,右侧是会变化的就绪后端集合。两侧不能画等号:对普通 ClusterIP Service,DNS 回答的是入口地址而不是“此次请求的 Pod IP”;实际端点的选择依赖集群的数据面实现及当时的端点状态。Istiod 和 Envoy 都不在这里的无网格数据路径中。 从 DNS 到后端的四个环节 以 frontend 在同一 namespace 访问 http://orders:8080/quote 为例,以下是应该逐层检查的对象,而非在没有集群...
容器 34:冷启动时间花在哪一段
拿一条从 docker run 到终端提示的总耗时,无法推断是拉取慢、快照准备慢、进程启动慢还是应用就绪慢。先定义测量的边界与可关联的对象,然后谈数值。 同一只 Pod 在两台节点的“启动耗时”可能相差很大:一台已有内容存储和快照,另一台要从 registry 取层;应用的探针又可能固定等待十秒。只看 kubectl wait 结束时刻,连缓存条件都无法还原,更谈不上给 OCI create 分配多少毫秒。本篇把一次启动拆成控制面可见事件、节点运行时原始事件、应用首次就绪三条时间轴;先标记哪些区间目前还不可观测,再决定能不能比较冷、热启动。 先写实验设计,后写结论 同一 probe-app 固定 image digest、节点 CPU 架构、内核与运行时版本,在专用 VM 上分冷缓存与热缓存两组。时间戳分别落在开始拉取、manifest/layer 验证结束、本机解包与 snapshot 准备结束、OCI create/start、进程首条日志、/ready=200。对同一 ID 记录事件时钟来源,跨组件不能把不同机器的墙钟未经校准就做相减。预先确定预热次数、重复次数、并发、输入...
容器 33:签名通过了,运行权限还要验证什么
通过10 的 digest可以检验拿到的 blob 是否和 manifest 里写的一样;但“内容没有变化”并未回答谁构建的、输入从何而来、程序是否有漏洞、运行时拥有何种权限。签名与构建来源同样不能替代08 的运行约束。 一次发布往往把四类判断缩成“镜像可信”:客户端按 digest 拉取,注册表带着一份签名、SBOM 和构建声明,部署模板又宣称自己以非 root 运行。真正有用的是分层问“哪个验证器接受了哪个断言,签名主体绑定哪一个 digest,运行容器的权限由谁执行”。有签名不代表签署者被允许交付生产,有来源也不表示二进制没有漏洞;权限正确更不能反推构建过程可信。本篇用同一 probe-app 制品和两份临时测试公钥构造明确拒绝路径,而不上传任何私钥。 四个问题独立判定 digest 用于核对字节;签名需要验证身份、信任策略和被签对象的 digest;SBOM 描述制品包含的软件成分,只有核对生成器、覆盖范围与版本才可用于下一步扫描;SLSA Provenance v1.0 为构建过程的输入、构建器和产物提供可核查的谓词语义,不能凭存在 attestation 就推断构建平...
容器 32:从错误症状回到失败层
启动失败的提示往往出现在最高一层:CLI、Pod condition 或 HTTP 超时。修复需要逆向定位首个出错环节。沿10 的 digest、17 的 bundle、24 的网络对象与28 的就绪状态逐层判断,先记录假设,再以明确证据排除。 “启动不了”本身不是故障层:Pod Pending 可能是调度等待、取不到镜像,也可能卡在挂载或 CNI;容器 Running 但 Service 超时则已经跨过了前面几个阶段。没有同一个 Pod UID、事件时间和实际 containerID,将某一层的日志套到另一层只会反复修错。每个反例先保存错误出现前后对象,确定第一个不可达阶段;修复时只改动造成这一次失败的输入,然后让该 Pod 名称重新创建并以新 UID 保存结果。 四种会混成一个“起不来”的情况 第一,镜像无法解析或 layer blob 缺失:还没有完整根文件,查看拉取响应与 digest。第二,平台不匹配:索引没该平台或启动可执行文件时产生架构错误,核对节点架构与目标文件格式。第三,运行时配置或挂载权限被拒:先找 OCI bundle/config、宿主权限与 errno,...










