Docker Forwarding を活用して、コンテナサーバーをローカルコマンドで操作する。

先日、VirtualBox を必要としない OS X/Windows ネイティブの Docker が発表されました[^1]。でもまだプライベートベータで試せません。とりあえずは、現状の OS X における Docker Forwarding を活用した Docker の使い方をまとめておきます。ネイティブ Docker がリリースされた暁には、更新したいです。


# VirtualBox

## インストール

仮想化環境の選択肢は色々あるかも知れませんが、VirtualBox を使います。インターフェースは Vagrant を使うので、VirtualBox はインストールしたら忘れてしまいしょう。

```sh
curl -LO http://download.virtualbox.org/virtualbox/5.0.16/VirtualBox-5.0.16-105871-OSX.dmg
hdiutil attach VirtualBox-5.0.16-105871-OSX.dmg
sudo installer -pkg /Volumes/VirtualBox/VirtualBox.pkg -target /
hdiutil detach /Volumes/VirtualBox
```

- [Downloads – Oracle VM VirtualBox](https://www.virtualbox.org/wiki/Downloads)


# Vagrant

## インストール

VirtualBox を CLI から操作できるよう Vagrant もインストールします。Vagrant 自体も特に設定はしなくていいです。 `vagrant` コマンドが使えればとりあえず大丈夫でしょう。

```sh
curl -LO https://releases.hashicorp.com/vagrant/1.8.1/vagrant_1.8.1.dmg
hdiutil attach vagrant_1.8.1.dmg
sudo installer -pkg /Volumes/Vagrant/Vagrant.pkg -target /
hdiutil detach /Volumes/Vagrant
```

- [Download - Vagrant by HashiCorp](https://www.vagrantup.com/downloads.html)


# Docker Toolbox

## インストール

ローカルマシンで `docker` コマンドを使えるようにするため、Docker Toolbox をインストールします。**ここで注意が必要です。**

```sh
curl -LO https://github.com/docker/toolbox/releases/download/v1.9.1j/DockerToolbox-1.9.1j.pkg
sudo installer -pkg DockerToolbox-1.9.1j.pkg -target /
```

- [Releases · docker/toolbox](https://github.com/docker/toolbox/releases)

### 注意点

今回、CoreOS を VirtualBox 内で動作するホスト Linux マシンとします。そしてその CoreOS 内で動作する Docker と、ローカルマシンの Docker Toolbox のバージョンを一致させる必要があります。バージョンが一致しないと通信できないため、Docker Forwarding が機能しません。

CoreOS には 3 つのリリースチャンネル Stable/Beta/Alpha があります。それぞれの最新版チャンネルに含まれる Docker のバージョンは次のページで確認できます:

- [CoreOS Release Notes](https://coreos.com/releases/)

今回は Stable チャンネルの CoreOS を使用します。現状の Stable チャンネルの CoreOS バージョンは **899.13.0** で、Docker のバージョンは **1.9.1** です。なので前述通り、Docker Toolbox は *1.9.1j* をインストールしています。

## セットアップ

ローカルマシンから接続するコンテナサーバーのアドレスを環境変数に定義します。

```sh:~/.bashrc
export DOCKER_HOST=tcp://172.17.8.101:2375
```

定義先ファイルは `.bashrc` でなくてもいいのですが、とにかく、環境変数 `DOCKER_HOST` にコンテナサーバーのアドレスを上記のように定義します。

IP アドレスとポート番号は、後述の CoreOS の設定で変更できます。変更した場合は、上記のアドレスもそれに沿って変更して下さい。


# CoreOS

## インストール

さて今回は、ホスト Linux マシン CoreOS を、ローカルマシン上で合わせて動作させます。なので、Vagrant 用の公式 CoreOS イメージを GitHub から取得します。

適当な作業ディレクトリを作成して、git-clone します:

```sh
git clone https://github.com/coreos/coreos-vagrant ./coreos-vagrant
cd ./coreos-vagrant
```

- [coreos/coreos-vagrant: Minimal Vagrantfile for CoreOS](https://github.com/coreos/coreos-vagrant)

## セットアップ

先ほど git-clone したリポジトリに、`config.rb.sample` と `user-data.sample` というサンプルファイルが 2 つあります。これらをそれぞれ `config.rb` と `user-data` という名前でコピーして、内容を次にように編集します。

```sh
cd ./coreos-vagrant
cp config.rb.sample config.rb
cp user-data.sample user-data
```

```diff:config.rb
56c56
< #$update_channel='alpha'
---
> $update_channel='stable'
74c74
< #$share_home=false
---
> $share_home=true
```

通常、Vagrant は `Vagrantfile` を編集して起動するサーバーの設定を変更します。しかしこのリポジトリでは `config.rb` に設定した内容を `Vagrantfile` が読み込んで設定するようになっています。なので、設定情報は `config.rb` に集約して `Vagrantfile` は基本的にさわらないようにします。

前項の `DOCKER_HOST` 環境変数で指定するポート番号も、`config.rb` で変更できます。設定項目は色々あるので、コメントを読んで必要な項目を設定します。

```yaml:user-data
#cloud-config

---
coreos:
  units:
  - name: docker-tcp.socket
    command: start
    enable: true
    content: |
      [Unit]
      Description=Docker Socket for the API

      [Socket]
      ListenStream=2375
      Service=docker.service
      BindIPv6Only=both

      [Install]
      WantedBy=sockets.target
```

`user-data` には Docker Forwarding に必要なデーモンの設定をします。`ListenStream` で指定するポート番号は `config.rb` で指定したものと同じ番号を指定します。デフォルトは `2375` です。

尚 `user-data` は [Cloud-Init](https://launchpad.net/cloud-init) の `cloud-config` ファイルなので、上記以外にもサーバー起動時設定したい内容を定義できます。

- [Documentation — Cloud-Init 0.7.7 documentation](http://cloudinit.readthedocs.org/en/latest/)
- [Customize with Cloud-Config](https://coreos.com/os/docs/latest/cloud-config.html)

### 通信の暗号化

今回はコンテナサーバーとローカルマシンが、同じマシンなので設定しませんが、Docker Forwarding の通信を暗号化することも可能です。

詳しくは CoreOS の Docker Forwarding についての解説ページが丁寧です:

- [Customizing docker](https://coreos.com/os/docs/latest/customizing-docker.html)

## コンテナサーバーの起動

ここまで終わったら、Vagrant でコンテナサーバーを起動します:

```sh
cd ./coreos-vagrant
vagrant up
```

これで CoreOS のディスクイメージが自動でダウンロードされ、バックグラウンドで CoreOS のコンテナサーバーが起動します。

サーバーの IP アドレスは、基本的に `172.17.8.101` になります。複数のコンテナサーバーを建てた場合は、末尾の `101` が起動順で `+1` されて行きます。

コンテナサーバー自体にログインしたい時は、Vagrant で SSH ログインします:

```sh
vagrant ssh
``` 

# ローカルマシンから Docker を操作

ここまで終わったら、ローカルマシンで普通に `docker` コマンドを叩くと、CoreOS 上の Docker コンテナを操作できます。

```sh
docker pull centos:latest
```

```shell-session
NAME                            DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
centos                          The official build of CentOS.                   2054      [OK]
ansible/centos7-ansible         Ansible on Centos7                              67                   [OK]
...
```

便利!


# OS X ログイン時にコンテナサーバーを自動起動

ローカルマシンログインの度に CoreOS を `vagrant up` しておくのが面倒なので [LaunchAgents](https://developer.apple.com/library/mac/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html) を使って自動起動するようにしてみました。

## visudo の設定

まず、Vagrant がインタンス起動時に NFS をマウントすると、パスワードを聞くのを止めます。これをやらないと自動起動がパスワードプロンプトの時点で止まってしまい、正常に起動できません。

```sh
visudo
```

```:/etc/sudoers
# 略
Cmnd_Alias VAGRANT_EXPORTS_ADD = /usr/bin/tee -a /etc/exports
Cmnd_Alias VAGRANT_NFSD = /sbin/nfsd restart
Cmnd_Alias VAGRANT_EXPORTS_REMOVE = /usr/bin/sed -E -e /*/ d -ibak /etc/exports
%admin ALL=(root) NOPASSWD: VAGRANT_EXPORTS_ADD, VAGRANT_NFSD, VAGRANT_EXPORTS_REMOVE
```

- [NFS - Synced Folders - Vagrant by HashiCorp](https://www.vagrantup.com/docs/synced-folders/nfs.html)

## LaunchAgents の設定

ファイル名はなんでもいいのですが、次のように XML 形式の plist ファイルを作成して `~/Library/LaunchAgents` 以下に保存します。

```sh
touch ~/Library/LaunchAgents/com.github.coreos.coreos-vagrant.plist
```

```xml:com.github.coreos.coreos-vagrant.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>Label</key>
    <string>com.github.coreos.coreos-vagrant</string>
    <key>ProgramArguments</key>
    <array>
      <string>/bin/sh</string>
      <string>-c</string>
      <string><![CDATA[
        export PATH="/usr/local/bin:${PATH}"
        COREOS_PATH=$(readlink "${HOME}/.coreos-vagrant")
        ! which vagrant &> /dev/null || [ ! -d ${COREOS_PATH} ] && exit 0
        COREOS_INSTANCE=$(vagrant global-status | awk -v coreos_path=${COREOS_PATH} 'match($5, coreos_path) {print $1,$4}')
        if [ -z ${COREOS_INSTANCE} ]
        then
          cd ${COREOS_PATH} && vagrant up | logger -i -t COREOS_VAGRANT
        elif [ 'running' != $(echo ${COREOS_INSTANCE} | awk '{print $2}') ]
        then
          vagrant up $(echo ${COREOS_INSTANCE} | awk '{print $1}') | logger -i -t COREOS_VAGRANT
        fi
      ]]></string>
    </array>
    <key>RunAtLoad</key>
    <true/>
  </dict>
</plist>
```

今回は、`~/.coreos-vagrant` というシンボリックリンク先に、前項の Vagrant 用の公式 CoreOS イメージのディレクトリがある構成にしています。ログイン時に `vagrant global-status` コマンドで、そのディレクトリにあるインスタンスのステータスを取得し、`running` になっていない場合は、`vagrant up` コマンドを実行するようなつくりです。

ここまでやったら次のコマンドを実行しておきます:

```sh
launchctl load ~/Library/LaunchAgents/com.github.coreos.coreos-vagrant.plist
```

これで登録した LaunchAgents のスクリプトが実行されます。`vagrant global-status` で CoreOS のインスタンスが無事起動していれば成功です。次回のログイン時からは、同じく自動起動するはずです。

尚、残念ながら LaunchAgents ではログアウト時に自動でスクリプトを走らせる術がありません。なので、ログアウト時は、コンテナサーバーを手動で停止しておくのを忘れないようにしましょう:

```sh
vagrant halt
```

なんか良い方法ないかなー。

ネイティブ Docker がどんなものになるか楽しみですね。


[^1]: [Docker for Mac and Windows Beta: the simplest way to use Docker on your laptop | Docker Blog](https://blog.docker.com/2016/03/docker-for-mac-windows-beta/)