Docker Toolbox環境でハマってから解決するまでの話

# Windows 10 HomeでDocker Composeを始める

## はじめに

この記事はWin10 Proユーザである私が、Win10 HomeでDocker Composeを使うにあたって**ハマったこと**と**解決方法**をご紹介する記事です。
Docker Toolboxを使い始めた人のお役に立てると幸いです。

## やりたいこと

* Win10 HomeでPHPのフレームワークLaravelを使ってWebサーバを動かす。
* Dockerのオーケストレーションはdocker-composeを利用する。
* docker-composeはLaravel向け全部入りプロジェクトである[Laradock](https://laradock.io/)を利用する。
* ホストマシンの外からLaradockのサービスへアクセスする。

## 前提条件

* Virtual Boxがインストールしている
* Docker Toolboxがインストールしている
* `docker run hello-world`が動作することを確認している
* Laradockのセットアップが終わっており、`workspace`コンテナに`exec`できる(参考記事: @souichirou
殿の[dockerコンテナの中に入って作業をしたい時](https://qiita.com/souichirou/items/6e701f6469822a641bdd#docker%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%81%AE%E4%B8%AD%E3%81%AB%E5%85%A5%E3%81%A3%E3%81%A6%E4%BD%9C%E6%A5%AD%E3%82%92%E3%81%97%E3%81%9F%E3%81%84%E6%99%82))

## いざ実践
### 作業手順

以下の手順で作業を行います。
なお手順末尾に「**※**」があるものは私のハマりポイントです。

1. [composerコマンドの参照先リポジトリを近くに変更し、快適な環境を準備する(任意) ](#1-composer%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%81%AE%E5%8F%82%E7%85%A7%E5%85%88%E3%83%AA%E3%83%9D%E3%82%B8%E3%83%88%E3%83%AA%E3%82%92%E8%BF%91%E3%81%8F%E3%81%AB%E5%A4%89%E6%9B%B4%E3%81%97%E5%BF%AB%E9%81%A9%E3%81%AA%E7%92%B0%E5%A2%83%E3%82%92%E6%BA%96%E5%82%99%E3%81%99%E3%82%8B%E4%BB%BB%E6%84%8F)
2. [composerの依存解決を並列化し、快適な環境を準備する(任意)](#2-composer%E3%81%AE%E4%BE%9D%E5%AD%98%E8%A7%A3%E6%B1%BA%E3%82%92%E4%B8%A6%E5%88%97%E5%8C%96%E3%81%97%E5%BF%AB%E9%81%A9%E3%81%AA%E7%92%B0%E5%A2%83%E3%82%92%E6%BA%96%E5%82%99%E3%81%99%E3%82%8B%E4%BB%BB%E6%84%8F)
3. [Laravelのインストール](#3-laravel%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB)
4. [LaravelのWEB画面を開発マシンで表示する**※**](#4-laravel%E3%81%AEweb%E7%94%BB%E9%9D%A2%E3%82%92%E9%96%8B%E7%99%BA%E3%83%9E%E3%82%B7%E3%83%B3%E3%81%A7%E8%A1%A8%E7%A4%BA%E3%81%99%E3%82%8B)
5. [LaravelのWEB画面を開発マシンの外から表示する**※**](#5-laravel%E3%81%AEweb%E7%94%BB%E9%9D%A2%E3%82%92%E9%96%8B%E7%99%BA%E3%83%9E%E3%82%B7%E3%83%B3%E3%81%AE%E5%A4%96%E3%81%8B%E3%82%89%E8%A1%A8%E7%A4%BA%E3%81%99%E3%82%8B)

### 1. composerコマンドの参照先リポジトリを近くに変更し、快適な環境を準備する(任意) 

PHPのパッケージ管理機能を提供してくれる`composer`ですが、デフォルトのリポジトリがpackagist.orgまで距離があるため、日本国内からのアクセスが早いpackagist.jpに変更します。
※Packagist JPについては[こちらから](https://packagist.jp/)

```bash:workspaceコンテナ内での操作
root@(container-id)# composer config -g repos.packagist composer https://packagist.jp
```

### 2. composerの依存解決を並列化し、快適な環境を準備する(任意)

`composer`はパッケージリポジトリへのアクセスが並列化されておらず、Laravelのように依存関係が多いフレームワークのインストールではかなりの時間がかかる傾向がある。
@Hiraku殿のprestissimoを導入し、依存解決を並列化します。

```bash:workspaceコンテナ内での操作
root@(container-id)# composer global require hirak/prestissimo
```

### 3. Laravelのインストール

Laravelのインストールを以下のコマンドで実行します。

```bash:workspaceコンテナ内での操作
root@(container-id)# cd /var/www
root@(container-id)# composer create-project --prefer-dist laravel/laravel ./
```

※`cd /var/www`する理由は`Laradock`のデフォルトのWEBサーバである`nginx`では、`/var/www`に`Laravel`が配置されている前提となっているため。

### 4. LaravelのWEB画面を開発マシンで表示する**※**

まず私がハマったポイントは、まさかの画面を表示する時でした。

手順3を行うことでLaravelのセットアップが完了し、LinuxやWin10 Pro上でDockerを使っている場合は`http://localhost`で以下のようなLaravelの初期画面が表示されるはずです。

![Laravelの初期画面.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407538/ef0f91a0-f849-2038-19e5-10c4cca09c97.png)

しかし、Win10 Home + Docker Toolboxでは一筋縄ではいきません。

**本手順におけるハマりポイント**
Win10 Home + Docker Toolboxを使う場合は、Docker上のサービスはlocalhostとして動作しません。（VirtualBox上のVMで動いているため）
私はWin10 ProとLinuxでしかDockerを触っていなかったためここでハマりました。

**解決方法**
Docker Toolboxを利用する際は、`Docker Quickstart Terminal`というアプリをインストールしているはずです。
このアプリを起動した直後に表示される以下の画面をご覧ください。
![DockerQuickstartTerminal.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407538/b11cc00e-d081-9241-ccab-fcf0d0bfa12e.png)

表示する際に必要となるVirtualBox内のdefaultマシンのIPアドレスが記載されています。
（ここでは`192.168.99.100`）
このIPアドレスを指定してWEBブラウザでアクセスすることで表示できます。
WEBブラウザに以下のURLを指定してアクセスしてみましょう。

```
http://(Docker Quickstart Terminalに記載されているIPアドレス)
```

私の場合(`192.168.99.100`)は以下の通りです。

```
http://192.168.99.100
```

このように指定することでWin10 Home + Docker Toolboxの組み合わせでLaravelの初期画面が表示されます。

###5. LaravelのWEB画面を開発マシンの外から表示する**※**

次に私がハマったポイントは、開発マシン以外からDocker Toolboxで動いているサービスにアクセスする時でした。

仮に開発マシンのIPアドレスが`192.168.0.1`であるとしましょう。
LinuxやWin10 Pro上でDockerを使っている場合は開発マシン以外からは以下のURLでサービスが見えるはずです。

```
http://192.168.0.1
```

しかし、Win10 Home + Docker Toolboxでは一筋縄ではいきません。（2回目）

Docker ToolboxでDockerコンテナを動かす際は、VirtualBox上で動作している`default`マシン内でDockerを立ち上げており、この`default`マシンに設定されているNICは`NAT`と`ホストオンリーアダプタ`の2つだけです。
仮に`default`マシンに`ブリッジアダプタ`を割り当てる場合、`default`マシンに独自のIPアドレスが振ることでアクセスできそうな気がします。

しかし以下の利用により、NICに変更を加えることを避けることにしました。

* ブリッジ接続によって`default`マシンにIPを振りたくない(ローカルIPアドレスがもったいない)
*  「DockerといえばホストマシンのIPで動くものだ！」というイメージがあるため
*  `default`マシンに変更を加えたくない（人の作ったVMを修正したくない）
*  開発マシンの外側（他人）に対して、Docker Toolboxの存在を意識させたくないため

**解決方法**

#### 「開発マシンへの通信をポートフォワードでdefaultマシンに流し込む」

つまり以下のイメージです。
![DockerToolboxSSHPortforward.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407538/9288850d-4a25-d78f-84a6-921cc3cd9bb6.png)

Win10 HomeのDocker Toolboxに含まれている`docker-machine`コマンドを利用して実現します。

`Docker Quickstart Terminal`アプリを立ち上げて、以下のコマンドを実行してください。

```shell:DockerQuickTerminalアプリでの操作
docker-machine ssh default -g -L 8080:192.168.99.100:80
```

このコマンドを実行することで`default`マシンにSSHポートフォワードをします。

#### `-g`オプション

ローカル以外のマシンから来た通信に対してもポートフォワードしてくれます。

####`-L`オプション

ポートフォワードの設定です。
ホストマシンのポート8080番に来た通信を、`default`マシンのポート80番へ転送することが可能となります。
上記の操作例では`-L 8080:192.168.99.100:80`と指定しておりますが、これはコロン`:`区切りで以下のように指定します。

```
-L (ホストマシン側の紐づけるポート番号):(転送先マシンのIPアドレス):(転送先マシンのポート番号)
```

これにより、ホスト以外のマシンから`(ホストマシンのIP):8080`を宛先として指定することで、`default`マシンへ通信が到達するようになります。

ホストマシンの外（別のPC等）のWEBブラウザで以下のURLを指定することで表示できるようになりました。

**ホストマシンの外（別のPC等）からWEBブラウザで確認**

```
http://(ホストマシンのIPアドレス):8080
```

以上、めでたしめでたし。

## 余談1

手順1と手順2については、コンテナを再度立ち上げしたら消えてしまうので、workspaceのDockerfile後半に下記の通り追加することをお勧めします。

```dockerfile:workspace/Dockerfile
###########################################################################
# Check PHP version:
###########################################################################

RUN set -xe; php -v | head -n 1 | grep -q "PHP ${LARADOCK_PHP_VERSION}."

###
# 追加設定 ここから
###

# composerのpackagistを近くに変更
RUN composer config -g repos.packagist composer https://packagist.jp

# composerコマンド並列化
RUN composer global require hirak/prestissimo

###
# 追加設定 ここまで
###

#
#--------------------------------------------------------------------------
# Final Touch
#--------------------------------------------------------------------------
#
```

## 余談2

手順5でポートフォワードですが、`-f`オプションをつけることでバックグラウンドでフォワードしてくれるようになります。