はじめに
こんにちは!
本記事は「本気で学ぶKubernetes」シリーズの第10回です。このシリーズでは、Kubernetesの基礎から実践まで、段階的に学んでいきます。
このシリーズは、第1回から順に読むことで体系的に学べる構成にしています。
まだご覧になっていない方は、ぜひ最初からご覧ください!
今回は少しコラム的に趣向を変えて「KubernetesはなぜDocker(エンジン)を捨てたのか」というテーマで書いていきます。
この記事は人間がKubernetesの公式ドキュメントを読み漁りながら、人間の手で書いていますのでご安心ください!
Kubernetesとコンテナについて
正直に言うと、私はアプリケーション開発の出身で、コンテナといえばDockerのことだと思っていた時期がありました。
ローカル開発でdevcontainerを使ったり、Docker Composeで複数コンテナを立ち上げたりするのが当たり前で、Kubernetesも「Dockerコンテナを大規模に動かすためのツール」という理解でした。
サーバー構築や運用のレイヤーで昔から携わってきた方からすると何を言ってるんだと思われるかもしれませんが、最近IT業界に入ってきた開発者にとってDockerはコンテナにおけるデファクトスタンダードになっていると思います。私ももれなく「コンテナ開発のZ世代」ですね。
今回Kubernetesを細かく勉強する中で「Kubernetesでdocker(ランタイム)が非推奨になる」という記事を見かけて、「え、Dockerが使えなくなるの?」と驚きました。(数年前の記事)
というかそもそも「コンテナランタイムってなんだろう」という疑問が出てきたので調べてみました。
コンテナオーケストレーションについて
私の書いているシリーズがKubernetesですので、そもそもこれがどんな立ち位置のツールなのかを整理しておきます。
普段の開発でdocker runやdocker-compose upを使っている方も多いと思いますが、これらは基本的に「1台のマシン上でコンテナを動かす」ためのツールです。
しかし、本番環境で数十〜数百のコンテナを複数のサーバーで動かす場合、手動で管理するのは現実的ではありません。
そこで登場するのがコンテナオーケストレーションツールです。
ここまでの記事でまとめた通りコンテナオーケストレーションツールは、以下のような機能を提供しています。
- コンテナの起動・停止・スケーリング(台数の増減)
- 複数サーバーへの配置と負荷分散
- ヘルスチェックと障害時の自動復旧
- ネットワークやストレージの管理
コンテナで動作するサーバーないしアプリケーションを効率よく運用していくための技術ですね。
コンテナオーケストレーションツールとしては、以下のようなものがありました。
- Kubernetes: 現在のデファクトスタンダード
- Docker Swarm: Docker公式のオーケストレーションツール
- Nomad: HashiCorp製のツール
この中で、Kubernetesが圧倒的なシェアを持っています。
私は日常の開発でよくAWSやGoogle Cloudのサービスを使用しますが、あまり触ったことがなくてもコンテナオーケストレーションといえばKubernetesという印象でAWS EKSやGKEといったサービスは知っています。
実際、Kubernetesはコンテナオーケストレーションの世界で確固たる地位を築いていると思います。
「コンテナ = Docker」ではない
さて、ここからが本題です。
多くの開発者(私も含めて)は「コンテナ = Docker」だと思っていたのではないでしょうか。
しかし、実際にはコンテナは技術の総称であり、Dockerはその実装の1つに過ぎません。
物理的なホストOSや仮想化、ハイパーバイザーときてコンテナが普及してきたという歴史があるようですが、すべて触れると細かい話になりそうなので詳細は省きます。
コンテナの本質は、ホストOSのカーネルを共有しつつ、cgroup(リソース制限)とnamespace(プロセスの隔離)を使って、プロセスを隔離して動かす仕組みです。
簡単にcgroupはメモリやCPUなどのリソース制限を行うカーネルの機能で、namespaceはネットワークインターフェースなどのプロセスごとにリソースを分離するものです。
Dockerは、この仕組みを使いやすくパッケージ化したツールです。
そしてDockerのコンテナを実現するツールの1つにすぎず、それ以外にもいくつかの選択肢があります。
- containerd: Dockerの内部でも使われているコンテナランタイム
- CRI-O: Kubernetes専用に設計されたコンテナランタイム
- Podman: DockerのようにCLIで使えるコンテナツール
containerdとは何か
Docker Engineの内部では、containerdというコンテナランタイムが実際のコンテナ起動・停止を担当しています。
Docker Engineの構成を簡単に図にすると、以下のようになります。
containerdは「高レベルランタイム」と呼ばれ、イメージの取得、展開、コンテナのライフサイクル管理を担当します。
その下には、runcという「低レベルランタイム」があり、Linuxカーネルの機能を使って実際にコンテナプロセスを起動します。
つまり、Dockerを使っている時も、実際にコンテナを動かしているのはcontainerd(とrunc)というランタイムになっています。
冒頭で触れた、「KubernetesがDockerを捨てた」というのは Kubernetesが特定のランタイムをサポートしなくなっただけという話ということです。
containerdは、元々Dockerの一部として開発されていましたが、2017年にDocker社から独立してCNCF(Cloud Native Computing Foundation)のプロジェクトとなりました。現在では、Docker以外でも単体で使えるコンテナランタイムとして広く利用されているようです。
KubernetesがDockerから離れた理由
Kubernetesは、コンテナを起動・停止して管理するツールということはご存知かと思います。
前述した通り、コンテナを稼働させるランタイム自体は多くありますが、Kubernetes自身が本当に必要としているるのは、コンテナを起動・停止する機能だけです。
Docker Engineは多くの機能が搭載されています。
Docker CLIでコンテナをビルドしたり、起動させたり。イメージをビルドしたりDocker Composeで連携させたりなどなど。
ローカル開発では非常に便利ですが、Kubernetesから見ると運用上で不要な機能と言わざるをえません。
Kubernetesはコンテナイメージさえあれば、それを起動・停止して運用できれば問題ないということです。
dockershimとは
そこで今回の主題ですが、Kubernetesは元々コンテナランタイムとして直接Docker Engineを呼び出していました。
Docker Engineは標準的なインターフェースを持っていないということで、Kubernetesはdockershimというレイヤーを用意してコンテナを起動するようにしていたようです。
流れとしては以下のイメージです。
Kubernetes → dockershim → Docker Engine → containerd → コンテナ起動
- dockershimのメンテナンスがKubernetes開発者の負担になっていた
- Docker Engineを経由することで、余計なオーバーヘッドが発生していた
- Docker Engine自体がKubernetesに必要ない機能を多く含んでいた
CRI(Container Runtime Interface)について
dockershim自体はKubernetesがDocker Engineを経由してコンテナを起動する上で必要ではありましたが、開発コストや不要機能を含んでいる冗長性などがあったため**CRI(Container Runtime Interface)**という標準インターフェースを導入しました。
これはコンテナランタイムが実装すべき仕様を定めたもので、これに対応していれば事実上どんなコンテナランタイムでもKubernetesで使えるようになります。
containerdやCRI-OといったランタイムはCRIに直接対応しているため、変換レイヤーなしでKubernetesから呼び出せます。
これによりDocker Engineを経由する必要がなくなったため、Kubernetes 1.20以降はdockershimが非推奨となリマした。
出典: Kubernetes公式ブログ - Don't Panic: Kubernetes and Docker
何が変わって、何が変わらなかったか
ここまで読むと、「じゃあDockerは使えなくなったの?」と思うかもしれません。
結論から言うと、開発者にとっては何も変わっていません。
変わったのは「Kubernetes内部の実装」だけで、開発フローやDockerfileの使用には影響は特にないようです。
具体的に変わったこと
- 以前: Docker Engine(dockershim経由)
- 現在: containerdやCRI-Oを直接使用
この変更は、Kubernetesクラスタの内部で起こっている話なので開発者が意識する必要はありません。
開発者の作業フロー自体も以前と全く同じで、Dockerfileは引き続き使えます。
というのもDockerfileは、OCI(Open Container Initiative)という標準仕様に基づいています。
containerdやCRI-OもOCI仕様に対応しているため、Dockerfileで作ったイメージはそのまま動くということです。
ローカルマシンでのdocker buildやdocker runは引き続き使えますし、Kubernetesへのデプロイフローも大きく変わらないかなと思います。
Kubernetes側がcontainerdを使っていようと、開発者はDockerfileを書いてイメージをpushするだけで、あとはKubernetesが勝手にコンテナを起動してくれます。
まとめ
今回は、「KubernetesがDocker(エンジン)を捨てた理由」について整理してきました。
Kubernetesは、Docker Engineの中で実際にコンテナを動かしていたcontainerdを直接使うようになり、余計な変換レイヤー(dockershim)が不要になりました。
今回はコラム的な記事として「コンテナ = Docker」という認識からそもそもコンテナとは、コンテナランタイムとはといった疑問に触れてみました。
KubernetesがDocker Runtimeを使わなくなったのも、Kubernetesが普及してきてより標準化されてきているとも言い換えられます。
大手クラウドでもKubernetesのマネージド環境が提供されており、利用率も年々上昇しているということでぜひKubernetesを学習してみませんか!
それでは、また次回!
