Docker で作ったイメージは、Docker Engine が入っていない Kubernetes のノードでも動きます。 「イメージを作る道具」と「コンテナを動かす道具」は、同じでなくてもよいからです。
では、かつて Kubernetes から Docker を直接使っていた構成は、どう変わったのでしょうか。 この章では、共通の呼び出し方が作られ、Docker 専用の接続部分が外れるまでを追います。
GOAL この章のゴール
- ランタイムを交換するために共通の取り決めが必要になった理由が分かる
- dockershim の削除で変わった部分を説明できる
- Docker で作ったイメージが引き続き使える理由が分かる
年表
1.0: ランタイムごとのコードを抱えた kubelet
2015 年 7 月の Kubernetes 1.0 のソースを見ると、kubelet のディレクトリに dockertools と rkt という 2 つのディレクトリが並んでいます。
前者は Docker の API を直接叩くコードで、コンテナの作成、イメージの pull、ログの取得が全部 Docker の Go クライアントへの呼び出しです。
後者は CoreOS 社のランタイム rkt のための同じ種類のコードです。
2015 年にはコンテナランタイムの選択肢がほぼ Docker しか無く、kubelet がランタイムの API を直接呼ぶのは自然な設計でした。
2016 年の CRI の設計文書は、この構造の問題を 3 つ挙げています。
Pod という概念を持たないランタイムのために、ランタイムごとに大きな変換コードが要ること。
ランタイムごとに Pod の同期処理 (SyncPod) が丸ごと実装され、コードを共有できないこと。
Pod の spec が変わるたびに、全ランタイムの実装を直さなければならないことです。
rkt と rktnetes
rktロケットCoreOS 社が 2014 年 12 月に公開したコンテナランタイム。デーモンを置かず、systemd の仕組みで Pod を直接起動する。2016 年に Kubernetes 1.3 で対応 (rktnetes)、2019 年 8 月に CNCF でアーカイブ。用語集で見る は、Docker と正反対の設計でした。
Kubernetes のブログによると、rkt は常駐デーモンを持たず、Pod を最初から起動の単位として扱い、systemd と統合され、実行環境の部分 (stage1) を差し替えれば KVM の VM として起動することもできました。
2016 年 7 月の Kubernetes 1.3 で、--container-runtime=rkt で選べる形で正式に組み込まれ、「rktnetes」と呼ばれました。
rkt が kubelet に入ったことで、「ランタイムを差し替えられる kubelet」が初めて現実になりました。 同時に、前の節の問題も現実になります。 2 つのランタイムを抱えた kubelet には、それぞれの API を呼ぶコードが二重にあり、どちらかが変わるたびに Kubernetes 側の修正が必要でした。
CRI の誕生 (1.5)
2016 年 12 月の 1.5 で、Kubernetes は kubelet がランタイムに頼む方法を仕様として決め、アルファとして出しました。 それが前の章で見た CRI です。 設計文書と発表のブログから、決まったことを 3 つ拾います。
- gRPC over UNIX socket:kubelet は全ノードにいて、ランタイムも同じノードに常駐する。だからネットワーク越しの RPC は要らず、同じノードのソケット 1 本で話す。
- 2 つのサービス:イメージを扱う ImageService と、Pod とコンテナを扱う RuntimeService に分ける。
- 命令型でコンテナ単位の API と sandbox:「sandbox を作れ」「コンテナを起動しろ」という手続きを kubelet が順に呼ぶ。設計文書は、Pod 単位の依頼にすると Pod の意味づけや再起動の規則を全ランタイムが実装し直すことになる、という理由でこの形を選んでいる。Pod の概念を持たないランタイムでも実装できるように、Pod の土台だけを sandbox という単位にした。
この取り決めのおかげで、ランタイムごとの変換コードは kubelet の中心部から切り離されました。 発表のブログは、Docker 用の dockershim と rkt 用の rktlet を、CRI とランタイムの間に挟む変換層 (shim) として紹介しています。
rkt の退場
rkt 用の変換層 rktlet は作られましたが、2019 年 12 月にアーカイブされました。 それより前の 2019 年 8 月に、CNCF は rkt プロジェクト自体をアーカイブしています。 CNCF の発表が挙げた理由は、利用者の採用が大きく減ったこと、プロジェクトの活動と貢献者が減り続けたこと、未修正の CVE が残ったことで、利用者は同じ CNCF の containerd と CRI-O に移っていました。 rkt は、CNCF が新しく作ったアーカイブ手続きの最初の対象でした。
rkt が目指した方向は、別のプロジェクトに残っています。 Kubernetes からの依頼だけを受けるランタイムとしては、Red Hat が 2016 年に始めた CRI-O があります。 常駐デーモンを持たない設計としては、Docker の代わりに手元で使う Podman が、自分を「デーモンレス」のコンテナエンジンと名乗っています。
dockershim
CRI ができた 2016 年の時点で、dockerd が受け付けるのは Docker 独自の REST API だけでした。 kubelet が Docker を使い続けるには、CRI の呼び出しを Docker API に変換する層が要ります。 その変換層が、kubelet の中に置かれた dockershimドッカーシムkubelet に内蔵されていた CRI → Docker API の変換層。1.20 で非推奨、1.24 (2022 年) で削除された。用語集で見る です。 Docker を使うクラスタは kubelet → dockershim → dockerd → containerd → runc と 5 段を経由していました。
一方の containerd は、Docker から独立していきます。 Docker 1.11 (2016 年 4 月) で dockerd の内部に入り、2016 年 12 月に独立プロジェクトになり、2017 年 3 月に rkt と一緒に CNCF に寄贈されました。 2017 年 4 月には Docker 社が、Docker を組み立てる部品 (containerd など) を公開する Moby project を発表しています。 2018 年 4 月の containerd 1.1 で CRI plugin が containerd 本体に入り、kubelet が containerd に直接話せるようになりました。 Kubernetes のブログはこのとき、kubelet → dockershim → dockerd → containerd → runc と、kubelet → containerd → runc の 2 つの経路を並べて、後者には中間のデーモンが無いことを示しています。
2020 年 12 月の 1.20 で dockershim は非推奨になり、2022 年の 1.24 で削除されました。 Kubernetes の FAQ によると、非推奨の理由は、dockershim の保守が kubelet に大きな負担をかけていたことと、Docker が CRI を実装していないことです。 削除されたのは kubelet 内の変換層だけで、Docker で作ったイメージはそのまま動きます (OCI Image Spec だからです)。 Docker を使い続けたい場合は、Mirantis が保守する cri-dockerd を別プロセスとして置けば、CRI の口ができます。
dockerd が持つ 3 つの機能
Docker を外せた理由がもう 1 つあります。
Kubernetes のブログ「Don’t Panic」は、Docker を「人間が使うために作られたもので、ネットワーク、ボリューム、UX を含む」と説明し、それらは CRI が求めるものに入っていない、と述べています。
dockerd を 3 つのサービスに分けると、コンテナを動かすランタイムサービス、docker network のネットワークサービス、docker volume のボリュームサービスです。
Kubernetes が dockerd に頼むのは、このうちランタイムだけです。
ネットワークは CNI、ストレージは CSI と、それぞれ別のインターフェースを持っているからです (Part 2 で「Kubernetes は docker0 を使っていない」と確かめたのはこのことです)。
Docker を Kubernetes のノードに置くと、使わない 2 つのサービスが常駐し、使うランタイムの部分も dockerd 経由の遠回りになります。 containerd を直接使えば、必要な部分だけが残ります。
ふりかえり
QKubernetes 1.24 で削除されたのは?
削除されたのは CRI → Docker API の変換層だけです。OCI 形式のイメージはそのまま動きます。
QCRI が gRPC over UNIX socket を選んだ理由として適切なのは?
同じノードの中でしか話さないので、ソケット 1 本で済みます。
QCNCF が rkt をアーカイブした理由として挙げたのは?
利用者は containerd と CRI-O に移っていました。CNCF の新しいアーカイブ手続きの最初の対象でした。
参考
- Kubernetes ブログ「Introducing Container Runtime Interface (CRI) in Kubernetes」(2016 年 12 月): https://kubernetes.io/blog/2016/12/container-runtime-interface-cri-in-kubernetes/
- Kubernetes 設計文書「Container Runtime Interface v1」: https://github.com/kubernetes/design-proposals-archive/blob/main/node/container-runtime-interface-v1.md
- Kubernetes ブログ「rktnetes brings rkt container engine to Kubernetes」(2016 年 7 月): https://kubernetes.io/blog/2016/07/rktnetes-brings-rkt-container-engine-to-kubernetes/
- Kubernetes ブログ「Kubernetes Containerd Integration Goes GA」(2018 年 5 月): https://kubernetes.io/blog/2018/05/24/kubernetes-containerd-integration-goes-ga/
- Kubernetes ブログ「Don’t Panic: Kubernetes and Docker」(2020 年 12 月): https://kubernetes.io/blog/2020/12/02/dont-panic-kubernetes-and-docker/
- Kubernetes ブログ「Dockershim Deprecation FAQ」(2020 年 12 月): https://kubernetes.io/blog/2020/12/02/dockershim-faq/
- Kubernetes ブログ「Updated: Dockershim Removal FAQ」(2022 年 2 月): https://kubernetes.io/blog/2022/02/17/dockershim-faq/
- Kubernetes ドキュメント「Container Runtimes」: https://kubernetes.io/docs/setup/production-environment/container-runtimes/
- Kubernetes ドキュメント「Container Runtime Interface (CRI)」: https://kubernetes.io/docs/concepts/architecture/cri/
- Kubernetes v1.0.0 のソース (pkg/kubelet): https://github.com/kubernetes/kubernetes/tree/v1.0.0/pkg/kubelet
- rktlet README: https://github.com/kubernetes-retired/rktlet
- CNCF「CNCF Archives the rkt Project」(2019 年 8 月): https://www.cncf.io/blog/2019/08/16/cncf-archives-the-rkt-project/
- CNCF「containerd and rkt join CNCF」(2017 年 3 月): https://www.cncf.io/announcements/2017/03/29/containerd-rkt-join-cloud-native-computing-foundation/
- Docker ブログ「Introducing containerd」(2016 年 12 月): https://www.docker.com/blog/introducing-containerd/
- Docker ブログ「Introducing Moby Project」(2017 年 4 月): https://www.docker.com/blog/introducing-the-moby-project/
- OCI「About the Open Container Initiative」: https://opencontainers.org/about/overview/
- Red Hat ブログ「Red Hat contributes CRI-O to the CNCF」(2019 年 4 月): https://www.redhat.com/en/blog/red-hat-contributes-cri-o-cloud-native-computing-foundation
- Podman ドキュメント: https://docs.podman.io/en/latest/
- cri-dockerd: https://github.com/Mirantis/cri-dockerd
- containerd RELEASES.md: https://github.com/containerd/containerd/blob/main/RELEASES.md