LoginSignup
4
9

More than 5 years have passed since last update.

Docker Swarmを試してみた

Posted at

基礎知識

非常に今更ですが、そもそもコンテナ自体が初めてなので、基礎知識を確認。

Docker

Linuxカーネルのもつコンテナ機能を利用して、アプリケーションのデプロイを自動化するためのソフトウェア。コンテナ仮想化の技術(コンテナエンジン)においてはデファクトスタンダード。
Comunity Edition(CE)とEnterprise Edition(EE)があり、EEの中でもBasic, Standard, Advancedがある。フリーでずっと使用できるのはCE。

Dockerそのものでは、自身のインストールされたホスト上のコンテナしか管理できないため、複数ホスト(クラスタ)上でコンテナを運用するには別途オーケストレーションツールが必要。オーケストレーションツールに関しては、どれがよいかはまだ議論がある。
参考1
参考2
参考3
→比較という意味ではこれが一番わかりやすい。

Docker Swarm

Docker1.12より、Docker自体にバンドルされたオーケストレーションツール。オートスケーリングやリバランスの機能がないなど、他に比べると機能が劣るということだが…。Dockerだけで完結できるので、手軽さという意味ではよさそう。

Kubernetes(k8s)

Googleが開発して、今はCloud Native Computing Foundationが管理。複数のコンテナをPodという単位にまとめ、Pod単位で管理を行う。またPodにServiceというエンドポイントを持たせることができるため、コンテナがスケールアウトしたり、別のホストに移動しても同一のURLでアクセスが継続できる。
KVS(Key Value Store)としてetcdを使用。

Mesos

Apacheが運営。Mesosphere社はディストリビューションを提供。
他2つとは異なり、コンピューティングレイヤーでの抽象化を行う。別の言い方では、複数のホストを1つの巨大なホストのように見立てて、そのうえでコンテナを動かすという考え方。
またそもそもコンテナ管理ツールではないため、コンテナを動かすには別のツールとの連携が必要(Marathon,Chronos)。
KVS(Key Value Store)としてZooKeeperを使用。

インストール

今回はDocker + Swarmで試してみる。

インストール要件

環境

CentOS 7(minimal)

Install

公式通りで問題なし。
追加で、
https://docs.docker.com/engine/installation/linux/linux-postinstall/#configure-docker-to-start-on-boot
の自動起動だけはやっておく。

Tutrial

Image作成

以下3つのファイルを作成して、空きディレクトリに格納。
- Dockerfile
- requirements.txt
- app.py

Dockerfile
# Use an official Python runtime as a base image
FROM python:2.7-slim

# Set the working directory to /app
WORKDIR /app

# Copy the current directory contents into the container at /app
ADD . /app

# Install any needed packages specified in requirements.txt
RUN pip install -r requirements.txt

# Make port 80 available to the world outside this container
EXPOSE 80

# Define environment variable
ENV NAME World

# Run app.py when the container launches
CMD ["python", "app.py"]
requirements.txt
Flask
Redis
app.py
from flask import Flask
from redis import Redis, RedisError
import os
import socket

# Connect to Redis
redis = Redis(host="redis", db=0, socket_connect_timeout=2, socket_timeout=2)

app = Flask(__name__)

@app.route("/")
def hello():
    try:
        visits = redis.incr("counter")
    except RedisError:
        visits = "<i>cannot connect to Redis, counter disabled</i>"

    html = "<h3>Hello {name}!</h3>" \
           "<b>Hostname:</b> {hostname}<br/>" \
           "<b>Visits:</b> {visits}"
    return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname(), visits=visits)

if __name__ == "__main__":
        app.run(host='0.0.0.0', port=80)

そのディレクトリにて、イメージを作成。

[root@localhost testapp]# ls
app.py  Dockerfile  requirements.txt
[root@localhost testapp]#
[root@localhost testapp]# docker build -t friendlyhello .
Sending build context to Docker daemon 4.608 kB
Step 1/7 : FROM python:2.7-slim

[root@localhost testapp]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
friendlyhello       latest              d058024f6d16        38 seconds ago      195 MB
python              2.7-slim            1c7128a655f6        13 days ago         183 MB
hello-world         latest              48b5124b2768        4 months ago        1.84 kB

起動

[root@localhost testapp]# docker run -p 4000:80 friendlyhello
 * Running on http://0.0.0.0:80/ (Press CTRL+C to quit)

コンテナの視点ではポート80で起動しているが、ホストから見ると、ポート4000にマッピングして外部にエクスポートしている。そのため、外からブラウザでアクセスする際は4000に接続すればよい。
20170525_023.jpg

上記ではフォアグラウンドで起動してしまうが、バックグラウンドで起動するには、
docker run -d -p 4000:80 friendlyhelloと-dをつける(Detach mode)。
停止は下記のように、docker psでプロセスIDを特定して、docker stopすればよい。

[root@localhost testapp]# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                  NAMES
9a8a215706d0        friendlyhello       "python app.py"     6 seconds ago       Up 5 seconds        0.0.0.0:4000->80/tcp   festive_snyder
[root@localhost testapp]#
[root@localhost testapp]#
[root@localhost testapp]# docker stop 9a8a215706d0
9a8a215706d0

イメージのシェア

Docker Cloudというサイトに登録すると、フリーでレポジトリを使用することができ、そこにイメージのアップロード、および公開が可能。
https://cloud.docker.com

初回アクセス時はSign Upして、適当なレポジトリを作成する。Docker IDがユーザ名に当たるが、大文字は使用不可らしい。

上記完了したら、Dockerホストにて以下を実行すると、タグ付けしたイメージがアップロードされる。
# docker tag friendlyhello keisuke1208/test:test
→friendlyhelloというイメージに、keisuke1208というアカウントの、testというレポジトリの、testというタグを割り当てている。

[root@localhost testapp]# docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: keisuke1208
Password:
Login Succeeded
[root@localhost testapp]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
friendlyhello       latest              d058024f6d16        37 minutes ago      195 MB
python              2.7-slim            1c7128a655f6        13 days ago         183 MB
hello-world         latest              48b5124b2768        4 months ago        1.84 kB
[root@localhost testapp]#
[root@localhost testapp]# docker tag friendlyhello keisuke1208/test:test
[root@localhost testapp]# docker push keisuke1208/test:test
The push refers to a repository [docker.io/keisuke1208/test]
afe75cea1bdd: Pushed
a95f70df3cfb: Pushed
3f925f4b7c74: Pushed
7b7f69d6236f: Mounted from library/python
667e68ed0db3: Mounted from library/python
5eac2de68a97: Mounted from library/python
8d4d1ab5ff74: Mounted from library/python
test: digest: sha256:a1e7034a88bc3f272ea0ec78845a0c13824ce4635dc3b88e3b5a990c8adfd788 size: 1787

Swarm

1.12以上であれば、何も意識しなくてもインストールされている。

Serviceの設定

複数のコンテナにてある1つの「サービス」を提供するときに、これをserviceと定義する。Serviceの定義はdocker-compose.ymlというymlにて行う。以下は1例。
webという名前のserviceを、2台のコンテナで起動する。それぞれのコンテナはホストの0.1(10%)のCPUと、50MBのメモリを使用できる。ポートはコンテナ上の80をホストの4000にマップしている。
最初のversionはこのファイルの書式のバージョン。
書式の詳細はこちら

docker-compose.yml
version: "3"
services:
  web:
    image: keisuke1208/test:test
    deploy:
      replicas: 2
      resources:
        limits:
          cpus: "0.1"
          memory: 50M
      restart_policy:
        condition: on-failure
    ports:
      - "4000:80"
    networks:
      - webnet
networks:
  webnet:

ファイルを作成したら、swarmを起動したうえでservice(正確にはStack)を起動させる。
以下の例では、getstartedlabというstackを起動しているが、webというserviceはこのstackの一部。
stackはserviceをまとめたもので、1つのStackが複数のサービスを持つことができる。

[root@localhost testapp]# docker swarm init
Swarm initialized: current node (z2ng430u16l3ztxb7xpxneedl) is now a manager.

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

    docker swarm join \
    --token SWMTKN-1-262rqbrvz7gy0sylfdpxnizsd2u6q0qf5n5mg207khkhj0ll8r-4tsvhobo7tq0j4t74k7xhv6g1 \
    10.32.2.55:2377

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

[root@localhost testapp]#
[root@localhost testapp]# docker stack deploy -c docker-compose.yml getstartedlab
Creating network getstartedlab_webnet
Creating service getstartedlab_web
[root@localhost testapp]#
[root@localhost testapp]# docker stack ps getstartedlab
ID            NAME                 IMAGE                  NODE                   DESIRED STATE  CURRENT STATE           ERROR  PORTS
rk2tqj9jqb5l  getstartedlab_web.1  keisuke1208/test:test  localhost.localdomain  Running        Running 13 seconds ago
r1n4klevxsn4  getstartedlab_web.2  keisuke1208/test:test  localhost.localdomain  Running        Running 12 seconds ago
[root@localhost testapp]#

docker stack psコマンドで、2つのコンテナが起動していることが確認できる。また4000番にアクセスすると、サービスも起動していることが確認できる。

image

スケールアウトさせるには、ymlのreplicaの指定を変更して、再度stack deployすればよい。いったんダウンさせる必要はない。

[root@localhost testapp]# cat docker-compose.yml
version: "3"
services:
  web:
    image: keisuke1208/test:test
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: "0.1"
          memory: 50M
      restart_policy:
        condition: on-failure
    ports:
      - "4000:80"
    networks:
      - webnet
networks:
  webnet:
[root@localhost testapp]#
[root@localhost testapp]#
[root@localhost testapp]#
[root@localhost testapp]# docker stack deploy -c docker-compose.yml getstartedlab
Updating service getstartedlab_web (id: mq21pewumaemvlw0ckkpkqsx2)
[root@localhost testapp]#
[root@localhost testapp]# docker stack ps getstartedlab
ID            NAME                 IMAGE                  NODE                   DESIRED STATE  CURRENT STATE           ERROR  PORTS
rk2tqj9jqb5l  getstartedlab_web.1  keisuke1208/test:test  localhost.localdomain  Running        Running 26 minutes ago
r1n4klevxsn4  getstartedlab_web.2  keisuke1208/test:test  localhost.localdomain  Running        Running 26 minutes ago
u336wzrj40we  getstartedlab_web.3  keisuke1208/test:test  localhost.localdomain  Running        Running 6 seconds ago

本当に大丈夫なのか、別のホストからncでウォッチしてみたところ、一瞬だけ落ちているようだが、基本的には問題なさそう。

[root@tsslin01 ~]# while true; do echo -en "GET / HTTP/1.1\n\n" | nc 10.32.2.55 4000 | head -n 1 >> temp3; date >> temp3;sleep 1;done
^C
[root@tsslin01 ~]#
[root@tsslin01 ~]# cat temp3
HTTP/1.0 200 OK
Thu May 25 16:19:30 JST 2017
HTTP/1.0 200 OK
Thu May 25 16:19:31 JST 2017
HTTP/1.0 200 OK
Thu May 25 16:19:32 JST 2017
Thu May 25 16:19:33 JST 2017
HTTP/1.0 200 OK
Thu May 25 16:19:34 JST 2017

SwarmでCluster化

参考
もう一台同じようにDockerをインストールしたホスト(rainy)を用意。InstallまででTutrialの内容は未実施。
まずはManagerノード(元々swarm initしたホスト,sunny)にて状態確認。

[root@sunny ~]# docker node ls
ID                           HOSTNAME             STATUS  AVAILABILITY  MANAGER STATUS
z2ng430u16l3ztxb7xpxneedl *  sunny.weather.local  Ready   Active        Leader

Join用のトークンを発行。今回はWorkerとして追加。

[root@sunny ~]# docker swarm join-token worker
To add a worker to this swarm, run the following command:

    docker swarm join \
    --token SWMTKN-1-262rqbrvz7gy0sylfdpxnizsd2u6q0qf5n5mg207khkhj0ll8r-4tsvhobo7tq0j4t74k7xhv6g1 \
    10.32.2.55:2377

表示されたコマンドをrainyにて実行。

[root@rainy ~]# docker swarm join --token SWMTKN-1-262rqbrvz7gy0sylfdpxnizsd2u6q0qf5n5mg207khkhj0ll8r-4tsvhobo7tq0j4t74k7xhv6g1 10.32.2.55:2377
This node joined a swarm as a worker.

これでSwarm Clusterが形成される。

[root@sunny ~]# docker node ls
ID                           HOSTNAME             STATUS  AVAILABILITY  MANAGER STATUS
kyjd450c6ixluglc6ixu8arwo    rainy.weather.local  Ready   Active
z2ng430u16l3ztxb7xpxneedl *  sunny.weather.local  Ready   Active        Leader

この状態でStackをデプロイすると、3台のコンテナ中2台がrainy,1台がsunnyで起動していることがわかる。

[root@sunny testapp]# docker stack deploy -c docker-compose.yml getstartedlab
Creating network getstartedlab_webnet
Creating service getstartedlab_web
[root@sunny testapp]#
[root@sunny testapp]# docker stack ps getstartedlab
ID            NAME                 IMAGE                  NODE                 DESIRED STATE  CURRENT STATE             ERROR  PORTS
jmzusd2ta971  getstartedlab_web.1  keisuke1208/test:test  rainy.weather.local  Running        Preparing 36 seconds ago
4j91vu2pn91q  getstartedlab_web.2  keisuke1208/test:test  rainy.weather.local  Running        Preparing 36 seconds ago
qreob82mno3p  getstartedlab_web.3  keisuke1208/test:test  sunny.weather.local  Running        Running 35 seconds ago

この状態でアクセスすると、ラウンドロビンで3台にアクセスされていることがわかる。
image
image
image

ここで表示されるホスト名は、各コンテナに設定されているらしい。

[root@sunny testapp]# ^ls^cat
cat /var/lib/docker/containers/f71ab01c50019138939cc897906c690f518c8325086f9b6c274a486f5a78ee15/hostname
f71ab01c5001

障害が発生すると??

Worker(rainy)のdockerサービスを落として疑似障害を発生させる。

[root@sunny testapp]# docker stack ps getstartedlab
ID            NAME                 IMAGE                  NODE                 DESIRED STATE  CURRENT STATE          ERROR  PORTS
i572m3d8ngpa  getstartedlab_web.1  keisuke1208/test:test  rainy.weather.local  Running        Running 7 seconds ago
w2yd90vqk3e5  getstartedlab_web.2  keisuke1208/test:test  sunny.weather.local  Running        Running 7 seconds ago
nqdoy8a7ymy4  getstartedlab_web.3  keisuke1208/test:test  sunny.weather.local  Running        Running 7 seconds ago
[root@sunny testapp]#
(Worker(rainy)のDockerサービスをStop)
[root@sunny testapp]#
[root@sunny testapp]# docker stack ps getstartedlab
ID            NAME                     IMAGE                  NODE                 DESIRED STATE  CURRENT STATE                   ERROR  PORTS
x66fmwazzoxy  getstartedlab_web.1      keisuke1208/test:test  sunny.weather.local  Running        Running less than a second ago
i572m3d8ngpa   \_ getstartedlab_web.1  keisuke1208/test:test  rainy.weather.local  Shutdown       Running 20 seconds ago
w2yd90vqk3e5  getstartedlab_web.2      keisuke1208/test:test  sunny.weather.local  Running        Running 39 seconds ago
nqdoy8a7ymy4  getstartedlab_web.3      keisuke1208/test:test  sunny.weather.local  Running        Running 39 seconds ago
[root@sunny testapp]#
(Worker(rainy)のDockerサービスをStart)

[root@sunny testapp]# docker node ls
ID                           HOSTNAME             STATUS  AVAILABILITY  MANAGER STATUS
kyjd450c6ixluglc6ixu8arwo    rainy.weather.local  Ready   Active
z2ng430u16l3ztxb7xpxneedl *  sunny.weather.local  Ready   Active        Leader
[root@sunny testapp]#
[root@sunny testapp]# docker stack ps getstartedlab
ID            NAME                     IMAGE                  NODE                 DESIRED STATE  CURRENT STATE               ERROR  PORTS
x66fmwazzoxy  getstartedlab_web.1      keisuke1208/test:test  sunny.weather.local  Running        Running about a minute ago
i572m3d8ngpa   \_ getstartedlab_web.1  keisuke1208/test:test  rainy.weather.local  Shutdown       Shutdown 12 seconds ago
w2yd90vqk3e5  getstartedlab_web.2      keisuke1208/test:test  sunny.weather.local  Running        Running 2 minutes ago
nqdoy8a7ymy4  getstartedlab_web.3      keisuke1208/test:test  sunny.weather.local  Running        Running 2 minutes ago

このshutdownとなっている古いプロセスの情報は、任意には消せないらしい。
docker service update <service name> --force
でリバランスは可能だが、生きているやつもすべて落としてリバランスしている。

[root@sunny testapp]# docker service update getstartedlab_web --force
getstartedlab_web
[root@sunny testapp]#
[root@sunny testapp]# docker service ps getstartedlab_web
ID            NAME                     IMAGE                  NODE                 DESIRED STATE  CURRENT STATE           ERROR  PORTS
qnkadpxcvu9f  getstartedlab_web.1      keisuke1208/test:test  rainy.weather.local  Ready          Ready 2 seconds ago
x66fmwazzoxy   \_ getstartedlab_web.1  keisuke1208/test:test  sunny.weather.local  Shutdown       Running 2 seconds ago
i572m3d8ngpa   \_ getstartedlab_web.1  keisuke1208/test:test  rainy.weather.local  Shutdown       Shutdown 4 minutes ago
y24fw1ggg3lc  getstartedlab_web.2      keisuke1208/test:test  rainy.weather.local  Ready          Ready 2 seconds ago
w2yd90vqk3e5   \_ getstartedlab_web.2  keisuke1208/test:test  sunny.weather.local  Shutdown       Running 2 seconds ago
vsftpfdx56y3  getstartedlab_web.3      keisuke1208/test:test  sunny.weather.local  Ready          Ready 2 seconds ago
nqdoy8a7ymy4   \_ getstartedlab_web.3  keisuke1208/test:test  sunny.weather.local  Shutdown       Running 2 seconds ago

感想

他を使ったことがないのであまり参考にならないが、Docker + Swarmは確かに簡単そう。複数台ホストに分散したシステムも簡単に作れた。ドキュメントの精度も高く、ほとんどつっかかるポイントがなかったのも助かる。
リバランスが乱暴なのが気になるが、もう少し使ってみないと何ともいえない。

その他参考

KVS比較
https://www.consul.io/intro/vs/zookeeper.html

Docker CE,EE
https://www.slideshare.net/Docker/docker-online-meetup-announcing-docker-ce-ee

Docker Doc
https://docs.docker.com

4
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
9