cloud-sre
Apple container、Docker、Kubernetes の container は何が違うのか
Apple container、Docker、Kubernetes を同じレイヤー図で整理し、誰が image を扱い、誰が process を起動し、誰が Pod を管理するのかを分けて見る。
Apple の container、Docker、Kubernetes はどれも container という言葉を使いますが、見ているレイヤーは同じではありません。
混乱しやすい点はここです。Kubernetes には container がありますが、Kubernetes 自身が Linux container を直接実装しているわけではありません。Pod が node に schedule されたあと、その node の kubelet が CRI runtime を呼び、runtime stack が Pod sandbox と container を作ります。
Apple の container はさらに別の設計です。OCI image を扱えますが、macOS kernel の中に Linux namespace を再現するのではなく、Linux container ごとに軽量 Linux VM を起動します。
用語の説明
| 用語 | 意味 |
|---|---|
| OCI image | Open Container Initiative の image format。Docker、containerd、Kubernetes runtime、Apple container が共通して扱う土台です。 |
| Runtime | image と spec から実行中の process を作る component。文脈によって Docker daemon、containerd、CRI-O、runc、Apple container の runtime helper などを指します。 |
| Namespace / cgroup | Linux container の主要な隔離機能。namespace は process、network、mount などの見え方を分け、cgroup は CPU や memory などの resource を制限します。 |
| VM-backed container | container process が Linux VM の中で動く形。非 Linux host で Linux container を動かすときによく使われる境界です。 |
| Pod sandbox | Kubernetes/CRI における Pod の基礎実行環境。network など Pod 単位の共有境界を先に作り、application container がそこに参加します。 |
| CRI | Kubernetes Container Runtime Interface。kubelet が container runtime と話すための interface です。 |
| CNI | Container Network Interface。Kubernetes が Pod network を設定するために使う plugin interface です。 |
先に結論
「container の中の process は誰が起動するのか」で見ると、次のように分かれます。
| System | レイヤー | 最後に process を起動するもの | 隔離境界 |
|---|---|---|---|
Apple container | macOS 上の local OCI container tool/runtime | host の runtime helper が軽量 Linux VM を起動し、VM 内の vminitd が OCI process を起動する | Linux container ごとに軽量 VM |
| Docker Engine | local image、build、network、volume、container lifecycle tool | Docker daemon が container runtime stack に委譲する | Linux では namespace/cgroup。macOS の Docker Desktop も Linux VM が必要 |
| Kubernetes | cluster orchestration | kubelet が CRI runtime を呼び、runtime が Pod sandbox と container を作る | Pod が schedule 単位で、container は Pod 内の process 単位 |
この三つは単純な置き換え関係ではありません。
Docker と Apple container は local に container を実行するレイヤーに近く、Kubernetes は複数 node 上で Pod を期待状態どおりに維持するレイヤーです。
Apple container の仕組み
Apple container は Swift で書かれた CLI と local service です。OCI image を扱うため、表面的な使い方は見慣れた形になります。
container run alpine uname -a
ただし内部では、macOS kernel に Linux container の隔離を直接任せていません。macOS には Linux namespace/cgroup の interface がありません。Apple が選んだのは VM-backed な設計です。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 側が API server と helper service に分かれていることです。Apple の technical overview では、container-apiserver、image service、network service、Linux runtime helper が分けて説明されています。CLI が Linux process を直接 fork するわけではありません。
二つ目は、Linux の境界が Virtualization.framework から来ていることです。apple/containerization の VZVirtualMachineInstance は VZVirtualMachine を包み、Linux kernel を設定し、kernel command line に init=/sbin/vminitd を渡します。
三つ目は、guest 側の vminitd です。host は vsock/gRPC で vminitd と通信し、guest Linux 内で mount、network、runtime spec、target process の起動を行わせます。
つまり Apple container は「macOS 版 runc」ではありません。OCI を向いた、macOS native な VM-backed runtime と見る方が近いです。
Docker の container はどう起動するのか
Docker の model はより古く、一般的です。
docker CLI
-> Docker daemon
-> container runtime stack
-> isolated Linux process
Linux host では、container は namespace、cgroup、mount、capability などで隔離された通常の Linux process です。image が root filesystem と metadata を提供し、runtime が OCI spec に沿って環境を準備し、最後に application process を exec します。
macOS ではもう一つ境界があります。macOS kernel は Linux container を直接動かせないため、Docker Desktop は Docker Engine と Linux container を載せる Linux VM を必要とします。つまり Docker Desktop for Mac も VM-backed ですが、通常は一つの共有 Linux VM の中に複数 container を置く model です。
Apple container との大きな違いは VM の粒度です。
| Point | Docker Desktop for Mac | Apple container |
|---|---|---|
| VM の粒度 | 多くの場合、一つの共有 Linux VM が複数 container を載せる | Linux container ごとに軽量 VM |
| Ecosystem | Docker CLI、Dockerfile、Compose、registry、BuildKit workflow が成熟している | Apple Silicon/macOS native の実験的な方向で、Virtualization.framework と深く統合される |
| 隔離感 | 複数 container が同じ Linux VM kernel を共有する | container 間に VM 境界がある |
| Kubernetes との関係 | Docker は Kubernetes そのものではない。以前は dockershim が bridge していたが、現代の Kubernetes からは外れている | CRI 互換 layer がなければ Kubernetes runtime にはならない |
比較を「VM を使うかどうか」だけにすると本質を外します。macOS で Linux container を動かす以上、どこかに Linux は必要です。違うのは、誰が VM を管理するのか、VM の粒度はどこか、どの runtime API を持つのか、どの ecosystem に寄せているのかです。
Kubernetes の container は誰が起動するのか
Kubernetes の中心は生の container ではなく Pod です。
よくある起動経路は次のようになります。
kubectl apply / controller
-> API Server が Pod または Deployment を保存
-> Scheduler が node を選ぶ
-> その node の kubelet が Pod の desired state を見る
-> kubelet が CRI runtime を呼ぶ
-> containerd または CRI-O が Pod sandbox を作る
-> runtime が application container を作って起動する
-> CNI plugin が Pod network を設定する
Kubernetes が管理するのは desired state と orchestration です。
- この Pod が存在すべきか。
- replica はいくつ必要か。
- どの node に置けるか。
- 失敗した container を再起動するか。
- Service でどう公開するか。
- どの Secret、ConfigMap、volume、service account が必要か。
実際の container process を作るのは node 上の runtime stack です。
現在の Kubernetes は CRI を通じて runtime と話します。よく使われる runtime は containerd と CRI-O です。Docker Engine は現在の Kubernetes で直接組み込まれた標準 runtime path ではありません。以前は kubelet と Docker Engine をつなぐ dockershim がありましたが、Kubernetes 1.24 で削除されています。
同じ Pod 内の container は通常 network namespace を共有するため、localhost で互いに通信できます。そのため Kubernetes は先に Pod sandbox を作ります。sandbox が Pod 単位の network 境界を持ち、application container がそこに参加します。
一枚の mental model
三つを同じ図に置くとこうなります。
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 と同種の product ではありません。Docker は local build/run に強く、Kubernetes は複数 machine 上の orchestration に強いものです。production では Kubernetes が containerd 経由で OCI image を動かし、developer machine では Docker や Apple container で image を build/test する、という組み合わせが自然です。
次に、Pod は container の名前を変えたものではありません。Pod は Kubernetes の schedule 単位であり、network などを共有する単位です。container は Pod 内の process 単位です。一つの Pod に application container だけがある場合もあれば、sidecar が入る場合もあります。
最後に、OCI image を動かせることと Kubernetes node runtime になれることは別です。Kubernetes に接続するには、Pod sandbox lifecycle、container lifecycle、logs、exec、port forwarding、stats、image service など CRI の意味論を満たす必要があります。
どう選ぶか
Mac で Linux image をすばやく実行したいなら、Docker Desktop も Apple container も候補になります。Docker の強みは成熟した ecosystem です。Dockerfile、Compose、BuildKit、registry、team workflow が揃っています。Apple container の面白さは、container ごとの軽量 VM、Swift 実装、macOS Virtualization.framework との統合です。
検証環境や production で service を長期運用するなら、議論は一つ上のレイヤーに移ります。必要なのは Kubernetes などの orchestrator、安定した node runtime、CNI、CSI、registry、monitoring、rollout strategy です。
container という言葉を理解するときは、次の境界を分けると楽です。
image format は OCI。
local runtime が image を process にする。
Kubernetes は machine 群の上で Pod を orchestrate する。
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/