A talk at Kubernetes Novice Tokyo #10
Kubernetesをとりまくコンテナランタイムの栄枯盛衰Presented by @inductor
View Slide
今日の目標● コンテナランタイムがどうしてDockerだけじゃないのかを知る● コンテナランタイムの歴史を知ることで、Kubernetesで動かすアプリケーションの裏側について少しだけ詳しくなる● ランタイムの選択肢にはどんなものがあって、どんな特徴があるのかを知ることで、Docker非推奨の話の裏側を少し理解できる
2019年の夏...↓こんな話をしました
Kubernetesの特徴(リリース時)● Google Borgの10年以上に渡る運用知見を集約した基盤のOSS○ リリース当初はGoogleがメインで開発● Docker、etcdを中心とした分散型コンテナ実行基盤● APIを起点に、etcdに書かれたステートを正としあるべき状態を定義されたとおりに基盤上で維持する自立型のシステム● 管理用のマスター、実行用のミニオンと呼ばれるマシンからなる
Kubernetesの特徴(現在)● Google Borgの10年以上に渡る運用知見を集約した基盤のOSS○ CNCFがホスティング、コミュニティを主体とした開発体制● コンテナランタイム、etcdを中心とした分散型コンテナ実行基盤● APIを起点に、etcdに書かれたステートを正としあるべき状態を定義されたとおりに基盤上で維持する自立型のシステム● 管理用のコントロールプレーン、実行用のワーカーからなる
Kubernetesの特徴(現在)● Google Borgの10年以上に渡る運用知見を集約した基盤のOSS○ CNCFがホスティング、コミュニティを主体とした開発体制● コンテナランタイム、etcdを中心とした分散型コンテナ実行基盤● APIを起点に、etcdに書かれたステートを正としあるべき状態を定義されたとおりに基盤上で維持する自立型のシステム● 管理用のコントロールプレーン、実行用のワーカーからなるDockerが非推奨になったの!?で話題になった1.20.0リリース時のアレ
ランタイムのこれまでについてお話していきます
Kubernetes初期のランタイムとの関係● Google Borgの10年以上に渡る運用知見を集約した基盤のOSS○ リリース当初はGoogleがメインで開発● Docker、etcdを中心とした分散型コンテナ実行基盤● APIを起点に、etcdに書かれたステートを正としあるべき状態を定義されたとおりに基盤上で維持する自立型のシステム● 管理用のマスター、実行用のミニオンと呼ばれるマシンからなる
Kubernetes 1.0 当時の実装https://github.com/kubernetes/kubernetes/blob/release-1.0/pkg/kubelet/dockertools/manager.gokubeletのdockertools配下にDockerのための実装があった→ 完全にDockerで動くことだけを想定
Kubernetes 1.0 当時の実装https://github.com/kubernetes/kubernetes/blob/release-1.0/pkg/kubelet/dockertools/manager.gokubeletのdockertools配下にDockerのための実装があった→ 完全にDockerで動くことだけを想定ちょっとだけソースの中身を覗いてみましょう
Kubernetes 1.0 当時の実装https://github.com/kubernetes/kubernetes/blob/release-1.0/pkg/kubelet/dockertools/manager.gokubeletのdockertools配下にDockerのための実装があった→ 完全にDockerで動くことだけを想定docker周りの実装rkt周りの実装(!)
rktとはなんだったのか● CoreOS社(Red Hatが2018年に買収)が作ったコンテナランタイム○ CoreOS社は他にもetcdやコンテナホスト用LinuxディストロCoreOSを開発するなど、Kubernetesのエコシステムに貢献してきた○ CoreOS Meetup Tokyo #1 の資料を見ると当時の状況がよくわかって面白い● Docker社が事実上独占していたコンテナのエコシステムを打破するきっかけになった○ Dockerはデーモンを配置してノード上に常駐させるモデルなのに対し、 rktはsystemdによる隔離を応用した”デーモンレス”なコンテナ技術○ Dockerとは違いPodのネイティブサポートをするなど、 Kubernetesの仕組みを意識
rktとはなんだったのか● CoreOS社(Red Hatが2018年に買収)が作ったコンテナランタイム○ CoreOS社は他にもetcdやコンテナホスト用LinuxディストロCoreOSを開発するなど、Kubernetesのエコシステムに貢献してきた○ CoreOS Meetup Tokyo #1 の資料を見ると当時の状況がよくわかって面白い● Docker社が事実上独占していたコンテナのエコシステムを打破するきっかけになった○ Dockerはデーモンを配置してノード上に常駐させるモデルなのに対し、 rktはsystemdによる隔離を応用した”デーモンレス”なコンテナ技術○ Dockerとは違いPodのネイティブサポートをするなど、 Kubernetesの仕組みを意識https://kubernetes.io/blog/2016/07/rktnetes-brings-rkt-container-engine-to-kubernetes/
rktとはなんだったのか● CoreOS社(Red Hatが2018年に買収)が作ったコンテナランタイム○ CoreOS社は他にもetcdやコンテナホスト用LinuxディストロCoreOSを開発するなど、Kubernetesのエコシステムに貢献してきた○ CoreOS Meetup Tokyo #1 の資料を見ると当時の状況がよくわかって面白い● Docker社が事実上独占していたコンテナのエコシステムを打破するきっかけになった○ Dockerはデーモンを配置してノード上に常駐させるモデルなのに対し、 rktはsystemdによる隔離を応用した”デーモンレス”なコンテナ技術○ Dockerとは違いPodのネイティブサポートをするなど、 Kubernetesの仕組みを意識https://kubernetes.io/blog/2016/07/rktnetes-brings-rkt-container-engine-to-kubernetes/https://speakerdeck.com/ytaka23/mao-demowakaru-rkt-plus-kubernetes-number-ichigayageek
Kubernetes 1.3: rktをランタイムとして採用● これまでDockerだけを前提に作られていたkubeletやランタイム周りの仕組みを外部注入可能な形に置き換えるきっかけに● コンテナイメージの署名やコンテナケイパビリティの制限をデフォルトで実装した「secure by default」な実装● UNIX哲学に基づいたシンプルなプロセスモデル○ Dockerdがないのでrkt process = pod process● 当時としてはまだ新しかったユーザー名前空間の採用やSELinuxへの対応、軽量VMへの対応など夢がてんこ盛り
Kubernetes 1.3: rktをランタイムとして採用● これまでDockerだけを前提に作られていたkubeletやランタイム周りの仕組みを外部注入可能な形に置き換えるきっかけに● コンテナイメージの署名やコンテナケイパビリティの制限をデフォルトで実装した「secure by default」な実装● UNIX哲学に基づいたシンプルなプロセスモデル○ Dockerdがないのでrkt process = pod process● 当時としてはまだ新しかったユーザー名前空間の採用やSELinuxへの対応、軽量VMへの対応など夢がてんこ盛り systemdによるisolation(その他kvm等も選べる)
Kubernetes 1.3: rktをランタイムとして採用● これまでDockerだけを前提に作られていたkubeletやランタイム周りの仕組みを外部注入可能な形に置き換えるきっかけに● コンテナイメージの署名やコンテナケイパビリティの制限をデフォルトで実装した「secure by default」な実装● UNIX哲学に基づいたシンプルなプロセスモデル○ Dockerdがないのでrkt process = pod process● 当時としてはまだ新しかったユーザー名前空間の採用やSELinuxへの対応、軽量VMへの対応など夢がてんこ盛りflyと呼ばれる分離とオーバーヘッドが少ないシンプルなモードもあった
Kubernetes 1.3: rktをランタイムとして採用● これまでDockerだけを前提に作られていたkubeletやランタイム周りの仕組みを外部注入可能な形に置き換えるきっかけに● コンテナイメージの署名やコンテナケイパビリティの制限をデフォルトで実装した「secure by default」な実装● UNIX哲学に基づいたシンプルなプロセスモデル○ Dockerdがないのでrkt process = pod process● 当時としてはまだ新しかったユーザー名前空間の採用やSELinuxへの対応、軽量VMへの対応など夢がてんこ盛りflyと呼ばれる分離とオーバーヘッドが少ないシンプルなモードもあったsystemdでプロセスを管理(Podの初期化など)APIサービス経由で任意のコマンド実行したりhttps://speakerdeck.com/surbaniak/meetup-london-2016-rkt-plus-kubernetes
Kubernetes 1.3: rktをランタイムとして採用● これまでDockerだけを前提に作られていたkubeletやランタイム周りの仕組みを外部注入可能な形に置き換えるきっかけに● コンテナイメージの署名やコンテナケイパビリティの制限をデフォルトで実装した「secure by default」な実装● UNIX哲学に基づいたシンプルなプロセスモデル○ Dockerdがないのでrkt process = pod process● 当時としてはまだ新しかったユーザー名前空間の採用やSELinuxへの対応、軽量VMへの対応など夢がてんこ盛りflyと呼ばれる分離とオーバーヘッドが少ないシンプルなモードもあったsystemdでプロセスを管理(Podの初期化など)APIサービス経由で任意のコマンド実行したりhttps://speakerdeck.com/surbaniak/meetup-london-2016-rkt-plus-kubernetesよさそうでは?
Kubernetes 1.5: CRIの登場● これまでDockerに依存していた部分を、(Kubernetesにとって)コンテナを動かすための仕組み、すなわち「CRI(Container Runtime Interface)」として仕様を定義○ ノード上にDaemonを配置○ イメージとコンテナを操作するための gRPCサービス(サーバーの存在が前提 )○ ランタイムを外部ソケット通信にすることで Kubernetes側とライフサイクルを切り離し
なぜCRI?● ランタイムに依存するコードがKubernetes内部に多かった○ rktにしてもDockerにしても、それぞれが持つ APIを呼び出すためのコード○ 各ランタイムのバージョンに依存する修正が発生すると、 Kubernetesに変更が発生○ 各ランタイム自体(特にDocker)はKubernetesだけを対象にしてないので、容赦なしに変更を加えてバージョンアップしてくる● 外部コンポーネント化することで関心を分離しないとメンテナンスが大変○ Kubeletは各ノードにいるので、 gRPC over UNIX Socketな構成が都合よかった○ 結局DockerもMoby projectを発表し、containerdがのちにCRIランタイムとして台頭
こまったrktくん● デーモンレスなプロセスモデル○ KubernetesがCRIを発表したので、なくなく CRIに対応せざるを得ない状況に● ランタイムでの再実装が不要なgRPCによる命令型のAPIモデル○ rktはsystemdでPod(プロセス)を立ち上げてハイ終わりなモデルなので厳しい● rktletとか作っていろいろ頑張るも、2019年にはプロジェクトの凍結が発表された○ CNCFプロジェクトとしては (自分が知る限りは)いまだ唯一の”Archived”プロジェクトhttps://www.cncf.io/blog/2019/08/16/cncf-archives-the-rkt-project/
rktの意思を継ぐもの、OCID(CRI-O)とPodman● Red Hatが2016年に発表したOCIDは現在CRI-Oという名前で現役で活躍○ Linuxケイパビリティの制限やセキュリティ周りの対応など、 rktでも培われたいくつかの思想が垣間見える○ Red Hat OpenShift 4からはデフォルトのランタイムに採用● デーモンレスなコンテナランタイム「Podman」○ Red Hatを主体としたオープンソースプロジェクトとして現在も鋭意開発中○ Docker Compose互換な実装やネットワークへの対応、ユーザー名前空間の実装など、デーモンを使わずによく頑張ってるなと思う
rktの意思を継ぐもの、OCID(CRI-O)とPodman● Red Hatが2016年に発表したOCIDは現在CRI-Oという名前で現役で活躍○ Linuxケイパビリティの制限やセキュリティ周りの対応など、 rktでも培われたいくつかの思想が垣間見える○ Red Hat OpenShift 4からはデフォルトのランタイムに採用● デーモンレスなコンテナランタイム「Podman」○ Red Hatを主体としたオープンソースプロジェクトとして現在も鋭意開発中○ Docker Compose互換な実装やネットワークへの対応、ユーザー名前空間の実装など、デーモンを使わずによく頑張ってるなと思う● 手元のDockerを置き換えるPodman● Kubernetesのために作られたランタイムとしてのCRI-Oそれぞれの違いを理解して使い分けてみるのも良いかもしれません
閑話休題:Dockerをやめるのはどうなのか
Dockerが持つ主な3つのサービス● ランタイムサービス○ Containerdが管理するコンテナを動かすためのコア● ネットワークサービス○ docker network 配下のAPI● ボリュームサービス○ docker volume 配下のAPI
Kubernetesが使うDockerの機能...● ランタイムサービス○ Containerdが管理するコンテナを動かすためのコア● ネットワークサービス○ docker network 配下のAPI● ボリュームサービス○ docker volume 配下のAPIつかうつかわない
Kubernetesが持つ複数のインターフェース● CRI○ ランタイム● CNI○ ネットワーク● CSI○ ストレージ● CPI○ クラウドプロバイダーDockerはどのレイヤとも被りうる・・・
Dockershimの存在https://jaco.udcp.info/entry/2020/12/03/172843CRIの登場によりCRI -> Docker API変換のためのDockershimが登場
CRI対応済みランタイム● Containerd○ Dockerの裏側で動くランタイムサービス○ DockerネイティブなAPIとCRIの両方をサポート● CRI-O○ Kubernetesのためのランタイム仕様である CRIを満たすべく作られたランタイム● Docker(with Dockershim)○ DockershimがなけりゃただのDocker
コンテナランタイムの役割(Docker)dockerddocker pulldocker runREST APIcontainerdgRPCrunCOCI(containerd内でJSONのコンフィグを渡しながらバイナリを直接実行)OCIHigh levelruntimeLow levelruntime
コンテナランタイムの役割(Kubernetes)Kuberneteskubectl runkubectl applyREST APIcontainerdCRI(gRPC)kube-apiserverとかetcdとかkubeletとかいろいろ含む※CRIは各ノード上のkubeletが喋るrunCOCI(containerd内でJSONのコンフィグを渡しながらバイナリを直接実行)OCIHigh levelruntimeLow levelruntime
コンテナランタイムの役割(Kubernetes)Kuberneteskubectl runkubectl applyREST APIcontainerdCRI(gRPC)kube-apiserverとかetcdとかkubeletとかいろいろ含む※CRIは各ノード上のkubeletが喋るrunCOCI(containerd内でJSONのコンフィグを渡しながらバイナリを直接実行)OCIHigh levelruntimeLow levelruntimeCRI: Kubernetesレイヤで動くランタイムOCI: Linux kernelレイヤで動くランタイム
ここまでのまとめ● Kubernetesは当初、アプリケーションをコンテナで動かすために事実上唯一の選択肢だったDockerを採用(Dockerに寄せた実装)● コミュニティの成長とともに、さまざまな実装、アイデア、選択肢が増えてきた● Kubernetesコア(kubelet)における実装及びメンテナンスコストを抑えるための仕組みとしてCRI(Container Runtime Interface)を考案、現在のスタンダードとなった● 分離の過程でOCIという団体、仕様が生まれ、イメージやコンテナを動かすための低いレベルでの仕組みが標準化された
ここから先は時間と体力があれば軽くやります
CRIとOCI、各ランタイムの違いをもう少し● CRIランタイム○ KubernetesからのgRPC命令を受け付けるサーバープロセス○ 各ノード上にプロセスとして動いている (systemd status containerd/cri-o すると見れる)○ コンテナの作成や起動そのものは OCIランタイムにおまかせ● OCIランタイム○ ただのバイナリファイル○ Linuxのcgroups/namespaceと対話してコンテナを生成 /削除する役割○ ただし!!バイナリファイルは実行したらおわりで、その後コンテナのプロセスの面倒をみる役割が必要(なんで???)■ Runtime Shimというやつを使う■ とても良い入門資料 : https://speakerdeck.com/moricho/deep-dive-into-runtime-shim
OCIランタイムの役割
OCIランタイムの分離レベル● OCIランタイムの役割は○ ホスト上で動くコンテナ (プロセス)や、隔離に使う名前空間などの作成と削除○ 分離の方法は一つではなく、 Linuxの名前空間を使う方法もあれば、コンテナを動かすためのVMを作成してその上でアプリケーションを動かすような方法もある■ OCIの仕様を満たしていればわりとなんでもよい
OCIランタイム● runC○ 何も考えてなけりゃみんなが使ってる○ Dockerなどで使われる最も基本形 (Linux namespace + cgroups on host)● gVisor○ Googleが作ったサンドボックスランタイム○ ゲストカーネルを動かしてコンテナに提供。コンテナはホストに直接悪いことができない● Kata container○ Intelのプロジェクトが発端のランタイム○ KVMを立ち上げて分離するので起動オーバーヘッドが大きい代わりに分離レベルが高い● Firecracker○ 今回はややこいので説明なし
runCの仕組みホストマシンLinuxカーネルコンテナ コンテナ コンテナrunCDocker&CRIファイルシステムの展開プロセスの初期化イメージの管理
gVisorの仕組みホストOS (ホストカーネル)コンテナ コンテナ コンテナgVisorのゲストカーネルrunSC
Kata containersの仕組みホストOS (ホストカーネル)KVM(qemu)KVM(qemu)KVM(qemu)Kata runtimeコンテナ コンテナ コンテナホストカーネルを利用してKVMベースのマシンを起動
分離レベルを気にしないといけない理由は何?● runCはカーネルを呼べる権限を剥奪されるとホスト全体に影響がある● マルチテナントなどのユースケースでは分離レベルの高いほかのランタイムを使うことも考慮すべき● Kubernetesでは、1クラスター上で複数のランタイムを使える機能がある○ RuntimeClass■ Podで指定できる■ 神資料↓■ https://speakerdeck.com/makocchi/lets-learn-about-kubernetes-runtime-class■ https://blog.inductor.me/entry/2021/04/30/022505
まとめ● Kubernetesは当初、アプリケーションをコンテナで動かすために事実上唯一の選択肢だったDockerを採用(Dockerに寄せた実装)● コミュニティの成長とともに、さまざまな実装、アイデア、選択肢が増えてきた● Kubernetesコア(kubelet)における実装及びメンテナンスコストを抑えるための仕組みとしてCRI(Container Runtime Interface)を考案、現在のスタンダードとなった● 分離の過程でOCIという団体、仕様が生まれ、イメージやコンテナを動かすための低いレベルでの仕組みが標準化された● さまざまな要件に答えるために複数の違うアプローチを取ったランタイムがコミュニティから提供されている
おわり!!!!!