Kubernetes の コンテナランタイムについて、改めて見つめなおしてみた

# はじめに
[NRI OpenStandia Advent Calendar 2020](https://qiita.com/advent-calendar/2020/nri-openstandia)の 6日目は、Kubernetesとコンテナランタイムについて題材にします。

12月1-3日頃、「Kubernetesが v1.20 からコンテナランタイムとしてDockerを選択することが非推奨となる」というニュースが話題になりました。
話題になりすぎて、Kubernetesが[「パニックになるな」という公式アナウンス](https://kubernetes.io/blog/2020/12/02/dont-panic-kubernetes-and-docker/)を出す展開となりました。

たしかに「非推奨」という単語は衝撃的でしたが、実はこちらほとんどの開発者には影響は及ぼしません。
コンテナランタイムとしてDocker自体を使うことが非推奨になるだけでcontainerd（Dockerの機能の一部）を使うことは引き続き推奨されています。

とはいえ「どうして非推奨なのに、ほとんどの開発者に影響がないのか？」という疑問も多くの方は抱いたのではないでしょうか。
そこで本稿にて、コンテナランタイムとはどういうものであるかを改めて見つめなおしてみました。

ちなみにこのニュースに関して、Kubernetesドキュメント和訳プロジェクトのオーナをされているinductorさんが
詳しく書いてくださっています。あわせてご確認ください！

- https://blog.inductor.me/entry/2020/12/03/061329
- https://blog.inductor.me/entry/2020/12/03/144834

jacopenさんの記事もわかりやすいですね！必見です。

- https://jaco.udcp.info/entry/2020/12/03/172843

# コンテナランタイムとは（導入編）

コンテナランタイムとは、「コンテナを操作してくれる機能」のことです。
コンテナランタイムには「高レベルランタイム」と「低レベルランタイム」があり、この2つのおかげで
私たちはコンテナを作成することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/534839/bd77b1d8-2bf2-039e-52cc-9c8ea0d97325.png)

(※ほかのコンポーネントの説明は、[Kubernetes公式ドキュメント参照](https://kubernetes.io/ja/docs/concepts/overview/components/))

|    |  高レベルランタイム |  低レベルランタイム  |
| ---- | ---- | ---- |
|  役割  |  Kubernetes（kubelet）からの指示に対応する  |  「高レベルランタイム」の指示に従ってコンテナ操作を実行  |
|  主なランタイム  |  containerd,cri-o  |  runC,gVisor  |


またコンテナランタイムの話をすると、OCIやCRIという単語が出てきます。これらは高レベルランタイムや
低レベルランタイムが指示のやりとりを行う際の規格（ルール）のことです。

高レベルランタイムと低レベルランタイムのやりとりは、[Open Container Initiative（OCI）](https://opencontainers.org/)とよばれる世界標準の規格が使われています。

そして、Kubernetesと高レベルランタイムのやりとりは、[Container Runtime Interface（CRI）](https://kubernetes.io/blog/2016/12/container-runtime-interface-cri-in-kubernetes/)というKubernetes考案の規格が使われています。

# Dockerは高レベルランタイム

Kubernetesを用いる際Dockerは高レベルランタイムとして使用されます。
しかしながら、Kubernetes考案のCRIには準拠していません。

そのため、Kubernetes内のdockershimとよばれるブリッジを介して、KubernetesとDockerはやりとりをしていました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/534839/5c07825e-32ee-d66b-4851-f65db43dfd1b.png)


# Kubernetes～Docker間のやりとりをスマートにしたのが、containerd

コンテナランタイムとしてDockerは非常に優秀であるものの、Kubernetesが望む本来のやりとり（dockershimを挟まずに、CRIでやりとり）ができないことが課題としてありました。

そこで、containerdです。
containerdは、Dockerの中の高レベルコンテナランタイムとしての機能を抽出したものになります。

containerdは Docker社から [Cloud Native Computing Foundation（CNCF）](https://www.cncf.io/)に寄贈され、その後バージョンアップを経て CRI に準拠した高レベルランタイムになりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/534839/962071db-aab0-d4b2-d78a-bafd54940506.png)

つまり、containerdを高レベルランタイムとして活用すれば、Dockerを高レベルランタイムとして活用するときと全く同じ機能を使うことができます。

# containerdでDockerの機能をそのまま使えるなら、わざわざdockershim使わなくていいのでは？

そう思いませんでしょうか？Kubernetesの開発メンバーも、その結論に至りました。

- https://github.com/kubernetes/kubernetes/pull/94624

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/534839/0d2dcd73-93d3-876d-1512-f819a2bea159.png)

「Deprecate Dockershim（dockershim 非推奨）」のGitのIssueの記載の通り、Kubernetesはcontainerdやcri-oといったCRIに準拠した仕組みの活用を望んでいます。
そして、containerdで全く同じ機能が実現できるにも関わらず、あえてDockerを選択する人のためにdockershimをメンテナンスし続けることは非生産的であるため、dockershimを削除することを決定されました。

# 高レベルランタイムをDockerから他のものに変えても、コンテナ自体に影響なし

今までDockerを高レベルランタイムとして使っていた人の中には、「途中でコンテナランタイムを入れ替えたら、今までのDockerイメージに影響があるのでは？」という疑問が出るのではないでしょうか。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/534839/717ff5a8-7464-4ab9-5ff9-ccfc216a2501.png)

図の通り、Kubernetesと高レベルランタイムのやりとりは必ずCRIが規格として用いられており、
高レベルランタイムと低レベルランタイムのやりとりは必ずOCIが規格として用いられています。

つまり、高レベルランタイムがDockerから別のものに変わったとしても、インプット（kubeletからの指示）は同じ形式であり、アウトプット（低レベルランタイムへの指示）もDockerと全く同じ形式のものとなります。

このため、過去にDockerで作成したコンテナをcontainerdやcri-oでも操作することが可能です。

# 逆にどういったケースのときに、影響あるか

Docker CLIやDocker APIを別途利用していた場合、高レベルランタイムをDockerから
変えるときに、対応が必要になります。

特に言われているのが、Docker in Dockerを実施していたケースです。

Docker in Dockerとは。言葉の通りDockerコンテナの中にDockerコンテナを入れる手法です。
この手法はCIツールを用いる際に取り入れられていました。

その他の詳しいケースは、inductorさんの記事をお読みください。

- https://blog.inductor.me/entry/2020/12/03/144834

# containerd or cri-o ？

高レベルランタイムをDockerから移行する際、主な選択肢としてcontainerdとcri-oがあります。

containerdはDockerから分離したというバックグラウンドからもわかるよう、機能がとても充実しています。
対してcri-oは最小限の機能にしぼっているため、非常に軽量です。

# まとめ

Kubernetesでは containerd や cri-o といったCRI規格に準拠したコンテナランタイムを使うことが推奨されています。これは、KubernetesとDockerの技術を組み合わせる際の最適解であるためです。

これからも、Kubernetes・Docker両方の技術がともに発展していくのを楽しみにしています。

# 付録：高レベルランタイムとは

導入編でも記載した通り、高レベルランタイムはKubernetes(kubelet)からCRI規格の指示を受けます。
CRI規格の指示は、unix socket経由で送られてきます。

# 付録：低レベルランタイムとは

低レベルランタイムは、高レベルランタイムからの指示をもとに実際にコンテナを操作します。
そのため、セキュリティ面が非常に重要となります。

低レベルランタイムの主な選択肢として、runCとgVisorがあります。

runCは2020年時点でデファクトスタンダードになっている低レベルランタイムです。
カーネルを共有することで、コンテナに載せたアプリケーションの性能への影響を最小限にします。

gVisorはGoogleが開発している低レベルランタイムです。
 [動作するアプリケーションに制限がある](https://gvisor.dev/docs/user_guide/compatibility/)ものの、アプリケーションとホストカーネルが分離されているため、runCよりもセキュアなつくりとなっています。

動作するアプリケーションであれば、gVisor検討してみるのもいかがでしょうか。