Kubernetes でアプリを動かすには、コンテナを起動し、ネットワークをつなぎ、必要ならディスクを用意します。 これらは別々のプログラムが担当していますが、利用者は Pod の設定からまとめて頼めます。
担当するプログラム同士は、決まった呼び出し方で仕事を受け渡します。 この章では、その約束事である インターフェース と、担当を交換できる理由を見ていきます。
GOAL この章のゴール
- コンテナ、ネットワーク、ストレージ、クラウド連携の担当を区別できる
- 共通の取り決めでプログラムを交換できる理由が分かる
- 自分のクラスタで各担当のプログラムを調べられる
頼む相手が別のプログラムになっている
たとえば、ネットワークの担当を Cilium から別のプログラムに替えたいとします。 担当ごとに頼み方が違えば、呼び出す側のプログラムも、その都度変更しなければなりません。
そこで「この形式で設定を渡し、この形式で結果を返す」という取り決めを決めます。 呼び出す側と呼ばれる側が同じ取り決めに従えば、担当を交換しやすくなります。 この接点を インターフェース と呼びます。 Kubernetes は、コンテナの起動やストレージの操作などを、こうした接点を通して外部のプログラムに任せています。
仕事ごとに名前が付いていて、次の 4 つが基本です。
- CRIシーアールアイContainer Runtime Interface。kubelet がコンテナランタイムを呼ぶための gRPC の取り決め。用語集で見る:コンテナを起動する仕事。頼み先は containerd や CRI-O。
- CNIシーエヌアイContainer Network Interface。Pod に NIC を生やし、IP を配るプラグインの規約。用語集で見る:Pod のネットワークの仕事。頼み先は Cilium や Calico。
- CSIシーエスアイContainer Storage Interface。ボリュームの作成、ノードへの接続、マウントをストレージベンダーの driver に任せる gRPC の規約。用語集で見る:ディスクの仕事。頼み先は EBS 用の driver など。
- CPIシーピーアイCloud Provider Interface。cloud-controller-manager がクラウドの API を呼ぶための Go の interface。用語集で見る:クラウドの仕事。頼み先は各クラウド用の cloud-controller-manager。
gRPC は、プログラム同士が関数を呼ぶ形で話すための通信方式です。 この章では、なぜこの形になったのか、4 つがどんな運び方で頼まれているのか、自分のクラスタでは誰が引き受けているのかを順に見ます。
kubelet が太りすぎた
Kubernetes 1.0 (2015 年 7 月) の kubelet のソースには、dockertools と rkt というディレクトリが並んでいました。
前者は Docker の API を直接叩くコード、後者は CoreOS の rkt というランタイム専用のコードです。
kubernetes.io の 2016 年のブログは、当時の構造を「kubelet が Docker と rkt に密に結合していて、ランタイムを 1 つ足すたびに kubelet の内部を深く知った実装が要る」と説明しています。
この構造の問題は、Docker が Kubernetes のためだけに作られていないことにあります。
Docker は自分の都合で API を変え、バージョンを上げます。
そのたびに kubelet 側の dockertools を直す必要があり、修正は Kubernetes のリリースに乗らなければ利用者に届きません。
1.5 (2016 年 12 月) で CRI が入り、kubelet は「コンテナを起動しろ」を gRPC で外のプロセスに投げるだけになりました。 kubelet とランタイムの間のやり取りを gRPC の文書に固定したので、ランタイムは別プロセスとして自分のペースで開発でき、ランタイム側の変更が kubelet のコードに影響しません。 kubernetes.io はこれを「クラスタのコンポーネントを再コンパイルせずに、さまざまなランタイムを使えるようにするプラグインインターフェース」と説明しています。
同じ発想がネットワーク、ディスク、クラウドにも広がった
kubelet はランタイムのほかにも、ネットワーク、ボリューム、クラウドの API に依存していました。 ランタイムで効いた「別プロセスに切り出して、決まった方法で頼む」という手は、これらにも同じように効きます。 Pod のネットワークは CNI、ボリュームは CSI、クラウドとの連携は CPI として、それぞれ Kubernetes 本体の外へ切り出されました。 切り出す利点は、公式ドキュメントでも同じ形で説明されています。 ストレージでは、ベンダーが Kubernetes 本体のコードに触れずに driver を書いて配れること。 クラウド連携では、クラウド事業者が本体とは別のリリース周期で機能を出せることです。
結果として Kubernetes は、ひとつの巨大なプログラムというより、決まった取り決めでつながる別々のプロセスの集まりになっています。 コントロールプレーンの各プロセスは apiserver を介してしか会話せず、ノード側の部品は kubelet から socket 越しに呼ばれます。
AWS の語彙で言うなら、ECS が Docker や containerd と話す部分 (ecs-agent) が CRI、awsvpc モードで ENI を Task に差す部分が CNI、EBS を Task にアタッチする部分が CSI、ELB にターゲットを登録する部分が CPI の Service controller にあたります。
ECS はそれらを AWS が一体で持ちますが、Kubernetes は一つずつ外に出し、誰でも実装できる形にしました。
全体図
主要な 4 つに、その後で増えた周辺を加えると次の図になります。
- CRI:コンテナランタイム。containerd / CRI-O。
- CNI:Pod のネットワーク。Cilium / Calico / Flannel / Amazon VPC CNI。
- CSI:ボリューム。aws-ebs-csi-driver、このクラスタでは kubevirt-csi-driver。
- CPI:クラウド連携。cloud-provider-aws など、このクラスタでは kubevirt-cloud-controller-manager。
- Device Plugin / DRA:GPU などのデバイス。デバイスをコンテナに渡す書式として CDI。
- Gateway API:L4 / L7 の入口。Ingress の後継で、実装は Cilium / Envoy Gateway など。
- CRD と集約 API:自分でリソース種別を足す入口。cert-manager や Argo CD、metrics-server。
kubernetes.io の「Extending Kubernetes」は、これらを含む拡張点を一覧にしています。 kube-scheduler の中にプラグインを差す scheduling framework や、apiserver への要求を検査、書き換えする admission webhook もその一覧にありますが、この Part では扱いません。 下の図で各項目を選ぶと、「誰が誰を呼ぶか」「どう運ぶか」「無いと何が起きるか」が出ます。
- 名前
- CRI Container Runtime Interface
- 誰が呼ぶ
- kubelet コンテナランタイム (containerd / CRI-O)
- 運び方
- gRPC over UNIX socket (/run/containerd/containerd.sock)。RuntimeService と ImageService の 2 サービス。
- 実装の例
- containerd / CRI-O / cri-dockerd (Docker を繋ぐ shim)
- 無いと
- kubelet がノードを登録できない。Pod は一つも起動しない。
CRI の中心は kubelet です。ノード側は「kubelet が呼ぶ」、クラスタ側は「controller が apiserver を見て動く」という向きの違いが、設計の分かれ目です。
呼び方は 4 種類
Part 3 の CRI は gRPC で、Part 2 の CNI は実行ファイルの呼び出しでした。 kubelet や controller が外部のプログラムに頼むときの運び方は、全部で 4 種類あります。
| 運び方 | 使うインターフェース | 特徴 |
|---|---|---|
| gRPC over UNIX socket | CRI、CSI Node、Device Plugin、DRA | ノード上の常駐デーモンと話す。kubelet が client |
| バイナリの exec と JSON | CNI | 呼ばれたら終わる。デーモン不要、設定は /etc/cni/net.d |
| sidecar が翻訳する gRPC | CSI Controller | sidecar が apiserver を watch し、driver は Kubernetes を知らない |
| apiserver を watch する controller | CPI、Gateway API、CRD | kubelet は関与しない。クラウド API など外の世界を操作する |
CNI だけが exec なのは、CNI が Kubernetes 専用の規約として作られていないからです。
CNI は CoreOS が提案し、「多くのランタイムやオーケストレーターが同じ問題 (ネットワーク層を差し替え可能にすること) を解こうとするので、重複を避けるために共通のインターフェースを定める」という目的で作られました。
常駐プロセスを前提にしない方が、どのランタイムからも呼びやすい。
実際、CNI のプロジェクトは利用者として Kubernetes のほかに Amazon ECS、Apache Mesos、Cloud Foundry などを挙げていて、ECS の awsvpc モードでは ecs-agent が CNI プラグインを呼んでタスクに ENI を差しています。
実機で API グループを眺める
インターフェースの多くは、apiserver に専用の API グループを持ちます。 自分のクラスタで一覧を見てみましょう。
kubectl get --raw /apis | jq -r '.groups[].name' | sort出力例
apps
cilium.io
gateway.networking.k8s.io
node.k8s.io
resource.k8s.io
storage.k8s.io
...storage.k8s.io に CSIDriver / CSINode / VolumeAttachment、resource.k8s.io に DRA のリソース、gateway.networking.k8s.io に Gateway API、node.k8s.io に RuntimeClass が入っています。
CRI と CNI はこの一覧に出ません。
どちらもノードの中で完結し、apiserver にリソース種別を持たないからです。
この違いも、次の章から順に見ていきます。
ふりかえり
QCRI が作られた直接の動機はどれ?
1.0 の kubelet は dockertools と rkt のコードを内蔵していて、ランタイムの API 変更のたびに kubelet を直す必要がありました。ランタイムを別プロセスに出して、gRPC で頼む形にしたのが CRI です。
Qkubelet が直接呼ばないインターフェースはどれ?
CPI は cloud-controller-manager が apiserver を watch してクラウドの API を叩く形で、kubelet は関与しません。CRI と CSI Node plugin は kubelet が UNIX socket 越しに gRPC で呼びます。
QCNI だけが「バイナリを exec する」方式なのはなぜ?
CNI は ECS や Mesos など複数のランタイムが同じプラグインを呼べるように作られた規約で、「設定 JSON を stdin で渡してバイナリを実行する」形になっています。
参考
- Container Runtime Interface (CRI), Kubernetes Documentation: https://kubernetes.io/docs/concepts/architecture/cri/
- Cluster Architecture, Kubernetes Documentation: https://kubernetes.io/docs/concepts/architecture/
- Introducing Container Runtime Interface (CRI) in Kubernetes (2016), Kubernetes Blog: https://kubernetes.io/blog/2016/12/container-runtime-interface-cri-in-kubernetes/
- Extending Kubernetes, Kubernetes Documentation: https://kubernetes.io/docs/concepts/extend-kubernetes/
- Cloud Controller Manager, Kubernetes Documentation: https://kubernetes.io/docs/concepts/architecture/cloud-controller/
- Introduction, Kubernetes CSI Developer Documentation: https://kubernetes-csi.github.io/docs/introduction.html
- Network Plugins, Kubernetes Documentation: https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/network-plugins/
- Container Network Interface (CNI) README: https://github.com/containernetworking/cni/blob/main/README.md
- Amazon ECS CNI Plugins README: https://github.com/aws/amazon-ecs-cni-plugins/blob/master/README.md
- Dockershim Removal FAQ, Kubernetes Blog: https://kubernetes.io/blog/2022/02/17/dockershim-faq/
- Kubernetes v1.34: DRA has graduated to GA, Kubernetes Blog: https://kubernetes.io/blog/2025/09/01/kubernetes-v1-34-dra-updates/
- Kubernetes 1.0 の kubelet ソースツリー (
pkg/kubelet): https://github.com/kubernetes/kubernetes/tree/v1.0.0/pkg/kubelet