Blox Introduction

Dec 9, 2016   #Blox  #ECS  #docker  #AWS 

この記事はDocker Advent Calendar 2016の9日目の記事です。

先日AWSのre:Invent 2016でBloxが発表されました。

BloxはEC2 Container Service(ECS)関連のオープンソースのツール群のことです。

そしてそのツールとは主にECSのカスタムスケジューラを指します

ECSはマネージドなスケジューラとマネージャを標準で備えていますが、Bloxはそれとは別に自分でホスティングする必要があります。

しかし、ECSに足りない機能を補ってくれるため導入するメリットは大きいでしょう。

先日リリースされた、CloudWatchEventsのECSイベントストリームを利用することで、よりスムーズにECSのクラスタの状態を監視してカスタムスケジューラを作ることができるようになりました。

Bloxはこれを使った一例と言えます

この記事ではBloxについて試してみて分かった内容や所感について書いていきます

Blox thumbnail

Bloxの主な機能

Bloxが提供している機能は現在以下の2つです

  • cluster-state-service
  • daemon-scheduler

これらの機能をREST APIで扱うことができます。

どのような仕組みで動いているのかはAWS公式ブログにて分かりやすい図が載っていますのでまずはこちらを読むことをおすすめします

この記事ではその辺りには特に触れません。主に使い方などをまとめました。

今後の追加機能に関してはロードマップが作られていますのでそちらを参照してください

それではそれぞれの機能について見ていきます

cluster-state-service

ECSの課題

ECSには様々なAPIが用意されており、コンテナインスタンスやタスクの情報などを取得することができます。

しかし、痒いところには手が届きません。

例えば、あるクラスタで動いている特定のタスクの一覧を取得したいときにECSのAPIだとあるクラスタで動いているタスクの一覧を取得し、そこから取得したいタスクをフィルタリングする…のような手順が必要で面倒です。

cluster-state-serviceが提供するもの

cluster-state-serviceはECSクラスタの状態を取得するためのものです。

冒頭でも書いたように、CloudWatchEventsのECSイベントストリームを利用できるようになりましたが、ECSのAPIからはそれらの情報は取得できません。

cluster-state-serviceはこのECSイベントストリームの情報を内部に保存し、外から参照できるREST APIを提供します。

cluster-state-serviceに関しては私はあまり理解できていませんが、クラスタ内の状態をより詳細に取得するための扱いやすいAPIというイメージです。

今のところGETするAPIしか提供していませんが、今後ECSのクラスタの状態を変更するためのAPIなども提供されるかもしれません。

また、後述のdaemon-schedulerでも内部的にcluster-state-serviceを使っているようです。

APIドキュメント

APIのエンドポイントは特にドキュメントがなかったのでswaggerから読む必要があります。

go-swaggerを使ってブラウザで表示すると読みやすいと思います

以下の場所にswagger.jsonが置いてあります

$ swagger serve cluster-state-service/handler/api/v1/swagger/swagger.json

daemon-scheduler

ECSの課題

ECSにはサービスという概念があります。

常駐しておきたいコンテナ群(タスク)をサービスとして動かすことで、希望するタスクの数をクラスタ内で一定に保つことができます。

これは非常に便利なのですが、クラスタ内の全コンテナインスタンスに対して1つずつタスクを実行させたい場合は使うことができません。

ポートを静的に指定すれば1コンテナインスタンスに1つずつタスクが実行されるのですが、「コンテナインスタンスの数 = タスクの数」を常に維持しなければいけないため、標準機能だけではできません。

また、ポートを静的に指定するというところもナンセンスです

daemon-schedulerが提供するもの

daemon-schedulerはクラスタ内の全コンテナインスタンスに対して1つずつデーモンタスクを実行させるためのものです

例えば、dd-agentのような監視用コンテナを動かしたい場合、今まではUserdataを使ってインスタンスが立ち上がったタイミングで自分自身に対してECSのStartTaskを実行していましたが、daemon-schedulerを使えばその必要はなくなります。

クラスタ内に新たにコンテナインスタンスが立った場合、daemon-schedulerに設定しておいたTaskDefinitionを新たなコンテナインスタンスに対して勝手に実行してくれます。

APIドキュメント

こちらもAPIのエンドポイントは特にドキュメントがなかったのでswaggerから読む必要があります。

以下の場所にswagger.jsonが置いてあります

$ swagger serve daemon-scheduler/generated/v1/swagger.json

試す

※ 残念ながら試せたのはdaemon-schedulerのみです

セットアップ

Bloxが用意しているCloudFormationTemplateを使うとcluster-state-serviceとdaemon-schedulerが同時に動くようになっていますので同時にセットアップを行う形となります。

まずはBloxをAWSにデプロイします。

ドキュメントに従ってやっていきます 今回は本番を想定してローカルではなくAWS上で動かします。

/tmp/blox_parameters.jsonを用意します

EcsAmiIdは2016/12/07時点で最新のECS Optimized AMIを使います。ちなみに東京リージョンです

[
  { "ParameterKey": "EcsAmiId", "ParameterValue": "ami-9cd57ffd" },
  { "ParameterKey": "InstanceType", "ParameterValue": "t2.micro" },
  { "ParameterKey": "KeyName", "ParameterValue": "my-keypair" },
  { "ParameterKey": "EcsClusterName", "ParameterValue": "Blox" },
  { "ParameterKey": "QueueName", "ParameterValue": "blox_queue" },
  { "ParameterKey": "ApiStageName", "ParameterValue": "blox" }
]

それではデプロイします。

AWSの認証情報は適宜用意しておいてください。

$ git clone [email protected]:blox/blox.git
$ cd blox
$ aws --region ap-northeast-1 cloudformation create-stack \
      --stack-name BloxAws \
      --template-body file://./deploy/aws/conf/cloudformation_template.json \
      --capabilities CAPABILITY_NAMED_IAM \
      --parameters file:///tmp/blox_parameters.json

CloudFormation管理画面上でスタックのステータスがCREATE_COMPLETEになったらデプロイ完了です

CloudFormation Status

Bloxのエンドポイントを取得します。

$ aws --region ap-northeast-1 cloudformation describe-stacks \
      --stack-name BloxAws \
      --query 'Stacks[0].Outputs[0].OutputValue' \
      --output text
https://kiwozuwzpf.execute-api.ap-northeast-1.amazonaws.com/blox

BloxのAPIを使うには認証が必要です。

BloxはAPI Gatewayを経由してAWSのIAM認証が可能です。

お使いのIAM Userに必要な権限がアタッチされていない場合はこちらを参考に設定してください

認証にはAWS SignatureをサポートしているAPIクライアントのPostmanを使うと便利です

Authenticate with Postman

APIのテストとしてdaemon-schedulerのエンドポイントである/v1/pingを叩いてみます

無事200 OKが返ってきました

API Test

これでBloxのセットアップは完了です

cluster-state-service

Bloxが提供しているCloudFormationTemplateを読むと、cluster-state-serviceはELBと連携していないので、外部からのApiGateway経由ではアクセスできません。

おそらくクラスタ内部で扱うものだと思いますので今回は特に試しませんでした。

Internal ELBにも紐付いていないため、APIを使うとしたらBloxと同じタスク内で使うことになるかと思います。

daemon-scheduler

まずはBloxクラスタとは別に任意のアプリケーションなどを動作させるためのクラスタを準備します。

ecs-cliを使うと楽です。

以下のようなyamlを用意します

検証用に用意しただけなので、どのようなコンテナでも構いません

# docker-compose.yml
version: '2'
services:
  web:
    image: "nginx:alpine"
    ports:
      - "80:80"

以下のコマンドでクラスタを作成します

$ ecs-cli up --keypair my-keypair --capability-iam --size 1

クラスタが作成できたら、TaskDefinitionの登録と実行をします

$ ecs-cli compose up

無事、クラスタ内でタスクが実行されました

Setup ECS cluster

さて、それではdaemon-schedulerを触っていきます。

Bloxのリポジトリ内にはAPIを叩くための便利なクライアントが用意されているのでそれを使います。

まずはEnvironmentを作成します

Environmentで定義するのは、どのクラスタでどのタスクを動かすか、です。

$ ./blox-create-environment.py --apigateway
== Blox Demo CLI - Create Blox Environment ==

- Enter CloudFormation stack name: BloxAws
- Enter Blox environment name: my-environment
- Enter ECS cluster name: tsub-sandbox
- Enter ECS task definition arn: ecscompose-ecs-cli


HTTP Response Code: 200
{
  "deploymentToken": "e22fadc7-3bbd-4de4-b28b-ecafae2c3826",
  "health": "healthy",
  "name": "my-environment",
  "instanceGroup": {
    "cluster": "arn:aws:ecs:ap-northeast-1:000000000001:cluster/tsub-sandbox"
  }
}

そして、先ほど作ったEnvironmentを使ってDeploymentを作成します。

Deploymentを作ることで対象のクラスタで実際にタスクが実行され始めます。

$ ./blox-create-deployment.py --apigateway
== Blox Demo CLI - Create Blox Deployment ==

- Enter CloudFormation stack name: BloxAws
- Enter Blox environment name: my-environment
- Enter Blox deployment token: e22fadc7-3bbd-4de4-b28b-ecafae2c3826


HTTP Response Code: 200
{
  "status": "pending",
  "environmentName": "tsub-environment",
  "id": "66fee44a-63c3-494e-aa34-476f14b4c4e3",
  "failedInstances": [],
  "taskDefinition": "arn:aws:ecs:ap-northeast-1:000000000001:task-definition/ecscompose-ecs-cli:1"
}

試しにコンテナインスタンスの数を増やしてみましょう。

Increament cluster instance

新たに追加されたコンテナインスタンスでタスクが実行され始めました 👏

daemon-scheduler DEMO

このように、特に何もしなくともコンテナインスタンスが追加されるたびにdaemon-schedulerが自動で対象のタスクを実行してくれるようになります。

所感

まさにECSの不満な部分を解決する救世主が現れた感じですね。

まだまだドキュメントや利用例が充実していないため、今回は手探りで使ってみたので間違ってるところなどあればご指摘いただければと思います。

特に使ってみた感触として、今回はBloxとアプリケーションは別々のクラスタで動かしましたが、Bloxを動かすクラスタはアプリケーションと同じクラスタを想定しているような気がしていて、その辺り引っかかっています。

今後の開発に期待ですね 😄