LXD 2.0: LXDの中でDocker [7/12]

## この文書について

この文書は、連載記事「[LXD 2.0: Blog post series](https://www.stgraber.org/2016/03/11/lxd-2-0-blog-post-series-012/)」（[日本語版目次](http://qiita.com/m-shibata/items/029b19d90af2a79116c2)）の一つである以下の記事を翻訳したものです。

* https://www.stgraber.org/2016/04/13/lxd-2-0-docker-in-lxd-712/

この文書のライセンスは原文と同じく、[Creative Commons BY-NC-SA 2.5](http://creativecommons.org/licenses/by-nc-sa/2.5/)のもとに提供されています。

![CC-BY-NC-SA 2.5](http://i.creativecommons.org/l/by-nc-sa/2.5/88x31.png)

---------

## DockerをLXDの中で動かす理由

[このシリーズの最初の投稿](http://qiita.com/m-shibata/items/a818859c3c7bcccae742)でも簡単に説明したように、LXDはシステムコンテナに注力しています。つまり、特別な手を加えていない完全なLinuxディストリビューションをコンテナの中で動かすということです。このためLXDはコンテナの中でどのような負荷が動いているかは気にしません。単にコンテナの名前空間とセキュリティポリシーを設定して、`/sbin/init`を立ち上げ、コンテナが停止するまで待つだけです。

DockerやRktが実装しているアプリケーションコンテナは、アプリケーションを配布するという異なった考えを持っています。それらは一般的にコンテナの中で単一のメインプロセスを起動しますし、LXDのコンテナに比べるとより刹那的です。

この二種類のコンテナは相互に排他的というわけではなく、アプリケーションの配布においてDockerコンテナを使う価値は十分にあると考えています。このため、私たちはLXDの中でDockerを実行できるようにすべく数年かけて対応を行ってきました。

Ubuntu 16.04とLXD 2.0の組み合わせであれば、ユーザーにDockerをインストールし、必要なアプリケーションを実行できるようなUbuntuシステムのコンテナを提供できるのです。


## 必要なもの

動作させるためにはいくつかのツールが必要です。Ubuntu 16.04であれば、これらはすべて提供されています：

* CGroup名前空間をサポートしたカーネル（Ubuntuカーネルなら4.4、メインラインカーネルなら4.6）
* LXC 2.0とLXCFS 2.0を使うLXD 2.0
* カスタム版のDocker（もしくは私たちが投稿したパッチをすべて適用してビルドしたDocker）
* ユーザー名前空間で制約された環境でもうまく動くDockerイメージ、もしくは特権コンテナとして設定された（`security.privileged=true`）LXDコンテナ内で動いていること


## Dockerを動かす

ではさっそく、コンテナの中でDockerを動かしてみましょう！

まずはじめに、次のようにUbuntu 16.04コンテナを作る必要があります：

```
lxc launch ubuntu-daily:16.04 docker -p default -p docker
```

「`-p default -p docker`」は、作成するコンテナに「`default`」プロファイルと「`docker`」プロファイルの双方を適用するようLXDに通知しています。`default`プロファイルは基本的なネットワーク設定が、`docker`プロファイルには必要ないくつかのカーネルモジュールをロードするような指示とコンテナにいくつかマウントする指示が含まれています。`docker`プロファイルは、コンテナのネストも有効化しています。

コンテナができたら、システムを更新し、Dockerをインストールしましょう：

```
lxc exec docker -- apt update
lxc exec docker -- apt dist-upgrade -y
lxc exec docker -- apt install docker.io -y
```

これですべてです！ コンテナの中にDockerをインストールし、動いている状態になりました。二つのDockerコンテナで基本的なウェブサービスを立ち上げてみましょう。
Now lets start a basic web service made of two Docker containers:

```
stgraber@dakara:~$ lxc exec docker -- docker run --detach --name app carinamarina/hello-world-app
Unable to find image 'carinamarina/hello-world-app:latest' locally
latest: Pulling from carinamarina/hello-world-app
efd26ecc9548: Pull complete
a3ed95caeb02: Pull complete
d1784d73276e: Pull complete
72e581645fc3: Pull complete
9709ddcc4d24: Pull complete
2d600f0ec235: Pull complete
c4cf94f61cbd: Pull complete
c40f2ab60404: Pull complete
e87185df6de7: Pull complete
62a11c66eb65: Pull complete
4c5eea9f676d: Pull complete
498df6a0d074: Pull complete
Digest: sha256:6a159db50cb9c0fbe127fb038ed5a33bb5a443fcdd925ec74bf578142718f516
Status: Downloaded newer image for carinamarina/hello-world-app:latest
c8318f0401fb1e119e6c5bb23d1e706e8ca080f8e44b42613856ccd0bf8bfb0d

stgraber@dakara:~$ lxc exec docker -- docker run --detach --name web --link app:helloapp -p 80:5000 carinamarina/hello-world-web
Unable to find image 'carinamarina/hello-world-web:latest' locally
latest: Pulling from carinamarina/hello-world-web
efd26ecc9548: Already exists
a3ed95caeb02: Already exists
d1784d73276e: Already exists
72e581645fc3: Already exists
9709ddcc4d24: Already exists
2d600f0ec235: Already exists
c4cf94f61cbd: Already exists
c40f2ab60404: Already exists
e87185df6de7: Already exists
f2d249ff479b: Pull complete
97cb83fe7a9a: Pull complete
d7ce7c58a919: Pull complete
Digest: sha256:c31cf04b1ab6a0dac40d0c5e3e64864f4f2e0527a8ba602971dab5a977a74f20
Status: Downloaded newer image for carinamarina/hello-world-web:latest
d7b8963401482337329faf487d5274465536eebe76f5b33c89622b92477a670f
```

これでDockerコンテナが二つ動いている状態になりました。LXDコンテナのIPアドレスを取得すれば、そのサービスにアクセスできるはずです！

```
stgraber@dakara:~$ lxc list
+--------+---------+----------------------+----------------------------------------------+------------+-----------+
|  NAME  |  STATE  |         IPV4         |                      IPV6                    |    TYPE    | SNAPSHOTS |
+--------+---------+----------------------+----------------------------------------------+------------+-----------+
| docker | RUNNING | 172.17.0.1 (docker0) | 2001:470:b368:4242:216:3eff:fe55:45f4 (eth0) | PERSISTENT | 0         |
|        |         | 10.178.150.73 (eth0) |                                              |            |           |
+--------+---------+----------------------+----------------------------------------------+------------+-----------+

stgraber@dakara:~$ curl http://10.178.150.73
The linked container said... "Hello World!"
```


## まとめ

このように、LXDコンテナの中でDockerコンテナを動かすことはとても簡単です。

以前にも言及したように、すべてのDockerイメージが今回の例のようにうまく動くわけではありません。特にユーザー名前空間のように、追加の権限をLXDに与える必要があるかもしれないからです。

今回のモードでは、Dockerのoverlayfsストレージドライバーのみが動作します。そのストレージドライバーは、環境内部で動作するイメージの数など、いくつかの制約を持っています。

もし何かアプリケーションが動かない場合、LXDコンテナの中のユーザーを信頼するのであれば、次のように設定できます：

```
lxc config set docker security.privileged true
lxc restart docker
```

これはユーザー名前空間を無効化し、コンテナを特権モードで動かします。ただし、このモードでは、コンテナの中のrootはホストのrootと同じUIDを持っていることに注意してください。そのようなコンテナにおいては、コンテナのユーザーがホストのroot権限を取得する方法がいくつも知られています。LXDコンテナの中のユーザーがホストのroot権限を取得しても問題ない程度に信頼できる場合にのみ適用するようにしてください。


## その他の情報

* LXDのウェブサイト：[https://linuxcontainers.org/ja/lxd/](https://linuxcontainers.org/ja/lxd/)
* 開発はGitHub上で行なっています：[https://github.com/lxc/lxd](https://github.com/lxc/lxd)
* メーリングリスト：[https://lists.linuxcontainers.org](https://lists.linuxcontainers.org)
* IRCチャンネル：#lxcontainers on irc.freenode.net
* LXDをオンラインで試したい場合：[https://linuxcontainers.org/ja/lxd/try-it/](https://linuxcontainers.org/ja/lxd/try-it/)