【初心者向け】Docker for Macで開発環境を作る（アプリサーバとDBサーバのコンテナ間連携まで）

いまさらDockerをはじめてみました。
コマンドを忘れやすいし、古かったり断片的にしか欲しい情報がなかったりして
どのように動作しているのか把握に苦しんだので備忘録含めてまとめておきます。
（この記事だけで一通りの使い方はわかるようにしたつもりです。）
Dockerを使うことのメリットは次のようなものがあります。

* Dockerコンテナ上に仮想環境（OS+ミドルウェア）を構築するため、実環境が汚れない
* 作成したDockerコンテナはイメージファイル化（Dockerイメージ）できる
* DockerイメージはDocker Hubでプライベートもしくはパブリックリポジトリにバージョン管理＆公開できる
* Dockerイメージを持ってきてコンテナ起動するだけなので環境移行が楽
* Dockerfileと呼ばれる環境作成ファイルからDockerイメージの作成もできる

DBサーバの各コンテナを用意し、
アプリサーバのコンテナから別コンテナのDBサーバにアクセスし
コンテナ外からはNginXにアクセスできるような構成にします。

<img width="427" alt="image.png" src="https://qiita-image-store.s3.amazonaws.com/0/55077/daa8abc3-7615-c05a-d04b-43d4c4f39648.png">


その中で今回は開発でよく使うであろう、
NginxコンテナからMySQLコンテナにアクセスができる構成をDockerで作成してみます。
(一つのコンテナにNginXとMySQLを乗せることもできるのですが、その構成だとコンテナを増やした時にDBの共有に不都合が起きるため、コンテナを分けます。)

Docker自体の概念、Dockerfileの作り方、docker-composeについては次の記事が参考になります。
[いまさらDockerに入門したので分かりやすくまとめます](https://qiita.com/gold-kou/items/44860fbda1a34a001fc1)

Dockerコンテナが立ち上がらなくなった場合のトラブルシュートは次の記事が参考になります。
[dockerでコンテナが立ち上がらないときやってみること](https://qiita.com/mom0tomo/items/35dfacb628df1bd3651e)

#インストール
OSX前提で以下をインストールします。
[Docker for Mac](https://docs.docker.com/docker-for-mac/)
boot2dockerは非推奨となっています。
あとこちらのほうが仮想化にVirtual Boxを使ってなくて早いらしいです。

インストールするとデスクトップにランチャーが追加されます。
<img width="271" alt="Docker1.png" src="https://qiita-image-store.s3.amazonaws.com/0/55077/3a2d5613-a634-bc7c-26ab-e9c7ab5973b4.png">

Docker is runningのステートになるとターミナルからdockerコマンドが使えるようになります。

```
$docker
```

#よく使うコマンド
詳しくはこちらを参考にしてください
[Docker ハンズオン - 基本コマンド編](http://qiita.com/hihihiroro/items/6dda871dc2566801a6da#-docker-ps)

さらにこの中でも特に使うと思ったコマンドを抜粋します。

* docker pull:Docker Hubからイメージのダウンロード
* docker build:Dockerfileからイメージの作成
* docker images:Dockerイメージの一覧取得
* docker rmi:Dockerイメージの削除
* docker ps:Dockerコンテナの起動確認
* docker run:DockerイメージからDockerコンテナの作成（と起動）
* docker start:Dockerコンテナの起動
* docker exec:Dockerコンテナ上の環境に対しコマンド実行
* docker cp:Dockerコンテナへファイルコピー
* docker stop:Dockerコンテナの停止
* docker rm:Dockerコンテナの削除
* docker commit:DockerコンテナのDockerイメージ化
* docker push:DockerイメージをDocker Hubにアップロード

#Dockerイメージのダウンロード

各種DBのDockerイメージをダウンロードします。(今回使うのはmysqlのみですが)

* MySQL
* PostgresSQL
* Redis
* MongoDB

[Docker Hub](https://hub.docker.com/)と呼ばれるイメージファイルのGitみたいな公開リポジトリからダウンロードします。
イメージファイルのダウンロードにはdocker pullコマンドを使います。

```
# Mysql
$docker pull mysql
# PostgresSQL
$docker pull postgres
# Redis
$docker pull redis
# MongoDB
$docker pull mongo
```

次にNginXをのせるアプリサーバ用のイメージを作成します。
今回はCentOS6のイメージをとってきてその上に構築します。

```
$docker pull centos:6
```

インストール済みイメージの確認には
docker imagesコマンドでイメージ一覧を表示します。

```
$docker images
REPOSITORY              TAG                 IMAGE ID            CREATED             SIZE
mysql                   5.7                 b7dc06006192        9 days ago          385.6 MB
mysql                   5.7.15              b7dc06006192        9 days ago          385.6 MB
mysql                   latest              b7dc06006192        9 days ago          385.6 MB
centos                  6                   f07f6ca555a5        10 days ago         194.6 MB
mongo                   latest              48b8b08dca4d        2 weeks ago         366.4 MB
redis                   latest              0d1cbfaa41da        2 weeks ago         185 MB
postgres                latest              6f86882e145d        2 weeks ago         265.9 MB
```

# DBサーバコンテナ作成とDB作成

## コンテナの作成と起動
次のコマンドでMySQLコンテナの作成と起動を行います。

```
$docker run -it --name mysql -e MYSQL_ROOT_PASSWORD=password -d mysql:latest
```

-itは-iオプションと-tオプションの併用です。（interactive+tty）このオプションがないと起動後、一瞬で終了してしまいます。
--nameオプションはDockerコンテナの起動コンテナ名を指定します。
-eオプションで環境変数の設定行います。MYSQL_ROOT_PASSWORDにはrootのパスワードを指定します。
-dオプションはバックグラウンド起動を行います。(イメージ名):(バージョン)を指定します。

起動に成功したかはdocker psコマンドで確認できます。

```
$docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
550c25522be8        mysql:5.7           "docker-entrypoint.sh"   3 days ago          Up 4 seconds        3306/tcp            mysql
```

ちなみに-aオプションをつけると全ての作成済みdockerコンテナが確認できます。（停止中のコンテナ含めて表示されます。）

```
$docker ps -a
```
## コンテナの起動と停止（２回目以降）
２回目以降はdocker startでコンテナ起動、docker stopでコンテナ停止を行います。

```
# コンテナ起動
$docker start (コンテナ名)
# コンテナ停止
$docker stop (コンテナ名)
```

## コンテナにログイン
docker execコマンドでmysqlコンテナにログインします。

```
$docker exec -it mysql bash
```

docker exec -it (コンテナ名) bash
とすることで指定のコンテナのbash（シェル）を起動する（ログインする）ことができます。

ログイン後、DBの作成を行います。（ここではdbというデータベースを作成します。）

```
#mysql -u root -p
Enter password:password
mysql>create database db;
mysql>show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| db                 |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
5 rows in set (0.01 sec)
mysql>exit;
#exit
```

# アプリサーバコンテナにNginXとDBクライアントをインストール
CentOSのmycentosコンテナを作成と起動し、ログインします。

```
$docker run -it --name mycentos -d centos:6
```

ログイン後、NginXと各種DBクライアントをインストールします。

```
# NginX
$rpm -ivh http://nginx.org/packages/centos/6/noarch/RPMS/nginx-release-centos-6-0.el6.ngx.noarch.rpm
$yum install -y nginx
# MySQL
$yum install -y mysql
# PostgresSQL
$yum install -y postgresql
# Redis
$rpm -ivh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm    
$yum --enablerepo=epel install -y redis
# MongoDB
$echo -e "[mongodb]" > /etc/yum.repos.d/mongodb.repo
$echo -e "name=MongoDB Repository" >> /etc/yum.repos.d/mongodb.repo 
$echo -e "baseurl=http://downloads-distro.mongodb.org/repo/redhat/os/x86_64" >> /etc/yum.repos.d/mongodb.repo
$echo -e "gpgcheck=0" >> /etc/yum.repos.d/mongodb.repo
$echo -e "enabled=1" >> /etc/yum.repos.d/mongodb.repo
$yum install -y mongodb-org
$exit
```

# コンテナを保存する

## コンテナのDockerイメージ化
docker psコマンドにて起動コンテナのmycentosのCONTAINER IDを確認します。

```
$docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
8be3061220cc        centos:6            "/bin/bash"              8 seconds ago       Up 6 seconds                            mycentos
550c25522be8        mysql:5.7           "docker-entrypoint.sh"   3 days ago          Up 33 minutes       3306/tcp            mysql
```

docker commitにて指定のコンテナをDockerイメージ化します。
次のようなフォーマットで保存します。
docker commit (コンテナID) (ユーザ名)/(イメージ名):(バージョン)

```
$docker commit 8be3061220cc myuser/centos:1.0
```

保存後、docker imagesで確認すると追加されています。

```
daikiterai$ docker images
REPOSITORY         TAG                 IMAGE ID            CREATED             SIZE
myuser/centos      1.0                 ccfb04ce118d        7 hours ago         656.1 MB
(略)
```

このようにdocker commitでイメージを作る他にDockerfileというファイルに
OSやミドルウェアのインストール手順を記載することも可能です。
詳細は[効率的に安全な Dockerfile を作るには](http://qiita.com/pottava/items/452bf80e334bc1fee69a)が参考になります。
Dockerfileからイメージを作成するにはdocker buildコマンドを使います。

## Dockerイメージの削除
余力があったら次の例のようにMySQLのコンテナの方も定期的にイメージ化してバックアップを取ると良いでしょう。（イメージ化することでデータ丸ごとバックアップが取れます。）
古いバージョンのイメージはdocker rmiコマンドで消せます。

```
$docker commit 550c25522be8 myuser/mysql:1.0
# バージョンアップ時
$docker commit 550c25522be8 myuser/mysql:2.0
# 古いやつは消す
$docker rmi myuser/mysql:1.0
```

## コンテナの削除
無事イメージ化したので一旦コンテナを削除します。

```
$docker rm -f 8be3061220cc
もしくは
$docker rm -f mycentos
```

-fオプションは強制的に行うオプションです。
docker psコマンドで確認すると指定のコンテナが削除されます。

```
$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
550c25522be8        mysql:5.7           "docker-entrypoint.sh"   3 days ago          Up About an hour    3306/tcp            mysql
```

# DockerイメージをDocker Hubにアップロードする
まず先に[Docker Hub](https://hub.docker.com/)のアカウントを作成しておいてください。
アカウント作成後、docker loginコマンドにてログインします。

```
$docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: (ユーザ名)
Password: (パスワード)

Login Succeeded
```

ログイン成功したらdocker pushコマンドでイメージのアップロードができるようになります。

```
$docker push (イメージ名)
```

成功するとDocker Hubのリポジトリにアップロードされます。
<img width="818" alt="dockerhub.png" src="https://qiita-image-store.s3.amazonaws.com/0/55077/0500acfa-0158-b8a5-a64a-51f30575faeb.png">

イメージのダウンロードは他のイメージ同様
docker pullコマンドでダウンロードできます。

```
$docker pull (イメージ名)
```

# アプリサーバコンテナからDBサーバコンテナにアクセスする

## コンテナ結合＆ポートフォワーディング指定でコンテナ作成
先ほど作ったイメージでtestappコンテナを作成し起動させます。

```
$docker run -it --name testapp --link mysql:mysql -d -p 8080:80 myuser/centos:1.0 
```

--linkオプションは結合するコンテナの指定です。（リンクしたいコンテナ名）:(エイリアス)で指定します。エイリアスは環境変数にマッピングされるプレフィックスとなります。
-pオプションはポートフォワーディングの指定です。外部から指定したポートをコンテナ内部のポートに転送する指定を行います。（転送元ポート）：（転送先ポート）
複数のポートフォワーディングをしたい場合は複数指定します。
-p 80:80 -p 443:443

testappコンテナにログインします。

```
$docker exec -it testapp bash
```

envコマンドで確認するとMySQLコンテナの環境変数（エイリアス名：MYSQL_*）が追加されます。

```
#env
(中略)
MYSQL_NAME=/centos/mysql
MYSQL_PORT_3306_TCP_PROTO=tcp
MYSQL_PORT_3306_TCP_ADDR=172.17.0.2
LESSOPEN=||/usr/bin/lesspipe.sh %s
MYSQL_ENV_MYSQL_MAJOR=5.7
MYSQL_PORT=tcp://172.17.0.2:3306
```

mysqlクライアントでDBサーバコンテナ側のMySQLにアクセスできれば成功です。

```
# mysql -u root -p -h $MYSQL_PORT_3306_TCP_ADDR
Enter password:password 
mysql> 
```

## 外部からアプリサーバ(NginX)へのアクセスを確認する
testappコンテナ内のNginXを起動します。

```
$nginx
```

ブラウザから下記URLを指定
http://localhost:8080

コンテナ内のNginXの80ポートに転送されていれば成功です。

<img width="586" alt="nginx.png" src="https://qiita-image-store.s3.amazonaws.com/0/55077/24df3392-d7ed-47ff-6eab-f991bbd72e1b.png">

コンテナのネットワークに関してもっと知りたい場合は下記を参考に
[Docker の基本学習 ~ コンテナ間のリンク](http://qiita.com/Arturias/items/75828479c1f9eb8d43fa)
[参考訳：Docker コンテナ・ネットワークの理解](http://qiita.com/zembutsu/items/1da05cb6a60e1c5acc25)

# Dockerコンテナへのファイルコピー
Dockerコンテナ間のファイル転送にはdocker cpコマンドを使います。
ファイル数が多い時はSSHポート（２５番ポート）をフォワーディングして
sshfsでマウント（共有フォルダ化）してしまったほうが早いかもしれません

## ローカルからDockerコンテナへファイルコピー
次のようなフォーマットになっています、
docker cp (ローカルのコピー元パス) （コンテナ名）:（コンテナのコピー先パス）
test.txtをtestappコンテナにコピーしたい場合

```
$docker cp ~/Desktop/test.txt testapp:test.txt
```

## Dockerコンテナからローカルへファイルコピー
Dockerコンテナへのコピーと逆です。
docker cp （コンテナ名）:（コンテナのコピー元パス） (ローカルのコピー先パス) 

```
$docker cp testapp:test.txt ~/Desktop/test2.txt
```

ちなみにコンテナ同士のコピーは直接はできないようです。