【図解】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？ 色々ありすぎて意味不明

という方

後編では「Docker Machine、Docker Swarm」について書いて行きます。

前編は[**こちら**](https://qiita.com/kotaro-dr/items/b1024c7d200a75b992fc)
中編は[**こちら**](https://qiita.com/kotaro-dr/items/88ec3a0e2d80d7cdf87a)

# ⑦ Docker Machine
Docker Machineは、**Docker Engineを搭載した仮想マシンの管理(作成、起動、停止、再起動など)をコマンドラインから実行できるツール**です。

Mac OSの場合は、仮想化ソフト(Virtual Box)をドライバーに使用して、Docker Engineを搭載した仮想マシンを管理するのがDocker Machineです。

以下の図のように、PC内で複数のdocker-machineを起動して、その中でコンテナの実行などのdockerの機能が利用できるといったイメージになります。

<img width="875" alt="スクリーンショット 2019-01-20 21.30.47.png" src="https://qiita-image-store.s3.amazonaws.com/0/209909/9231bf2f-bfea-2689-bac7-41276faeed3d.png">

またdocker-machineによって起動したDocker Engineが動作している仮想マシンのことを**Dockerホスト**と呼びます。

上記の図はLocalマシンの中で複数のDockerホストを立てるようなイメージになっていますが、もちろんリモートサーバにDockerホストを立てることも可能です。

参考： [Docker EngineとDocker Machineの概念の違い](https://docs.docker.jp/machine/overview.html#docker-engine-docker-machine)

## Docker Machineの管理コマンド

#### dockerホストの一覧表示
```
$ docker-machine ls
```

#### Dockerホストを作成
```
$ docker-machine create —driver virtualbox ホストマシンに付ける名前
```
指定した名前で新しいDockerホストを作成
—driver オプションは仮想化ソフトの指定に使用しています。

#### Dockerホスト起動
```
$ docker-machine start ホストマシン名
```

#### Dockerホスト停止

```
$ docker-machine stop ホストマシン名
```

#### 指定したDockerホストを操作対象にするための環境変数一覧表示
```
$ docker-machine env ホストマシン名
```

この環境変数を設定することで、操作対象のdockerホストを変更できます。

#### DockerホストをActiveにする
```
$ eval $(docker-machine env ホスト仮想マシン名)
```
指定したDockerホストに環境変数を設定
環境変数を設定すると、指定したホスト仮想マシンがActiveになります。

反対に、docker ホストのアクティブ選択を解除するには、設定した環境変数を削除する必要があります。

#### Dockerホストを非アクティブにする

```
$ docker-machine env -u
```
で、環境変数削除のためのコマンドが表示
-uはunsetするというオプションです。

```
$ eval $(docker-machine env -u)
```

で現在ActiveなDockerホストを非アクティブにできます。


#### Dockerホストにssh接続

```
$ docker-machine ssh Dockerホスト名
```

#### DockerホストのIP確認

```
$ docker-machine ip Dockerホスト名
```

#### Dockerホスト上でコンテナ実行

Dockerホストがアクティブになっている状態で

```
$ docker run イメージ名
```

でコンテナを起動すると、ActiveなDockerホスト上でコンテナが実行されます。


## クラウドサービスへのDockerホストのプロビジョニング
Docker Machineを使用して、localのコマンドラインからAWSやGCEなどにDocker Engineを搭載した仮想マシン(Dockerホスト)を作成・管理することができます。

以下に一例としてDocker Machineを使用してAWS EC2上にDockerホストを立てる手順を記述します。

1. AWSのIAMからdocker-testとか適当な名前つけてアクセス認証キーを取得

```
アクセスキーID : -----------------
シークレットアクセスキー： ----------------------
```
2.その認証キーをホームディレクトリの ~/.awsディレクトリに作成したcredentialsファイルに記述した後

```
$ docker-machine create --driver amazonec2 --amazonec2-open-port 8000 --amazonec2-region ap-northeast-1 仮想マシン名
```

でdriverをamazonec2で指定した仮想マシン名で仮想マシンを立ち上げる

3.

```
$ docker-machine env AWS上に立ち上げた仮想マシン名
$ eval $(docker-machine env AWS仮想マシン名)
```

でAWS上に立ち上げた仮想マシン(dockerホスト)をアクティブにする

4.

```
$ docker run イメージ名
```
で、イメージからコンテナを立てる

5.

```
$ docker-machine ip Dockerホスト名
```
で仮想マシンのIPを確認

```
$ docker-machine stop Dockerホスト名
```

で仮想マシンを停止

```
$ docker-machine rm Dockerホスト名
```
で仮想マシンを削除

のような基本的な操作も可能


## ここまでの全体像

<img width="850" alt="スクリーンショット 2019-01-20 21.32.04.png" src="https://qiita-image-store.s3.amazonaws.com/0/209909/5080a137-f855-aa1a-e9d7-03d246d201e2.png">




# ⑧ Docker Swarm
Docker Swarmは、**複数台のDockerホストマシンの間でコンテナ間通信の設定・管理を自動化する手法**(コンテナオーケストレーションエンジン)の1つです。

コンテナオーケストレーションエンジンには、Docker Swarmの他にもKubernetesやDC/OSなどがありますが、ここではDocker Swarmについて見て行きます。

Docker Engineの動作モードの一つに**Swarm Mode**があり、Swarm Modeは

***クラスタ管理機能：***複数台のマシンをネットワーク接続してひとまとまりのシステムとして管理する機能
***オーケストレーション機能：***複数のマシンを自動で管理する機能(負荷分散、コンテナ起動など)

を持ちます。


より噛み砕いて説明すると、

Docker Swarmは

1. Dockerホストマシン同士をネットワーク接続する
2. その上で、「複数のホストマシン」と「その中で動作するコンテナ達」を管理・運用する様々な機能を提供する

というものです。
([中編](https://qiita.com/kotaro-dr/items/88ec3a0e2d80d7cdf87a)で登場したDocker Networkは"単一Dockerホスト内"での"コンテナ同士"を接続するネットワークでした)

## (1) Dockerホストマシン同士をネットワーク接続する
Docker Swarmがまずやっていることは、「複数のDockerホストマシンをネットワーク接続して、Swarmというひとまとまりのシステムにする」ということです。

具体的には、作成したSwarmにDockerホストマシンを所属させ、Swarm内のノードとなったDockerホストは、**Ingressオーバーレイネットワーク**というネットワークで相互に接続されます。

ノードには

1. コンテナを実行する役割 を持つworkerノード
2. コンテナを実行する役割 + 他のノードを管理する役割 を持つmanagerノード

があります。

<img width="864" alt="スクリーンショット 2019-01-20 23.38.58.png" src="https://qiita-image-store.s3.amazonaws.com/0/209909/76e565a0-ebdd-798b-ae47-8cf66d5fbaa3.png">

### Swarmの管理コマンド
#### Swarmクラスタの作成
```
$ docker-machine ip [Dockerホスト名]
$ docker swarm init --advertise-addr 他のノードと通信可能なIP(上記で確認したIP)
```

managerノードとなるホストマシンがActiveな状態で実行してSwarmクラスタを作成する

#### Swarmクラスタに所属しているノード一覧表示
managerノードで

```
$ docker node ls
```


#### Swarmクラスタにworkerノードを追加

managerノードがActiveな状態で

```
$ docker swarm join-token worker
```
を実行し、表示された

```
$ docker swarm join --token [トークン] [IP]
```
というコマンドを

```
$ eval $(docker-machine env [workerノード　にするdockerホスト名])
```
でworkerノードにするDockerホストをActiveにしてから実行

#### Swarmクラスタにmanagerノードを追加
上記のworkerノード追加の場合の最初のコマンドを

```
$ docker swarm join-token manager
```

にして、同じ手順で追加可能



**※注意点**
Swarmで使われるポート番号は
・クラスタ管理通信用：TCP 2377
・ノード間通信用：TCP/UDP 7946
・オーバーレイネットワークトラフィック用：UDP 4789
です。


Swarmに参加するDockerホスト(ノード)は必ずこれらのポートを開けておかなければならないという点に注意して下さい。

## (2) 様々な管理機能

Dockerホストを繋ぐネットワークをSwarmという形で構築することによって、様々な管理機能が利用できます。

通常、Webシステムの本番環境サーバーは、1台のマシンではなく、複数台のマシンが組み合わさって、インターネットを通じてUserから送られてくるリクエストに対処しています。

この複数台のマシンをDocker machineというツールで起動・管理し、さらにDockerホストマシンを相互接続して、ひとまとまりに管理することによって、システムの本番環境で外部からのリクエストを負荷分散したり、運用に伴う様々な機能を提供するのがDocker Swarmなのです。

<img width="867" alt="スクリーンショット 2019-01-21 0.23.32.png" src="https://qiita-image-store.s3.amazonaws.com/0/209909/7893b572-5c55-0209-40aa-81a4cead041d.png">

### ①ServiceとTaskによるDockerホストを跨いだコンテナ起動

まず、Docker Swarmでは、**ServiceとTask**を使用することによってDockerホストをまたいでコンテナを起動・管理することができます。

Serviceとは**「どのノードでどのイメージからいくつのコンテナを起動するかを定義したもの」**で、Taskとは**「Serviceに基づいて実際にコンテナを実行する1つ1つの指示」**のことです。

サービスはmanagerノードで作成されます。

例えば

```
$ docker service create --replicas 3 nginx
```
で、managerノードが「nginxイメージからコンテナを3つ起動する」サービスを作成した場合は、「nginxイメージからコンテナを起動する」というTaskが3つ存在するということになります。


Serviceを使用することで、コンテナをそれぞれのDockerホストマシンで起動するのではなく、一気に起動することができ、かつ、どのコンテナをどのノードで起動するか(タスクの分配)を自動的に決定してくれます。

このようにサービスによって作成されたタスクを、各ノードに振り分ける機能を**スケジューリング**と言います。

特別な指定がない場合には、CPUやメモリの空きリソースの状況に従ってスケジューリングが行われるため、ユーザはどのDockerホストに配置するかなどを管理する必要がありません。

<img width="1035" alt="スクリーンショット 2019-01-20 23.58.55.png" src="https://qiita-image-store.s3.amazonaws.com/0/209909/829fd7e8-eba1-ee4f-8f4e-cd6ab40b4e09.png">


#### ノードのAvailability

Swarmクラスタに所属しているノードの一覧は、

```
$ docker node ls
```

コマンドで確認可能です。

各ノードは、Availabilityという状態を持っており、**AvailabilityにはActive, Pause, Drainの3種類**があります。

Activeは「スケジューラーがノードにタスクを割り当てることが可能な状態」
Pauseは「スケジューラーがノードにタスクを割り当てられないが、既存のタスクはそのまま実行し続ける状態」
Drainは、「スケジューラーがノードにタスクを割り当てられず、既存のタスクも終了する状態」であり、この状態によってスケジューラーにTaskを割り当てられるかどうかが決まります。

また、

```
$ docker node update --availability [変更したい状態] [ノード名]
```

で、指定したnodeのAvailabilityを変更することができます


#### グローバルモードとレプリカモード

レプリカモードはこれまで見てきた通り、``--replicas 3``のようにサービス作成時に実行したいタスク数を指定できるモードです。

一方で、グローバルモードは、必ず1ノードで1taskを実行し、レプリカ数の指定は行わないモードです。



```
$ docker service create --name web3 --mode global nginx
```

のようにグローバルモードではレプリカ数を指定しなくても、全てのノードで1つずつタスクが実行されます。

デフォルトではレプリカモードになっています。


#### Serviceの管理コマンド

##### サービスを作成
```
$ docker service create --name [作成するサービス名] --replicas [レプリカ数] [起動するイメージ名]
```
```
$ docker service create --name [作成するサービス名] --constraint 'node.role==[managerまたはworker]' --replicas [レプリカ数] --detach=true [起動するイメージ名]
```
のように —constraintフラグをつけて、サービスを作成・起動することで、タスクの割当先ノードを指定できます。


##### サービスの一覧表示

```
$ docker service ls
```
##### サービスの詳細を表示

```
$ docker service inspect [サービス名]

$ docker service inspect —pretty [サービス名]
```

prettyフラグをつけると読みやすい形で表示されます。

##### 作成済みのサービスの設定を変更できる

```
$ docker service update —publish-add 8000:80 —detach=true [サービス名]
```

##### 指定したサービスを一つ前の状態に戻す
```
$ docker service rollback [サービス名]
```

##### 指定したサービス内のプロセスを表示
```
$ docker service ps [サービス名]
```

##### サービスの削除
```
$ docker service remove [サービス名]
```


### ② ローリングアップデート

Webシステムを運営していると、システムに使用しているソフトウェアのversionをupdateしなくてはならない場合があります。

Swarmで複数のDockerホストを管理してシステム運営をしていれば、サービスのタスクをまとめて更新するのではなく、遅延時間を設定して1タスクずつupdateしていけるというのが、ローリングアップデートという機能です。

この機能によって、実行中のコンテナを残して、そのコンテナでリクエストを受けつつ、順番に別のコンテナをupdateしていくことができます。

以下でredisのアップデートの例を見ていきます。


```
$ docker service create --replicas 3 --name redis --update-delay 10s redis:3.0.6
```

のようにサービスを作成する際に、update-delayフラグでupdateの時間差を設定します。
並列更新するタスクの最大数を設定するフラグもあります。

アップデートをする場合は、

```
$ docker service update —image redis:3.0.7 redis 
```
で、ローリングアップデートを開始できます。


### ③ Service scale / Resource management / Auto Scaling
Swarmで運用しているシステム全体の負荷が大きくなってきたら、サービス全体として起動するコンテナの数をスケールさせることができます。


```
$ docker service scale [サービス名]=[レプリカ数]
```
で指定したサービスのレプリカ数を変更できます。

もちろんオートスケールを設定することも可能です。

そもそもマシンリソース自体が足りなくなってきた場合には、マシン(AWSにおけるインスタンス)自体を追加で起動することもできます。

### ④ Auto Healing
大量のリクエストによる過剰な負荷がかかるなどで、Swarm内のノードがダウンしてしまった時には、そのマシンで起動していたコンテナを他のマシンが補って自動的にコンテナが起動するようAuto Healingという機能があります。

Managerノードがダウンしてしまった場合にも、複数のmanagerノードを用意することで対応可能です。

<img width="1043" alt="スクリーンショット 2019-01-20 23.56.33.png" src="https://qiita-image-store.s3.amazonaws.com/0/209909/61deb9b0-9ac0-41da-2922-678d9023c385.png">


## Docker composeによるServiceの作成

``$ docker service create``コマンドでサービスを作成してきましが、サービスの作成すらも``docker-compose.yml``に記述することでファイルに定義した複数のサービスを自動的に作成することができます。

<img width="1047" alt="スクリーンショット 2019-01-21 1.16.15.png" src="https://qiita-image-store.s3.amazonaws.com/0/209909/1c507a3d-0ca1-941a-f030-2779638437c2.png">


docker-compose.ymlには、以下のように記述します。

```yml:docker-compose.yml
version: '3.4' # docker-composeの記述形式のversion指定

services: #実行するコンテナを記述していく
  wordpress: #wordpressコンテナ
   image: wordpress # wordpress:latestイメージを使用
   ports:
    - 8080:80 # ポートはマシンの8080番を外部に公開して、それをコンテナの80番ポートと繋げる
   environment:
    WORDPRESS_DB_PASSWORD: ----- # mysqlコンテナのPWと合わせる
   deploy: #　ここにDocker SwarmのService設定
    replicas: 2 #レプリカ数
    placement:
      constraints: 
       - node.role == worker # workerノードのみで実行
   depends_on:
     - mysql # mysqlから先に起動する設定

  mysql: #mysqlコンテナ
   image: mysql:5.7 # 使用するイメージ指定
   environment:
    MYSQL_ROOT_PASSWORD: ----- # パスワード設定
   volumes:
    - mysql_vol:/var/lib/mysql # mysql_volというvolumeを作成して、コンテナ上の指定したディレクトリにマウント
   deploy: #　ここにDocker SwarmのService設定
    replicas: 1
    placement:
     constraints:
      - node.role == manager # managerノードで実行

volumes:
  mysql_vol: ＃volumeの定義
```

その後、以下のように``$ docker stack deploy``コマンドを使用して、docker-compose.ymlファイルからSwarmにおけるサービスを作成することができます。

```
$ docker stack deploy --compose-file docker-compose.yml(composeファイルの指定) [作成するスタック名]
```

スタック名は作成されるサービス名の接頭辞になります。


### stackの管理コマンド

#### スタックを作成する
```
$ docker stack deploy --compose-file [composeファイルの名] [作成するスタック名]
```

#### スタックの一覧表示
```
$ docker stack ls
```

#### スタックの削除
```
$ docker stack remove スタック名
```