自作Linuxコンテナの時代

最近、Docker以外のコンテナ型仮想化技術の流れとして、自作コンテナエンジンの時代が来るのではないかと感じている。

自作コンテナエンジンとは、コンテナ型仮想化技術を構成する個々の要素技術を組み合わせ、自分の用途にあわせて最適化したコンテナエンジンのことだ。 他のOSのコンテナ仮想化技術について疎いため、以下ではLinuxに限定して話を進める。

概要

Dockerも含めて、Linuxコンテナはコンテナを構成する複数の要素技術の組み合わせである。自分のやりたいことに対して、Dockerをはじめ既存のコンテナエンジンが複雑すぎるケースがある。そこで、自分の用途にあわせてコンテナエンジンを自作することを考えてみる。libcontainerに代表されるように、Linuxコンテナエンジンを自作しやすい環境が整いつつある。今後は、巨大なコンテナエンジンに対して、UNIX哲学に基づいて制御可能な小さなコンテナエンジンを自作する道もあるのではないか。

自作Linuxコンテナ

Dockerの登場により、コンテナ仮想化技術がいわゆるウェブエンジニアにとっても身近なものになってきた。

しかし、コンテナとDockerはイコールではない。 Dockerはコンテナという仕組みそのものではなく、既存のLinuxコンテナの要素技術を応用したソフトウェアだ。

Dockerはコンテナ型仮想化技術の中でも、最大レベルの複雑さを持つと思う。 コンテナの基礎となるLinux Namespacesに加えて、プログラマブルなAPI、Dockerfile、レイヤ構造をもつイメージフォーマット、DockerHub/Registry、、Machine/Compose/Swarm、最近ではオーバレイネットワークなどもある。KubernetesのようなDockerを前提とした3rd Partyのソフトウェアを合わせると大変なことになる。

これだけ複雑なソフトウェアであれば、用途によってはオーバテクノロジーとなりうる可能性がある。 既存の運用と整合性をとるのも一苦労だ。

そこで、コンテナを使いたい(隔離環境を作りたい)ときにDockerを使わずに、用途に合わせてコンテナエンジンを自作するという選択肢もある。 例えば、kazuhoさんのjailingはその一つだ。

jailingは、ホスト環境の一部(/usr/libや/etc/hostsなど) を共有したchrootベースの手頃なjail環境を提供してくれるスクリプトだ。 外部公開するサーバソフトウェアをセキュアに保つためになるべき隔離した環境で動かしたい。 しかし、Dockerやその他の既存のコンテナエンジンだとサービスごとにシステム環境を用意しなければならないため、特にディスク容量を消費する。 ホスト環境を共有するjailingであれば、ディスク容量の消費をある程度抑えられる (ディスク容量以外にもDockerや既存のコンテナエンジンはいろいろ面倒な点はある)。

他にも、Linuxコンテナの要素技術を組み合わせた軽量なコンテナエンジンはたくさんある。

rconは任意のコマンドに対して、cgroupにより、各種リソース(CPU、メモリなど)を隔離・制限するコマンドラインツールだ。 cgroup単体でコンテナと呼ばれることはあまりないのかもしれない。rconの場合、与えたコマンドをリソース制限をかけたjailに閉じ込めるようなニュアンスをもつため、広義のコンテナといってよいのではないかと思う。

virtuald は古くからあるらしいが結構おもしろい。理解が間違っていなければ、chrootとinetdの組み合わせで、NICとIPアドレスの割り当てを意識せずに、ホストに設定した複数のIPアドレスごとに異なるサーバソフトウェアをサービスできる。IPアドレスごとにchroot jail環境を割り当て、あたかも1つのホスト上に異なるIPアドレスをもつホストが存在するようにみせている。

発想がどれもおもしろくて、どの要素技術をどう組み合わせるかが工夫のしどころだ。 自分ではDrootというのを作っている。

yuuki.hatenablog.com

自作コンテナを作るためには

自作コンテナを作るためには、Linuxコンテナの要素技術の理解と、それらを扱うためのライブラリやモジュールが必要だ。

自作コンテナといっても、機能が多くなければ、実装するのはそれほど難しくない。 スクリプト1枚で十分ということもある。

ただし、作るもののアイデアはLinuxコンテナの要素技術をある程度理解していなければでてこないと思う。

ライブラリやモジュールがなければ、生で叩くか自分で作る必要がある。chroot程度であれば生で叩く程度でも十分だ。 幸いDockerの実装言語であるGo言語にはLinuxコンテナを扱うためのパッケージが整っていると言える。

Linuxコンテナの要素技術

Linuxコンテナについては、@ten_forward さんの資料がわかりやすい。

簡単にまとめると以下のようになる。

Linuxコンテナといっても、カーネルにコンテナという単一の機能があって、コンフィグを有効にすると使えるというものではない。 広義には、Linux Namespacesを中心に、cgroup、chroot(pivot_root)、Linux capabilities、veth、macvlan、Unionファイルシステム(AuFS、OverlayFS)などの、コンテナの要素技術を組み合わせて、OS内に作成したなんらかの隔離環境をコンテナと呼ぶ。

cgroupやLinux capabilities、veth/macvlanなどは本来コンテナ専用の機能ではない。 コンテナエンジンと組み合わされることの多い技術をコンテナの要素技術と呼ぶことにする。

狭義には、LinuxコンテナはLXC単体かLXC関連のプロジェクトを指すこともある。

より詳しくは同じく @ten_forward さんの連載の第1回から第6回を読むとよい。

拙作のDrootの場合は、chroot、Bind Mount、Linux capabilitiesを併用している。

Go言語のコンテナ向けパッケージ

Dockerから派生したlibcontainerなど、Dockerのおかげでコンテナを自作するための足回りは揃っていると思う。 コンテナに限らず、システムプログラミングや運用ツールの作成をする上で役に立つものも多い。

これからのコンテナ仮想化技術

id:matsumoto_rさんの資料の最後にあるように、コンテナによりホスト環境をプロセスと同レベルに高速に作れるようになり、OS機能がホストの外まで展開されるようになる。 OSの上にOSを作るようなものかもしれない。 これは、Mesosphere が提唱するDC/OSに通じるところがある。

一方で、DC/OSを実現するようなソフトウェアであるMesosphereやKubernetesは、内部的に複数のコンポーネントに分かれているとはいえ一つの巨大で複雑な仕組みを提供するようにみえる。 ただでさえ複雑なOSの上に、さらに複雑な層を設けて、その上にアプリケーションをのせるという考え方で、人間がシステムの挙動をコントロールできるのか疑問はある。

ウェブ業界の歴史も長くなり、10年以上動き続けるシステムも珍しくなくなってきた。 ソフトウェアにはバージョンがあり、10年間動き続けるシステムを支えていくためには、ソフトウェアをアップデートしつづけていくことになる。(ハードウェアも当然壊れるので、新しくし続けていく必要がある。)

一つの大きなソフトウェアをアップデートするのは大変だが、UNIX哲学に基づいた小さなソフトウェアをアップデートしたり差し替えることは比較的容易だ。 コンテナをプロセスとして見立てるという発想はそのままに、一つ一つコントール可能なものを組み合わせて、全体でなめらかなシステムを作るのが理想だと思う。

コンテナエンジンも用途にあった制御可能なものを自作して組み込めばよいのではないか。

ちょうど似たような構造がウェブアプリケーションフレームワーク界隈にもある。 Railsのようなフルスタックのフレームワークか、小さなモジュールが組み合わさった最小の機能を提供するフレームワークか、という話だ。 特にPerlでは後者の傾向が強い。これについては、はてなで大規模サービスのインフラを学んだ - ゆううきブログ でも触れている。 はてなだとプロジェクトごとに最小化したPlackベースのフレームワークを作成している(はず)。 もしフルスタックのフレームワークの採用を続けていたら、フレームワークをバージョンアップし続けるのはおそらく厳しかったのではないか。

参考

Linuxプログラミングインタフェース

Linuxプログラミングインタフェース

あとがき

先日、福岡のペパボオフィスで開催された第9回コンテナ型仮想化技術@福岡に登壇させていただいた。 都合3度目の登壇となる。 最初に登壇したのは2年前の第3回で、普通にDockerの話をした気がする。このときは、Linux Namespacesがなにか知らなかった。 次の参加はその年の11月で、その時はDockerのパフォーマンスの話をした。このときぐらいから、Linux Namespacesが少しわかってきた。 今回はコンテナを自作する話になった。 少しずつ、Linuxコンテナの要素技術そのものに近づいてきた。

実は今回登壇するまで、自作コンテナという概念を考えていたわけではなかった。 よくも悪くもDockerに囚われていたため、Docker思想である「Build, Ship, Run」を部分的にchrootで実現するという程度の気持ちだった。 当日、福岡へ向かう新幹線でこの発表って結局なにが言いたいんだっけと考えて、自然と「コンテナは自分で作れる」というスライドを足していた。 id:matsumoto_r さんの発表を拝聴したり、その後の懇親会でお話させていただいてその考えは深まった。

京都に帰ってきて、mizzy(@gosukenetor)さんがおっしゃっていたことを思い出す。

最近は、ユーザランドのサーバソフトウェアが各々実装しているような機能をカーネルの汎用機能で置き換えられないかを考えている。 ジャストアイデアだけど、例えば、ウェブアプリケーション開発に新言語を採用したときにインフラで考えたことで書いたように、preforkが運用しやすいならば、preforkに対応できないサーバソフトウェアをコンテナに入れて、複数のコンテナを起動し、Linux IPVSで内部分散してしまえばよい、といったことだ。定期的にプロセス(コンテナ)を生成しなおす仕組みやGracefulに再起動する仕組みをどうするかはまだ考えているところだが。IPVSのルーティングを動的にweight 0にしてActiveConnが0になったらコンテナごと捨てて作りなおすというところまで思いついた。これができればいわゆるワーカープロセス単位でリソース制限することも簡単だし、harakiri的なことも簡単にできるかもしれない。

発表資料