inductor's blog

nothing but self note :)

DockerのデフォルトランタイムをrunCからKata containers + Firecrackerに変えるのが簡単すぎてビビった話

はじめに

これはコンテナランタイム好きのオタク記事です。DockerでrunC以外のランタイムを動かしてみようと思ってはいたもののずっとやれてなかったので、実際にやってみたら超簡単でした、という記事です。

ランタイムを変える意味について

Dockerでは、コンテナを作成する機能をrunCという低レベルコンテナランタイムを用いて実現しています。

runCではLinuxカーネルの機能を呼び出すためにホストOSの特権を利用しますが、これはrunCの命令を実行する瞬間に悪意のあるコードを実行されると、ホストの特権を攻撃者に奪われてしまうリスクもあります。詳しくはrunCのこのへんとかを読んでみるとおもしろいかもしれません(?)

ところで、runCというランタイムはOCIの標準仕様に基づいて作られています(厳密には、LXCを置き換えるために作られたrunCが持っている機能を標準化したものがOCIという認識)。

OCI Specの低レベルコンテナランタイムにはいくつかの種類があって、その中で今回はKata Containersについて扱います。これに関してはPublickeyさんのこちらの記事に非常に詳しく書かれているので割愛しますが、要するにKVMベースの仮想マシンをホストOSの上で動かして、その上でコンテナを立ち上げ、それによってホストOSが持つホストカーネルとの隔離性を高めることができるのがこのKataの特徴です。

KVMはVMなので立ち上がってくるのに時間がかかったり、リソース消費量が通常のコンテナよりも多いといった問題があります。また、VMの上で立ち上がるOSの動作に関してはKata自体で保証することはできないという問題もあります。

QEMUはKVMの中でも最も多く使われているソリューションですが、コンテナを動かすことが目的で作られていないために不要な部分が大きく、また動作も「軽量」とは呼べません。

そこで、Firecrackerが選択肢に上がってきます。FirecrackerはAmazonが作ったマイクロVMで、AWSの中ではFargateやLambdaといったサービスの基盤を担っている技術でもあります。

先程のPublickeyのニュースではちょうどこのFirecrackerがKataで動くようになったというニュースなのですが、これが当時試したときには(細かいことは忘れましたが)エラーで動かず萎えた記憶があります。ところが、今日さっき動かしてみたらメチャクチャ簡単に動いてびっくりしたので、今この記事を書いているわけです。

実際にやってみよう

ランタイムを変える操作はDocker Desktop for MacやWindowsでは恐らく保証されないので、Linux上にDockerを入れてやってみましょう。今回はUbuntuですが、基本的には他のディストロでも変わらないはずです。

やってみた例はいくつかネットにも記事が上がってますが、古いので参考にならない部分もありました。また、Kata公式のGitHub Wikiも内容が古いので、細かい制約などは全部すっ飛ばしています。特にバージョン問題などはこちらでは発生しませんでした。

  1. まずはお手元のUbuntuでDocker CEを入れます

手元でのバージョン

$ docker -v
Docker version 19.03.12, build 48a66213fe
  1. Kataの最新リリースを落としてきて、/opt配下に展開します(勝手にoptに展開されます)
$ curl -LO https://github.com/kata-containers/runtime/releases/download/1.11.2/kata-static-1.11.2-x86_64.tar.xz
$ sudo tar xvf kata-static-1.11.2-x86_64.tar.xz -C /
  1. /etc/docker/daemon.jsonを以下の内容に書き換えます
{
  "default-runtime": "kata-fc",
  "runtimes": {
    "kata-fc": {
      "path": "/opt/kata/bin/kata-fc"
    },
   "kata-qemu": {
      "path": "/opt/kata/bin/kata-qemu"
    }
  },
  "storage-driver": "devicemapper"
}
  1. Kata + Firecracker構成で使うらしいので、vsockモジュールを読み込みます(お使いのUbuntuの基盤が対応してることを確認してください)。
$ sudo modprobe vhost_vsock
  1. Dockerデーモンを再起動します
$ sudo systemctl daemon-reload
$ sudo systemctl restart docker
  1. 試しにコンテナを動かしてみましょう
$ docker run --rm -it -d alpine ash
d6fc55e4962b3b935e4b15662256f21f4c58a8ae6b331aa103944ba67c61b428
$ ps -ae | grep -E "kata|fire"
  16651 ?        00:00:11 firecracker
  16656 pts/1    00:00:00 kata-shim

Kataとfirecrackerが動いていることが確認できます。

Qiitaで検証されていた方の記事ではFirecrackerのsocketに関して言及がありますが、これはFirecrackerがREST APIで操作できる設計になっているからですね。あまりドキュメントなどは出回ってませんが、ネットワーク越しにVMをぽんぽん立てたり消したりするのはLambdaなどでもよくある操作だと思うので、Firecracker自体がこのような作りになっていることは至極当然のように思えます。

さいごに

Dockerがデフォルトで使っているrunCは危ないけれどもランタイムを入れ替えるのはめんどくさいな・・・と思っている方は、これを機会に他のランタイムにも手を染めてみてください(沼への誘い)

以上、今日はこのへんで。