Understand the Design of ContainerRuntime
みなさんはKubernetesがどのようにコンテナの起動/停止/削除などの操作を行っているかご存知でしょうか?
Kubernetesはコンテナの実行管理を司るkubeletというコンポーネントを持っており、これを用いてPodやコンテナの作成/管理を行っています。
しかしkubeletも直接的にコンテナを触っているわけではありません。あくまでもKubernetesのコンポーネントの一つであり、コンテナ実行管理の司令塔のようなものです。
ではkubeletは何を通してコンテナの実行管理を行っているのでしょうか?その答えがコンテナランタイムです。
つまりコンテナランタイムは、Kubernetesなどのオーケストレータからの指示をもとにコンテナの作成/管理を行います。
これからこのコンテナランタイムの詳細について見ていきましょう。
ハイレベルランタイムとローレベルランタイム
コンテナランタイムはさらに二つの要素に分解できます。
“ハイレベルランタイム(CRIランタイム)”と”ローレベルランタイム(OCIランタイム)”です。
kubeletが出した指示はハイレベルランタイム => ローレベルランタイムと伝わっていき、最終的にローレベルランタイムがプロセスの実行を行います。
この二つのレイヤのランタイムについて、それぞれの役割などを見ていきます。
ハイレベルランタイム
kubeletとの通信の窓口となる部分です。kubeletとはgRPCを通して通信し、CRI(Container Runtime Interface)というインターフェースに基づいてPod作成などの命令を受け取ります。そのためCRIランタイムとも呼ばれます。
直接コンテナを操作するわけではなく、その次のローレベルランタイムへ命令を渡す役割を果たします。
またここではイメージの管理(pull/push/rm…)も担います。
代表的なハイレベルランタイムには、
・docker
・containerd … Dockerの内部componentが分離
・cri-o … OCI/CRIに準拠したk8s専用のランタイム
などが存在します。
ローレベルランタイム
ハイレベルランタイムから受け取った指示でコンテナの起動や停止をキックするなど、コンテナの直接的な操作を担当する部分です。
これはOCI(Open Container Initiative)ランタイムとも呼ばれます。
OCIとはコンテナ技術の標準仕様を策定する団体の名前です。この仕様に沿うことで、ハイレベルランタイムは統一的なインターフェースでローレベルランタイムを扱うことができます。
代表的なローレベルランタイムには、
・runC … Dockerの内部でも使われている最も代表的なもの。
・gVisor(runsc) … ユーザ空間カーネルを使ってホストカーネルとコンテナ間の分離を強める。Google製。
・Kata Containers(kata-runtime) … PodごとにVMを作り、そのVM内のゲストカーネルでコンテナを実行。ホストカーネルとコンテナ間の分離がさらに強め。
・Nabla Containers(runnc) … unikernel技術を応用。アプリケーションはホストカーネルのユーザ空間でunikernelとして動作。
などが存在します。
ちなみにDockerはrunc + containerdで構成されており、KubernetesはコンテナランタイムとしてデフォルトでDockerを使うようになっています。
まとめ
コンテナランタイムの大まかな仕組みや種類について紹介しました!
OCI/CRIが策定されたことにより、ランタイムの入れ替えがより柔軟にできるようになりました。
例えば上図は3つのPHPアプリケーションが実行されていますが、ハイレベルランタイムにCRI-Oまたはcontainerd, そしてローレベルランタイムにruncまたはkata containersを使って実行されている例です。
今後はコンテナごと、アプリケーションごとに適切なセキュリティ実装のコンテナランタイムを選択できるようになると、Kata Containersのブログでも示されています。
次は興味のあるコンテナランタイムの特徴や詳細を掘り下げていければと思います!