2019年7月6日土曜日

docker Swarm 上で docker-compose で定義したコンテナを実行してみた

概要

docker swarm + docker-compose を試してみました
docker stack という機能を使うことが実現できるのでその使い方を紹介します

環境

  • macOS 10.14.5
  • Vagrant 2.1.1
  • Ubuntu 16.04 LTS
  • docker 18.09.7

Swarm 環境構築

Vagrant でパパっと構築していきます

  • vim Vagrantfile
Vagrant.configure("2") do |config|
  config.vm.define "vm01" do |v|
    v.vm.box = "ubuntu/xenial64"
    v.vm.network "private_network", type: "dhcp"
    v.vm.provider "virtualbox" do |vb|
      vb.memory = "1024"
    end
  end
  config.vm.define "vm02" do |v|
    v.vm.box = "ubuntu/xenial64"
    v.vm.network "private_network", type: "dhcp"
    v.vm.provider "virtualbox" do |vb|
      vb.memory = "1024"
    end
  end
end
  • vagrant up
  • vagrant ssh

それぞれにログインして作業します
docker をインストールして Swarm クラスタを構築します

  • sudo apt-get remove docker docker-engine docker.io containerd runc
  • sudo apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common
  • curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
  • sudo apt-key fingerprint 0EBFCD88
  • sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
  • sudo apt-get update
  • sudo apt-get install docker-ce docker-ce-cli containerd.io
  • sudo curl -L "https://github.com/docker/compose/releases/download/1.24.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
  • sudo chmod +x /usr/local/bin/docker-compose
  • sudo gpasswd -a $USER docker
  • sudo hostnamectl set-hostname vm01

これで docker のインストールは完了です

  • docker swarm init
  • docker swarm join --token SWMTKN-1-19rwa01lw9elmef3qiz9fmgn2mkvzfvdwivyxv1bgihhj3k3wv-6mcld04qufib76sni5a1kj82n 172.28.128.3:2377

vm01 で init を実行し vm02 で join コマンドを実行しましょう

  • docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION l414hf95bwjcv52nz72hpofzh * vm01 Ready Active Leader 18.09.7 p2ph2kvwm8ygwn0wri6vzosya vm02 Ready Active 18.09.7

これで Swarm の準備は OK です

とりあえず docker-compose の作成

とりあえずコンテナを複数デプロイする docker-compose.yml を作成してみます
確認のため環境変数 DUDE に動作しているコンテナホスト名を設定しています

  • vim docker-compose.yml
version: '3.4'

services:
  nginx:
    image: nginx
    environment:
      DUDE: "{{.Node.Hostname}}"
    deploy:
      replicas: 3

これを Swarm 上にデプロイしてみます
デプロイする場合は stack deploy を使います

  • docker stack deploy -c docker-compose.yml test

スタックの一覧を確認すると「test」というスタックが確認できます

  • docker stack ls
NAME SERVICES ORCHESTRATOR test 1 Swarm

デプロイが完了したら ps コマンドで確認してみましょう

  • docker stack ps test
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS ww8tdxhvkbhq test_nginx.1 nginx:latest vm01 Running Running 3 minutes ago 0ok7rtsycvui test_nginx.2 nginx:latest vm02 Running Running 3 minutes ago 3i6hekkj0780 test_nginx.3 nginx:latest vm02 Running Running 3 minutes ago

vm01 と vm02 に分散されてコンテナがデプロイされているのがわかります
exec コンテナが動作しているホストに対して docker ps を実行しコンテナ ID か名前を取得してそれに対して実行しましょう
以下を実行するとちゃんと DUDE 変数にコンテナホスト名 vm01 が設定されているのが確認できると思います

  • docker exec test_nginx.1.ww8tdxhvkbhqh92vv822d7qx7 env

動作が確認できたらスタックを削除します
スタックを削除するとコンテナもすべて削除されます

  • docker stack rm test

ポートを使ってみる

今度はポートを使ってみます
stack deploy を使った場合、nginx プロセスが立ち上がりコンテナを自動でロードバランシングしてくれます

  • vim docker-compose.yml
version: '3.4'

services:
  nginx:
    image: valian/nginx-test-page
    ports:
      - "80:80"
    deploy:
      replicas: 3

イメージは動作確認しやすいイメージにしています
これでデプロイしてみましょう

  • docker stack deploy -c docker-compose.yml test

先程同様に test という stack が作成されています
これで vm01 or vm02 で localhost にアクセスしてみるとコンテナにアクセスできるのが確認できると思います

service について

ports を指定すると service も自動で作成されます

  • docker service ls
ID NAME MODE REPLICAS IMAGE PORTS arla23uvljz5 test_nginx replicated 3/3 valian/nginx-test-page:latest *:80->80/tcp

vm01 のコンテナホストに nginx のプロセスが立ち上がり overlay ネットワークを通して各コンテナホストにデプロイされたコンテナにアクセスすることができます
vm01 のコンテナホストに curl してみると以下のようにバランシングされていることが確認できます (この結果は vm02 から実行しても同じになります)

vagrant@vm01:~$ curl localhost
HOSTNAME=c0e0d2ae816c
vagrant@vm01:~$ curl localhost
HOSTNAME=8680d41c963b
vagrant@vm01:~$ curl localhost
HOSTNAME=4461b5233fc3

また docker ps -a で確認するとわかりますがホストのポートにはバインドしておらず dockerd が直接ルーティングしていることがわかります

  • sudo lsof -i:80
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME dockerd 1254 root 41u IPv6 72420 0t0 TCP *:http (LISTEN)

ボリュームを使ってみる (ホストボリュームマウント)

次にボリュームを使ってみます
まずはコンテナホストのボリュームをマウントしてみます

version: '3.4'

services:
  redis:
    image: redis
    ports:
      - "6379:6379"
    volumes:
      - ./data:/data
    deploy:
      placement:
        constraints: [node.role == manager]
  • mkdir data
  • docker stack deploy -c docker-compose.yml test

これで実行すると redis コンテナが 1 つ vm01 側で起動します
また service を確認すると 6379 へのルーティングが設定されており vm01, vm02 どちらの localhost にアクセスしても redis コンテナにアクセスできるようになっています

  • redis-cli set a a
  • redis-cli get a

=> a

また stack を rm して再度 deploy しても同じ値が再度取得できるのが確認できると思います
次に動作するコンテナホストを変更してみます

  • vim docker-compose.yml
version: '3.4'

services:
  redis:
    image: redis
    ports:
      - "6379:6379"
    volumes:
      - ./data:/data
    deploy:
      placement:
        constraints: [node.role == worker]
  • mkdir data
  • docker stack deploy -c docker-compose.yml test

これで vm02 でコンテナが起動したら先程のデータが取得できるか確認してみましょう

  • redis-cli get a

=> nil

という感じでデータがないのが確認できます
これは当然と言えば当然ですが vm01 の ./data をマウントしておりそこにデータも保存しているため起動するコンテナホストを vm02 にしたら当然保存したデータもありません

というわけで

service について

  • docker service ls
ID NAME MODE REPLICAS IMAGE PORTS i0ewsq4w9s5i test_redis replicated 1/1 redis:latest *:6379->6379/tcp

nginx のとき同様で dockerd が iptables を使って 6379 ポートをルーティングしてくれています

  • sudo iptables -L DOCKER-INGRESS
Chain DOCKER-INGRESS (1 references) target prot opt source destination ACCEPT tcp – anywhere anywhere tcp dpt:6379 ACCEPT tcp – anywhere anywhere state RELATED,ESTABLISHED tcp spt:6379 RETURN all – anywhere anywhere
  • sudo lsof -i:6379
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME dockerd 1312 root 43u IPv6 90625 0t0 TCP *:6379 (LISTEN)

ボリュームを使ってみる (ボリューム領域作成)

ではホストの領域をマウントする方式ではなくボリュームを作成したらどうなるか確認してみます

  • vim docker-compose.yml
version: '3.4'

services:
  redis:
    image: redis
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
    deploy:
      placement:
        constraints: [node.role == manager]
volumes:
  redis_data:
    driver: local

これで起動しデータをセットします

  • docker stack deploy -c docker-compose.yml test
  • redis-cli set a aa

そして docker-compose.yml 上は同じ volume を指定し動作させるコンテナホストを変更してみます
先程と同じように constraints: [node.role == worker] に変更して rm -> deploy してみましょう

  • docker stack rm test
  • docker stack deploy -c docker-compose.yml test
  • redis-cli get a

=> nil

するとデータは nil で別のボリュームを参照していることが確認できると思います 
各コンテナホストで docker volume ls してみるとわかりますがそれぞれでボリュームが作成されており領域的には別になっていることが確認できます
これを回避する方法はいくつかありますが簡単そうなのは

  • ボリュームコンテナを起動してその領域をマウントする
  • nfs ドライバを使う

かなと思います
ただ docker-compose 3 系からは volumes_from が使えないようなので前者は厳しいと思います
となると後者の nfs を選択するしかなさそうです

Tips

コンテナを起動するコンテナホストにイメージがない場合は最初に docker pull が走るためコンテナの起動が遅れます

最後に

docker Swarm + docker-compose を試してみました
基本的には stack 機能を使えば OK です
stack deploy, rm, ls など基本的なコマンドは覚えておきましょう
service と network は dockerd が良い感じにやってくれるのでそこまで苦労はしないと思います

一番辛そうなのは volume かなと思います
どうやらこれだけはデフォルトだとコンテナホスト間で共有するすべがなさそうです
NFS が一番簡単そうですが driver のインストールから必要になります

参考サイト

0 件のコメント:

コメントを投稿