cloud-sre
Apple container、Docker 和 Kubernetes 里的 container 到底差在哪
把 Apple container、Docker 和 Kubernetes 放在同一张图里看:谁负责镜像,谁负责启动进程,谁负责 Pod 和集群编排。
同样叫 container,Apple 的 container、Docker、Kubernetes 说的其实不是同一层东西。
最容易混淆的是这句话:Kubernetes 里也有 container,但 Kubernetes 并不亲自实现 Linux container。它把 Pod 调度到某台 node 后,由那台 node 上的 kubelet 调 CRI runtime,再由 containerd、CRI-O、runc 这类组件把进程真正跑起来。
Apple 的 container 又是另一条路线。它兼容 OCI 镜像和运行习惯,但在 macOS 上不是复刻 Linux namespace,而是把每个 Linux container 放进一个轻量虚拟机里跑。
文中用语说明
| 用语 | 含义 |
|---|---|
| OCI image | Open Container Initiative 定义的镜像格式。Docker、containerd、Kubernetes runtime 和 Apple container 都围绕它工作。 |
| Runtime | 负责把镜像变成运行中进程的组件。不同语境里可能指 Docker daemon、containerd、CRI-O、runc,或者 Apple container 的 runtime helper。 |
| Namespace / cgroup | Linux container 的核心隔离能力。namespace 隔离进程、网络、挂载点等视图;cgroup 限制 CPU、内存等资源。 |
| VM-backed container | 容器进程跑在 Linux VM 里。对 macOS 这种非 Linux host 来说,这是运行 Linux container 的常见边界。 |
| Pod sandbox | Kubernetes/CRI 里的 Pod 基础运行环境。它先建立 Pod 的网络等共享边界,再让业务 container 加进去。 |
| CRI | Kubernetes 的 Container Runtime Interface。kubelet 通过它调用 container runtime。 |
| CNI | Container Network Interface。Kubernetes 用它给 Pod 配网络。 |
先给结论
如果只看“谁启动了 container 里的进程”,三者可以这样拆:
| 系统 | 它处在哪一层 | 谁最后启动进程 | 隔离边界 |
|---|---|---|---|
Apple container | macOS 本机 OCI container 工具和运行时 | host 上的 runtime helper 启动轻量 Linux VM,VM 里的 vminitd 再启动 OCI 进程 | 每个 Linux container 一个轻量 VM |
| Docker Engine | 本机镜像、构建、网络、volume 和 container 生命周期工具 | Docker daemon 通过 containerd/runc 等 runtime 启动进程 | Linux 上主要是 namespace/cgroup;macOS Docker Desktop 也需要 Linux VM |
| Kubernetes | 集群编排系统 | node 上的 kubelet 调 CRI runtime,runtime 再创建 Pod sandbox 和 container | Pod 是调度单位,container 是 Pod 内的进程单元 |
所以它们不是简单替代关系。
Docker 和 Apple container 更接近“本机运行容器”的层。Kubernetes 是“让一组机器长期按期望状态运行 Pod”的层。
Apple container 是怎么做的
Apple 的 container 是一个 Swift 写的命令行工具和本机服务。它消费和产出 OCI 镜像,所以你看到的体验会接近:
container run alpine uname -a
但它底层不是让 macOS kernel 直接提供 Linux container 隔离。macOS 没有 Linux namespace/cgroup 那套接口。Apple 选择的实现是:每个 Linux container 背后都有一个轻量 Linux VM。
启动链路大致是:
container CLI
-> container-apiserver
-> container-core-images / container-network-vmnet / container-runtime-linux
-> apple/containerization Swift package
-> macOS Virtualization.framework
-> Linux VM
-> /sbin/vminitd
-> OCI process
这里有几个关键点。
第一,host 侧有 apiserver 和几个 helper。Apple 的技术说明里把 container-apiserver、镜像服务、网络服务、Linux runtime helper 分开描述。CLI 不是直接 fork 出 Linux 进程,而是把请求交给这些服务。
第二,真正的 Linux 边界来自 Virtualization.framework。apple/containerization 代码里有 VZVirtualMachineInstance,它包装 VZVirtualMachine,配置 Linux kernel 和启动参数。kernel command line 里会指定 init=/sbin/vminitd。
第三,VM 里的 vminitd 是这套设计的关键。host 通过 vsock/gRPC 跟它通信,让它在 guest Linux 内部完成 mount、网络、runtime spec 和进程启动。
也就是说,Apple container 不是“macOS 版 runc”。它更像一个面向 OCI 的 macOS 原生 VM-backed runtime。
Docker 的 container 是怎么做的
Docker 的抽象更老也更常见:
docker CLI
-> Docker daemon
-> container runtime stack
-> isolated Linux process
在 Linux host 上,container 的本质是被 namespace、cgroup、mount、capability 等机制隔离起来的普通 Linux 进程。镜像提供 root filesystem 和 metadata,runtime 按 OCI spec 准备环境,最后 exec 业务进程。
在 macOS 上,情况会多一层。因为 macOS kernel 不能直接运行 Linux container,Docker Desktop 需要一个 Linux VM 来承载 Docker Engine 和 Linux container。也就是说,Docker Desktop for Mac 也是 VM-backed,只是它通常是“共享 Linux VM 里跑多个容器”的模型。
这和 Apple container 的关键差异在 VM 粒度:
| 点 | Docker Desktop for Mac | Apple container |
|---|---|---|
| VM 粒度 | 通常一个共享 Linux VM 承载多个 container | 每个 Linux container 一个轻量 VM |
| 生态 | Docker CLI、Dockerfile、Compose、registry workflow 成熟 | Apple Silicon/macOS 原生实验性路线,更贴近 Apple Virtualization.framework |
| 隔离感 | 多个 container 共享同一个 Linux VM kernel | container 间 VM 边界更硬 |
| Kubernetes 关系 | Docker 本身不是 Kubernetes;历史上有 dockershim,现在 Kubernetes 不再内置 dockershim | 不是 Kubernetes runtime,除非未来额外适配 CRI |
所以不要只用“有没有 VM”判断二者差异。macOS 上二者都绕不开 Linux VM;真正不同的是谁管理 VM、VM 的粒度、runtime API 和生态兼容面。
Kubernetes 里的 container 是谁启动的
Kubernetes 的核心对象不是 container,而是 Pod。
一次常见启动链路是:
kubectl apply / controller
-> API Server 保存 Pod 或 Deployment
-> Scheduler 选择 node
-> 目标 node 上的 kubelet 发现需要运行这个 Pod
-> kubelet 调 CRI runtime
-> containerd 或 CRI-O 创建 Pod sandbox
-> runtime 创建并启动业务 container
-> CNI plugin 配 Pod 网络
在这个链路里,Kubernetes 做的是期望状态和编排:
- 这个 Pod 应该存在。
- 它应该有几个副本。
- 它应该调度到哪类 node。
- 它失败后要不要重启。
- 它怎样通过 Service 暴露。
- 它需要哪些 Secret、ConfigMap、volume 和 service account。
真正创建 container 进程的是 node 上的 runtime stack。
现在的 Kubernetes 通过 CRI 跟 runtime 对话。常见 runtime 是 containerd 和 CRI-O。Docker Engine 不是现代 Kubernetes 的直接默认 runtime;以前 Kubernetes 内置过 dockershim,用来把 Docker Engine 接进 kubelet,但这个组件已经从 Kubernetes 1.24 移除。
Pod 里的多个 container 通常共享同一个网络命名空间,所以它们可以通过 localhost 互相访问。这也是为什么 Kubernetes 要先建 Pod sandbox:sandbox 先占住 Pod 级别的网络边界,后面的业务 container 再加入这个边界。
一个更直观的分层图
把三者放到同一个图里,大概是这样:
Kubernetes
-> kubelet on each node
-> CRI runtime: containerd / CRI-O
-> low-level runtime: runc / crun / VM-based runtime
-> Linux process
Docker Engine on Linux
-> dockerd
-> container runtime stack
-> Linux namespace + cgroup
-> Linux process
Docker Desktop on macOS
-> Docker Desktop VM
-> Docker Engine inside Linux VM
-> Linux container process
Apple container on macOS
-> container-apiserver
-> container-runtime-linux
-> Virtualization.framework
-> one lightweight Linux VM per container
-> vminitd
-> OCI process
这张图能解释几个常见问题。
第一,Kubernetes 不是 Docker 的同类产品。Docker 偏本机构建和运行,Kubernetes 偏集群编排。生产里常见的是 Kubernetes 通过 containerd 跑 OCI 镜像,而开发机上用 Docker 或 Apple container 构建和测试镜像。
第二,Pod 不是 container 的改名。Pod 是 Kubernetes 调度和网络共享单位;container 是 Pod 里的运行进程。一个 Pod 可以只有一个 container,也可以有 sidecar。
第三,Apple container 兼容 OCI,不代表可以直接变成 Kubernetes node runtime。要接 Kubernetes,关键不是能不能跑镜像,而是能不能实现 kubelet 需要的 CRI 语义:Pod sandbox、container lifecycle、日志、exec、port forwarding、stats、image service 等。
实际怎么选
如果是在 Mac 上快速跑一个 Linux 镜像,Docker Desktop 和 Apple container 都是候选。Docker 的优势是生态成熟,Compose、BuildKit、registry workflow、团队习惯都在那。Apple container 的看点是每个容器独立轻量 VM、Swift 实现、和 macOS Virtualization.framework 深度集成。
如果是在生产或测试集群里长期跑服务,讨论点就不该停在 Docker 或 Apple container。你需要的是 Kubernetes 这类编排层,以及 node 上稳定的 CRI runtime、CNI、CSI、镜像仓库、监控和发布策略。
如果是在理解“container 到底是什么”,可以记这个边界:
镜像格式是 OCI。
本机运行容器靠 runtime。
集群里运行服务靠 Kubernetes 编排。
macOS 跑 Linux container 必须经过 Linux VM。
把这几层分开后,Apple container、Docker 和 Kubernetes 的关系就清楚很多:它们都围绕 container 工作,但真正负责的层不同。
References
- Apple container README: https://github.com/apple/container
- Apple container technical overview: https://github.com/apple/container/blob/main/docs/technical-overview.md
- Apple containerization README: https://github.com/apple/containerization
- Apple containerization
VZVirtualMachineInstance: https://github.com/apple/containerization/blob/main/Sources/Containerization/VZVirtualMachineInstance.swift - Docker overview: https://docs.docker.com/get-started/docker-overview/
- Docker Desktop Mac settings: https://docs.docker.com/desktop/settings/mac/
- Kubernetes Container Runtime Interface: https://kubernetes.io/docs/concepts/architecture/cri/
- Kubernetes container runtimes: https://kubernetes.io/docs/setup/production-environment/container-runtimes/
- Kubernetes Pods: https://kubernetes.io/docs/concepts/workloads/pods/
- Kubernetes dockershim removal FAQ: https://kubernetes.io/blog/2022/02/17/dockershim-faq/