Windows10マシンにDocker Toolbox を入れて個人用の開発環境を作る




# 概要
Windowsマシン上にDocker Toolboxをインストールして個人用のアプリ開発環境を作ってみます。Windowsマシンそのものへのインストールツールは最小限にして、Docker Toolboxに含まれる仮想マシン上にDockerを使ってSonarQubeなどをセットアップしてみます。

__2016.3.29 新しいインストーラ(DockerToolbox-1.10.3.exe)でやり直してみました。__



## 前提としている環境
* Windows 10 (7以上だったらいけるはず)
* Intel VT対応 (それ相当の仮想化テクノロジが使えればよい。最近のPCはほとんど対応しているはず。 )

## 目指すこと
JavaでのWebアプリ開発で使うだろう開発環境を構築します。構築するミドルウェア、ツールは以下のようなイメージです。

### 導入するDockerコンテナ
* Jenkins
* MySQL
* SonarQube
* Selenium
* Glassfish

### ymlファイルなどの置き場所
https://github.com/osuo/DevelopEnvironment


# Docker環境の準備
## Docker Toolboxのインストール
Dockerのオフィシャルサイトの[Docker Toolbox](https://www.docker.com/docker-toolbox)からDocker Toolboxのインストーラを入手します。このインストーラにはVirtualBoxやGit for Windowsが同梱されてます。

次に、Docker Toolboxのインストーラを起動してDocker関連のコンポーネントをインストールします。

![WS000038.JPG](https://qiita-image-store.s3.amazonaws.com/0/79341/23a956cf-5c2d-8337-d613-8e3c545596ce.jpeg)

__重要!__
一番下のチェックボックス「Install VirtualBox with NDIS5 driver[default NDIS6]」にチェック入れるのを忘れずに！
![WS000042.JPG](https://qiita-image-store.s3.amazonaws.com/0/79341/48226739-30cd-fc66-50d3-416036d7625b.jpeg)

理由などこちらが参考になるかと。
[Failed to open/create the internal network Vagrant on Windows10](http://stackoverflow.com/questions/33725779/failed-to-open-create-the-internal-network-vagrant-on-windows10)


## Docker用の仮想マシンを構築
__Docker Quickstart Terminal__を起動します。初回のみVirtualBox上にDocker用の仮想マシンを作成してくれます。その後、仮想マシンのコンソールにログインしてくれます。

![WS000015.JPG](https://qiita-image-store.s3.amazonaws.com/0/79341/9971c39b-a115-8644-5108-9d202ffeb38b.jpeg)

しばらくすると...Dockerエンジンが動作しているDockerホストの仮想マシンに接続する。
![WS000016.JPG](https://qiita-image-store.s3.amazonaws.com/0/79341/46df438c-4c67-fe3b-a01f-4b7b6c5d97cb.jpeg)

VirtualBoxには__default__インスタンスができてる。
![WS000017.JPG](https://qiita-image-store.s3.amazonaws.com/0/79341/d07c1288-bb7f-1149-c5c4-43cd5cded083.jpeg)




## WindowsマシンからDcokerの動作確認
Dockerのオフィシャルサイトの例を試す。

```bash
$ docker run hello-world
```

こんな感じになればOK。
![WS000018.JPG](https://qiita-image-store.s3.amazonaws.com/0/79341/89f64608-737c-2a25-fdae-be15ebd55b27.jpeg)

docker-machineでも確認してみる。

```bash
$ docker-machine ls
NAME      ACTIVE   DRIVER       STATE     URL                         SWARM   ERRORS
default   *        virtualbox   Running   tcp://192.168.99.101:2376
```

defaultという名前のDockerホストが起動状態にあることが分かる。

`docker-machine stop default`でdefaultを止めたり、`docker-machine start default`で開始したりできる。


# Dockerコンテナの起動と利用
Dockerコンテナの制御に__docker-compoise__を使う。素のdockerだと起動時に指定するパラメータを毎回コマンドラインに指定することになるのですが、docker-composeだと__yml__ファイルに記述できすっきりするから、です。

適当なフォルダを作り、その中にdocker-compose.ymlなどを置いて試します。
とりあえず、__work__ディレクトリを作っておきましょう。

```bash
$ mkdir work
$ cd work
```



## Jenkinsを動かす
docker-compose.ymlを作成します。

```yaml
jenkins:
  image: jenkins
  ports:
    - "8080:8080"
```

docker-compose.ymlを作成したディレクトリで`docker-compose`を実行します。初回はjenkinsのDockerイメージをDockerHubから入手してから起動します。

```bash
$ docker-compose up -d
...
Status: Downloaded newer image for jenkins:latest
Creating work_jenkins_1
```

Dockerコンテナの状態を確認してみます。

```bash
$ docker-compose ls
     Name                   Command               State                 Ports 
-------------------------------------------------------------------------------------------
work_jenkins_1   /bin/tini -- /usr/local/bi ...   Up      50000/tcp, 0.0.0.0:8080->8080/tcp
```

Ports欄の__0.0.0.0:8080->8080/tcp__というのは、Dockerホストの8080ポートをDockerコンテナの8080ポートにポートフォワードしますよ、ってことです。
なのでブラウザでDockerホストの8080ポートにアクセスすればjenkinsのコンソールが開きます。DockerホストのIPアドレスは`docker-machine`でわかります。

```bash
$ docker-machine ls
NAME      ACTIVE   DRIVER       STATE     URL                         SWARM   ERRORS
default   *        virtualbox   Running   tcp://192.168.99.101:2376
```
こんな画面がでるはず。
![WS000021.JPG](https://qiita-image-store.s3.amazonaws.com/0/79341/a651b557-17e7-01bd-9b7c-56c143a99035.jpeg)


## Jenkinsを動かす(Data Volumes コンテナとともに)
jenkinsのユーザデータはDockerコンテナ内の__/var/jenkins_home__に格納されます（ワークスペース、各種設定やPluginなど）。これを__Data Volumesコンテナ__として独立させることができます。Jenkins本体のDockerコンテナとそのユーザデータのDockerコンテナを分けることで、Jenkins本体をアップグレードすることが楽にできるようになります。より実践的な使い方です。

### 1. JenkinsのDockerコンテナの内容を確認する
念のため。中身がどうなっているかを調べておきます。`docker exec`で確認します。Jenkinsのコンテナを削除してもユーザデータが影響を受けないか（残っているか）の確認のためです。

`docker inspect`で確認。

```bash
$ docker inspect -f "{{ .HostConfig.VolumesFrom }} " work_jenkins_1
[]
```


DataVolumeコンテナーの有り無しを`df`で確認できるかと思ったけどできず。なんでだろう？また調べよう。（/proc/mounts でも違いがわからず。）

```bash
$ docker exec -it work_jenkins_1 df -h
Filesystem      Size  Used Avail Use% Mounted on
none             19G 1007M   17G   6% /
tmpfs           499M     0  499M   0% /dev
tmpfs           499M     0  499M   0% /sys/fs/cgroup
/dev/sda1        19G 1007M   17G   6% /etc/hosts
shm              64M     0   64M   0% /dev/shm
```





### 2. 先ほど作成したJenkinsのDockerコンテナを削除する
`docker-compose stop jenkins`でJenkinsを停止して、`docker-compose rm jenkins`でJenkinsのコンテナを削除します。後は、Dockerコンテナの状態を`docker-compose ps`で確認します。

```bash
$ docker-compose stop jenkins
Stopping work_jenkins_1 ...
[1Bping work_jenkins_1 ... done
```

```bash
$ docker-compose ps
     Name                   Command                State     Ports
------------------------------------------------------------------
work_jenkins_1   /bin/tini -- /usr/local/bi ...   Exit 143
```

```bash
$ docker-compose rm jenkins
Going to remove work_jenkins_1
Are you sure? [yN] y
Removing work_jenkins_1 ...
[1Bving work_jenkins_1 ... done
```

```bash
$ docker-compose ps
Name   Command   State   Ports
------------------------------
```

JenkinsのDockerコンテナを削除できました。




### 3. docker-compose.ymlを修正してユーザデータ格納用のDataVolumesコンテナを追記する
docker-compose.ymlを次のように修正します。

```yaml
# jenkins container
jenkins:
  image: jenkins
  ports:
    - "8080:8080"
  volumes_from:
    - jenkins_data

# jenkins data volume container
jenkins_data:
  image: jenkins
  command: echo "jenkins data volume container."
```


### 4. コンテナを起動する
`docker-compose up -d`で起動します。

```bash
$ docker-compose up -d
...
Status: Downloaded newer image for busybox:latest
Creating work_jenkins_data_1
Creating work_jenkins_1
```

```bash
$ docker-compose ps
       Name                      Command               State
Ports
-------------------------------------------------------------------------------------------------
work_jenkins_1        /bin/tini -- /usr/local/bi ...   Up       50000/tcp, 0.0.0.0:8080->8080/tcp
work_jenkins_data_1   /bin/tini -- /usr/local/bi ...   Exit 0
```

### 5. 確認する

```bash
$ docker inspect -f "{{ .HostConfig.VolumesFrom }} " work_jenkins_1
[6f65fc53675392163fc090be8ceeaa0e7ced5f757a17ec59cf92258fc1d66a3f:rw]
```

お！この__6f65fc536753...__というのがDataVolumeコンテナである__work_jenkins_data_1__ のことです。`docker ps -a`で確認します。

```bash
$ docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                     PORTS                               NAMES
452d31f1400d        jenkins             "/bin/tini -- /usr/lo"   7 minutes ago       Up 7 minutes               0.0.0.0:8080->8080/tcp, 50000/tcp   work_jenkins_1
6f65fc536753        jenkins             "/bin/tini -- /usr/lo"   About an hour ago   Exited (0) 7 minutes ago                                       work_jenkins_data_1
```

次に、JenkinsのDockerコンテナを削除しても、DataVolumeコンテナにあるユーザデータが残っているかを確認してみます。
手順はこんな感じです。

1. JenkinsのPluginを更新する
2. JenkinsのDockerコンテナを作り直す
3. Pluginが更新済みであるかを確認する

#### 1. JenkinsのPluginを更新する
更新前：
__(アップデートあり)__となっています。
![WS000023.JPG](https://qiita-image-store.s3.amazonaws.com/0/79341/c1f61bf4-7562-bc2b-7d56-1e6cba90717b.jpeg)

更新：
こんな感じで。
![WS000026.JPG](https://qiita-image-store.s3.amazonaws.com/0/79341/774400d4-5f7d-7011-dd79-44282b69552d.jpeg)

#### 2. JenkinsのDockerコンテナを作り直す
止めて、

```bash
$ docker-compose stop jenkins
Stopping work_jenkins_1 ...
[1Bping work_jenkins_1 ... done
```

削除して、

```bash
$ docker-compose rm jenkins
Going to remove work_jenkins_1
Are you sure? [yN] y
Removing work_jenkins_1 ...
[1Bving work_jenkins_1 ... done
```

確認します。DataVolumeコンテナのみ残ってます。

```bash
$ docker-compose ps
       Name                      Command               State    Ports
---------------------------------------------------------------------
work_jenkins_data_1   /bin/tini -- /usr/local/bi ...   Exit 0
```

起動する。

```bash
$ docker-compose up -d
Starting work_jenkins_data_1
Creating work_jenkins_1
```


#### 3. Pluginが更新済みであるかを確認する
先ほどのPlugin画面を見てみる。__(アップデートあり)__がない。
![WS000025.JPG](https://qiita-image-store.s3.amazonaws.com/0/79341/198ca988-215e-f8e8-024e-d505c0a565d1.jpeg)


意図通りに動作しました。DataVolumeコンテナを通常のコンテナと別にすることで、ユーザデータなどの保持がしやすくなります。




## MySQLをバックエンドにしてSonarQubeを動かす
docker-compose.ymlに次の内容を追記する。

```yaml
# mysql container
mysql:
  image: mysql
  environment:
    - MYSQL_ROOT_PASSWORD=hogehoge
  ports:
    - "3306:3306"
  volumes_from:
    - mysql_data

# mysql container
mysql_data:
  image: mysql
  command: echo "sonarqube data volume container."

# sonarqube container
sonarqube:
  image: sonarqube
  environment:
    - SONARQUBE_JDBC_USERNAME=sonar
    - SONARQUBE_JDBC_PASSWORD=sonar
    - SONARQUBE_JDBC_URL=jdbc:mysql://mysql/sonar?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true
  links:
    - mysql:mysql
  ports:
    - "9000:9000"
  volumes_from:
    - sonarqube_data

# sonarqube data volume container
sonarqube_data:
  image: sonarqube
  command: echo "sonarqube data volume container."
```

sonarqube用のDatabase作成スクリプトを用意する。
まず、create_sonar_db.sql。

```sql:create_sonar_db.sql
CREATE DATABASE sonar CHARACTER SET utf8;

CREATE USER 'sonar' IDENTIFIED BY 'sonar';
GRANT ALL ON sonar.* TO 'sonar'@'%' IDENTIFIED BY 'sonar';
GRANT ALL ON sonar.* TO 'sonar'@'localhost' IDENTIFIED BY 'sonar';
FLUSH PRIVILEGES;
```

次に、create_sonar_db.sh。

```bash:create_sonar_db.sh
#!/bin/bash

#
DIRNAME=${0%/*}
MYSQL_CONTAINER=work_mysql_1
MYSQL_PASSWORD=hogehoge

# dockerコンテナを起動してデータをロードする
docker run -i --link $MYSQL_CONTAINER:mysql --rm mysql mysql -h mysql -uroot -p$MYSQL_PASSWORD < ./$DIRNAME/create_sonar_db.sql
```

起動は次のようにやります。
1. mysqlのみを起動してsonarqube用のDatabaseを作成する
2. sonarqubeを起動する

### 1. mysqlのみを起動する

```bash
$ docker-compose up -d mysql
Starting work_mysql_data_1
Creating work_mysql_1
```

```bash
$ docker-compose ps
        Name                       Command               State                  Ports
---------------------------------------------------------------------------------------------------
work_jenkins_1          /bin/tini -- /usr/local/bi ...   Up       50000/tcp, 0.0.0.0:8080->8080/tcp
work_jenkins_data_1     /bin/tini -- /usr/local/bi ...   Exit 0                 
work_mysql_1            /entrypoint.sh mysqld            Up       0.0.0.0:3306->3306/tcp
work_mysql_data_1       /entrypoint.sh echo sonarq ...   Exit 0
```

### 2. sonarqube用のDatabaseを作成する

```bash
$ bash ./create_sonar_db.sh
mysql: [Warning] Using a password on the command line interface can be insecure.
```

確認します。

```bash
$ docker run --rm -it --link work_mysql_1:mysql mysql mysql -h mysql -uroot -
phogehoge
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 3
Server version: 5.7.9 MySQL Community Server (GPL)

Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show tables;
ERROR 1046 (3D000): No database selected
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sonar              |
| sys                |
+--------------------+
5 rows in set (0.00 sec)
```


### 3. sonarqubeを起動する

```bash
$ docker-compose up -d sonarqube
Starting work_sonarqube_data_1
Starting work_mysql_data_1
work_mysql_1 is up-to-date
Creating work_sonarqube_1
```

```bash
$ docker-compose ps
        Name                       Command               State                  Ports
---------------------------------------------------------------------------------------------------
work_jenkins_1          /bin/tini -- /usr/local/bi ...   Up       50000/tcp, 0.0.0.0:8080->8080/tcp
work_jenkins_data_1     /bin/tini -- /usr/local/bi ...   Exit 0                 
work_mysql_1            /entrypoint.sh mysqld            Up       0.0.0.0:3306->3306/tcp
work_mysql_data_1       /entrypoint.sh echo sonarq ...   Exit 0                 
work_sonarqube_1        ./bin/run.sh                     Up       0.0.0.0:9000->9000/tcp
work_sonarqube_data_1   ./bin/run.sh echo sonarqub ...   Exit 0
```

ブラウザでDockerホストの9000番ポートにアクセスしてみましょう。
![WS000028.JPG](https://qiita-image-store.s3.amazonaws.com/0/79341/159b085d-4b66-2d22-507c-3c4b51e9c049.jpeg)

管理者アカウントでログインして、メニューよりAdministration→System→System Infoを参照すると、DatabaseがMySQLとなっているはずです。なお、デフォルトの管理者アカウント/パスワードは、admin/adminです。

![WS000028.JPG](https://qiita-image-store.s3.amazonaws.com/0/79341/ad04f817-ed12-70e5-2423-f25e9649d17d.jpeg)

![WS000029.JPG](https://qiita-image-store.s3.amazonaws.com/0/79341/f8ffcfe3-c4a4-1636-7865-5d23c700ede9.jpeg)

![WS000030.JPG](https://qiita-image-store.s3.amazonaws.com/0/79341/3ae80412-1b5e-ab1b-4a17-847a8ae27cb6.jpeg)


## Seleniumを動かす

docker-compose.ymlに以下の内容を追記します。

```yaml
# sonarqube data volume container
sonarqube_data:
  image: sonarqube
  command: echo "sonarqube data volume container."

# selenium hub container
selenium-hub:
  image: selenium/hub
  ports:
    - "4444:4444"

# selenium node chrome container
chrome:
  image: selenium/node-chrome-debug
  ports:
    - "5900:5900"
  links:
    - selenium-hub:hub

# selenium node firefox container
firefox:
  image: selenium/node-firefox-debug
  ports:
    - "5901:5900"
  links:
    - selenium-hub:hub
```

起動します。

```bash
$ docker-compose up -d
```

```bash
$ docker-compose ps
        Name                       Command               State                  Ports
---------------------------------------------------------------------------------------------------
work_chrome_1           /opt/bin/entry_point.sh          Up       0.0.0.0:5900->5900/tcp
work_firefox_1          /opt/bin/entry_point.sh          Up       0.0.0.0:5901->5900/tcp
・・・
work_selenium-hub_1     /opt/bin/entry_point.sh          Up       0.0.0.0:4444->4444/tcp
・・・
```

hubのコンソールを覗いてみます。Dockerホストの4444番ポートです。

![WS000031.JPG](https://qiita-image-store.s3.amazonaws.com/0/79341/dc36276f-7795-cf1d-9397-db06f63e20c2.jpeg)

UltraVNCViewerなどでDockerホストの5900に接続するとChrome用のSeleniumNodeに接続できます。
![WS000032.JPG](https://qiita-image-store.s3.amazonaws.com/0/79341/f2cf7abf-647e-1142-cbe3-adc9eda784db.jpeg)




## GlassFishを動かす
docker-compose.ymlに以下の内容を追記します。

```yaml
# glassfish
glassfish:
  image: glassfish
  ports:
    - "14848:4848"
    - "18080:8080"
    - "18181:8181"
```

起動する。

```bash
$ docker-compose up -d glassfish
```

ブラウザで接続する。Dockerホストの14848番ポート。

![WS000033.JPG](https://qiita-image-store.s3.amazonaws.com/0/79341/6580bbd2-b92a-1a48-9775-c8e2643f603e.jpeg)

リモートからログインできるようにしておく。
※そのうちスクリプト化しよう。
1. asadmin change-admin-password
2. asadmin login
3. asadmin enable-secure-admin

作成したアカウントは、admin/12345678です。

```bash
$ docker exec -it work_glassfish_1 bash
root@0512d1444d81:/usr/local/glassfish4# asadmin change-admin-password
Enter admin user name [default: admin]>
Enter the admin password>
Enter the new admin password>
Enter the new admin password again>
Command change-admin-password executed successfully.
root@0512d1444d81:/usr/local/glassfish4#
root@0512d1444d81:/usr/local/glassfish4# asadmin login
Enter admin user name [Enter to accept default]> admin
Enter admin password>
Login information relevant to admin user name [admin] for host [localhost] and admin port [4848] stored at [/root/.gfclient/pass] successfully.
Make sure that this file remains protected. Information stored in this file will be used by administration commands to manage associated domain.
Command login executed successfully.
root@0512d1444d81:/usr/local/glassfish4#
root@0512d1444d81:/usr/local/glassfish4# asadmin enable-secure-admin
You must restart all running servers for the change in secure admin to take effect.
Command enable-secure-admin executed successfully.
root@0512d1444d81:/usr/local/glassfish4# exit
exit
```

glassfishのDockerコンテナを再起動する。

```bash
$ docker-compose restart glassfish
```

改めてGlassfishの管理コンソールに接続する。セキュリティの警告を無視してログイン画面に進む。

![WS000034.JPG](https://qiita-image-store.s3.amazonaws.com/0/79341/4a9f5b31-6820-70e1-fac4-e7593ab25228.jpeg)

先ほど設定したユーザ/パスワードでログインする。

![WS000035.JPG](https://qiita-image-store.s3.amazonaws.com/0/79341/80b3046b-137c-6304-cca2-2b435d356d9e.jpeg)

いつもの画面です。




# Dockerのバックアップ
DockerコンテナでData Volumes コンテナをバックアップする仕掛けを用意してみました。参考になればうれしいです。
[docker-backup](https://github.com/osuo/docker-backup)

Data Volumesコンテナのバックアップについては [ここ](https://thinkit.co.jp/story/2015/09/29/6385)が参考になりそうです。

# Windowsでの注意点
windows10でdockerを操作していて気付いたことをメモしておきます。
## execでls /とするとWindows側のパスを見ちゃう

```bash
$ docker exec -it work_jenkins_1 ls /var/jenkins_home
ls: cannot access D:/Apps/Git/var/jenkins_home: No such file or directory
```

Dockerコンテナの/var/jenkins_homeを見たいのだけど、変になる。相対パスにしてやればとりあえず見える。

```bash
$ docker exec -it work_jenkins_1 ls var/jenkins_home
Download metadata.log          jobs                      secrets
Workspace clean-up.log         nodeMonitors.xml          updates
copy_reference_file.log        nodes                     userContent
hudson.model.UpdateCenter.xml  plugins                   war
identity.key.enc               secret.key
init.groovy.d                  secret.key.not-so-secret
```



# 最後に
Dockerを使った開発環境づくりのメモを書きました。Windows10のはまりどころを知れて良かったです（VirtualBoxのところ）。そのうちもっと簡単になるでしょうね。



# 参考
* [Docker Toolbox](https://www.docker.com/docker-toolbox)
* [Installation on Windows](http://docs.docker.com/engine/installation/windows/)
* [Windows 10 + VirtualBox 5.0 + Vagrant 1.7.4 + VCCW 2.18.0 でのエラー回避](http://atachibana.hatenablog.jp/entry/2015/11/28/164944)
* [Failed to open/create the internal network Vagrant on Windows10](http://stackoverflow.com/questions/33725779/failed-to-open-create-the-internal-network-vagrant-on-windows10)
* [VirtualBox](https://www.virtualbox.org/wiki/Downloads)
* [Dockerにおけるデータ専用コンテナ、KVM仮想化環境からの移行](https://thinkit.co.jp/story/2015/09/29/6385)
* [docker-backup](https://github.com/osuo/docker-backup)