Help us understand the problem. What is going on with this article?

Docker for AWS 試してみた その2

More than 3 years have passed since last update.

前回に引き続き、今回は Docker for AWS で生成される swarm クラスタで実際に
アプリを起動させたりスケールさせたりしてみます。

8月1日出版予定の AWS 本で ECS や ECR の章書きました!
 AWS での Docker 利用の手引きにぜひご参照ください)

目次

通常の Swarm クラスタとの違いは、docker service をご覧ください。

  • Swarm マネージャに接続する
  • docker node
  • docker service
  • 続編..

Swarm マネージャに接続する

2 つ方法 があります(いずれも Docker を利用する上で一般的1 なもので
Docker for AWS や Swarm クラスタならではというわけではありません)。

  1. Docker ホストに SSH ログインして、ホストの Docker クライアントから直接操作
  2. SSH トンネルを利用して、ローカルの Docker クライアントから Swarm マネージャを操作

方法 1. SSH ログイン

SSH の接続先は、前回の構成図にある通り、SSH 用の ELB エンドポイントです2

1. CloudFormation でスタックが CREATE_COMPLETE になったら Outputs を覗きます

2. SSH 行、Value 列の SSH コマンドをコピーしましょう
cfn_outputs.png

3. コピーした ELB のエンドポイントが <ssh-host-name> だとして、
CloudFormation 実行時に指定した キーペアも -i で渡し、以下のコマンドを実行します

ssh-login
$ ssh -i docker.pem docker@<ssh-host-name>

Welcome to Docker!
~ $ 

4. 接続確認

docker-ver
~ $ docker version

Client:
 Version:      1.12.0-rc4
 API version:  1.24
 Go version:   go1.6.2
 Git commit:   e4a0dbc
 Built:        Wed Jul 13 03:28:51 2016
 OS/Arch:      linux/amd64
 Experimental: true

Server:
 Version:      1.12.0-rc4
 API version:  1.24
 Go version:   go1.6.2
 Git commit:   e4a0dbc
 Built:        Wed Jul 13 03:28:51 2016
 OS/Arch:      linux/amd64
 Experimental: true

方法 2. SSH トンネル

こちらは OpenSSH 6.7 以降3 可能な方法です。

1. トンネルを掘ります

ssh-tunnel
$ ssh -i docker.pem -NL localhost:2374:/var/run/docker.sock docker@<ssh-host-name> &

2. docker -H localhost:2374 ps などと、都度のホスト指定が嫌なら環境変数をセット

set-envvar
$ DOCKER_HOST="localhost:2374"

3. 接続確認

docker-ver
$ docker version

docker node

ノード一覧

docker node ls で Swarm クラスタのノード一覧が返ってきます。

list-node
$ docker node ls

ID                           HOSTNAME                                          MEMBERSHIP  STATUS  AVAILABILITY  MANAGER STATUS
1liubx22e0l66dodtsyum0lug    ip-192-168-33-30.ap-northeast-1.compute.internal  Accepted    Ready   Active
1omamwirdem28ktytpucw3m73    ip-192-168-33-29.ap-northeast-1.compute.internal  Accepted    Ready   Active
2xt623cbm1riexpus7w92tg3j *  ip-192-168-33-53.ap-northeast-1.compute.internal  Accepted    Ready   Active        Leader
489thmtfpn5zfj63i5022b1hf    ip-192-168-34-87.ap-northeast-1.compute.internal  Accepted    Ready   Active

docker node ls コマンドを受け付けたマネージャノードの行は、ID 列に * が付いています。また、上記は ManagerSize を 1、ClusterSize を 3 台で CloudFormation スタックを生成した結果ですが、マネージャが複数の場合リーダー以外のノードでは MANAGER 列には * がつきます。

ノードの詳細確認

試しに、マネージャノードの詳細を確認してみましょう。マネージャノードの ID を使い docker node inspect します。(Docker のよくある API 同様、ID は一意なら短い指定でも OK)

inspect-node
$ docker node inspect 2xt

[
    {
        "ID": "2xt623cbm1riexpus7w92tg3j",
        "Version": {
            "Index": 10
        },
        "CreatedAt": "2016-07-18T04:18:44.240072915Z",
        "UpdatedAt": "2016-07-18T04:18:44.52518808Z",
        "Spec": {
            "Role": "manager",
            "Membership": "accepted",
            "Availability": "active"
        },
        "Description": {
            "Hostname": "ip-192-168-33-53.ap-northeast-1.compute.internal",
            "Platform": {
                "Architecture": "x86_64",
                "OS": "linux"
            },
            "Resources": {
                "NanoCPUs": 1000000000,
                "MemoryBytes": 1041694720
            },
            "Engine": {
                "EngineVersion": "1.12.0-rc4",
                "Plugins": [
                  ...
                ]
            }
        },
        "Status": {
            "State": "ready"
        },
        "ManagerStatus": {
            "Leader": true,
            "Reachability": "reachable",
            "Addr": "192.168.33.53:2377"
        }
    }
]

結果の JSON をみておわかりのように、ID を複数並べれば、複数分の情報が返ります。
また、docker inspect 同様、--format オプションでデータの絞り込みが使えます。
検査対象が自分自身なら self を使うといったこともできます。

inspect-self-node
$ docker node inspect --format '{{ .ManagerStatus.Addr }}' self

192.168.33.53:2377

ノードの更新

追い追い、サービスを起動するノードに制約を設けるために docker node update コマンドを使おうと思ったのですが、ドキュメントに反してまだラベルの更新には対応していない模様。(by using the docker node update command って書いてあるのに..)

仕方がないので、後ほどドレインしてみます。敵から HP を吸収する某魔法を毎回思い出してしまう私ですが、Swarm のドレインは、ほぼ Kubernetes のドレインです。ECS にもほしいなあ。

docker service

Swarm は サービス という単位で、その生成・更新・スケールイン / アウトが行えます。
docker run で起動する通常のコンテナとは違い、サービスとして起動したコンテナには
スケールアウトやローリングアップデートといった便利な機能が付加されます。

例えば Nginx や Redis はいずれももちろん docker run でも起動しますが
サービスとして起動すれば、とても簡単にスケールアウトできるようになります。

サービスの起動

さていよいよ、Docker for AWS の一番盛り上がるところです!
Swarm マネージャにサービスの生成を依頼してみましょう。

hello-world
$ docker service create --name hello -p 80:80 dockercloud/hello-world

コマンドは一瞬で返ってきますが、裏でイメージの pull などをせっせと頑張っています。
そして・・・

dynamic-port-mapping.png

・・・自動的に追加された!!
Description タブから DNS name のエンドポイントをブラウザで開くと、

03.png

Hello world〜

では調子に乗って「Alpine Linux で軽量な Docker イメージを作る」にある
軽量イメージなどから、いくつかそれのみで動くサービスを起動してみます。

サービスの起動
$ docker service create --name mysql -p 3306:3306 -e MYSQL_USER=test -e MYSQL_PASSWORD=test -e MYSQL_DATABASE=db mysql:5.7
$ docker service create --name postgres -p 5432:5432 -e POSTGRES_USER=test -e POSTGRES_PASSWORD=test -e POSTGRES_DB=db kiasaki/alpine-postgres
$ docker service create --name redis -p 6379:6379 redis:3.0-alpine
$ docker service create --name nginx -p 8080:80 nginx:1.11-alpine
$ docker service create --name php -p 10080:80 pottava/php:5.6

すると、ELB のリスナーはこうなります。

04.png

おもしろいですね。もちろん、各サービスはいつものように使えます。

サービスへの接続
$ SERVICE_HOST=<ELB のエンドポイント>
$ mysql -h $SERVICE_HOST -u test -p db
$ psql -h $SERVICE_HOST -d db -U test
$ redis-cli -h $SERVICE_HOST
$ curl -i $SERVICE_HOST:8080
$ curl -i $SERVICE_HOST:10080

サービスの一覧・詳細表示

docker service ls でサービス一覧を取得してみます。

list-services
$ docker service ls

ID            NAME      REPLICAS  IMAGE                    COMMAND
0l6soy9e1dd3  mysql     1/1       mysql:5.7
4bs6bm51eeal  nginx     1/1       nginx:1.11-alpine
7e6v87yy1lav  hello     1/1       dockercloud/hello-world
7sz1yy4hsg9a  redis     1/1       redis:3.0-alpine
ck5fydfm2oaj  php       1/1       pottava/php:5.6
ejbdhcm735h4  postgres  1/1       kiasaki/alpine-postgres

docker service inspect で hello サービスの詳細を確認しましょう。

inspect-service
$ docker service inspect hello

[
    {
        "ID": "7e6v87yy1lavv3jrfie5d4ov2",
        "Version": {
            "Index": 42
        },
        "CreatedAt": "2016-07-18T07:24:25.085911486Z",
        "UpdatedAt": "2016-07-18T07:37:55.512100018Z",
        "Spec": {
            "Name": "hello",
            "TaskTemplate": {
                "ContainerSpec": {
                    "Image": "dockercloud/hello-world"
                },
                "Resources": {
                    "Limits": {},
                    "Reservations": {}
                },
                "RestartPolicy": {
                    "Condition": "any",
                    "MaxAttempts": 0
                },
                "Placement": {}
            },
            "Mode": {
                "Replicated": {
                    "Replicas": 1
                }
            },
            "UpdateConfig": {},
            "EndpointSpec": {
                "Mode": "vip",
                "Ports": [
                    {
                        "Protocol": "tcp",
                        "TargetPort": 80,
                        "PublishedPort": 80
                    }
                ]
            }
        },
        "Endpoint": {
            "Spec": {
                "Mode": "vip",
                "Ports": [
                    {
                        "Protocol": "tcp",
                        "TargetPort": 80,
                        "PublishedPort": 80
                    }
                ]
            },
            "Ports": [
                {
                    "Protocol": "tcp",
                    "TargetPort": 80,
                    "PublishedPort": 80
                }
            ],
            "VirtualIPs": [
                {
                    "NetworkID": "1yaai0qnqhby71xdetn1yrlri",
                    "Addr": "10.255.0.7/16"
                }
            ]
        }
    }
]

Docker v1.12 で導入された内部ロードバランサ4 によって、難しい環境設定などなしに
コンテナではなくサービスごとに公開ポートを用意できていることが見てとれます。

公開ポート
$ docker service inspect --format '{{ range .Endpoint.Ports }}{{ .PublishedPort }}{{ end }}' hello

80

サービスのスケール

引き続き、サービスを docker service scale でスケールさせてみましょう。

サービスのスケール
$ docker service scale hello=10

hello scaled to 10

しばらくすると、レプリカ数が増えています。

レプリカ数の確認
$ docker service inspect --format '{{ .Spec.Mode.Replicated.Replicas }}' hello

10

実際に、コンテナが 10 こにスケールしたことを確かめましょう。
以下のコマンドを何度も投げると、ホスト名が変わることが確認できます。

コンテナIDの返却
$ curl -s docker-elb-2031741514.ap-northeast-1.elb.amazonaws.com | grep host

    <h3>My hostname is 808e0322e5c7</h3>    </body>

・・これ、ECS だとできないやつですね。
そう、ELB 配下のインスタンス数 < コンテナ数 です。
ECS、Swarm モードをとりいれるなどして、動的ポートマッピング早く対応してほしい・・

タスクの一覧

いま 10 こにスケールした hello サービスですが、それぞれのコンテナが
どのホストで動いているかを docker service tasks コマンドで把握しましょう。

タスク一覧
$ docker service tasks hello

ID                         NAME      SERVICE  IMAGE                    LAST STATE              DESIRED STATE  NODE
6vlexizzn84zm8fk6tmpulc9f  hello.1   hello    dockercloud/hello-world  Running 2 hours ago     Running        ip-192-168-33-29.ap-northeast-1.compute.internal
5xayab428r6osmutnqp57b9a1  hello.2   hello    dockercloud/hello-world  Running 10 minutes ago  Running        ip-192-168-33-30.ap-northeast-1.compute.internal
1liu4q4drydufsxev01fyoc9k  hello.3   hello    dockercloud/hello-world  Running 10 minutes ago  Running        ip-192-168-34-87.ap-northeast-1.compute.internal
bbamufe9ylyzo1wepd2asjcpl  hello.4   hello    dockercloud/hello-world  Running 10 minutes ago  Running        ip-192-168-33-29.ap-northeast-1.compute.internal
626g75ru8r1vmen17la3u48w5  hello.5   hello    dockercloud/hello-world  Running 10 minutes ago  Running        ip-192-168-33-53.ap-northeast-1.compute.internal
0hl6r1th2dkfwy6rpxgqji73t  hello.6   hello    dockercloud/hello-world  Running 10 minutes ago  Running        ip-192-168-34-87.ap-northeast-1.compute.internal
169g3qbtp2nwvnap8pp90qkgv  hello.7   hello    dockercloud/hello-world  Running 10 minutes ago  Running        ip-192-168-33-30.ap-northeast-1.compute.internal
4zbdkx4d8xp6mllmapn8kvrxw  hello.8   hello    dockercloud/hello-world  Running 10 minutes ago  Running        ip-192-168-33-30.ap-northeast-1.compute.internal
19vcdgfj0vzio7g5d5d0d0mzt  hello.9   hello    dockercloud/hello-world  Running 10 minutes ago  Running        ip-192-168-33-53.ap-northeast-1.compute.internal
8pulhjksyx01junvmvvjuzt4m  hello.10  hello    dockercloud/hello-world  Running 10 minutes ago  Running        ip-192-168-33-29.ap-northeast-1.compute.internal

さてここで、みなさん忘れかけているであろうドレインを実行してみます。

drain-node
$ docker node ls

ID                           HOSTNAME                                          MEMBERSHIP  STATUS  AVAILABILITY  MANAGER STATUS
1liubx22e0l66dodtsyum0lug    ip-192-168-33-30.ap-northeast-1.compute.internal  Accepted    Ready   Active
1omamwirdem28ktytpucw3m73    ip-192-168-33-29.ap-northeast-1.compute.internal  Accepted    Ready   Active
2xt623cbm1riexpus7w92tg3j *  ip-192-168-33-53.ap-northeast-1.compute.internal  Accepted    Ready   Active        Leader
489thmtfpn5zfj63i5022b1hf    ip-192-168-34-87.ap-northeast-1.compute.internal  Accepted    Ready   Active

$ docker node update --availability drain 1om

1om

しばらくしてタスクをもう一度表示してみましょう。

タスク一覧
$ docker service tasks hello

ID                         NAME      SERVICE  IMAGE                    LAST STATE              DESIRED STATE  NODE
6vlexizzn84zm8fk6tmpulc9f  hello.1   hello    dockercloud/hello-world  Running 14 seconds ago  Running        ip-192-168-33-30.ap-northeast-1.compute.internal
5xayab428r6osmutnqp57b9a1  hello.2   hello    dockercloud/hello-world  Running 18 minutes ago  Running        ip-192-168-33-30.ap-northeast-1.compute.internal
1liu4q4drydufsxev01fyoc9k  hello.3   hello    dockercloud/hello-world  Running 18 minutes ago  Running        ip-192-168-34-87.ap-northeast-1.compute.internal
bbamufe9ylyzo1wepd2asjcpl  hello.4   hello    dockercloud/hello-world  Running 14 seconds ago  Running        ip-192-168-33-87.ap-northeast-1.compute.internal
626g75ru8r1vmen17la3u48w5  hello.5   hello    dockercloud/hello-world  Running 18 minutes ago  Running        ip-192-168-33-53.ap-northeast-1.compute.internal
0hl6r1th2dkfwy6rpxgqji73t  hello.6   hello    dockercloud/hello-world  Running 18 minutes ago  Running        ip-192-168-34-87.ap-northeast-1.compute.internal
169g3qbtp2nwvnap8pp90qkgv  hello.7   hello    dockercloud/hello-world  Running 18 minutes ago  Running        ip-192-168-33-30.ap-northeast-1.compute.internal
4zbdkx4d8xp6mllmapn8kvrxw  hello.8   hello    dockercloud/hello-world  Running 18 minutes ago  Running        ip-192-168-33-30.ap-northeast-1.compute.internal
19vcdgfj0vzio7g5d5d0d0mzt  hello.9   hello    dockercloud/hello-world  Running 18 minutes ago  Running        ip-192-168-33-53.ap-northeast-1.compute.internal
8pulhjksyx01junvmvvjuzt4m  hello.10  hello    dockercloud/hello-world  Running 14 seconds ago  Running        ip-192-168-33-53.ap-northeast-1.compute.internal

ip-192-168-33-53 というホストからコンテナがいなくなりました。

地味に裏で動いていた Nginx や PHP コンテナは 1 つだけだったので
しばらく接続できない状態になったかもしれませんが、この hello サービスは
スケールした状態であったためサービスそのものは落とさずにドレインできました。
これで Docker ホストのメンテナンスも簡単ですね!

補足

  • サービス起動時の制約
    Specify service constraints を使えばコンテナを起動するホストを制限できます。
    これまで使えていた他の制約や affinity はどこにいったのだろうか・・

  • スケジューリング・ストラテジ
    CloudFormation で生成された Swarm クラスタのストラテジをどこで変更できるかは不明..

続編

長くなってしまったので、以下はまた次回。次回があれば。

  • docker deploy
  • クラスタそのものの構成変更

 


  1. 今回は AWS のキーペアを使っていますが、TLS での接続ができるとなおいいですね〜 

  2. Swarm マネージャの起動する AutoScaling グループに ELB が設定されています 

  3. バージョン不明な方は ssh -V などで 

  4. 参考: Docker ロードバランサ内部実装(次回の JAWS-UG コンテナ支部で登壇くださいます) 

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした