Moby で LinuxKit を起動してみる

DockerCon 2017 で発表された [Moby プロジェクト](https://mobyproject.org/) を利用して
LinuxKit が動作するところまでを確認します。

# Moby

## もともと使われている意味の Moby

Docker 社のロゴにもなっている、このクジラの愛称[^1]。
[^1]: 厳密には [Moby Dock](https://blog.docker.com/2013/10/call-me-moby-dock/) だけれど、界隈ではモビィって呼ばれてる気がする。

![Moby](https://i1.wp.com/blog.docker.com/wp-content/uploads/2015/06/mobydock_dockercon15.png?w=600&ssl=1)
(https://blog.docker.com/2015/06/winners-dockercon-2015-contests/ より拝借)

## 最近チラ見せしていた Moby OS

Alpine Linux ベースの軽量 OS。Docker for Mac の記事でよく
Message of The Day のアスキーアートとともに紹介されていますが

                        ##         .
                  ## ## ##        ==
               ## ## ## ## ##    ===
           /"""""""""""""""""\___/ ===
      ~~~ {~~ ~~~~ ~~~ ~~~~ ~~~ ~ /  ===- ~~~
           \______ o           __/
             \    \         __/
              \____\_______/

Docker for AWS などでは Docker Inc. が用意したであろう
Moby な AMI で EC2 クラスタが起動するなど、
Docker 製品群では近頃大活躍している OS です。

## Moby

今回発表された**コンテナ環境を組み立てるためのフレームワーク**。

標準化されたランタイム、オーケストレーション、ネットワーキング
セキュリティ、ビルド / イメージ配布方法などを利用して、
様々なプラットフォーム / アーキテクチャ上で実行可能なものを
組み立てることができます。
Linux や Mac、Windows なら実行形式ファイルを、
クラウドや仮想化事業者に向けては VM イメージが生成されます。

上述の Moby OS なども、いずれは Moby 経由で生成されるかもですね。
CTO の [Solomon Hykes](https://twitter.com/solomonstre) 氏が [PR](https://github.com/moby/moby/pull/32691) で Moby への移行を語っています。

## LinuxKit

[Publickey さんの記事](http://www.publickey1.jp/blog/17/dockerlinuxkitlinux_subsystemdockercon_2017.html) がわかりやすかったのですが
DockerCon のデモでもあったように、この LinuxKit を
Moby を使えば簡単にビルドすることができます。やってみましょう。

# LinuxKit を動かすまで

以下、MacBook Pro (Early 2015) macOS Sierra で動作確認しています。

## 1. moby コマンドのインストール

### 方法 1

Go 環境があれば楽勝

```sh:install-moby
go get -u github.com/docker/moby/src/cmd/moby
```

### 方法 2

- GNU make
- GNU or BSD tar (not busybox tar)
- Docker

のインストールされた環境で

```sh:install-moby
git clone https://github.com/linuxkit/linuxkit.git
cd linuxkit
make
```

でビルドすれば、bin 以下に moby バイナリが生成されます。
PATH の通ったところに置いて実行権限をつけましょう。

## 2. LinuxKit イメージのビルド

moby 自体も内部的に Docker の仕組みを使うので
Docker がインストールされた環境で以下のコマンドを実行します。

```sh:build-linuxkit
moby build linuxkit.yml
```

するとログにあるように、linuxkit-xxx というファイルが生成されましたね！

```
Create outputs:
  linuxkit-bzImage linuxkit-initrd.img linuxkit-cmdline
  linuxkit.iso
  linuxkit-efi.iso
```

## 3. LinuxKit を起動します

とても簡単です。

```sh:run-linuxkit
moby run linuxkit
```

以下のような表示になるまで待ちましょう

```
Welcome to LinuxKit

                        ##         .
                  ## ## ##        ==
               ## ## ## ## ##    ===
           /"""""""""""""""""\___/ ===
      ~~~ {~~ ~~~~ ~~~ ~~~~ ~~~ ~ /  ===- ~~~
           \______ o           __/
             \    \         __/
              \____\_______/


/ # [    2.449400] IPVS: Creating netns size=2104 id=1
[    2.449828] IPVS: ftp: loaded support on port[0] = 21
[    2.699956] IPVS: Creating netns size=2104 id=2
[    2.700419] IPVS: ftp: loaded support on port[0] = 21
[    3.408548] clocksource: Switched to clocksource tsc
```

# LinuxKit をさわってみる

## 動いているものの確認

デモにもありましたが、プロセスはこんな感じ。

```tex:pstree
/ # pstree
init-+-containerd-+-containerd-shim---nginx---nginx
     |            `-containerd-shim---tini---rngd
     |-containers---2*[ctr]
     |-sh---pstree
     `-sh
```

containerd が見えますが、runc からコンテナの状態も確認できます。

```txt:runc-list
/ # runc list
ID          PID         STATUS      BUNDLE                        CREATED                        OWNER
nginx       543         running     /run/containerd/linux/nginx   2017-04-18T22:12:11.1635803Z   root
rngd        599         running     /run/containerd/linux/rngd    2017-04-18T22:12:11.3155512Z   root
```

## コンテナへアクセス

まず host ネットワーク上で nginx コンテナが動いているので
HTTP リクエストをローカルホストに送信してみます。

```html:wget
/ # wget -qO - 127.0.0.1
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
```

設定値を runc 経由で確認してみます。

```nginx:runc-exec
/ # runc exec nginx cat /etc/nginx/conf.d/default.conf
server {
    listen       80;
    server_name  localhost;

    #charset koi8-r;
    #access_log  /var/log/nginx/log/host.access.log  main;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    # proxy the PHP scripts to Apache listening on 127.0.0.1:80
    #
    #location ~ \.php$ {
    #    proxy_pass   http://127.0.0.1;
    #}

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    #location ~ \.php$ {
    #    root           html;
    #    fastcgi_pass   127.0.0.1:9000;
    #    fastcgi_index  index.php;
    #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
    #    include        fastcgi_params;
    #}

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    #location ~ /\.ht {
    #    deny  all;
    #}
}
```

# LinuxKit 上で Docker を動かす

LinuxKit は読み込み専用で起動する設定になっているため
ファイルに書き込むような操作は何もできません。

そこで、Nginx の代わりに Docker が起動する設定の LinuxKit を起動してみます。

## ビルドと起動

リポジトリにある [このサンプル](https://github.com/linuxkit/linuxkit/blob/master/examples/docker.yml) を使ってビルド・起動します。
（Docker イメージがちゃんと保存されるようになっている）

```sh
$ moby build docker.yml
$ moby run docker
```

## 起動した Docker サービスコンテナに接続[^2]

[^2]: @abeq さんにご指摘いただきました。これが抜けていたらまずいですね。失礼しました・・

VM の中に起動している Docker サービスに runc exec で
sh プロセスを起動して、-t でターミナルをアタッチします。

```txt
/ # runc exec -t docker sh
```

## Docker 内の挙動を確認

dind ならぬ docker in runc 状態ですが、
docker コマンドが打てるこの安心感。

```yaml:docker
/ # docker system info
Containers: 0
 Running: 0
 Paused: 0
 Stopped: 0
Images: 1
Server Version: 17.04.0-ce
Storage Driver: overlay2
 Backing Filesystem: tmpfs
 Supports d_type: true
 Native Overlay Diff: true
Logging Driver: json-file
Cgroup Driver: cgroupfs
Plugins:
 Volume: local
 Network: bridge host macvlan null overlay
Swarm: inactive
Runtimes: runc
Default Runtime: runc
Init Binary:
containerd version: 422e31ce907fd9c3833a38d7b8fdd023e5a76e73
runc version: 9c2d8d184e5da67c95d601382adf14862e4f2228
init version: 949e6fa
Security Options:
 seccomp
  Profile: default
Kernel Version: 4.9.22-moby
Operating System: Alpine Linux v3.5 (containerized)
OSType: linux
Architecture: x86_64
CPUs: 1
Total Memory: 992.5MiB
Name: moby-025000000008
ID: NIKL:AY2Z:MQQL:OJYX:6OZD:BKYA:T3ZT:TKJF:T5XD:C46N:N5BF:V7ZZ
Docker Root Dir: /var/lib/docker
Debug Mode (client): false
Debug Mode (server): false
Registry: https://index.docker.io/v1/
Experimental: false
Insecure Registries:
 127.0.0.0/8
Live Restore Enabled: false
```

## Docker コンテナの起動

`df -h` するとわかりますが、ここまでの流れで起動した VM は
ディスク容量がとても少ないのであまり遊べません。
というわけで node.js あたりを。

```sh:docker-run
docker run --rm -it node:7.9-alpine sh
```

普通に使えますね。

```txt:node
/ # node -v
v7.9.0
/ # node
> 1+1;
2
```

この時点で、こんなことになってます。

Mac -> LinuxKit + docker -> Node.js コンテナ -> Node.js REPL

なので、Mac まで戻るには

Ctrl + D -> Ctrl + D -> Ctrl + D -> halt

です。