Dockerハンズオン　[2/2]

# はじめに
[こちら](https://qiita.com/Nidhog-tm/items/b3fc8387627bb4ab4e34)の続きになります。
本稿では以下のことを行います。

- DBの永続化
- マルチコンテナアプリケーション

# 1. DBの永続化

前回作成したコンテナを再起動してください。
`YOUR-USER-NAME`はDocker IDで置換してください。

```
docker run -dp 3000:3000 YOUR-USER-NAME/101-todo-app
```

3000のバッジが表示されたらクリックしてください。
変更したアプリが表示されます。
TODOリストが初期化された状態で表示されると思います。
TODOリストを登録して、コンテナを再起動すると毎回TODOリストが初期化されます。
これはなぜでしょう？
ここからは、コンテナがどのように機能しているかを見てみましょう。

## 1.1. コンテナボリューム
コンテナはファイルを作成、更新、削除できますが、コンテナが削除され、そのコンテナに隔離されると、それらの変更は失われます。
ボリュームを使用すると、これらすべてを変更が失われなくなります。
ボリュームは、コンテナの特定のファイルシステムパスをホストマシンに接続する機能を提供します。
コンテナ内のディレクトリがマウントされている場合、そのディレクトリの変更はホストマシンでも確認できます。
コンテナの再起動時に同じディレクトリをマウントすると、同じファイルが表示されます。
ボリュームには主に2つのタイプがあります。最終的には両方を使用しますが、名前付きボリュームから始めます。

## 1.2. Todoデータの永続化
デフォルトでは、todoアプリはそのデータを/etc/todos/todo.dbのSQLiteデータベースに保存します。
SQLiteに慣れていなくても心配いりません！
SQLiteとは、すべてのデータが単一のファイルに格納されるリレーショナルデータベースです。
SQLiteは大規模なアプリケーションには最適ではありませんが、小規模なデモには有効です。
後でSQLiteを実際のデータベースエンジンに切り替える方法について説明します。

データベースが単一のファイルであるため、そのファイルをホスト上で永続化し、次のコンテナで使用できるようにすれば、最後のコンテナが中断した場所を取得できるはずです。
ボリュームを作成し、データが格納されているディレクトリにボリュームをアタッチ（「マウント」と呼ばれることが多い）することで、データを永続化できます。
この状態でコンテナがtodo.dbファイルに書き込むと、ボリューム内のホストにデータを永続化できます。

前述のように、名前付きボリュームを使用します。
名前付きボリュームは単なるデータのバケツと考えてください。
ボリュームを使用するとDockerはディスク上の物理的な場所を維持し、ボリュームの名前を覚えるだけで済みます。
以下の手順で、ボリュームを使用するたびに、Dockerで正しいデータが提供されることを確認します。

1.`docker volume create`コマンドを使用してボリュームを作成します。

```
docker volume create todo-db
```

2.todoコンテナーを開始しますが、`-v`フラグを追加してボリュームマウントを指定します。
名前付きボリュームを使用して`/etc/todos`にマウントし、パスで作成されたすべてのファイルをキャプチャします。

```
docker run -dp 3000:3000 -v todo-db:/etc/todos YOUR-USER-NAME/101-todo-app
```

3.コンテナが起動したら、アプリを開いて、ToDoリストにいくつかの項目を追加します。
4.Todoアプリのコンテナーを削除します。`docker ps`を使用してIDを取得し、次に`docker rm -f <id>`を使用して削除します。
5.上記と同じコマンドを使用して新しいコンテナを開始します。
6.アプリを開きます。リストにまだアイテムが表示されているはずです！ 

これでデータを永続化する方法が学習できました！

## 1.3. ボリュームの場所
「名前付きボリュームを使用するときに、Dockerが実際にデータを保存する場所はどこですか？」という質問をよく聞きます。
データの保存場所を知りたい場合は、`docker volume inspect`コマンドで確認できます。

```
docker volume inspect todo-db
[
    {
        "CreatedAt": "2019-09-26T02:18:36Z",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/todo-db/_data",
        "Name": "todo-db",
        "Options": {},
        "Scope": "local"
    }
]
```

# 2. マルチコンテナアプリ
ここまでは、単一のコンテナアプリで作業してきました。
ここでアプリケーションスタックにMySQLを追加します。

MySQLを追加する場合、「MySQLはどこで実行すればいいですか？同じコンテナにインストールしたほうがいいですか？個別に実行したほうがいいですか？」という質問をよく耳にします。
回答としては、各コンテナは1つのことを行い、適切に実行する必要があるという事になります。
理由は以下になります。

- データベースとは異なる方法でAPIとフロントエンドをスケーリングする必要がある可能性が高い
- 各コンテナごとに個別にバージョンアップおよび更新できる

# 2.1. コンテナネットワーキング
コンテナはデフォルトで単独で実行され、同じマシン上の他のプロセスやコンテナについては何も知らないことに注意してください。
では、あるコンテナが別のコンテナと通信できるようにするにはどうすればいいでしょう？
答えはネットワーキングです。

## 2.2. MySQLの起動
コンテナをネットワークに配置するには2つの方法があります。
1）起動時にコンテナを割り当てる
2）既存のコンテナを接続する
ここでは、最初にネットワークを作成し、起動時にMySQLコンテナを接続します。

1.ネットワークを作成します。

```
docker network create todo-app
```

2.MySQLコンテナを起動し、ネットワークに接続します。

```
docker run -d \
    --network todo-app --network-alias mysql \
    -v todo-mysql-data:/var/lib/mysql \
    -e MYSQL_ROOT_PASSWORD=secret \
    -e MYSQL_DATABASE=todos \
    mysql:5.7
```

3.データベースが稼働していることを確認するには、データベースに接続し、接続を確認します。

```
docker exec -it <mysql-container-id> mysql -p
```
パスワードプロンプトが表示されたら、`secret`を入力します。 
MySQLシェルでデータベースをリストし、todosデータベースが表示されることを確認します。

```
mysql> SHOW DATABASES;
```

次のような出力が表示されます。

```
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| todos              |
+--------------------+
5 rows in set (0.00 sec)
```
`todos`テーブルが作成された状態でMySQLが起動されました。

# 2.3. MySQLでアプリを実行する
1.コンテナをアプリネットワークに接続します。

```
docker run -dp 3000:3000 \
  --network todo-app \
  -e MYSQL_HOST=mysql \
  -e MYSQL_USER=root \
  -e MYSQL_PASSWORD=secret \
  -e MYSQL_DB=todos \
  YOUR-USER-NAME/101-todo-app \
  sh -c "yarn install && yarn run dev"
```

2.ブラウザでアプリを開き、ToDoリストにいくつかの項目を追加します。
3.mysqlデータベースに接続し、アイテムがデータベースに書き込まれていることを確認します。

```
docker exec -ti <mysql-container-id> mysql -p todos
```
MySQLシェルで`todo_items`テーブルをselectする。

```
+--------------------------------------+-------+-----------+
| id                                   | name  | completed |
+--------------------------------------+-------+-----------+
| 0a826d56-c329-4891-981e-83274b902f97 | test  |         0 |
| d0dbaa00-18d2-4d83-ab59-53b21b4172be | aaaaa |         1 |
| c812fa17-adff-4a78-8d86-d956519b7e8c | bbbb  |         0 |
+--------------------------------------+-------+-----------+
```
登録した内容が保存されている事が確認できます。

# おわりに
以上でハンズオンは終了となります。
Dockerの入門として皆様のお役に立てたでしょうか？
本家のハンズオン資料はとてもわかりやすく、順序立てて進めてくれているので興味を持った方はぜひ実施してみてください。
この和訳では筆者の独断と偏見で割愛している部分が多々あります。（スミマセン）