比較的簡単と言われているDockerSwarmが全然簡単じゃなかった件。

# はじめに
* この記事を書いてる途中にQiitaに素晴らしい記事が投稿されたのでぜひ最初にそちらを読むことをオススメする。

[【図解】Dockerの全体像を理解する -前編-](https://qiita.com/kotaro-dr/items/b1024c7d200a75b992fc)

# 序章
と、言う訳で作成中の　WebサービスにDockerSwarmでクラスタ作ろうと思ったら予想以上に手こずった話。
日頃からお世話になってたDocker様ですがSwarm modeと言うものがあるのを昨日気付いて興味本位で触ってみました。
と言うくらい初心者なので間違いがあったらコメントお願いします。

## DockerSwarmとは
* 今流行りのコンテナオーケストレーション
* Dockerの機能の１つとして提供されている
* 競合としてはGoogleのk8とか
* [Docker swarm mode Document](https://docs.docker.com/engine/swarm/)

## 目標
* アプリケーションサーバをクラスタ化してNginxでロードバランシングを行う。
* __アプリケーション自体は今回は題材から逸れるので触れませぬ。__

# 導入
## 現在の実行環境
```
▶ docker -v
Docker version 18.09.1, build 4c52b90
```

```
▶ docker-compose -v
docker-compose version 1.23.2, build 1110ad01
```

## 環境構築
### Docker Machineの作成
* 事前準備としてVirtualboxを入れておく。
  * これは頑張って調べてください…
* Docker Machineは自分の必要に合わせて何台か作ってください
    * 今回はManager１台、Worker２台つくりまする。
    
#### Manager Machineの作成
* 最初はboot2docker.isoがないとか言われるので適当にDLしといてください
* `--virtualbox-memory 4096`でmemoryを多めに確保しておく(何となく) 
 
```
▶ docker-machine create --virtualbox-memory 4096 MACHINE_NAME
Running pre-create checks...
Creating machine...
(machine1) Copying /***/.docker/machine/cache/boot2docker.iso to /***/.docker/machine/machines/machine1/boot2docker.iso...
(machine1) Creating VirtualBox VM...
(machine1) Creating SSH key...
(machine1) Starting the VM...
(machine1) Check network to re-create if needed...
(machine1) Waiting for an IP...
Waiting for machine to be running, this may take a few minutes...
Detecting operating system of created instance...
Waiting for SSH to be available...
Detecting the provisioner...
Provisioning with boot2docker...
Copying certs to the local machine directory...
Copying certs to the remote machine...
Setting Docker configuration on the remote daemon...
Checking connection to Docker...
Docker is up and running!
To see how to connect your Docker Client to the Docker Engine running on this virtual machine, run: docker-machine env machine1
```

* 確認

```
▶ docker-machine ls
NAME       ACTIVE   DRIVER       STATE     URL                         SWARM   DOCKER     ERRORS
machine1   -        virtualbox   Running   tcp://192.168.99.105:2376           v18.09.1
```
とりあえず出来た。

#### Worker Machineの作成
* やる事はManager Machineの作成と変わらないのでWorkerMachineを同じ要領で後２台作る
* 終わったら確認

```
▶ docker-machine ls
NAME              ACTIVE   DRIVER       STATE     URL                         SWARM   DOCKER     ERRORS
machine1          -        virtualbox   Running   tcp://192.168.99.105:2376           v18.09.1
machine2-worker   -        virtualbox   Running   tcp://192.168.99.106:2376           v18.09.1
machine3-worker   -        virtualbox   Running   tcp://192.168.99.107:2376           v18.09.1
```
出来た。

### Docker Machineの性能確認
* 以下のコマンドでメモリを確認
    * 他の要素も以下のサイトを参考に確認出来る
        * [docker docs](https://docs.docker.com/machine/reference/inspect/)

```
▶ for name in `docker-machine ls --format={{.Name}}`; do echo 'machine-name: '$name; echo 'Memory: '`docker-machine inspect ${name} -f {{.Driver.Memory}}`'MB'; done
machine-name: machine1
Memory: 4096MB
machine-name: machine2-worker
Memory: 4096MB
machine-name: machine3-worker
Memory: 4096MB
```

### Docker Clusterの作成
* 上の段階ではどれがDocker Manager,Workerになるか決まってないので決めに行く。
 

#### Docker Managerの選定

* `docker-machine ls`でManagerにしたいMachineのIPをメモ
しておく
* `docker-machine ssh MACHINE_ME`  
* `docker swarm init --advertise-addr Machine_IP`
* するとこんな感じでClusterに参加する為のコマンドを入手出来る

```
docker@machine1:~$ docker swarm init --advertise-addr 192.168.99.105
Swarm initialized: current node (***) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-***-epp7wdw48qby4wj33ttb129yo 192.168.99.105:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
```

* そして`exit`でManagerを抜けて、２台のWorker Machineで上記のコマンドを入力する
 
* `docker-machine ssh machine2-worker`
* `docker swarm join --token SWMTKN-1-***-epp7wdw48qby4wj33ttb129yo 192.168.99.105:2377`

* すると以下の表示が出てClusterに参加出来る

```
docker@machine2-worker:~$ docker swarm join --token SWMTKN-1-***-epp7wdw48qby4wj33ttb129yo 192.168.99.105:2377
This node joined a swarm as a worker.
```

* 上の作業を２台のWorker Machineでやった後にManagerに戻って以下のコマンドでClusterが作成されたのを確認する

```
docker@machine1:~$ docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
u69ejosqv26h0b9yq1atsp005 *   machine1            Ready               Active              Leader              18.09.1
8xrs7mm3t21nyi2udcvdhz2uy     machine2-worker     Ready               Active                                  18.09.1
j8ai7ixjh58f8y2c8u2nja5kj     machine3-worker     Ready               Active                                  18.09.1
```

### Application Depoly
* ここまでの設定を終えれば後は実際にアプリケーションをデプロイしていく
* ちなみに今作った３つのDocker Machineはローカルと別物なのでローカルにあるdocker imagesが使えない
    * `docker images`を打っても空っぽ
* DockerHubなどからpull出来るのであればそれでも構わないが今回はローカルでカスタマイズしたimageを使いたかったので泥臭く頑張る。
    * ここの改善点教えて欲しい…

#### 任意のDockerMachineを操作出来るようにする
* ローカルマシンに戻り以下のコマンドを打つことで任意のDockerMachinewのdockerコマンドを実行出来る
 
```
eval "$(docker-machine env MACHINE_NAME)"
```


#### ローカル側からDockerMachineにdocker-composeでimageをbuildする
* ローカルでdocker-compose.ymlのあるディレクトリに移り`docker-compose build`することでDockerMachineの中にdocker imageを作ることが出来る

#### docker-compose.yml
* 今回のアプリケーションで実際に使うdocker-compose.ymlが以下のファイル
    * 実際にはもう少し色々あるが、簡略化
* DB, NginxのみをManagerNodeに配置
* FlaskをWorkerNodeにReplica6個で配置
    * WorkerNodeが２つなので各Nodeに３つずつ配置される
* またDBの永続化用volumeは以下の書き方をしないとDBコンテナからpermission errorが返ってくる


```
version: '3.4'

services:
  db:
    image: posgre:1.0.0
    ports:
      - 5432:5432
    volumes:
      # 永続化するときにマウントするdir
      - dbdata:/var/lib/postgresql/data
      - ./docker/db/conf:/docker-entrypoint-initdb.d
    env_file:
      - ./docker/db/.env
    networks:
      - net
    deploy:
      placement:
        constraints:
          - node.role == manager

  flask:
    image: flask:1.0.0
    volumes:
      - ./app:/projects
    ports:
      - 3031:3031
    command: pipenv run uwsgi --ini /uwsgi.ini
    networks:
      - net
    env_file:
      - docker/flask/.env
    tty: true
    depends_on:
      - db
    deploy:
      replicas: 6
      placement:
        constraints:
          - node.role == worker

  nginx:
    image: nginx:1.0.0
    ports:
      - 80:80
    env_file:
      - docker/nginx/.env
    volumes:
      - ./docker/nginx/conf/nginx.conf:/etc/nginx/nginx.conf
      - ./docker/nginx/log:/var/log/nginx
    networks:
      - net
    tty: true
    command: ['nginx', '-g', 'daemon off;', '-c', '/etc/nginx/nginx.conf']
    deploy:
      placement:
        constraints:
          - node.role == manager
   
networks:
  net:
    external:
      name: net

volumes:
  dbdata:
``` 

#### docker network
* swarmで使うネットワークを普通に作るとscopeエラーが出るので以下のコマンドで作成しておく

```
docker network create --scope=swarm net
```

#### docker-composeでのデプロイ
* ここまで準備が出来たら後はデプロイ

```
docker stack deploy SERVICE_NAME -c docker-compose.yml
```

#### サービスの監視
* サービス監視コマンド

```
docker stack services SERVICE_NAME
```

* プロセス監視コマンド

```
docker stack ps SERVICE_NAME
```