Docker for AWS 試してみた その2

 Docker AWS Swarm
2016.07.18

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

 

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

目次

通常の Swarm クラスタとの違いは、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 で渡し、以下のコマンドを実行します

4. 接続確認

~ $ 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 -i docker.pem -NL localhost:2374:/var/run/docker.sock docker@<ssh-host-name> &

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

$ DOCKER_HOST="localhost:2374"

3. 接続確認

$ docker version

docker node

ノード一覧

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

$ 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)

$ 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 を使うといったこともできます。

$ docker node inspect --format '' 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 マネージャにサービスの生成を依頼してみましょう。

$ 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 でサービス一覧を取得してみます。

$ 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 サービスの詳細を確認しましょう。

$ 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 '' hello

80

サービスのスケール

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

$ docker service scale hello=10

hello scaled to 10

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

$ docker service inspect --format '' hello

10

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

$ 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

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

$ 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 ホストのメンテナンスも簡単ですね!

補足

続編

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

 

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

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

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

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