ECSで困ったときに読むための俺的Q&A

みなさんこんにちは

ここ一年でdockerを使った開発を進めた結果、ついには「デプロイもコンテナイメージでやったらええやん!」みたいに思い、そうするとAWSでECSというサービスがあるじゃあないですかとなって、実際に環境構築を試みたわけです。
謳い文句には「一瞬でできる!」とか「容易に」とか書かれていますが、やっぱり実際にはなれるまで時間がかかるわけです。
web上に転がっている情報は、どういうわけかaws cliの情報ばかりなので、超初心者であり普通のコンソール使おうとしている私にとっては、まあ、解読が難しいわけです。
そうしていると、変なノウハウが溜まっていくわけで、せっかくなのでこっちの方に書きなぐってしまおうと考えたわけです。

というわけでほぼ自分用のECSのQ&Aを作りました。
ただのバッドノウハウになっている可能性もありますが。

概念編

AWSの日本語って、単純に訳したかのような、ちょっと見慣れない or 聞きなれない表現があったりして、こちらの混乱を誘います。
そういう場合は一生懸命英語を読むわけですが、メモっておいたほうがいいかなと思いましたので、ここに記載しておこうと思います。

ECS (EC2 Container Service)

dockerコンテナを通して処理をしたりサービスを立ち上げたりと行った挙動をEC2上で容易に行うためのAWSのサービスです。
EC2上での操作は全部AWSでやってくれるので、こっちはコンテナイメージの作成に注力できるし、動作する際のパラメータも任意に決められます。
ちゃんと設定できればコンテナイメージを作ってそれをアップするだけで、簡単にアプリケーションがデプロイできる、みたいなシステムを構築することもできます。

ECR (EC2 Container Registry)

ECSはコンテナを操作するサービスだったのに対し、こちらはコンテナイメージをprivateな環境に格納しておけるサービスです。
また、当然ではありますが、ECSと連携することができて、ECSで使うコンテナイメージをECRから引っ張ってくるという使い方ができます。
私の場合、ECSでプライベートリポジトリを使う場合、ECRから取得する方法しか使ったことがないです。

ただ、ここにプッシュするためにはaws-cli の使用が必要になるようで、使ったことない人には敷居が高いです。

クラスター

ECSでコンテナを動かすためのホストマシンの集合体です。
クラスターを作るときに実際にホストマシンのサイズなどが指定でき、クラスターを作成するとAWSが提供するコンテナを動かすのに適したAMIでEC2インスタンスが起動します。

ちなみにこの時クラスターが持っているEC2のインスタンスのことをコンテナインスタンスって読んでいるようです。

タスク定義 (Task Definition)

タスク定義と言うのはアプリケーションの動作を一つ以上のコンテナで表現したものです。
ナンノコッチャというと、例えばWEBサーバの挙動を定義したければ、nginx+php-fpmの入ったコンテナを持ってきて、起動させてしまえばいいという感じです。

このとき、nginxとphp-fpmは別のコンテナで定義させておき、nginx -> php-fpmのリンクを設定することでも同じようなタスクを表現できます。
コンテナはアプリケーションの最小構成単位にまで落とし込んだほうがいいらしいので、分けられるのなら分けちゃったほうがいいかもですね。

リビジョン

タスク定義にはリビジョンというものが存在しています。タスク定義の変更は、新しいリビジョンを作成することによって行うことができます。
こいつの利点は、任意のリビジョンのタスクを実行できる点にあり、例えば間違った設定をして突如タスクが動かなくなってしまった場合は、一つ前のリビジョンを動かすことで、タスクの停止時間を抑えることができます。

サービス

クラスターの中の適当なインスタンスでタスクを起動させてもいいのですが、スケーリングとかELBの設定とかをいちいちやっているのは面倒くさいですよね。
そんなとき、各コンテナインスタンスにいい感じにタスクを配置させたり、ELBとの接続の設定を管理してくれたり、負荷が上がったときにスケーリングしてくれたりする機構がサービスとなります。
サービスにはタスクを一つ設定できますが、この時設定するタスクはリビジョン付きで設定します。

サービスは、タスクを更新 (=設定されているタスクのリビジョンを変更) した場合、突然いま動作している古いタスクを落とすわけではなく、新しいリビジョンのタスクを起動しつつ、必要タスク数を下回らないように注意しながら古いリビジョンのタスクを停止させていきます。
いわゆるブルーグリーン方式で、一時的に新旧のタスクを共存させることで、アプリケーションが一瞬でも完全停止しないように気を使っています。

突発的な作業のためには裸のタスク定義を使い、WEBサーバなど継続して動作すべきタスクに対してはサービスを作ってあげるといいと思います。

トラブルシューティング編

私が実際に動かしていて発生したトラブルに、どのように対応していったかを書き並べていきます。
事細かな詳細を書くこともできないので、ちょっとふわっとしています。
あと、大事なことですが、AWS マネジメントコンソール上での話です。

あれが削除できない

間違って作っちゃったものを削除してなかったことにしたいのは人間の心理ですが、わりとハードルが高いので困りものです。
え?そもそも削除しちゃだめ?ははは

タスク定義が削除できない

タスク定義が削除できない場合があります。
タスク定義を削除するためには、

  1. 有効なリビジョンが存在しない
  2. 全部のタスクが停止している

状態でなければなりません。
というわけで、クラスター上でまだ残っているタスク定義がないか調べた上で、動いているものがあったら停止していきましょう。
タスクが勝手に起動して停止できない!という場合はもう少し後ろの項を参照してください

サービスが削除できない

サービスは自身が管理しているタスクが起動していると、削除できません。
起動しているタスクを停止させてから削除しましょう。

やっぱり タスク or サービスが削除できない

タスク全部停止したのに、やっぱり削除できないやんけ!ってときは、もしかしたらタスクが自動的に起動している可能性があります。
サービスは自身が管理するタスクが停止し、必要タスク数を下回ってしまうと、自動的にタスクを起動して必要タスク数を満たすように努力します。
よって、一旦サービスの必要タスク数を0にしてから、起動中のタスクを落としていくようにすれば問題なしです。

クラスターが削除できない

VPCの設定でミスったり、よくわからないけど動かなかったりして、とりあえず今作ったクラスターを削除したいんだけど、どういうわけだか削除できない場合があります。

私の場合、クラウドフォーメーションの削除処理のときにセキュリティグループがほかから参照されていて削除できないという状況になっていました。

クラスターのコンテナインスタンスもセキュリティグループを持っており、同じVPC上のDBとかキャッシュを参照したいときにセキュリティグループのアクセス許可設定をするわけですが、そうしているとコンテナインスタンスに設定されているセキュリティグループが削除できず、結果としてクラスターも削除できないという状況になります。
他のセキュリティグループからコンテナインスタンスのセキュリティグループを手動で削除してからクラスターを削除してみましょう。

タスク定義が原因のトラブル

タスク定義って単純にdocker run するようなものだから、簡単ですよね?…というわけにも行かず、やっぱりローカルとは違うようねって思う今日このごろ
タスク定義で発生し得るミスなどを追ってみましょう。

タスクが一つしか起動できない

タスクなんてコンテナの集まりなのだから、リソースがある限りいくつも起動できるようにして欲しいところですが、実際に動かしてみると、コンテナが一つしか動いてくれないということがママあります。
私の場合は、コンテナ上のポートマッピングの際に、ホストに固定のポートを指定していることが原因でした。
同じタスクを起動させようとしても、すでにホストのポートが使われているので、起動できないというやつですね。
docker runでmysqlを別に2台立ち上げたときに、私も遭遇したことがあります。

こんなとき便利なのはポートの設定にダイナミックポートを設定してしまうことです。こうすることで、ホストの開いているポートを使ってポートマッピングしてくれます。
ダイナミックポートの設定方法は、コンテナのポートマップ設定のときに、ホストポートに0もしくは空欄にしておくことで、設定可能です。

タスクが動かない原因がわからない

タスクが起動できたかもしれないけど、いつの間にか落ちているっていうときは、ログを調べるべきなのですが、どんなふうにログを出したらよいかわからない方もいると思います。
コンテナログを簡単に出すならば、CloudWatchのログ機能を使うと、追跡がしやすいです。

まず、マネジメントコンソール上でCloudWatchのログ->アクション->ロググループの作成で適当なロググループを作っておきます。
次にタスク定義のコンテナのログ設定で、ドライバーを「awslog」にします。
すると、ロググループとリージョンを設定する項目が追加されるので、自分のリージョンと先程作成したロググループを入力して、リビジョンを作成します。
これで、新しいタスクを起動すると、ログが出力されるので、これを元にトラブルを解決しましょう。

サービスが更新されたのに、古いリビジョンのタスクが走り続けている

これはいくつか原因が考えられるので、コンソールのECSのサービスから、直近のイベントを眺めてみましょう。
サービスに設定されているリビジョンはすでに新しいリビジョンに更新されているにも関わらず、古いリビジョンが走り続けている場合は、新しいリビジョンのタスクが何らかの原因で起動していない可能性があります。
先に述べたとおり、サービスは私の場合、タスク( コンテナ )自体がエラーを出すときと、古いタスクがポートを専有しているため、新しいタスクが起動できないという2つの原因がありました。

ポートの問題に関しては上述したダイナミックポートを使い、コンテナ自身のエラーについては

サービスが原因のトラブル

サービスが原因になることってそんなにないのですが、ないわけではありません。

タスク定義が更新されたけど、古いタスクが走り続けている

タスク定義を更新してリビジョンが新しくなっても、サービスに設定されているタスクのリビジョンを変えなければ、サービスが管理しているタスクは更新されません。
サービスに設定されているタスクも更新しましょう。

古いタスクと新しいタスクが同時に走っている

サービスは新しいタスクに切り替えるときに、外見上アプリが完全停止しないように、新しいタスクが立ち上がってロードバランサに登録されるまでは、古いタスクを走らせ続けます。
これにより、アプリのダウンタイムを0にするだけでなく、新しいタスクが何らかの異常で停止した場合は、古いタスクをそのまま走らせ続けることで、長時間のアプリ停止を防ぐことができます。

まとめ

せっかくdockerで開発環境構築しているのなら、やはりデプロイもコンテナでやりたいですよね。
とりあえず身近で使いやすいコンテナ運用環境としてECSがあるので、どんどん使っていきたいです。

ちょっとストレージの部分がまだわからなかったりするので、そのへんは勉強しなきゃなぁと思っています。

今回はこんなところです。

参考

ECS
ECSのダイナミックポートについて

続きを読む

Dockerコンテナデプロイサービスの比較 (Beanstalk Multi-container Docker/ECS)

概要

最近Dockerコンテナでシステムの本番運用を始めました。その際にデプロイサービスとして検証したのがBeanstalk Multi-dockerとECS。今回は実際に両方触ってみて、両者のメリット・デメリットをまとめます。
尚、調査する上で、ECSで動作するRails コンテナ (Amazon Linuxイメージ) を作りました。ソースは GitHub で公開しています。

機能比較

Beanstalk、ECS共にDockerコンテナを動かすことはできますが、機能比較するとこんな感じです。

機能  Beanstalk ECS
EC2インスタンスの作成
EC2セキュリティグループの割当 アプリケーション環境単位で生成 既存のセキュリティグループを指定可能
EC2オートスケーリング
ELB (CLB) の作成
ELB (ALB) の作成
ローリングデプロイ
ブルーグリーンデプロイ
(OSS)
アラート通知
環境作成のエラー・ヘルスステータスの変化等を通知
ログ管理
RDSインスタンスの生成

Beanstalkの特徴

  • 環境構築が楽。EC2+RDSという一般的なアプリケーションであればBeanstalkコンソールでほぼ完結
  • コンテナを用いないアプリケーションも構築可能
  • Multi-container Dockerを利用すると、裏でECSが動く (ECSを意識しなくてもアプリケーションが動く)

Beanstalkはとにかく環境構築が簡単。ウィザードに従ってうっかり Create environment なんて押すもんならいきなりインスタンス起動してアプリケーションが動き出す。間違えて3回くらい押した。

beanstalk.jpg

Beanstalkは環境構築が簡単な反面、裏で何のサービスが連動しているのか分かりにくい側面もあります。障害が発生した時、問題の切り分け (コンテナが原因なのか、ECSか、それともBeanstalkの問題か) が困難となる印象です。
ちなみにBeanstalkでもELBは利用できますが、ELB利用時にセッションストレージとしてよく使うであろうElastiCacheはBeanstalkのコンソールからは連携できないようです。

あと地味に便利だと思ったのはCloudWatchへのログ転送。Lambdaのように自動で各種ログをCloudWatchに流してくれる。恐らく裏でCloudWatch Logs Agentが動いてると思われる。

Beanstalkには色々と良い面もあるのですが、個人的にはセキュリティグループが謎の命名規則によって環境ごとに生成されまくるのが嫌という理由でECSの検討を始めました。

ECSの特徴

  • Dockerコンテナのデプロイに特化
  • ALBが使える
    • CLBと比較してコストが安くパフォーマンスも高い
    • WebSocket、HTTP/2対応
    • L7ルーティング対応
    • 動的ポートマッピング対応
  • Dockerの新機能はBeanstalkより早く取り込まれる

ECSを利用する場合、Beanstalk Multi-container Dockerが裏でやってくれていた仕事 (クラスタの構築、タスクの作成、サービスの作成) を自前で準備する必要があります。これが結構面倒です。CloudFormationでテンプレート書けば良さそうですが。

またECS特有の「タスク」や「サービス」の仕組みを理解する必要がありますが、概念さえ理解してしまえば、コンテナに対するリソース制限からネットワーク設定まで幅広い制御が可能です。

もう一点、ECSの特徴としてALBが使えることは大きなメリットですが、中でも動的ポートマッピングが良い感じ。
CLBの場合、1ホスト (EC2 Container Instance) につき1コンテナしかポートマッピングできなかったのが、ALBの利用によって複数マッピング可能となりました。これによって、1ホストで複数のWebアプリケーションを動かすことが可能となります。
また、今までELB利用時は2台のインスタンスを分散配置させる必要がありましたが、ALBはポート単位でコンテナと接続するため、1ホストだけ配置しておけば、あとはよしなにリクエストを分散してくれるようになります。

ちなみにECSはいくつかデプロイツールが公開されてます。これも機能を比較してみました。

パッケージ  GitHub Watch GitHub Star GitHub Fork 特徴
aws/amazon-ecs-cli 67 491 79 AWSオフィシャル。Docker composeファイルが利用可能
silinternational/ecs-deploy 31 473 154 サービスのデプロイに特化
stormcat24/ecs-formation 8 78 9 ブルーグリーンデプロイが可能

ecs-cliはクラスタの設定を ~/.ecs/config に書き込むイマイチ仕様 (AWSのアクセスキーまで書き込む) だったので、今回はecs-deployを採用。
最近はDocker CI環境を構築し、ecs-deployをラップしたデプロイツールでコンテナの本番運用を始めてます。
次のフェーズとしてデプロイの自動化を検討しているので、これについてはまた次回書こうと思います。

続きを読む

[JAWS-UG CLI] AWS Batch #1 環境構築(Managed 編)

前提条件

EC2への権限

EC2、ECS、AWS Batch などに対してフル権限があること。

0. 準備

0.1. AWS CLIのバージョン

以下のバージョンで動作確認済

  • AWS CLI 1.11.36
コマンド
aws --version
結果(例)
 aws-cli/1.11.36 Python/2.7.5 Darwin/13.4.0 botocore/1.4.93

バージョンが古い場合は最新版に更新しましょう。

コマンド
sudo -H pip install -U awscli

0.2. リージョンの決定

JAWS-UG CLI 支部では「US-EAST-1」は非推奨ですが、2017 年 1 月現在、AWS Batch が GA になっているのはここだけなので、今日だけと念じてリージョンを指定してください。

変数の設定
export AWS_DEFAULT_REGION='us-east-1'

0.3. 資格情報の確認

プロファイルが想定のものになっていることを確認します。

変数の確認
aws configure list
結果(例)
      Name                    Value             Type    Location
      ----                    -----             ----    --------
   profile                  default           manual    --profile
access_key     ****************XXXX shared-credentials-file    
secret_key     ****************XXXX shared-credentials-file    
    region                us-east-1              env    AWS_DEFAULT_REGION

0.4. AWS アカウントの属性

EC2-Classic が見えない AWS アカウントであること。

コマンド
AWS_SUPPORT_PLATFORMS=$( \
         aws ec2 describe-account-attributes \
           --query 'AccountAttributes[?AttributeName == `supported-platforms`].AttributeValues[].AttributeValue' \
           --output text \
) && echo ${AWS_SUPPORT_PLATFORMS}
結果
 VPC

注釈: VPC の他に EC2 が表示される場合、別のアカウントを作成してください。

0.5. VPC / IAM ロールの作成

CloudFormation テンプレートのファイル名を定義

コマンド
CFN_STACK_NAME="aws-batch-`date +%s`"

テンプレートの取得

コマンド
curl --location --output ${CFN_STACK_NAME}.yaml \
  https://raw.githubusercontent.com/supinf/aws-batch-refarch/master/cloudformation/base.yaml

CloudFormation スタックの生成

コマンド
aws cloudformation create-stack \
  --stack-name ${CFN_STACK_NAME} \
  --template-body file://${CFN_STACK_NAME}.yaml \
  --capabilities CAPABILITY_IAM
結果(例)
{
    "StackId": "arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/aws-batch-xxxxxxxxxx/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}

このスタックにより以下のリソースが作成されます

  • AWS Batch 用 VPC
  • AWS Batch 用サービスロール
  • SpotFleet 用サービスロール
  • EC2 インスタンスプロフィール
  • EC2 インスタンス用セキュリティグループ
  • Job 用 IAM ロール

0.6. EC2 キーペアの作成

以下の手順を参考にキーペアを作ります。
ただし AWS_DEFAULT_REGIONus-east-1 のままで・・

[JAWS-UG CLI] EC2:#1 キーペアの作成 (新規)

キーペアの確認

コマンド
aws ec2 describe-key-pairs \
  --key-names ${EC2_KEY_NAME}

指定したキーペアの情報が返ることを確認します

結果(例)
{
    "KeyPairs": [
        {
            "KeyName": "your-keypair-name", 
            "KeyFingerprint": "xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx"
        }
    ]
}

0.7. jq や jsonlint のインストール

最新の aws-cli には jp.py が入っていない気がします・・
というわけで jqjsonlint をそれぞれ入れておいてください。

1. クラスタ起動

1.1. コンピューティング環境名の指定

環境名の指定

コマンド
COMPUTE_ENV_NAME="aws-batch-managed-`date +%s`"

同名の環境がないことを確認

コマンド
aws batch describe-compute-environments \
  --compute-environments ${COMPUTE_ENV_NAME}
結果
{
    "computeEnvironments": []
}

1.2. 利用する VPC / IAM の変数定義

スタックの生成を待機

コマンド
aws cloudformation wait stack-create-complete \
  --stack-name ${CFN_STACK_NAME}
結果
返り値なし

生成結果を取得

コマンド
CFN_STACK_RESULT=$( aws cloudformation describe-stacks \
  --stack-name ${CFN_STACK_NAME} \
) && echo ${CFN_STACK_RESULT} | jq .

スタックの生成結果(Output)から、必要な変数を抜き出します。

コマンド
BATCH_SERVICE_ROLE=$( echo ${CFN_STACK_RESULT} \
  | jq '.Stacks[].Outputs[]' \
  | jq -r 'select(.OutputKey=="AWSBatchServiceRole").OutputValue' )

EC2_INSTANCE_PROFILE=$( echo ${CFN_STACK_RESULT} \
  | jq '.Stacks[].Outputs[]' \
  | jq -r 'select(.OutputKey=="BatchInstanceProfile").OutputValue' )

EC2_SPOT_FLEET_ROLE=$( echo ${CFN_STACK_RESULT} \
  | jq '.Stacks[].Outputs[]' \
  | jq -r 'select(.OutputKey=="AmazonEC2SpotFleetRole").OutputValue' )

VPC_SUBNET_IDS='["'$( echo ${CFN_STACK_RESULT} \
  | jq '.Stacks[].Outputs[]' \
  | jq -r 'select(.OutputKey=="PublicSubnet1").OutputValue' \
  )'","'$( echo ${CFN_STACK_RESULT} \
  | jq '.Stacks[].Outputs[]' \
  | jq -r 'select(.OutputKey=="PublicSubnet2").OutputValue' )'"]'

VPC_SECURITY_GROUPS='["'$( echo ${CFN_STACK_RESULT} \
  | jq '.Stacks[].Outputs[]' \
  | jq -r 'select(.OutputKey=="SecurityGroup").OutputValue' )'"]'

1.3. コンピュータリソースの定義

コンピュータリソースを定義するファイル名の指定

コマンド
BATCH_RESOURCES_FILE="aws_batch_compute_resources.json"

変数の確認

コマンド
cat << ETX

    BATCH_RESOURCES_FILE: ${BATCH_RESOURCES_FILE}
    BATCH_SERVICE_ROLE:   ${BATCH_SERVICE_ROLE}

    EC2_KEY_NAME:         ${EC2_KEY_NAME}
    EC2_INSTANCE_PROFILE: ${EC2_INSTANCE_PROFILE}
    EC2_SPOT_FLEET_ROLE:  ${EC2_SPOT_FLEET_ROLE}

    VPC_SUBNET_IDS:       ${VPC_SUBNET_IDS}
    VPC_SECURITY_GROUPS:  ${VPC_SECURITY_GROUPS}

ETX

結果(例):


    BATCH_RESOURCES_FILE: aws_batch_compute_resources.json
    BATCH_SERVICE_ROLE:   arn:aws:iam::xxxxxxxxxxxx:role/service-role/aws-batch-xxxxxxxxxx-AWSBatchServiceRole-xxxxxxxxxxxx

    EC2_KEY_NAME:         your-keypair-name
    EC2_INSTANCE_PROFILE: arn:aws:iam::xxxxxxxxxxxx:instance-profile/aws-batch-xxxxxxxxxx-BatchInstanceProfile-xxxxxxxxxxxx
    EC2_SPOT_FLEET_ROLE:  arn:aws:iam::xxxxxxxxxxxx:role/aws-batch-xxxxxxxxxx-AmazonEC2SpotFleetRole-xxxxxxxxxxxxx

    VPC_SUBNET_IDS:       ["subnet-xxxxxxxx","subnet-xxxxxxxx"]
    VPC_SECURITY_GROUPS:  ["sg-xxxxxxxx"]

コンピュータリソース定義ファイルを生成します。

今回は m3.medium をスポットインスタンスで、入札価格の上限をオンデマンド価格に設定してみます。現在 Managed 環境では t2 系インスタンスが使えないためです。無料枠で収まらないのは無念ですが。

コマンド
cat << EOF > ${BATCH_RESOURCES_FILE}
{
  "instanceTypes": ["m3.medium"],
  "type": "SPOT",
  "bidPercentage": 100,
  "spotIamFleetRole": "${EC2_SPOT_FLEET_ROLE}",
  "desiredvCpus": 1,
  "maxvCpus": 1,
  "minvCpus": 1,
  "subnets": ${VPC_SUBNET_IDS},
  "securityGroupIds": ${VPC_SECURITY_GROUPS},
  "ec2KeyPair": "${EC2_KEY_NAME}",
  "instanceRole": "${EC2_INSTANCE_PROFILE}"
}
EOF
cat ${BATCH_RESOURCES_FILE}

コンピュータリソース定義ファイルの検証

コマンド
jsonlint -q ${BATCH_RESOURCES_FILE}

1.4. コンピューティング環境の作成

AWS Batch では、コンピューティング環境として MANAGED または UNMANAGED が選択できます。
MANAGED を選択すると、ECS クラスタも起動してくれるタイプ、
UNMANAGED は自分で ECS クラスタを用意するタイプです。

今回は MANAGED な環境を作成しましょう。

コマンド
aws batch create-compute-environment \
  --compute-environment-name ${COMPUTE_ENV_NAME} \
  --type MANAGED \
  --service-role ${BATCH_SERVICE_ROLE} \
  --compute-resources file://${BATCH_RESOURCES_FILE}

結果(例):

{
    "computeEnvironmentName": "aws-batch-refarch", 
    "computeEnvironmentArn": "arn:aws:batch:us-east-1:xxxxxxxxxxxx:compute-environment/aws-batch-refarch"
}

1.5. コンピューティング環境の確認

作成した環境の status が VALID
state が ENABLED であることを確認しましょう。

コマンド
aws batch describe-compute-environments \
  --compute-environments ${COMPUTE_ENV_NAME}

結果(例):

{
    "computeEnvironments": [
        {
            "status": "VALID", 
            "serviceRole": "arn:aws:iam::xxxxxxxxxxxx:role/service-role/aws-batch-xxxxxxxxxx-AWSBatchServiceRole-xxxxxxxxxxxxx", 
            "computeEnvironmentArn": "arn:aws:batch:us-east-1:xxxxxxxxxxxx:compute-environment/aws-batch-refarch", 
            "computeResources": {
                "subnets": [
                    "subnet-xxxxxxxx", 
                    "subnet-xxxxxxxx"
                ], 
                "spotIamFleetRole": "arn:aws:iam::xxxxxxxxxxxx:role/aws-batch-xxxxxxxxxx-AmazonEC2SpotFleetRole-xxxxxxxxxxxxx", 
                "tags": {}, 
                "desiredvCpus": 1, 
                "minvCpus": 1, 
                "instanceTypes": [
                    "m3.medium"
                ], 
                "securityGroupIds": [
                    "sg-xxxxxxxx"
                ], 
                "bidPercentage": 100, 
                "instanceRole": "arn:aws:iam::xxxxxxxxxxxx:instance-profile/aws-batch-xxxxxxxxxx-BatchInstanceProfile-xxxxxxxxxxxx", 
                "maxvCpus": 1, 
                "type": "SPOT", 
                "ec2KeyPair": "your-keypair-name"
            }, 
            "statusReason": "ComputeEnvironment Healthy", 
            "ecsClusterArn": "arn:aws:ecs:us-east-1:xxxxxxxxxxxx:cluster/aws-batch-refarch_Batch_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", 
            "state": "ENABLED", 
            "computeEnvironmentName": "aws-batch-refarch", 
            "type": "MANAGED"
        }
    ]
}

2. ジョブキューの作成

2.1. ジョブキュー名の指定

ジョブキュー名の指定

コマンド
JOB_QUEUE_NAME="aws-batch-job-queue-`date +%s`"

同名のジョブキューがないことを確認

コマンド
aws batch describe-job-queues \
  --job-queues ${JOB_QUEUE_NAME}

結果:

{
    "jobQueues": []
}

2.2. ジョブキューの作成

ジョブキューの優先順位は 1 〜 10 で指定しますが、今回は 5 にします

コマンド
JOB_QUEUE_PRIORITY=5

ジョブキューには 3 つまでコンピューティング環境を関連づけられますが
今回は先ほど作成した環境でのみ実行可能なジョブキューを作成します。

コマンド
aws batch create-job-queue \
  --job-queue-name ${JOB_QUEUE_NAME} \
  --priority ${JOB_QUEUE_PRIORITY} \
  --compute-environment-order \
  order=1,computeEnvironment=${COMPUTE_ENV_NAME}

結果(例):

{
    "jobQueueArn": "arn:aws:batch:us-east-1:xxxxxxxxxxxx:job-queue/aws-batch-job-queue-xxxxxxxxxx", 
    "jobQueueName": "aws-batch-job-queue-xxxxxxxxxx"
}

2.3. ジョブキューの確認

作成したジョブキューの status が VALID
state が ENABLED であることを確認しましょう。

コマンド
aws batch describe-job-queues \
  --job-queues ${JOB_QUEUE_NAME}

結果:

{
    "jobQueues": [
        {
            "status": "VALID", 
            "jobQueueArn": "arn:aws:batch:us-east-1:xxxxxxxxxxxx:job-queue/aws-batch-job-queue-xxxxxxxxxx", 
            "computeEnvironmentOrder": [
                {
                    "computeEnvironment": "arn:aws:batch:us-east-1:xxxxxxxxxxxx:compute-environment/aws-batch-refarch", 
                    "order": 1
                }
            ], 
            "statusReason": "JobQueue Healthy", 
            "priority": 5, 
            "state": "ENABLED", 
            "jobQueueName": "aws-batch-job-queue-xxxxxxxxxx"
        }
    ]
}

完了

環境構築は以上です。
AWS Batch #2 ジョブの作成と実行

続きを読む

ECS(Beanstalk Docker)でlambdaを使ってcron設定

ECS(Beanstalk Docker)の場合、docker内でcronを設定するのに悩むと思いますが、
lambdaを使ってお手軽cronを設定できたので簡単にメモ

SSMのインストール

ECSインスタンスに入ってdocker execを叩きたいのですが、
今回はSSMというものをインストールし、こいつをlambdaのpythonから呼び出す方針です。
http://dev.classmethod.jp/cloud/aws/introduce-to-run-a-command-button-for-linux/

ECSのsshでログインして、以下のコマンドでssm-agentをインストールして下さい。
amazon-ssm-ap-northeast-1のところが重要で、ここを間違えると動きません。

sudo yum -y update
curl https://amazon-ssm-ap-northeast-1.s3.amazonaws.com/latest/linux_amd64/amazon-ssm-agent.rpm -o amazon-ssm-agent.rpm
sudo yum install -y amazon-ssm-agent.rpm

ECS(EC2)のロールにAmazonEC2RoleforSSMをアタッチ

ECS(EC2)に紐付いているロールにssm-agentへのアクセス権限を与えるために、
AmazonEC2RoleforSSMをアタッチして下さい。

自分の場合はロールはaws-elasticbeanstalk-ec2-roleだったので、
こいつにAmazonEC2RoleforSSMをアタッチします。

EC2のコマンド実行が出来るか確認

EC2のコンソール画面でコマンド履歴という項目があると思いますが、ここでコマンド実行をクリックし、
AWS-RunShellScriptを選択した時に上記で設定したインスタンスが表示されるか確認して下さい。
これが出てこないのであれば上の設定がミスってるので確認して下さい。

lambdaの設定

こちらを参考にしました
http://dev.classmethod.jp/cloud/aws/lambda-ssm-pseudo-cron/

docker execを呼び出すことで、コンテナ内のコマンドを実行できます。
いつもは -it をつけていると思いますが、今回はttyに出力するわけでないのでtは不要です

ssm-agentをインストールしたインスタンスから、一つを選んで実行する感じです。
ssm-agentのインストールを手動でやってますが、インスタンスが増えた時に
自動で行えたほうがいいのですが、スマートなやり方がわかりません。誰かいい方法知ってる人いたら教えてください。

import boto3
import logging

logger = logging.getLogger()
logger.setLevel(logging.INFO)

ec2 = boto3.client('ec2')
ssm = boto3.client('ssm')

def lambda_handler(event, context):
    try:
        ec2_resp = ec2.describe_instances(Filters=[{'Name':'tag:Name','Values':['hogehoge_production']}, {'Name':'tag:ssm-agent','Values':['installed']}] )
        instances = [i["InstanceId"] for r in ec2_resp["Reservations"] for i in r["Instances"]]
        instances = instances[0:1] 

        logger.info(instances)

        ssm.send_command(
            InstanceIds = instances,
            DocumentName = "AWS-RunShellScript",
            Parameters = {
                "commands": [
                    "sudo docker exec -i $(sudo docker ps -q|head -n 1) rails r Hoge.hogehoge >> /tmp/lambda_cron"
                ],
                "executionTimeout": ["3600"]
            },
        )

    except Exception as e:
        logger.error(e)
        raise e

cronの設定

TriggerでCloudWatch Events – Scheduleを選択して、
Schedule expressionのところに  cron(5 * * * ? *) など
任意のスケジュールを設定すれば完了です。

便利な機能ですが、Schedule expressionの説明が薄い上に、
selectになってるのに、文字列を貼り付けて
登録するUIってのはどうなんですか? Amazonさんw

続きを読む