inductor's blog

nothing but self note :)

Kubernetes 1.20からDockerが非推奨になる理由

追記: Kubernetes側での公式のアナウンスが2本出ているのでこちらも合わせてご覧ください。

kubernetes.io

kubernetes.io

Kubernetesコミュニティを眺めていたら、やたらめったら色んな人達が1.20 RCのリリースノート引っ張り出して「Dockerが非推奨になるからちゃんと対策を検討してね!!!」とアナウンスをしていて、挙げ句SIG Contributexではその対策に追われてバタバタしている自体を観測しました。

CNCF Ambassador Slackでもだいぶ燃え上がっていて、見かねて dev.to に記事を投稿したのでそれをかんたんに日本語にまとめてみようと思います。英語のほうはこちらをご覧ください。

dev.to

追記2. 影響範囲を知りたい場合はまずこちらをお読みください

blog.inductor.me

ほんとにdockershim消えるん?

はい。KubernetesにおけるDocker利用は1.20から非推奨になります。Docker自体がなくなるわけではありません。

以下引用です。

Docker support in the kubelet is now deprecated and will be removed in a future release. The kubelet uses a module called "dockershim" which implements CRI support for Docker and it has seen maintenance issues in the Kubernetes community. We encourage you to evaluate moving to a container runtime that is a full-fledged implementation of CRI (v1alpha1 or v1 compliant) as they become available.

kubernetes/CHANGELOG-1.20.md at master · kubernetes/kubernetes · GitHub

簡単にまとめると、Kubernetesがコンテナランタイムと通信する際に使うCRIというAPI仕様をDockerはサポートしておらず、Kubernetesでは「dockershim」というブリッジ用のサービスを別で用意していました。こいつがDocker APIとCRIの変換をしてくれているわけですが、今回のdeprecationにより、ここから先は数マイナーリリース後に削除される見込みです。

ローカル環境でDockerを動かす分には非常に強力で、みなさんも開発環境で使われているという方は多いと思います(自分もそうです)。しかし、今回の騒動を理解するにあたっては「KubernetesにおけるDockerの立ち位置」を理解する必要があります。

Kubernetesはインフラオーケストレーションとして、複数のマシンリソースを1つにまとめあげ、大きなマシンプールをアプリケーション実行用に提供します。アプリケーションはそのプールを他のアプリケーションと共有しながら使うわけです。このとき、各アプリケーションは実在する仮想/物理マシンの上でコンテナとして動作するため、Dockerなどのランタイムがそれらの上で動き、コンテナの実行と削除を行うために存在しています。

https://res.cloudinary.com/practicaldev/image/fetch/s--8uPK452T--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/6hohswt225do6y8c3d7u.png

上記の図を御覧ください。ノード上のkubeletがコントロールプレーンのAPIサーバーからメタデータを取得している様子がわかります。この各ノードに存在するkubeletがCRIを用いてコンテナランタイムに対して命令を実行するわけです。

なんで非推奨にまでするの?

まず1つ目の理由に、先程も説明した「DockerはCRIをサポートしていない」という問題があります。dockershim自体のメンテナンスコストも低くはないということですね。

次の理由を説明するにはDockerのアーキテクチャを少し掘り下げる必要があります。下図をご覧ください。

https://res.cloudinary.com/practicaldev/image/fetch/s--d5Z6xaMJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/h54wfmf6utvuyih5z0du.png

赤い枠の中身がKubernetesが実際に使うDockerの機能です。Docker volumeやnetworkについては、Kubernetesでは利用しません。

使わない機能を所持し続けることはそれ自体がセキュリティリスクになります。最小構成にすることで攻撃半径を狭めることができます。

そんなわけで、他に代替がないか検討してみましょう。KubernetesのCRIをサポートするコンテナランタイムを、CRIランタイムと呼びます。

CRIランタイム

現状、CRIランタイムには大きく2つの実装があります。

Containerd

Dockerから移行する場合、まず最初に検討すべきはこのcontainerdです。Dockerアーキテクチャの図でも書かれていますが、containerdはDockerで内部的に使われているランタイムで、Dockerが実際にコンテナの管理やらなんやらを行うときにはこのランタイムが呼ばれています。つまり、ランタイムとしてのDockerの機能は100%カバーされているとも言えます。

containerdはオープンソースなのでドキュメントもGitHubにありますし、いかなる形であれコントリビュートすることも可能です。

github.com

CRI-O

CRI-Oは主にRed Hatの開発者たちが作っているCRIランタイムです。事実として、このランタイムはRed HatのKubernetesディストリビューションであるOpenShiftの上で動いています。RHEL7以降Dockerのサポートを切っているところからも言えるように、彼らはDockerからの依存を早くから脱出したがっていたフシがあります。

Dockerの代替ツールとしてはローカル用ランタイムのPodman、コンテナビルダーのBuildah、そしてCRIランタイムのCRI-Oをそれぞれ提供しています。

github.com

筆者が思うCRI-Oの最推しポイントはその設計におけるミニマリズムです。名前の通りCRI-OはCRIとなるべく開発されているため、Kubernetesが必要としない機能は実装されません。これはcontainerdと比較した時に大きな差になっていて、containerdはなりたちがDockerがオープンソース化を勧めた際のマイクロサービスとしての成果の一つだったところから言っても機能面などで細かな違いがたくさんあります。

一方でDockerからの移行は場合によっては大変なこともあるかもしれません。とはいえ、Kubernetesでアプリケーションを動かすための機能は全て揃っています。

One more thing...

コンテナランタイムの話をする際には、「どの種類の」ランタイムのことを言っているのかに注意する必要があります。コンテナランタイムにはCRIランタイムの他にもOCIランタイムというものがあるからです。

CRIランタイム

上述のように、CRIはKubernetesがコンテナランタイムと会話するために提供する規格です。内部的にはgRPC over IPCを使っていて、同一ホストに存在するkubeletとCRIランタイムが直接会話するような仕組みです。kubeletから命令を受けたCRIランタイムは、OCIランタイムに実際のコンテナの作成削除を、OCIランタイムのバイナリを実行することによって行います。は?CRIランタイムはコンテナ作ってないの?はい、そうです。図を見てみましょう。

f:id:inductor:20201203060608p:plain

ここでCRIランタイムがやってることを見ると

  1. kubeletからgRPCのリクエストを受ける
  2. JSONを生成してOCIランタイムのバイナリに渡す

OCIランタイム

OCIランタイムはLinuxのシステムコールであるnamesapceとcgroupを用いて実際のコンテナ(ホスト上で隔離されたプロセス)を作成、削除する役割を持ちます。runCgVisorという言葉に聞き覚えがある方は、それらがこのOCIランタイムです。

appendix1: runCの動き

あとで

appendix2: gVisorの動き

あとで

結論

1. Kubernetesを動かすためのDockerは非推奨になるため、containerdやCRI-OのようなCRIランタイムへの移行を検討してください。

  1. containerdはDockerと同じコンポーネントを使うため、互換性が非常に高いです
  2. CRI-OはKubernetesのための専用環境を最小構成で作りたい場合により適していると言えます。

2. CRIランタイムとOCIランタイムの違いについて大枠を理解しましょう。

ワークロードの種類によって、runCが常に最適ではないケースがあります。