runc を動かしてみる

次の業務で runc が使えそうだったので、使用感を得るために試してみた。

※ runc について一部勘違いしていたので、公開時の記載より該当箇所を修正しております。ブコメにてご指摘頂いた @mapk0y さん、ありがとうございました。

runc とは?

github.com

runc とは、OCI に準拠したコンテナランタイムである。 コンテナと聞いて一番最初に思い浮かぶのは Docker だが、Docker も containerd 経由で runc を呼んでいる。

runc は、ワンタイム実行可能なコマンドラインプログラムとして実装されている。 runc はシェルコマンド経由で実行可能で、コンテナは runc のサブプロセスとして動作する。 そのため、コンテナを動かすための準備も不要だし、Docker で指摘されることが多いセキュリティ面での問題も runc レベルでは該当しない。

runcとは、OCI に準拠したコンテナランタイムである。 コンテナと聞いて一番最初に思い浮かぶのは Docker だが、Docker は一つの統合プラットフォームである。 比較するのであれば、Docker の標準ライタイムとして採用されている containerd と比較するのがレイヤ的に正しい

containerd との違いは、デーモンプログラムでない点である。 Docker では、コンテナランタイムとして containerd をデーモンプロセスとして走らせ、そのサブプロセスとしてコンテナを動作する。 一方の runc は、ワンタイム実行可能なコマンドラインプログラムとして実装されている。 runc はシェルコマンド経由で実行可能で、コンテナは runc のサブプロセスとして動作する。 そのため、コンテナを動かすための準備も不要だし、Docker で指摘されることが多いセキュリティ面での問題も該当しない。

ちなみに、rkt も runc と同じくワンタイム実行可能なコンテナランタイムとして知られているが、 rkt はランタイム機能に加えて、コンテナのライフスタイル管理やイメージ管理といった統合プラットフォームとしての機能も備えている点で違っている。

runc でコンテナを動かすにはどうするか?

ざっと以下の手順をとる。

  • runc バイナリのビルド
  • イメージの準備
  • config.json の作成
  • コンテナの実行

runc コミュニティは生成済みのバイナリを配布していないので、自分でビルドする必要がある。 自分でビルドすると聞くと「コンパイルエラーが出たらどうしよう」と身構えてしまうが、 go build の1行を実行するだけなので大したことはない。 ちなみに、libseccomp がシステムにインストールされていないとエラーがでるが、 特に seccomp の統合機能が必要なければ、単にこの機能を外してしまえばよい。 この機能を外すには、環境変数として BUILDTAGS を空文字列で渡すことで実現できる。

runc ではコンテナイメージとして、ファイルシステムのルートとしたいディレクトリを指定する。 特定の Docker イメージを runc で動かしたい場合は、export して tar 展開すればよい。 ディレクトリパスはデフォルトで ./rootfs だが、後述する config.json で任意のパスを指定することもできる。

// busybox イメージを変換する例
docker export $(docker create busybox) | tar -C rootfs -xvf -

config.json は、イメージのメタデータを指定するためのものである。 runc spec コマンドでデフォルト設定のものを生成できるので、そこから始めて必要な設定だけ変更するようにすればよい。 ちなみに Docker と違って、環境変数の指定やコマンドパラメータの指定も config.json で行う。 コンテナ実行時にアドホックに渡すようなことは出来ないので気をつけること。 若干不便なので、頻繁に使うようであればラップしたコマンドでサポートするなど、なんとかした方がいい気がする。

systemd との連携

systemd をスーパバイザとして使う方法がよく紹介されるので、これについて補足する。 Docker や rkt と違って、runc 単体ではライフサイクルが管理できず機能不足である。 そこで systemd サービスの一つとして runc を実行するコマンドを用意しておくことで、 任意のコンテナアプリをサービスとして振る舞わせることができる。