Red Hatが新しく作ったVirtual Machine Monitorの「libkrun」

太田航平氏:これから「10分で完全理解したかったlibkrun」という話をしていきます。よろしくお願いします。

まずちょっと自己紹介をすると、太田と言います。ふだんインターネットではinductorという名前で活動していて、本職は日本ヒューレット・パッカード合同会社という会社で、DockerやKubernetesを使った基盤のアーキテクトをしています。

コミュニティ活動は「Container Runtime Meetup」とか「Cloud Native Days」というカンファレンスの運営をやったり、あとは「CNCF」という謎の団体のアンバサダーをやったりしています。好きなことは無限にスケールするインフラ(無限にスケールするとは言っていない)です。

今日はけっこうギリギリまでスライドを作るのをさぼっていたのですが、内容としては「コンテナが好きな素人が、ちょっと低いレイヤーに手を出そうとしたらぜんぜんわからなかった。つらい。」みたいな話をしようとしています。意図としては、低レイヤーに気軽に手を出しても意外とわかるところがあっておもしろいよというきっかけにしてもらえるといいなというところです。みなさまにツッコミとアドバイスをもらえるとうれしいなと思います。

そもそもタイトルにあるlibkrunは、Red Hatのエンジニアが作り始めたVirtual Machine Monitor(VMM)のことです。VMMとは何ですか? という人は、僕が前回のKernel/VM探検隊 part1で話した「コンテナ目線で考えるUnikernelとmicroVM」を読んでもらえるとだいたいわかるんじゃないかなと思います。

これもmicroVMの一種で、最低限のデバイスエミュレーションとライブラリとして使えるように、CのAPIを備えたRust製のMicroVMになっています。実装のけっこうなパートがFirecrackerとかrust-vmmとか、別のOSSから拝借している部分が多くて、デバイスとしてはvirtioのconsole、fs、vsockとかをサポートしています。

Firecrackerとかからもけっこうなコードを取っていて、なんでわざわざ既存のコードをたくさん使ってまでこんなものを新しく作ったんだろうなと、最初に疑問に思って調べ始めたのがきっかけです。どうやらmacOSでも動くらしいのですが、僕は今Macをもっていないので未確認です。

あとはCのAPIがあるので、ライブラリとしても使えて、実際にcrunというRed Hatが開発している低レベルのコンテナランタイムでも実験的にサポートを開始しています。

特徴としては、ネットワークコンポーネントやストレージの部分でけっこう新しいアプローチを実装をしています。ネットワークだと「革新的な方法」のTransparent Socket Impersonationを実装していて、libkrunfwという依存関係のライブラリがあります。

見てみると、ライブラリとして使えるLinux関連のバインディングをしているもので、ライブラリが依存関係で、この中でネットワーク回りの実装とかをしているそうです。

作っている本人が、M1 MacでLinuxが直接動いているみたいなことをツイートしていておもしろいなと思いました。

コンテナに特化したミニマリストな実装

デモなんですが、ちょっと僕の手元の環境だとエラーで動かなくてデモができない致命的な状況です。ただいくつか触ってみて、コンテナのプロセス分離をVMがやるためのツールであることがわかりました。なので、Virtual Machine Monitorと言いつつ、QEMUみたいなジェネリックなバーチャルマシン用の実装ではなくて、コンテナに特化したミニマリスト思考な実装になっています。

exampleの手順の中だと、PodmanというRed Hatが作っているDockerみたいなものが導入が必須であって、それの上で別のルートファイルシステムをコンテナ用に展開して動かすみたいなことをやっています。さっきも言いましたが、crunとのインテグレーションがサポートされていて、crunを動かす時のランタイムに、このlibkrunを指定することができて、いい感じにVMを切り出せるようになっていました。

コンテナは普通、プロセス分離をLinuxカーネルの中でnamespaceを切ってやるんですが、この場合はVMを立ててやっているんですね。サンドボックス型のランタイムに分類できるので、gVisor、Kata container、あとはここで分類すると若干語弊があるんですがFirecrackerなど、このあたりの実装に非常に近いというかたちになっています。

「libkrun」ではコンテナ間でnamespaceを共有して使う

これらの既存のVM型ランタイムなどがあるにもかかわらず、なんでわざわざこれを作ったかのかは、本人がカンファレンスで発表した資料の中に詳しく書かれています。既存の実装では、Podという複数のコンテナを1つのコンテキストにまとめるオブジェクトがあって、そのオブジェクトごとにnamespaceが切られるんですね。

実装の都合でnamespaceごとにVMを立てているので、例えば複数のコンテナを有するPodの場合、VMがその複数のコンテナを内包するかたちで1つ立ち上がってきます。実際のコンテナプロセスはKVMの中でさらに子プロセス的な扱いで、KVMを1つのホスト上のプロセスと見た時にさらにその子プロセスとして2つ生えてきます。

Dockerで実際にKata containerを使ってやってみると、Dockerの後ろでcontainerdが動いて、containerdの中でFirecrackerのVMがKata containerによって作られて、さらにその後ろでkata-shimというruncみたいなものを動かしているやつがいて……とポンポンとチャイルドプロセスがいくつか生えているのがわかります。

これが新しい実装のlibkrunだと、コンテナごとにVMが立ち上がります。このコンテナ間でnamespaceを共有して使うようになっているので、もっと隔離性が高いよみたいなことを謳っています。

これがVM間の連携の図です。virtioのvsockを使ったネットワークの実装と、virtioのfsを使ったボリュームの確認を行ったあとでnamespaceを共有して、いい感じに連携しているらしいです。僕はきちんと中身を読めていないので、実際にこれがどうなっているかまではごめんなさい、わからないです。

「VMをPodごとに作らない」のは、コンテナレベルまで分離ができるので、namespaceの中にいるコンテナを動的に管理できるのがメリットかなと思いました。異なるVMで実行されている場合においても、これらのマウントポイントを使ってコンテナ間で通信ができるので柔軟性があります。なので、コンテナのPodの中の構成がごちゃごちゃ変わってしまった時に、VMごとに作り直さなくても動的に管理ができるのがメリットなのかなと思います。

Deeper diveしたいところですが、さっき言ったとおりデモをしたくても僕の環境では動かせませんでした。CentOSとかFedoraとか、いろいろと使ってもダメだったので難しかったのと、そもそも既存の仮想化や既存のランタイムをそこまで深く理解できていなくて、Rust力が欠如していたので何もわからなくなってつらくなってしまいました。

これ以上のことは、開発者のスライドにも詳しく書かれているので気になる方はそちらもご覧ください。そして、スライドやソースコードを読んだ暁には僕にも知見を共有してほしいなと思っています。今回の発表は、紹介しつつ僕より詳しい人に教えてほしいというお願いの発表でした。というわけで以上です。ありがとうございました。

※掲載当初、一部「型コンテナ」となっていましたが、Kata containerの誤りでした。お詫びして訂正いたします。