【図解】Dockerの全体像を理解する -中編-

![docker.png](https://qiita-image-store.s3.amazonaws.com/0/209909/38170416-92c6-512d-e356-96be623ac3ca.png)



# この記事は何か
イメージやコンテナなどの基本からdocker-compose、docker-machine, docker swarmなどのDocker周りの様々な概念の**全体像を整理して、Dockerの仕組みを理解する**ための記事

### 対象読者
・Dockerって何？
・Dockerちょっと勉強したけどDocker compose？ Docker machine？ Docker Swarm？ 色々ありすぎて意味不明

という方

中編では、「データマウント(volume), Docker Network, Docker Compose」 について書いて行きます。
前編は[**こちら**](https://qiita.com/kotaro-dr/items/b1024c7d200a75b992fc)


# ④ Dockerにおけるデータ管理

起動したコンテナ内で扱う動的なデータは、読み書き可能な最上レイヤー(コンテナレイヤー)に置くこともできますが、

・コンテナが削除された時点でそのコンテナ内のデータは消える
・コンテナ間でデータ共有できない
・コンテナレイヤーへのデータ書き込みは、通常のファイルシステムと異なるユニオンファイルシステムが使われているため、書き込み速度が遅い

というデメリットがあります。

そのため、Dockerではホストマシン上にデータを管理し、それをコンテナにマウントする手法が使われます。

手法は主に3つあり、以下で説明して行きます。

## volume
ホストマシン上に自動生成される指定ディレクトリ(/var/lib/docker/volumes)をコンテナにマウントする手法

ホストマシン上で

```
$ docker volume create volumeの名前
```
とすることで、volume(/var/lib/docker/volumesディレクトリ)を作成し、コンテナを起動する際に

```
$ docker run -itd --name 作成するコンテナ名 --mount source=[マウントするvolume名],target=[コンテナ上のマウント先ディレクトリ] イメージ名

具体例：
$ docker run -itd --name mount-test --mount source=volume1,target=/app nginx
```

のように、--mountオプション(-vオプションでも可)をつけることで指定したvolumeをマウントすることができます。

マウントしたvolumesディレクトリは、マウント元であるホスト上のディレクトリからは直接操作するべきではない点に注意が必要です。

また、同じホスト内で立てている異なるコンテナでも、それぞれ同じvolumeをマウントすることでファイルの共有ができます。

さらに、volumeを複数のコンテナで共有させる場合には、コンテナごとに編集権限を設定することもできます。

```
$ docker run -itd —name mount-c4 —mount source=copy-vol, destination=/etc/nginx,readonly nginx
```

のように , で区切って編集権限を指定します。


### volumeの管理コマンド

##### volumeの一覧を確認
```
$ docker volume ls
```

##### volumeの詳細を確認
```
$ docker volume inspect volume名
```
##### volume を削除

```
$ docker volume rm volume名
```

コンテナを削除してもvolumeは残り続けるので、$ docker volume rm volume名　で削除する必要がある点に注意が必要です。


## bind mount

bind mount は、ホストマシン上の任意のディレクトリをマウントでき、ホスト側のディレクトリを直接操作をしても良いという点がvolumeと異なるマウント手法です。

volumeのように事前に設定する必要はなく、以下のコマンドでコンテナの起動時にオプションで指定してマウントします。

```
$ docker run -itd --name [コンテナ名] --mount type=bind,source=[マウント元ディレクトリ],target=[マウント先ディレクトリ] イメージ名

具体例:
$ docker run -itd —-name bind-mount-test —-mount type=bind,source=“$(pwd)”/mount,target=/app nginx
```

source(マウント元)のディレクトリが存在しない場合は、エラーになるので、事前に作成しておきましょう。(-vオプションを使用した場合は自動作成される)

bind mountでは、もしホスト上の空のディレクトリをコンテナ上の/userなどにマウントしたりした場合は、コンテナ上のデータが消えてしまい、コンテナがまともに動作しなくなることもあるので注意が必要です。

volume の時と同様の方法で、コンテナごとに編集権限を設定することもできます。

##tmpfs(テンプfs)

ホストマシンのメモリ領域をコンテナ上にマウントする手法

ホストマシンが終了した場合も、コンテナが終了した場合も、保持していたデータは解放されます。

```
$ docker run -itd --name [コンテナ名] --mount type=tmpfs,destination=[マウント先ディレクトリ] イメージ名

具体例:
$ docker run -itd --name tmpfs-test --mount type=tmpfs,destination=/app nginx
```

のようにコンテナ起動時に、--mountオプションのtypeにtmpfsを指定することでマウントができます。


また、ホスト上のメモリを無制限に使用してしまう可能性を排除するために、

```
$ docker run -itd --name tmpfs-test --mount type=tmpfs,destination=/app,tmpfs-size=800000000,tmpfs-mode=800 nginx
```

のようにオプションで使用可能なメモリサイズを制限することもできます。

## ここまでの全体像

<img width="1032" alt="スクリーンショット 2019-01-20 20.56.04.png" src="https://qiita-image-store.s3.amazonaws.com/0/209909/1ac2b486-bfe5-930d-5f42-6c2283e1d677.png">


# ⑤ Dockerネットワーク
これまで

・多様なイメージからコンテナというアプリケーションの実行環境を起動して、環境構築が簡単にできる

といったことを見てきましたが、**複数立ち上げたコンテナ間で通信する手法**についてここで解説します。


例えば、Webアプリケーションサーバーの運用を行う場合、APIサーバコンテナとMySQLコンテナを立ち上げるとすると、APIサーバコンテナはMySQLコンテナと通信をする必要があります。

ここでのコンテナ間通信の手法として、**複数のコンテナを接続し通信する**のがDocker ネットワークです。


Docker networkにはデフォルトで存在する3つのネットワークと独自で定義するネットワークがあります。

## Bridgeネットワーク
デフォルトで存在するネットワークで、作成されたコンテナがデフォルトで接続されるのが、bridge networkです。
bridge driverを使用しています。

Bridgeネットワークでは、同じネットワーク内に存在するコンテナとIPアドレス指定で通信することができます。
ただ、Bridgeネットワークでは、DNSが定義されていないのでコンテナ名では他のコンテナに通信できません。

つまり、Network指定をせずに起動したコンテナ同士は、IPアドレス指定で通信することは可能だが、コンテナ名指定で通信することはできないということです。


また、デフォルト状態では外部に公開されていないネットワークですが、-p オプションで指定したポートを解放することで外部からコンテナにアクセスできるようにもできます。

## Hostネットワーク

ホストネットワークは、host driver を使用したデフォルトで存在するネットワークです。

接続したコンテナはdocker hostと同じネットワーク設定になります。
例えば、host ネットワークに接続したコンテナでnginxコンテナを実行した場合、
hostマシンのIPの80番ポートでlistenしているのと同じことになります。

そのため　bridgeネットワークのようにコンテナ起動時に-p オプションを設定して外部にポート公開しなくても、コンテナを起動しただけで、Docker hostのIPの80番に接続すればそのコンテナに接続できることになります。


## Noneネットワーク
noneネットワークはデフォルトで存在するネットワークで、接続されたコンテナはネットワーク・インターフェースが無くなります。
noneネットワークに接続する場合は、他のネットワークから全て切断しなくてはならない点に注意が必要です。


## 独自ネットワーク

独自のネットワークを作成することで、コンテナ名でコンテナ間通信が可能になります。

```
$ docker network create ネットワーク名
```

で新しくネットワークを作成(デフォルトのdriverはbridgeになります)し、

```
$ docker network connect 接続するネットワーク名 コンテナ名
```

で、指定したコンテナを指定したネットワークに接続していきます。

ユーザー定義の独自ネットワークでは、Docker Daemonの組み込みDNSが機能してコンテナ名で名前解決してIPと紐づけてくれるので、コンテナ名で他のコンテナに接続できるようになっています。

## Dockerネットワーク(コンテナ間通信)のイメージ
<img width="754" alt="スクリーンショット 2019-01-16 23.52.09.png" src="https://qiita-image-store.s3.amazonaws.com/0/209909/c29a5550-3907-cc67-dd4e-04ee7fe70fb2.png">


## ネットワークの管理コマンド

#### ネットワーク一覧を確認

```
$ docker network ls
```
ホストマシン上で実行することで、そのホストマシンのネットワーク一覧を表示


#### ネットワークの詳細を確認
```
$ docker network inspect ネットワーク名
```

#### 新しくネットワークを作成
```
$ docker network create ネットワーク名
```
デフォルトのdriverはbridgeになる

#### コンテナをネットワークに接続

```
$ docker network connect 接続するネットワーク名 コンテナ名
```

#### コンテナをネットワークから切断

```
$ docker network disconnect ネットワーク名　コンテナ名
```
##ここまでの全体像

<img width="1021" alt="スクリーンショット 2019-01-20 20.59.22.png" src="https://qiita-image-store.s3.amazonaws.com/0/209909/14f47adc-d740-5a54-344a-948621a56a84.png">



# ⑥ Docker Compose
Docker Composeは複数コンテナのDocker アプリケーションを事前定義して実行するためのツールです。

コンテナ1つを起動するにも、volumeなどのマウント設定や所属させるDockerネットワークの設定などを明記して

```
$ docker run -itd —name mount-test —mount source=volume-test, destination=/etc/nginx,readonly nginx
```

のような複雑なオプション付きのコマンドを打ち込まなければなりません。

これはとても面倒ですし、ミスにも繋がり、円滑に環境の共有が出来ない原因になりかねません。

そこで、それらを自動化できるのが**Docker Compose**です。

あるWebサービスの実行環境をDockerで構築する場合、Webサーバー, DBサーバー, Cacheサーバーなどの定義を一つのdocker-compose.ymlファイルに記述しておくことによって、それを元に実行に必要なコンテナをまとめて起動・設定することができます。

手順は以下の通りです。

1. Dockerfileを用意する または Docker Hubなどに使用するイメージを用意する
2. docker-compose.ymlを定義する
3. ymlファイルがあるディレクトリで、$ docker-compose upを実行する

#### Docker Composeを利用しない場合
<img width="1023" alt="スクリーンショット 2019-01-16 22.39.05.png" src="https://qiita-image-store.s3.amazonaws.com/0/209909/67ce0681-7eb5-6451-c861-a2dc3127ec57.png">

各コンテナを一つ一つ、それぞれオプション設定をして起動しなければならない

#### Docker Composeを利用する場合

<img width="1019" alt="スクリーンショット 2019-01-16 22.39.16.png" src="https://qiita-image-store.s3.amazonaws.com/0/209909/7bd322c5-3f23-97b5-e967-aaf81427c9b1.png">

docker-compose.ymlを定義し、docker-compose upコマンド一つで複数コンテナを起動することができる。

## docker-compose管理コマンド一覧

#### docker-composeで起動したコンテナの一覧を表示

```
$ docker-compose ps
```

#### docker-compose.ymlに記述した設定からコンテナ起動

```
$ docker-compose up
```
既にコンテナが起動中であっても再起動してくれる

#### docker-composeで作成されたコンテナやネットワークを削除

```
$ docker-compose down
# $ docker-compose down -v
```
-vオプションでvolumeも一緒に削除される

#### 指定したサービスコンテナ内でコマンドを実行

```
$ docker-compose run [コンテナ名]　 [コマンド]
```

#### 一連のコンテナ停止

```
$ docker-compose stop
```

#### 一連のコンテナ起動
```
$ docker-compose start
```


## docker-composeによるアプリ実行環境構築例
PythonのWAFであるDjangoを使用したWebアプリの環境構築を例に具体的なフローを見て行きます。


#### 1. imageの用意
作業ディレクトリを作成してその中にDockerfileを作成します。

```text:Dockerfile
FROM python:3  # image名にpython3実行環境のイメージを指定
ENV PYTHONUNBUFFERED 1 # pythonの標準出力をバッファにため込まないための環境変数設定
RUN mkdir /service  # serviceディレクトリを作成
WORKDIR /service # serviceディレクトリに移動
COPY requirements.txt /service/  # requirements.txt(事前に作成)をserviceディレクトリに置く
RUN pip install -r requirements.txt # pip  installでパッケージをインストール
COPY . /service/ # buildコンテキストの内容を全て/service内に置く
```

#### docker-compose.ymlの作成

以下のようにdocker-compose.ymlファイルを作成します。

```yml:docker-compose.yml
version: '3' # docker-composeのversion指定
services: # 起動するサービスコンテナを書いていく
  db: # dbサーバーコンテナを起動
   image: postgres # イメージはdockerhub上のpostgres:latestを使用
  web: # webサーバーコンテナを起動
   build: . # カレントディレクトリにあるdockerfileからimage作成
   command: python3 manage.py runserver 0.0.0.0:8000  ＃ コンテナ起動時に実行されるコマンドを指定
   volumes:
    - .:/app # currentディレクトリを/appディレクトリにbind mountする
   ports:
    - "8000:8000" # コンテナの8000番を公開
   depends_on: # webサーバーコンテナを立ち上げる前にdbサーバーコンテナを立ち上げるようにする
    - db
```

`depends_on`は立ち上げコマンドを実行する順番を規定するだけで、「dbが起動完了してからwebを実行する」訳ではないです。
このことが原因で`docker-compose up`が上手く行かない場合があるので、その場合は`entrypoint:`で起動待ちをするshell scriptを実行するなどして対処します。

参考: http://docs.docker.jp/compose/startup-order.html

#### 3. docker-composeの実行

```
$ docker-compose run web django-admin.py startproject test .
```

でdocker-compose.ymlに定義したwebサービスコンテナを一時起動し、djangoプロジェクトを作成します。

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

でデタッチドモード(-d : バックグラウンド)で一連のコンテナを起動します。

このdocker-compose.ymlファイルを共有することで、別のマシンでも $ docker-compose up コマンド一つで一連のコンテナを実行することができるのです。


## ここまでの全体像

<img width="1023" alt="スクリーンショット 2019-01-20 21.00.54.png" src="https://qiita-image-store.s3.amazonaws.com/0/209909/ae790895-6860-b4fc-61ed-6805a0fbe9f5.png">

# [後編](https://qiita.com/kotaro-dr/items/40106f13d47bfcbc2572)に続く
間違っている点などあればコメント頂けると嬉しいです。