ボリューム(volumes)で何ができるか気づけたから解説！(docker-compose)

## はじめに
Dockerって機能多すぎて、とりあえず実装して何も理解してないケースありませんか？
僕はボリュームがそのうちの１つでした...。

『これから`Docker`や`docker-compose`を使い始める！』『`volumes`が何をしているのか分からない...』という方には是非見て頂きたい記事となっております！

## これを読むことで何が分かるのか
- Dockerのボリュームとは何か分かる
- docker-composeのvolumesに何を設定するべきか分かる
- `docker volume ls`をした時に何故か増えてるボリューム達が何なのか分かる
- DockerにてNginxを使いたい時に、コンテナ間のリソース共有実装の足がかりになる

## データを永続化するとは
突然ですが、貴重なデータが含まれているデータってシンプルに消えてほしくないですよね(｡･_･｡)
そんな貴重なデータをDockerにてDB(データベース)コンテナとして起動させていると、`docker ps`コマンドにて以下の画像のようにDBコンテナ(postgreSQL)が立ち上がっていることが確認できますよね。
![docker-ps加工済.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/436088/9261854b-b8b9-c72c-2f3d-d6fec685d779.jpeg)

ここでもし誤ってDBコンテナを消してしまう事があったら、どうなってしまうでしょうか...

**DBに蓄積してきたデータが全て消えます。**

ですがコンテナ管理において、デファクトスタンダードとされているDocker様がそんな状況を放置するでしょうか？いや否です。

**コンテナのデータを別の場所で保管できる<font color="red">ボリューム</font>機能があります！**

**ボリューム**を使えば、例えDBのコンテナを消してしまっても、データを別場所にて保管しているので、復元できるという仕組みですね(/･ω･)/

## ボリュームとは
こちらは僕が説明するより[コチラの記事(Docker、ボリューム(Volume)について真面目に調べた - Qiita *1)](https://qiita.com/gounx2/items/23b0dc8b8b95cc629f32)の説明が優れていると思ったので、引用させていただきます。
>- **ボリュームとは、データを永続化できる場所のことである。**
>  - 外部HDDのようなイメージ。コンテナ本体にマウントして使う。
>- `コンテナ` と `ボリューム` の違いについて
>  - コンテナ内部にデータ(ファイル)を保存しても、コンテナ破棄すると消えてしまう。
>  - なので、データを永続化したいときは、コンテナの外にデータを置く必要がある。
>  - その場所のことを、ボリュームと呼ぶ。
>- `ボリューム`(=データを永続化できる場所) は２種類ある。
>  - ホストのディレクトリ(ファイル)
>      - ホストで ls で見えるモノ。
>  - Docker の リソースとしての Volume
>      - docker volume ls で見えるモノ。
>  - （本当は、これ以外にもNFSなども指定できるようだ）
>- ボリュームをコンテナにマウントすることで、コンテナからアクセスできるようになる。
>- あるいは``--volumes-from <コンテナ名> ``とすると、指定したコンテナのマウントと同じようにマウントできる。


## 今までの僕のdocker-composeは...？
まずは『**こんな記事にわざわざ見に来ていただいて感謝**』ということで、恥ずかしながらも前まで設定していた駆け出し`docker-compose`を公開します。笑

```yaml:docker-compose.yml
version: "3"
services: 
    db:                     # データベース(つまり消えて欲しくない貴重なデータ群)が含まれているプログラム達
        image: postgres
        environment:
            - POSTGRES_USER
            - POSTGRES_PASSWORD
        ports:
            - "5432:5432"
    back:                   # バックエンド(静的ファイル配信やmodelの定義、routing設定など)のプログラム達
        build: .
        volumes:            # 注目
            - .:/code       # 注目
        ports: 
            - "8000:8000"
        command: gunicorn config.wsgi -b 0.0.0.0:8000 # サーバー起動
        depends_on: 
            - db
```

上記の『[ボリュームとは](#ボリュームdata-volumeとは)』から言うと、

```
volumes: 
- .:/code
```

という記述は、`ホスト側のディレクトリ(カレントディレクトリ化)`が`コンテナ内(code下)`を**マウント**して、**バックエンドのプログラムを永続化している**ということになります(/･ω･)/

dbにはvolumesを記述していないので、もちろん**消えて欲しくない貴重なデータが含まれたデータベースは永続化していません！?！?**

もう序盤で既にオチを悟った方もいるかもしれませんが(笑)、詳しくおかしな点について解説していきますね！

#### 余談(ここはスルーしてOKです)
*恐らくこれを見た経験者は『**いやそこボリュームとしてデータ永続化してどないすんねん！**』と言うでしょう。笑*
*しかも事前にvolumeの名前も定義もしていないので、`docker volume ls`でボリューム一覧を出力した時にも、どれがどのボリュームなのかわからない。*
*やはり独学で実装していると、`docker volume ls`した時に知らないボリュームが増えていくことに『違和感』を覚えることはありましたが、『おかしい』ことを確信せずに実装していたのです。*

## なぜ勉強することになったか？
現在(2020/2/7)開発中である投票サービス《Shappar》で静的ファイルをDocker×Nginxで配信しようと思ったところ、以下の記事を見つけました。

[DjangoをDocker Composeでupしよう！ *2](https://qiita.com/kyhei_0727/items/e0eb4cfa46d71258f1be#1djangogunicorn%E7%94%A8%E3%81%AE%E3%83%99%E3%83%BC%E3%82%B9%E3%82%A4%E3%83%A1%E3%83%BC%E3%82%B8%E3%82%92%E4%BD%9C%E6%88%90)

↑上記事は要約すると『DjangoをGunicorn×Nginx×docker-composeを使ってサーバーを立ち上げよう！』というものです。そこで`docker-compose`の使用例として以下のような記述を見つけました。

```yaml:docker-compose.yml
version: '3.7'
services:
  django:
    restart: always
    build: ./django
    expose:
      - "3031"
    depends_on:
      - postgres
    command: bash -c "python manage.py migrate && gunicorn my_docker_project.wsgi -b 0.0.0.0:3031"
    volumes:
      - "staticdata:/opt/static/"        # 3. ナンダコレ
  nginx:
    restart: always
    image: nginx
    depends_on:
      - django
    ports:
      - "80:80"
    volumes:
      - "./nginx/:/etc/nginx/"            # 1. ナンダコレ
      - "staticdata:/opt/apps/static/"    # 1. ナンダコレ  3. ナンダコレ
  postgres:
    image: postgres
    ports:
      - "5432:5432"
    volumes:
      - "dbdata:/var/lib/postgresql/data"
    environment:
      POSTGRES_PASSWORD: hogemojahogemoja

volumes:  # 2.ナンダコレ
  dbdata:
  staticdata:
```

### この記述を見て思った異変(自分の無知)の数々
1. `./nginx/:`って...下の`dbdata:`みたいに`カンマスラッシュ`付けずに`nginx:`←こうやって書き方統一しろよな！(<font color="red">※ヤバい間違いをしています。</font>)
2. なんか一番下に`volumes`おる...
3. なぜ2回も`staticdata`を指定しているのだ...

## 先ほどの疑問点について勉強を開始

### 1. ディレクトリの記述方法について
とりまdockerの[公式docs *3](http://docs.docker.jp/compose/compose-file.html#volumes-volume-driver)に飛びます。すると以下の記述がありました。

```yaml:docker-compose.yml
volumes:
  # 1.パスを指定したら、Engine はボリュームを作成
  - /var/lib/mysql

  # 2.絶対パスを指定しての割り当て
  - /opt/data:/var/lib/mysql

  # 3.ホスト上のパスを指定する時、Compose ファイルからのパスを指定
  - ./cache:/tmp/cache

  # 4.ユーザの相対パスを使用
  - ~/configs:/etc/configs/:ro

  # 5.名前付きボリューム（Named volume）
  - datavolume:/var/lib/mysq
```

#### 記述によって動作が全く違う件

引用してきた上記の`docker-compose`から、僕が疑問に思った記述方法は③と⑤に当てはまります。

結論から言うと、<font color="red">**`./nginx/:/etc/nginx/`と`nginx:/etc/nginx/`とでは、プログラムの実行内容が全然違います。**</font>

| `./nginx/:` | `docker-compose.yml`が存在しているディレクトリから、相対パスによってマウントする側の指定先として`ホスト側のディレクトリ`の指定を行います。 |
|:---:|---|
|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;||
| **`nginx:`** | **この書き方をすると、マウントする側の指定先として`nginx`という`名前付きボリューム`を探します。** |

マウントする側の指定方法として、`./nginx/:`はカレントディレクトリから〜、対して`nginx:`は`名前付きボリューム`ありきで〜、というように方法が全く異なっていますね。

はて？名前付きボリュームですか。

### 2. 名前付きボリュームとは？(一番下に`volumes`がいる訳)
ここで先ほどの２つ目の疑問点『なんか一番下に`volumes`おる...』が解決できます。
なんと**docker-composeのvolumesにて名前を定義することによって、ボリュームに名前を付けています。**

実際に先ほどのdocker-composeをもう一度見てみましょう。

```yaml:docker-compose.yml
version: '3.7'
services:
  django:
    restart: always
    build: ./django
    expose:
      - "3031"
    depends_on:
      - postgres
    command: bash -c "python manage.py migrate && gunicorn my_docker_project.wsgi -b 0.0.0.0:3031"
    volumes:
      - "staticdata:/opt/static/"         # staticdataは一番下のvolumeで定義してるやつ！
  nginx:
    restart: always
    image: nginx
    depends_on:
      - django
    ports:
      - "80:80"
    volumes:
      - "./nginx/:/etc/nginx/"            # docker-composeからnginxというディレクトリを相対パスにより指定
      - "staticdata:/opt/apps/static/"    # staticdataは一番下のvolumesで定義してるやつ！
  postgres:
    image: postgres
    ports:
      - "5432:5432"
    volumes:
      - "dbdata:/var/lib/postgresql/data" # dbdataはvolumesで定義してるやつ！
    environment:
      POSTGRES_PASSWORD: hogemojahogemoja

volumes:       # 名前付きボリュームを定義している
  dbdata:      # ボリューム
  staticdata:  # ボリューム
```

一番下の`volumes`で名前を付けていますね〜。これによりボリュームが作成されます！
もし既に名前を付けている`ボリューム`を`volumes`にて指定すると、その名前の付いた`ボリューム`からデータを参照してきます。

つまり『**データを永続化している**』訳ですね(/･ω･)/

そして以下のことが言えます！

- 先頭に`./`が付いていない`dbdata`と`staticdata`は一番下の`volumes:`で名前を付けてから、名前付きボリュームとしてコンテナの外部に保管し、データを永続化している。
- 逆に先頭に`./`が付いている`nginx`は相対パスでホスト側ののマウントするディレクトリを指定して、データを永続化している。ただし名前が付いていないので、`VOLUME NAME`には20文字くらいの文字列が自動で名前として付きます。なので『もう一度使える』という意味での《永続化》はしていないですね。

果たして名前が付いているか`docker volume ls`コマンドにて確認します！
![volumename.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/436088/48097414-b6c8-2f9d-9459-94bf57663590.jpeg)

《サービス名_ボリューム名》となっていることが確認できました〜。(画像ではアンダーバーが途切れてます。笑)

### 3. 何故2回も`staticdata`を指定しているのだ...
1と2からなんとなく推測してみましょう。

今までの文章から考察するとこうなるかな...

- １的観点から記述法を見ると、名前付きボリューム(staticdata)でバックエンドとNginxにマウントをとっているのがわかる。
- 2的観点から名前付きボリュームということはデータの永続化(データを別場所にて保管)をしている。
- staticdataとdbdataを比較すると、dbdataではpostgresのvolumesの1つしか設定していなかった。

`《データを別の場所にて管理》 ＆ 《バックエンドとNginxの2つマウント取ってる。》`

これらの要素からボリュームでデータを別場所で管理できることを利用して、**コンテナ間のリソース共有**をしている！

もしやバックエンドコンテナとNginxコンテナで静的ファイルを共有して、静的ファイルに関してだけNginxで配信するという形が取れるのでは...？

当初の僕の目的であった**Nginxで静的ファイルを配信すること**に繋がってきました:relaxed:

## まとめ
最後まで読んでいただきありがとうございました。
Docker系は使えるツールがあまりにも多いので、本当に把握しきれないです。特にボリュームの概念をつかむのはある程度触ってからでないと難しいのかも知れません(コンテナの概念やマウントという言葉の意味など...)。

ですがどこかのDocker初心者が僕のようにボリュームの概念について躓かないように、初心者である僕の目線でわかりやすく記事にさせていただきました！

間違っている点やシンプルな感想などでもございましたら、お気軽にコメントください！！

バックエンド(Django,RESTAPI)やAWS,Dockerなどの勉強していたり、機械学習にも少し手を出しているわたくしの姿を発信しているTwitterがあるので、もし良ければ...[@heacet43](https://twitter.com/heacet43)

## 参考文献
1. Docker、ボリューム(Volume)について真面目に調べた - Qiita [https://qiita.com/gounx2/items/23b0dc8b8b95cc629f32]
2. DjangoをDocker Composeでupしよう！ - Qiita [https://qiita.com/kyhei_0727/items/e0eb4cfa46d71258f1be]
3. Compose ファイル・リファレンス &mdash; Docker-docs-ja 17.06.Beta ドキュメント [http://docs.docker.jp/compose/compose-file.html#volumes-volume-driver]