2014年5月12日
コンテナ型仮想化の技術として注目されているDockerの勉強会「Docker Meetup Tokyo #2」が4月11日にグーグル東京オフィスで開催されました。
この勉強会には定員100名のところへ400名を超える申し込みがあり、参加できなかった方も多かったと思います。本記事では、最初のセッションとして行われた森和之氏による「今からでも間に合うDocker基礎+Docker 0.9概要」をダイジェストで紹介しましょう。
今からでも間に合うDocker基礎
株式会社トップゲートの森和之(@mainyaa)です。

手元の開発環境では動いていたプログラムが、プロダクション環境にデプロイしてみたら動きませんでした、というのは誰でも経験すると思います。
環境変数が違っていたり、実行環境のバージョンやライブラリのバージョンが違ったり。しかしプロダクションの環境にログインしてこれらを操作する、ということはやりたくないですよね。
また、Amazon Web ServicesやさくらインターネットのVPSなどいろんな実行環境があって、でも特定の環境にロックインされないようにするにはどうすればいいでしょうか。

そのためにDockerがあります。
Dockerは何を解決するのか?
Dockerとはオープンソースの次世代仮想化技術で、アプリケーションを「コンテナ」にパッケージングして、デプロイ、実行できます。
Go言語で書かれていてLinuxカーネルのみに依存します。

Dockerが何を解決するのかというと、デプロイの作業をもっと手軽にしようということです。
DockerfileにOSのスクラッチイメージからライブラリ、環境変数などのパッケージングするものを書きます。それをビルドして実行してテストしてデプロイするだけで、手元で動いていたものをプロダクション環境にそのまま持って行って動かすことができます。
つまりDocerfileによってインフラをコードとして扱えるんですね。
Dockerはコンテナ型仮想化を実現するのですが、仮想化と言ってもVirtualBoxとかVMwareなどの仮想化とは違っていて、コンテナ型仮想化はビルドもデプロイも高速、オーバーヘッドも少なく、しかし仮想化なのでプラットフォームやハードウェアからは隔離された環境となります。
問題が起きたときもDockerは威力を発揮します。本番とまったく同じコンテナを手元で動かせるので、ローカルでテストができます。

すべてのコンテナには固有のIDがあります。
Dockerfileの1行ごとにコンテナとコンテナIDが作られ、コンテナは親からの差分を保存していきます。例えば一番ベースとなるコンテナからapt-getなどをすると、その差分だけを保存することになります。何かに失敗したらgitのように失敗する前に戻すことができます。
例えばアプリケーション、DB、ネイティブツールといったものをコンテナに分けてそれぞれをデプロイして、動かなかったときにはそれぞれのコンテナごとに、依存するライブラリのバージョンなどを丸ごとロールバックできます。

DockerというのはDockerfileでインフラをコードとして管理できて、ゲストOSがなくカーネルを共有しているのでオーバヘッドが非常に少ない。
Dockerビルドをするときには、DockerfileでコンテナIDがすでに振られて実行済みのものは再実行せずキャッシュを利用します。つまり変更された部分からだけが新たに実行されますし、変更分も親からの差分なので非常に小さくて済みます。
DockerコンテナとLinuxコンテナはちょっと前まで同じものでしたが、最近、DockerはLinuxコンテナへの依存がなくなりました。Docker 0.9からはLinuxコンテナとはノットイコールになりました。

VM(仮想マシン)とコンテナの違い
これはVMとコンテナを比較した図ですが、VMではVMごとにゲストOSがあって、その上にライブラリとアプリが載っています。
右のコンテナではホストOSのうえにDocerエンジンがあって、その上にライブラリが横に伸びていますが、これはコンテナが同じライブラリを共有しているんですね。なので少ディスクスペースかつ少メモリ、それでいて隔離されている環境を実現します。

ここでいうコンテナとはなにか? コンテナとはハイレベルから見ると軽量VMにみえます。プロセスが隔離されているし、ネットワークも独自に振られていて、リソースも分離されているしrootとして実行できます。
さらにディスクにも書き込めますし、ログはすべてstdout/stderr/stdinに出てくるので、普通に見れば軽量VMに見えます。

一方でコンテナをローレベルから見ると、chrootの強化版なんです。1コンテナ=1プロセスなので、ホストOSからpsコマンドを叩くとDocker上で動いているプロセスがそのまま見えます。ですから非常に管理しやすい。
そしてカーネルをホストと共有していて、VMでも準仮想化でもなく、エミューレションはありません。また、Linuxのリソース制限のツールcgroupsで、CPUやメモリなどのリソース制限ができます。

デベロッパーと管理者にとって、コンテナとは
デベロッパーにとってのコンテナは隔離環境なので、この中だけを整備することに注力すればいいことになります。
アプリケーションのコードや依存ライブラリ、パッケージマネージャ、アプリ、テストデータなど、そういうものをコンテナに入れてコンテナの中で動くようにする。それをデベロッパーは気にすればいい。

システム管理者にとってコンテナとは何かというと、システム管理者はコンテナの中身は気にする必要はなくて、コンテナの外、つまりログだったりリモートアクセスだったりネットワーク設定だったりリソースモニタリング、コンテナにアクセスが集中したら別のインスタンスで動かすようにするとか、そういうことだけを気にすればよくなります。

Dockerfileとは何か
Dockerfileとは何でしょうか。
Dockerファイルとは、OSのスクラッチイメージからアプリが動くまでを書いたコードです。Dockerはビルド時に、Dockerfileの1行ごとにコンテナを作ります。で、Sha1ハッシュのコンテナIDを割り当てます。
Dockerfileの例です。1行目の部分がゲストOSのOSイメージで、そこから1行ずつ実行してそれが成功かどうか、成功だったら次へ進む、失敗だったらそこで止まってコンテナIDを返します。
一番下まで行ったらEXPOSEでポートを開放したり、ENTRYPOINTを指定してコンテナがどのプロセスを起動するかを指定したりします。

AUFSとは何か
Dockerのかなり重要な要素としてAUFSがあります。これはUnionFSの実装の1つなのですが、UnionFSとはメモリ上に配置するコピーオンライトのファイルシステムです。親のファイルシステムをすべてリードオンリーにして、その上にライタブルなレイヤを重ねてレイヤを通して1つのファイルシステムのように扱います。
この親コンテナからの差分のみを保存していくというところがDockerのキモです。

例えば1GBのOSを10個動かそうとすれば、仮想マシンではディスクスペースはOSが10個分で10GBを使いますが、Dockerなら親のOSイメージを共有するので1つ分の1GBで済みます。
これがAUFSのイメージで、一番下にカーネルのコードがあって、その上にルートのファイルシステム、例えばDebianがあるとすると、このDebianのレイヤに書き込むことはありません。その上にどんどんDockerfileの内容ごとに実行結果をコンテナIDを割り振って重ねます。
常に親からの差分をどんどん重ねていきます。

Dockerの制約ですが、いまのところDockerはLinuxでしか動きません。カーネルは3.8以降、64ビットマシンのみが対象です。バージョンはまだ0.10なので運用面ではセキュリティの課題が残っているので、まだプロダクション環境で動かすのはちょっと早いですね。
まとめです。

YouTubeによる録画「Docker Meetup Tokyo #2」「Docker meetup Tokyo #2 - 2」
≫次の記事
Docker 0.11が初めての正式版候補としてリリース。来月には1.0が登場か?
≪前の記事
アンディ・ベクトルシャイム氏が開発中の次世代フラッシュストレージを推測してみると、EMCはサーバベンダとなる