Docker for Mac上のコンテナから、Mac上のアプリケーションに簡単に接続する方法

## 対象の読者
* Docker for Macを使っている方
* Mac上でWebアプリケーションなどを開発しているなどで、DockerコンテナからMac上のアプリケーションに対し通信したい方

## 2020/1/18 追記 ：-> より優れた方法があります
* Docker for Mac 17.06以降では、コンテナ内でDockerホストの名前解決ができるようになっているため、本記事で紹介する対処は不要です。
* コンテナ内で下記を通信先に指定すれば、DockerホストのLoopBackインターフェイス経由で、Dockerホストと通信が行われます。

> docker.for.mac.localhost

* Docker for Mac 17.12では、上記が非推奨となり、以下が推奨されています。

> docker.for.mac.host.internal

* さらに、Docker for Mac 18.03以降は、上記が非推奨となり、以下が推奨されています。

> host.docker.internal

なかなか変化が多くカオスですね。。

## 記事概要
* Docker for Macでは、Dockerホスト上(mac)にdocker0ブリッジデバイスが生成されず、Dockerコンテナから通信するためのホストのIPアドレスが明示されません。
* Macのループバックインターフェイスに任意のIPアドレスのエイリアスを設定することで、そのIPアドレスに対しコンテナ上からアクセスすることができるようになります。

## 前提条件
* Docker for Mac: Version 1.12.3
* Mac OS X : Yosemite以降

## はじめに
最近(2016年前半くらい)まで、Mac上でDockerを動かそうとすると、VirtualBoxといった仮想化ソフト上にDockerホストを作成する必要がありました。それを簡易化するソフトウェアとして、Docker Toolboxなどがあります。

しかし、近年リリースされたDocker for Macを用いると、Mac OS Xに内包されるHypervisorを用いてDockerコンテナを利用でき、Dockerホストの設定や構成変更をシンプルに行えるようになります。

## 今回取り扱う問題
Docker for Macを用い、筆者は下記の問題に遭遇しました。

* LinuxをDockerホストとして用いる場合と違い、MacではDockerホスト上にdocker0ブリッジが生成されない
* それにより、DockerコンテナからDockerホスト(Mac)上のサービスに対し通信を発生させたい場合に、IPアドレスがわからない

筆者はMac上でWebアプリケーションを開発しており、結合テストの一環で、Dockerコンテナからホスト上のWebサーバにアクセスする必要がありました。
しかし、ifconfigを打っても、dockerブリッジがなく、DockerホストのIPアドレスがわかりません。困りました。

## 解法
まず、Mac(Dockerホスト)のインターフェイス情報を、ifconfigコマンドで取得したものを、以下に示します。 

```text:出力結果
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384
	options=3<RXCSUM,TXCSUM>
	inet6 ::1 prefixlen 128 
	inet 127.0.0.1 netmask 0xff000000 
	inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1 
	nd6 options=1<PERFORMNUD>

en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
	ether xx:xx:xx:xx:xx:xx 
	inet6 fe80::xxxx:xxxx:xxxx:xxxx%en0 prefixlen 64 scopeid 0x4 
	inet 192.168.1.3 netmask 0xfffff000 broadcast 192.168.1.255
	nd6 options=1<PERFORMNUD>
	media: autoselect
	status: active
...
```

#### ①あまりイケてない解法
ホストの物理インターフェイスに割り当てられているIPアドレス(例えば、192.168.1.3など)をDockerコンテナから内叩く事で、疎通をすることが出来ます。
実際にコンテナ上からcurlで、ホスト上の8000番ポートでListenしているWebサーバへリクエストを飛ばしてみます。

```bash:コマンドと出力結果
curl 192.168.1.3:8000
<h1>Not Found</h1><p>The requested URL / was not found on this server.</p>
```

WebサーバーからNot Foundが返ってきているので、疎通ができています。
しかし、Macは通常サーバとしては使わないので、物理インターフェイスにはDHCPを用いてIPアドレスを取得することが多いと思います。すると、この方法では、IPアドレスが変わる度にコンテナ内の設定を書き換える必要が出てしまい、とても不便です。

#### ②最終的にたどり着いた解法
よくよく公式ページを見ると、下記のページにより良い方法がありました。
> https://docs.docker.com/docker-for-mac/networking/

要約すると、以下の事を実施します。

* 任意のIPアドレスエイリアスを**Mac(Dockerホスト)の**ループバックアドレスに割り当てる

今回は、以下のコマンドを用い、10.200.10.1というエイリアスを、ループバックアドレスに割り付けます。

```bash
sudo ifconfig lo0 alias 10.200.10.1/24
```

**Mac(Dockerホスト)で**ifconfigを打つと、以下のようにエイリアスがループバックアドレスに付与されたことがわかります。(6行目)

```text:出力結果
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384
	options=3<RXCSUM,TXCSUM>
	inet6 ::1 prefixlen 128 
	inet 127.0.0.1 netmask 0xff000000 
	inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1 
	inet 10.200.10.1 netmask 0xffffff00 
	nd6 options=1<PERFORMNUD>
…
```

実際にコンテナ上からcurlでリクエストを飛ばしてみます。

```bash:コマンドと出力結果
curl 10.200.10.1:8000
<h1>Not Found</h1><p>The requested URL / was not found on this server.</p>
```

このように、エイリアスアドレスに対し、コンテナ上から疎通をすることができました。この方法であれば、コンテナから見たDockerホストのIPアドレスをエイリアスのアドレスで固定化することができます。

## 注意点など
Webサーバーの設定によっては、HTTPリクエストが受け付けられない場合があります。

例えば、DjangoのWebサーバをローカル環境で実行する場合、デフォルトでは以下のようなコマンドをDjangoプロジェクトのルートフォルダ配下で実行するかと思います。

```text:Webサーバ起動コマンド
python manage.py runserver 8000
```

```text:標準出力
Performing system checks...

System check identified no issues (0 silenced).
March 27, 2017 - 18:27:50
Django version 1.9, using settings 'WebApp.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.
```

この状態では、Webサーバーが動作するホストのIPアドレスが複数あった場合には、127.0.0.1宛で来た通信のみ受信を許可されます。Mac上のDockerコンテナから通信する場合、宛先はエイリアスのIPアドレスになるので、この設定ではWebサーバーはリクエストを受信してくれません。

したがって、Webサーバ起動時に、以下のようにリッスンディレクティブを変更する必要があります。

```bash:Webサーバ起動コマンド
python manage.py runserver 10.200.10.1:8000
```

```text:標準出力
Performing system checks...

System check identified no issues (0 silenced).
March 27, 2017 - 18:32:17
Django version 1.9, using settings 'WebApp.settings'
Starting development server at http://10.200.10.1:8000/
Quit the server with CTRL-BREAK.
```

これで、Dockerコンテナから、ホスト上のWebアプリケーションへHTTPリクエストが通るようになります。

余談ですが、Webサーバのリッスンディレクティブを10.200.10.1:8000にすると、Mac上のプロセスからこのWebサーバへの接続はできなくなります。
コンテナとMac上のプロセスの両方からこのwebサーバへアクセスしたい場合、Webサーバのリッスンディレクティブに0.0.0.0を指定してください。
(ただし、Mac外からのリクエストも受け付けるようになるので、セキュリティにはご注意ください。)

## まとめ
Docker for Macを利用する際は、ループバックアドレスにエイリアスを設定すると、コンテナとの連携が楽になります。

```bash：コマンド
sudo ifconfig lo0 alias 10.200.10.1/24
```

と打つだけなので、是非試してみてください。