Amazon EC2 Container Service 実践入門の前に

この記事はサーバーワークス Advent Calendar 2016の24日目の記事です。
qiita.com


お久しぶりの更新です。お元気でしたか?サーバーワークスの中の人でもOBでもありませんが、個人的に仲良くしていただいているので、参加してみることにしました。中の人達と違って芸人的な要素はないですが、お手柔らかによろしくお願いします。

はじめに

この題材を選んだのは、最近ようやくDockerを使って何かをしてみようかと思い始めたのがきっかけです。ハマるポイントがあればサービスでも使っていきたいなと思っているので、基礎から始めたいと思います。

そして、サーバーワークスさんとは切っても切れないAWS、自分もとある書籍を共著させていただいているAWSということで、「Amazon EC2 Container Service (ECS)」をほぼ初めて触ってみます。

Amazon EC2 Container Service (ECS)

EC2上で利用できるDockerのクラスタリングサービス。ELBやAutoScalingなど他のサービスとの組み合わせで、いい感じにDockerを用いたシステムを構築することができます。

ひとまずGetting Startedをやってみる

リポジトリ設定

何はなくとも、Management ConsoleでECSのメニューを開きましょう。

f:id:matetsu:20161223174527p:plain

このような画面が出てきますので、「Get Started」しちゃいましょう。(注)言語は英語になっています

f:id:matetsu:20161223174843p:plain

サンプルアプリを使ったECSとイメージ配置用のプライベートリポジトリであるECR(EC2 Contaier Registry)の設置をしてしまうか?と聞かれるので、チェックを入れたまま進みましょう。

f:id:matetsu:20161223175902p:plain

リポジトリ名を聞かれるので、好きな名前を入力します。ここでは、namespaceとして「matetsu」を、リポジトリ名としてはひとまず「ecs-sample」として作っています。

f:id:matetsu:20161223181405p:plain

設定が完了すると、ECRへのログイン方法やイメージのビルド、からのECRへのイメージの登録方法が表示されます。作業をしているPCでDockerやAWS CLIのインストールや初期設定がしてない場合は、ここでやってしまいましょう(本記事では省略)。AWS CLIは特に設定済みであっても、なるべく常に最新版を利用するようにしておきましょう。

表示された手順に従ってコマンドを実行していきましょう。

イメージの作成とアップロード (ちょっとCLI操作)

(1) docker loginコマンド(認証情報付き)の取得
$ aws ecr get-login --region ap-northeast-1
docker login -u [ユーザ名] -p [めちゃくちゃ長いパスワード] -e none https://[ACCOUNT_ID].dkr.ecr.ap-northeast-1.amazonaws.com

こんな感じで出力されますので、出力をコピーして実行しましょう。

(2) docker loginコマンドの実行
$ docker login -u [ユーザ名] -p [めちゃくちゃ長いパスワード] -e none https://[ACCOUNT_ID].dkr.ecr.ap-northeast-1.amazonaws.com
Login Succeeded

「Login Succeeded」と出力されればログイン成功です。

(3) Dockerfileからイメージを作成


超簡易的だけど、Dockerfileはこんな感じ。

FROM amazonlinux:latest

RUN yum update -y && \
      yum install nginx -y && \
      date +%s > /usr/share/nginx/html/index.html && \
      yum clean all && \
      rm -rf /var/cache/yum

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]

これを使ってイメージ作成。

$ docker build -t matetsu/ecs-sample .
Sending build context to Docker daemon 6.144 kB
Step 1 : FROM amazonlinux:latest
latest: Pulling from library/amazonlinux
8e3fa21c4cc4: Pull complete
Digest: sha256:f1d4ae3f7261a72e98c6ebefe9985cf10a0ea5bd762585a43e0700ed99863807
Status: Downloaded newer image for amazonlinux:latest
 ---> 5b52b314511a
Step 2 : RUN yum update -y &&       yum install nginx -y &&       date +%s > /usr/share/nginx/html/index.html &&       yum clean all &&       rm -rf /var/cache/yum
 ---> Running in b889b04f755b
(yum途中経過省略)
 ---> bad3749e433c
Removing intermediate container b889b04f755b
Step 3 : EXPOSE 80
 ---> Running in a6ee101a6b56
 ---> 44a602686c42
Removing intermediate container a6ee101a6b56
Step 4 : CMD nginx -g daemon off;
 ---> Running in 7e740836ca4e
 ---> 8c9087605ba1
Removing intermediate container 7e740836ca4e
Successfully built 8c9087605ba1

$ docker images                                                                                                                                                                  [~/works/.../ecs/test1]
REPOSITORY              TAG                 IMAGE ID            CREATED              SIZE
matetsu/ecs-sample      latest              8c9087605ba1        About a minute ago   385.1 MB
amazonlinux             latest              5b52b314511a        4 weeks ago          292.3 MB
(4) イメージのタグ付け
$ docker tag matetsu/ecs-sample:latest [ACCOUNT_ID].dkr.ecr.ap-northeast-1.amazonaws.com/matetsu/ecs-sample:latest
(5) イメージをリポジトリへpush
$ docker push [ACCOUNT_ID].dkr.ecr.ap-northeast-1.amazonaws.com/matetsu/ecs-sample:latest

ECSに関連する項目の設定

再びGUIでの操作に戻ってきました。次はTask Definitionの設定です。

f:id:matetsu:20161223215532p:plain

「Advanced Option」なるものもあるようですが、今回はすべてデフォルトで行ってみようと思います(ここで設定しているのはDocker Composeのcompoe.ymlで定義しているものと同じような感じなんですかね)。次はServiceの設定です。

f:id:matetsu:20161223220734p:plain

基本はデフォルトですが、ELBは使っておきたいので「No ELB」から「sample-app:80」に変更しておきます。次はクラスタの設定です。

f:id:matetsu:20161223221124p:plain

基本デフォルトで、コンテナインスタンス(Docker Host)用のRoleは新しく作成することにしています。これで設定は終わりで、後は設置項目の確認とLaunch待ちです。

f:id:matetsu:20161223222331p:plain

ECSとEC2周りのリソースが出来上がるとこんな感じになります。

出来上がったリソースなどの確認

サービスの確認

もろもろのリソース作成が完了下画面に、「View Service」なんてボタンがあるわけですので、早速確認してみましょう。

f:id:matetsu:20161223222950p:plain

Serviceの詳細ページが表示され、Taskが実行中であることが確認できます。

クラスタの確認

他にも、クラスタの一覧はこんな感じで、

f:id:matetsu:20161223223623p:plain

クラスタの詳細はこんな感じになっています。

f:id:matetsu:20161223223736p:plain

実際にECSで稼働しているwebページにアクセスしてみる

かんたんに確認するために、起動されたELBのDNS名にアクセスしてみましょう。

f:id:matetsu:20161223224113p:plain

イメージを作成したときのdate +%sの実行結果であろう値が確認できました。

Getting Startedを使ったからナビがあってそれに従えば必要なものがすべて準備されたのでよかったけど、ナビがないとなると結構大変な作業ですね、これ。。。

rolling updateをしてみる

ただ動かすだけだと何もしていないことになるので、まずはrolling updateを試してみましょう。

rolling updateをする場合は、ServiceのDeployment OptionにあるminimumHealthyPercentmaximumPercentが肝になるようです。 (参照: AWS Black Belt Online Seminar 2016 Amazon EC2 Container Service /
Amazon ECS launches new deployment capabilities; CloudWatch metrics; Singapore and Frankfurt regions | AWS Compute Blog )

  • min:50% / max: 100%

- リソースを効率的に使いながら、まずはTaskをDesiredの半分に減らして新規TaskをDesiredの半分数追加し、残りの半分を入れ替える

  • min: 100% / max: 200%

- Desiredと同量のTaskを一気に追加し、倍量になったところで古い方を一気に落とす

ひとまず、1つしかTaskを動かしていないので、後者の富豪型(現状の設定もそれ)でやってみることにする

Docket Imageの更新

更新されたことがわかるようにイメージをこのようにする。

FROM matetsu/ecs-sample:latest

RUN yum update -y && \
      yum install nginx -y && \
      date +%s > /usr/share/nginx/html/index.html && \
      echo " updated" >> /usr/share/nginx/html/index.html && \
      yum clean all && \
      rm -rf /var/cache/yum

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]

timestampが変更になるのと、「 updated」をいう文言を追加している。

$ docker build -t matetsu/ecs-sample:0.2.0 .
(略)

新イメージができたので、先程のようにECRにpushする。

$ docker tag matetsu/ecs-sample:0.2.0 [ACCOUNT_ID].dkr.ecr.ap-northeast-1.amazonaws.com/matetsu/ecs-sample:0.2.0                                                              [~/works/.../ecs/test1]
$ docker push [ACCOUNT_ID].dkr.ecr.ap-northeast-1.amazonaws.com/matetsu/ecs-sample:0.2.0

Task Definitionの新revisionの追加

Task Definisionに新しいRevisionを追加することでrolling updateを実行することができる。が、Container Instanceが1台のままだとポートが衝突してしまうためにコンテナを起動できない。

f:id:matetsu:20161224003650p:plain

まずはInstanceのDesired Countを2にしておく。

f:id:matetsu:20161223233817p:plain

このあとでTask DefinitionのRevision追加する。対象のTask Definitionを選択して「Create new revision」。

f:id:matetsu:20161223234139p:plain

Containerの部分のリンクをクリックすると、コンテナイメージの指定などを変更できる。

f:id:matetsu:20161224004824p:plain

イメージのタグを変更し、revisionの変わったTask Definitionを作成する。

f:id:matetsu:20161223234333p:plain

Serviceの更新

追加したTask DefinitionのRevisionを適用するために、Serviceの更新を行います。

f:id:matetsu:20161223234439p:plain

Task Definitionを「console-sample-app-static:[最新のrevision]」(色々と試行錯誤で作り直したので2ではない)に変更して適用します。Min/Maxは富豪型にするため、変更していません。

f:id:matetsu:20161223234725p:plain

これで、しばらくまっていると、新旧両方へのアクセスができる状態になり、更に待つと古い方は表示されなくなります。この間にサービスダウンは発生しないので、実際のサービスに適用するときもうれしいですね。

f:id:matetsu:20161224010555p:plain

今回は状況ややっていることを確認しながらの実施だったので手動で行いましたが、手動での適用は事故を起こしたり時間がかかってしまうので、実際に運用する場合はコマンドラインスクリプトなどを用いて事故が起こらないようにする必要があるでしょうね、

最後に

まだまだほんの初歩的な使い方をしてみただけで全然実践的な内容になっていませんが、まずは触ってみることでDockerイメージさえ整えば何かしらサービスにおm利用できそうなイメージが湧いてきました。もう少し深く使ってみて、続編がかけるようにしたいと思います。ECSについてはriywoさんのBlackBeltのスライド、Dockerについては前佛さんのスライドを見てもっと勉強します。

P.S. 反省点

ほとんどDockerを触ってこなかったのでここまでやるだけでも結構大変だったのですが、初心者の割にDockerの基本的な部分をすっ飛ばして書いてしまったのは反省してます。また、基本的にはGetting Startedやっただけで、おまけ的にrolling updateを付け足した感じになってしまったのも、コンテンツとしてはちょっと物足りな鋳物になってしまったと思っています


ただ、明日の最終日は最近話題のkokexaさんが鉄板ネタで攻めてきてくださるので、素敵な締めに期待したいと思います。