コンテナを使う動機　なぜDocker（コンテナ）が期待されるのか

## はじめに

この文書は半ば内輪の勉強会向けのものを公開資料としたものです。いまさらですが、

* Dockerって何ができるの？
* なんで使うの？

みたいな問いに答えてみよう、と書いてみました。

コンテナについて私個人の考え方は依存するものをすべてスタティックリンクした実行形式のファイルに近いものだと考えています。そう考えるに至ったプロセスを順に書いていきたいと思います。

### 昔話

その昔のアプリケーションはさほど複雑なものでは無かったので一つの実行形式ファイルに収まるものでした。

図1
![](https://cacoo.com/diagrams/V55Sg3UVjneVSwQv-83504.png)

依存するライブラリもそんな多いわけでなく、コンパクトだったので「全部ひとまとめにしてしまえ」という考え方です。
（Go言語はそんな考え方ですね。）

### スタティックリンクされたファイルはでかい

スタティックリンクをしてひとまとめにするというのはシンプルな反面ファイルサイズが大きくなります。キャンプに行く時に全員がバーベキューセットを持ってくるようなもので、インストールされるアプリケーション各々が依存ライブラリを持ち寄ると必然的にサイズが大きくなります。
サイズが大きくなるとディスク使用量も増えますし、配布時にもCD-ROMに収まらなかったり、細い回線ではダウンロード時間が長かったりして困るのです。

### ダイナミックリンク

そこで生まれたのがダイナミックリンクという考え方です。バーベキューセットは共同装備で誰かが一つ持ってきてみんなで共同利用すればいいですよね。それと同じでよく使われるライブラリはマシンに１個置いて共有してしまいましょうという話です。

図2
![](https://cacoo.com/diagrams/V55Sg3UVjneVSwQv-88979.png)

「ダイナミック」と言われる由来は実行時に動的に依存ライブラリへのリンクが行われるためです。実行するときに初めて依存ライブラリのファイルをメモリに読み込む方式です。スタティックリンクが最初から全部入りラーメンなのに対して、ダイナミックリンクは後のせトッピング方式です。

### ダイナミックリンクのデメリット

ダイナミックリンクはデメリットとしてインストールが面倒になります。実行ファイル１個で済んでいたのが複数のファイルになるので気にすることが増えてしまうのが欠点です。
先ほどのラーメンのトッピングの比喩ではでメンマみたいにどっちでもいい物（メンマに失礼…）かと思われるかもしれませんが、依存ライブラリは必要条件です。必要になったら後のせなのですが、「無いの？　じゃあもういいわ」というわけには行かず、「無いの？じゃあオレ仕事しないから！　(｀ω´#)ﾌﾟﾝｽｶ！」となってしまいます。

そのため、下記のようなことを気にしなければなりません。

* 依存ライブラリのファイルを事前にセットアップしておく
  * 別手段でライブラリを導入しておく
  * または、アプリケーションと同時に配備する  
   →その場合は他のアプリケーションでも使われることを考えた配置
* ライブラリは共有なので他のアプリケーションで使っているのを気にしなければならない
  * ライブラリのバージョン管理  
   → 別のアプリケーションを入れたおかげでライブラリのバージョンが変わって動かなくなるとか

即ち、依存性の管理が必要となってきました。

### さらなる複雑化

上記のようなデメリットはあるもののダイナミックリンクは現在においては主流の考え方となり、yum,apt,gem,pipと言った「AをインストールするにはBとCが必要」という依存解決をするパッケージ管理が生み出され、デメリット解消されています（多少うまくいかないこともあるでしょうが）。
ダイナミックリンクによる組み合わせの容易さからソフトウェアの世界は急速に進化していきました。

これでハッピーエンド？ いえ、最近のアプリケーション（アプリケーションというよりサービスと言った方が良いのかもしれません）はさらなる複雑化をしてきて、複数のミドルウェアを組み合わせて動作するようになっています。

* NginxとRailsアプリケーションを組み合わせる
* Railsアプリは複数のプラグインやGemに依存する
* 環境変数や設定ファイルを書く

組み合わせ自由の恩恵の代償として動作環境構築が面倒くさくなってきたのです。

### 構成管理ツール 〜面倒なら自動化すればいいじゃない〜

上記の問題を解決するために生まれた一つの手段がChefやAnsibleと言った構成管理ツールです。ここでは詳しくは書きませんが、これらのツールは環境の構築を自動化してとても楽にしてくれるものです。事前に構成情報を形式化されたテキストファイル（レシピ、プレイブックなどと呼ばれます）に書いておくことで10台でも100台でも同じ手順でサーバーを自動で構築してくれます。

### 難癖つけます

これでメデタシメデタシ…と言ったらここで話が終わってしまうので難癖をつけさせてもらいます。
正直言ってサーバー仮想化技術で十分なケースもありますが、コンテナがより良い解法となり得ますというところに持って行こうとしているのでご了承ください。

1. サーバーリソースの無駄  
   サーバー一箱に一つのアプリケーションというのもちょっと贅沢(？)  
    * 仮想化技術によってそうでもなくなってきているけど、リソースオーバーヘッドが大きめ
    * サーバーごとにOSが必要なのでOSが食うメモリ、ディスク量が必要
    * 仮想化はハードもエミュレートするのでその分の処理コスト
2. サーバーの上げ下げの時間
    * サーバーの起動停止って時間かかりますよね？ 
3. 可搬性
    * アプリケーションを別のハードに引っ越すとき
        * 仮想マシンならマシンイメージをコピーできるけど結構大きい

図3
![図3](https://cacoo.com/diagrams/V55Sg3UVjneVSwQv-C029A.png)


## コンテナでできること

### コンテナは大掛かりなスタティックリンクされたファイル

ダイナミックリンクや、複数のミドルウェアを組み合わせる構成の仕方は浸透し、もはやスタティックリンクだけでことが済んでいた時代には戻ることはできません。しかし、スタティックリンクは全部入りの一つのファイルを持ち運べばいいというシンプルさがありました。しかもスタティックリンクではファイル単位に依存関係が隔離されていて気楽です。
今の時代にも同じようなことはできないでしょうか？

その答えの一つがコンテナというわけです。下図4では`コンテナ`という箱単位に全部入りになっています。

図4
![](https://cacoo.com/diagrams/V55Sg3UVjneVSwQv-C6ACC.png)

仮想マシンでも同じだったじゃないかって？ まぁ、そうなんですけど図3と比較すると仮想サーバーの場合には`仮想ハードウェア`と`OS`がありましたがそれがなくなっています。
ホストマシンは`仮想化ハイパーバイザ`の代わりに`汎用OS`と`Docker`に置き換わっています。

この辺りが仮想マシンとコンテナが比較される所以なのですが、動作原理的には全然違っていて、先に上げたサーバーリソースのムダがかなり削減されます。

1. コンテナはホストOSの上で動くただのLinuxプロセスです
    * ps コマンドで出てきます
    * LinuxカーネルはホストOSのカーネルが使われます  
        → ゲストOS動作のリソースを食わない
2. コンテナはハードウェアをエミュレートしません  
    → ハードウェアエミュレートの処理コストが無い

### 本当は「イメージファイルは」大掛かりなスタティックリンクされたファイル

すいません、さっき嘘をつきました。実行形式ファイルに相当するのはコンテナではなく、イメージファイルというのが本当です。

|普通の場合              | dockerの場合     |
|--------------|------|
|実行形式ファイル|イメージ|
|プロセス       |コンテナ|

つまりイメージを実行(```docker run イメージ名```)することでコンテナが作られます。当然同じイメージから複数のコンテナを起動することもできるわけです。
仮想サーバーを起動停止、仮想サーバーを複製してインスタンスを増やすことに比べたらずいぶんと軽量だと思います。イメージの作りにもよりけりですが、通常はコンテナの起動は数秒と言ったレベルです。

### イメージファイルの可搬性

イメージファイルは```docker export コンテナID > ファイル名.tar```とある時点のコンテナの状態をエクスポートできるのでそのファイルを持っていくことができます。

ただ、通常はあまりこのようなベタな形で扱うことは少ないと思います。

#### DockerHub

dockerはgitにインスパイアされたと思われるバージョン管理の機能と、GitHubのような[DockerHub](https://hub.docker.com/explore/)というイメージ共有のサービスがあります。

```shell-session
$ docker commit コンテナID イメージ名   //コンテナをイメージとして保存する
$ docker push イメージ名  //DockerHubにイメージをアップロードする
```

くわしくは https://docs.docker.com/engine/userguide/eng-image/image_management/ を参照してください。

#### private docker registry

DockerHubはGitHubのようにpublicな空間なので社内開発物を載せるのには向いていません。（いちおう有償サービスメニューとしてprivateもあります）
Gitlabのように自前の社内サーバーにDockerHubのようなものを建てることもできます。

```shell-session
$ docker run -d -p 5000:5000 --name registry registry:2
```

ちなみにこのdocker registry自体docker imageとしてDockerHubで配布されています。
詳しくは https://docs.docker.com/registry/ を参照してください。

## もう疲れたので続きはまた今度

ということで、ここまでで、難癖つけた下記３点についてはそれなりに優位性があるというところにこぎつけました。

1. サーバーリソースの無駄
    * ハードのエミュレートをしない
    * カーネルはホストのカーネルが使われる
2. サーバーの上げ下げの時間
    * 単なるプロセス起動
3. 可搬性
    * DockerHubというイメージ共有／配布の仕組み

次回からはこれを踏まえてハンズオンで手を動かして確認していきます。

1. [ハンズオン Part1](http://qiita.com/NewGyu/items/24290b79ecb606785edc)
    * まずはDockerを使ってサンプルアプリケーションを実行してみる
2. [ハンズオン Part2](http://qiita.com/NewGyu/items/e200c9fac28d4e2e3cb4)
    * Dockerのイメージってどうなっているの？
3. [ハンズオン Part3](http://qiita.com/NewGyu/items/83610d2139a37b356e53)
    * Dockerのイメージを作ってみよう