いろいろ書いていく

やってみたなど

swarm tutorialをdindでやってみる

お題

dockerのオーケストレーションツールのひとつにswarmというのがある

docs.docker.com

複数のdockerホストをクラスタリングしたり,ロードバランシングしたりできるらしい.
これ系は今はKubernetesが優勢だとは思うが,デフォルトでdocker engineに組み込まれているということもあり,せっかくなので少し使ってみようと思った

やること

素人なので,いつも通りチュートリアルをやる.
docker swarm tutorialは公式のものだと以下が見つかった

github.com

注意書きにある通り,ここにはplay-with-dockerでできるチュートリアルをローカルでやる手順が書かれている.
具体的にはdocker-machine createというコマンドで,dockerがインストールされたVMを複数台立て,swarmでクラスタリングし,webというサービスをデプロイしている.
MacWindows(linuxでもいいけど)のふつうのPCにvirtualboxをインストールして,ここに書かれている通りやればだいたいうまくいくと思われる.

自分はなんとなくホストPCのMacに手を入れたくなかったので,前回使ってみたdind(docker in docker)を使って似たようなことができないか試してみた
結果,docker-machineを使ったチュートリアルとだいたい同じような挙動を追うことができた
完成形を図にするとたぶんこんなかんじ

f:id:koimedenshi:20190401220643p:plain

以下,上記の公式チュートリアルbash scriptとだいたい同じことをdindでやった場合の手順を書いていく. (今回の記事ではクラスタを立てるところまでで,次回の記事でこのクラスタにサービスをデプロイしたり,nodeを退出させたりするところをやる予定.)

注意

  • あくまでチュートリアルなので,セキュリティ設定等は考慮していない
  • dindはdockerと異なる仕組みで動いているため,環境によってはこの通り動かない

環境

Docker 18.09.3 on Ubuntu 18.04 (virtualbox on macOS)
ubuntuにdockerをインストールする手順はこちら:
koimedenshi.hatenablog.com

手順

元のbash scriptを参考に作成したbash scriptはこちら.
細かい点を除けば,docker-machineがやっていることをdocker container rundocker exec -itで置き換えているだけ
このスクリプトをローカルに保存して実行すれば,上図のようなdindクラスタが構築される

#!/bin/bash

## output debug_output
# exec 5> debug_output.txt
# BASH_XTRACEFD="5"
# PS4='$LINENO: '
# set -x

# create user defined bridge network
echo "======> Creating $nw network ...";
nw=dind-net
docker network create -d bridge $nw

# Swarm mode using Docker in Docker

managers=3
workers=3

# create manager machines
echo "======> Creating $managers manager machines ...";
for node in $(seq 1 $managers);
do
    echo "======> Creating manager$node machine ...";
    docker run -it --privileged --network $nw --name manager$node --hostname manager$node -d docker:stable-dind;
done

# create worker machines
echo "======> Creating $workers worker machines ...";
for node in $(seq 1 $workers);
do
    echo "======> Creating worker$node machine ...";
    docker run -it --privileged --network $nw --name worker$node --hostname worker$node -d docker:stable-dind;
done

# list all machines
docker container ls

# initialize swarm mode and create a manager
echo "======> Initializing first swarm manager ..."
docker exec -it manager1 docker swarm init --listen-addr $(docker exec -it manager1 hostname -i | tr -d '\r') --advertise-addr $(docker exec -it manager1 hostname -i | tr -d '\r')

# get manager and worker tokens
manager_token=$(docker exec -it manager1 docker swarm join-token manager -q | tr -d '\r')

worker_token=$(docker exec -it manager1 docker swarm join-token worker -q | tr -d '\r')

echo "manager_token: $manager_token"
echo "worker_token: $worker_token"

# other masters join swarm
for node in $(seq 2 $managers);
do
    echo "======> manager$node joining swarm as manager ...";
    docker exec -it manager$node \
        docker swarm join \
        --token $manager_token \
        --listen-addr $(docker exec -it manager$node hostname -i | tr -d '\r') \
        --advertise-addr $(docker exec -it manager$node hostname -i | tr -d '\r') \
        $(docker exec -it manager1 hostname -i | tr -d '\r');
done

# show members of swarm
docker exec -it manager1 docker node ls

# workers join swarm
for node in $(seq 1 $workers);
do
    echo "======> worker$node joining swarm as worker ..."
    docker exec -it worker$node \
    docker swarm join \
    --token $worker_token \
    --listen-addr $(docker exec -it worker$node hostname -i | tr -d '\r') \
    --advertise-addr $(docker exec -it worker$node hostname -i | tr -d '\r') \
    $(docker exec -it manager1 hostname -i | tr -d '\r');
done

# show members of swarm
docker exec -it manager1 docker node ls

順を追って説明する.

まずユーザ定義ネットワークを作成する.
ネットワークタイプはbridgeを選択

# create user defined bridge network
echo "======> Creating $nw network ...";
nw=dind-net
docker network create -d bridge $nw

swarmに参加させるノードコンテナを起動.
swarmでは必ずmanagerかworkerかのいずれかのロールを割り当てる.

docs.docker.com

それぞれの役割は読んで字のごとくという感じ?(雑)

manager node, worker nodeをそれぞれ3台ずつ,dindコンテナで起動する

# Swarm mode using Docker in Docker

managers=3
workers=3

# create manager machines
echo "======> Creating $managers manager machines ...";
for node in $(seq 1 $managers);
do
    echo "======> Creating manager$node machine ...";
    docker run -it --privileged --network $nw --name manager$node --hostname manager$node -d docker:stable-dind;
done

# create worker machines
echo "======> Creating $workers worker machines ...";
for node in $(seq 1 $workers);
do
    echo "======> Creating worker$node machine ...";
    docker run -it --privileged --network $nw --name worker$node --hostname worker$node -d docker:stable-dind;
done

# list all machines
docker container ls
  • --privilegedをつけるのは元のチュートリアルに倣っている.これをつけないと自分以外のコンテナの設定を変えることができない.
  • --name と --hostname で二回名前を指定しており冗長な感じもするが,--name がコンテナ名,--hostname がホスト名.前者はコンテナ同士の名前解決に用いる名前で,後者はこの後に出てくるnode lsなどの際に表示するホスト名となる.
  • 使用するコンテナイメージはdocker:stable-dind.(dindの中のdockerバージョンは"DockerVersion": "18.06.1-ce")

この時点では6台のdockerホストを立てただけ.ここからswarmクラスタを構築していく.
manager1を最初のswarmの参加者に指定する(リーダーになる)

# initialize swarm mode and create a manager
echo "======> Initializing first swarm manager ..."
docker exec -it manager1 docker swarm init --listen-addr $(docker exec -it manager1 hostname -i | tr -d '\r') --advertise-addr $(docker exec -it manager1 hostname -i | tr -d '\r')

--listen-addrおよび--advertise-addrに自身のホストIPを指定する.

manager1がswarmクラスタのmanager nodeになったので,クラスタ参加に必要なトークンを払い出す.

# get manager and worker tokens
manager_token=$(docker exec -it manager1 docker swarm join-token manager -q | tr -d '\r')

worker_token=$(docker exec -it manager1 docker swarm join-token worker -q | tr -d '\r')

echo "manager_token: $manager_token"
echo "worker_token: $worker_token"

見ての通り,ロールによってトークンが異なる.

払い出したトークンで,他のnodeをswarmクラスタに参加させる.

# other masters join swarm
for node in $(seq 2 $managers);
do
    echo "======> manager$node joining swarm as manager ...";
    docker exec -it manager$node \
        docker swarm join \
        --token $manager_token \
        --listen-addr $(docker exec -it manager$node hostname -i | tr -d '\r') \
        --advertise-addr $(docker exec -it manager$node hostname -i | tr -d '\r') \
        $(docker exec -it manager1 hostname -i | tr -d '\r');
done

# show members of swarm
docker exec -it manager1 docker node ls

# workers join swarm
for node in $(seq 1 $workers);
do
    echo "======> worker$node joining swarm as worker ..."
    docker exec -it worker$node \
    docker swarm join \
    --token $worker_token \
    --listen-addr $(docker exec -it worker$node hostname -i | tr -d '\r') \
    --advertise-addr $(docker exec -it worker$node hostname -i | tr -d '\r') \
    $(docker exec -it manager1 hostname -i | tr -d '\r');
done

# show members of swarm
docker exec -it manager1 docker node ls

正常に終了していれば,6台のノードがmanager/workerいずれかのロールでswarmクラスタに参加している状態となる.

ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
vjgd1myk9r10msdhi1x835kou *   manager1            Ready               Active              Leader              18.09.3
10ddklb0bysyhpadxjr6lvyar     manager2            Ready               Active              Reachable           18.09.3
q0cxi5rkcayiuac2kopjsg1j1     manager3            Ready               Active              Reachable           18.09.3
i78pa70deqx1aspefqvmjs4bj     worker1             Ready               Active                                  18.09.3
r2zv3coxlzvn6oh9xul8bf8ss     worker2             Ready               Active                                  18.09.3
quzmoin1qesj7gnqh9ffvq0st     worker3             Ready               Active                                  18.09.3

以上,dindでswarmクラスタを立てるところまでを実施した.
次回はこのクラスタにサービスをデプロイしたり,nodeを退出させたりするところをやる.
完結編はこちら↓

koimedenshi.hatenablog.com

うまくいかないとき

環境によりけりなので,エラーを潰していくしかない.
先述の通りdindは完全ではないらしいので,最初から無理という可能性もある.
その場合は元のチュートリアルをやるか,ふつうにplay-with-dockerをやるしかないかも

後片付け

用済みであれば,docker stop -> docker rm / network rmすればok

docker stop manager1 manager2 manager3 worker1 worker2 worker3
docker rm manager1 manager2 manager3 worker1 worker2 worker3
docker network rm dind-net

P.S.

正直docker-machineの方が便利かも.