この記事は Akatsuki Advent Calendar 2018 の15日目の記事です。 前回は sejimhpさんの、環境改善の必要性〜真っ白な Terminal から tmux へ移行〜 でした。
はじめに
システムを運用する上で、安価で運用負荷の少ないバッチ処理を設計したいニーズは多いと思います。そもそもバッチ処理自体を極力除外したいのですが、日々の運用ツールなども含めると、どうしても実装しなければならない場面が出てきます。
本記事では
- 安く
- サーバレスで
- 並列性高く
- スケジューラでもイベントドリブンでも
実行できる、AWS上でのバッチ環境の実装の話をしたいと思います。
よくあるAWSのバッチ処理のアーキテクチャパターン
AWSのバッチ処理のアーキテクチャパターンは Best Practice for Online Game Development on AWS が詳しいです。これらのアーキテクチャに対して私見をまとめると
EC2パターン
- Pros: シンプルで使いやすく、速やかに実装ができる。
- Cons: EC2料金が常にかかる。環境変数などの運用管理も必要。
Lambdaパターン
ECSパターン
- Pros: CodePipeline等のパイプラインに組み込める。
- Cons: 結局EC2が必要で、かつバッチで使うには比較的複雑。
Fargateパターン
- Pros: サーバレス。
- Cons: コストが割高で、プロビジョニングに1〜2分かかる。
例を挙げればAWS Batchなどのサービスもあるのでキリがないのですが、結局似たようなProsConsになるかと思います。今回は、これらのアーキテクチャを使わず、CodeBuildでバッチ処理を実装をする話をしていきます。
CodeBuildとは?
AWSが提供するマネージドビルド環境で、buildspec.ymlに記載した通りにビルドをすることができます。
よくあるCodeBuildの使い方
通常、docker build用の環境などに用います。 弊社でもdocker buildが主な用途で、CodeBuildでdocker build後、ECRへdocker pushし、ECS / Fargateへデプロイするために使用しています。
buildspec.ymlの例は以下のとおりです。 パラメータストアと連携して秘匿情報をセキュアに一元管理したり、ビルド済みのdocker imageを実行環境として使えるところも嬉しいです。
version: 0.2 env: parameter-store: AWS_ACCESS_KEY_ID: "project-id.aws.access.key.id" AWS_SECRET_ACCESS_KEY: "project-id.aws.secret.access.key" phases: pre_build: commands: - echo Logging in to Amazon ECR... - aws --version - $(aws ecr get-login --no-include-email --region ap-northeast-1) - REPOSITORY_URI=${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/${IMAGE_NAME} build: commands: - echo Build started on $(date) - echo Building the Docker image... - | docker build -t ${REPOSITORY_URI}:latest -f ${DOCKERFILE_PATH} . \ --build-arg YOUR_ENV=${YOUR_ENV} post_build: commands: - echo Build completed on $(date) - echo Pushing the Docker images... - docker push ${REPOSITORY_URI}:latest
CodeBuildを使用したバッチ環境
さて、本題です。 CodeBuildを使ってバッチ処理を実行する方法を説明します。
パターン1:バッチスクリプト実行
CodeBuildはビルド環境に様々な言語が用意されており、かつVPC内で実行することができるため、RubyやPythonのスクリプトを実行することができます。
実行後はCloudWatchEventsでイベントフックをして、slack通知をしてもいいですし、Lambdaで別の処理に移行するのも良いでしょう。CodePiplineやJenkinsのAWS CodeBuild Pluginを使用したPipelineに組み込めば、前後関係を豊富にカスタマイズできます。
この場合のbuild_spec.ymlは非常にシンプルに書けます。
version: 0.2 env: parameter-store: YOUR_ENV: "your.env" phases: build: commands: - python ./test.py artifacts: files: test.log
パターン2:ビルド済みアプリケーションの実行
パターン1ではスクリプトを実行するだけでしたが、ビルド済みのアプリケーションを使った運用ツールを実行したい場合があります。例えばデータベースのmigration、 ゲームアプリケーションロジックを用いたアイテムドロップシミュレータなどです。
この要望を満たすためには、ビルド済みのアプリケーションImageを再度CodeBuildにpullしてこればOKです。
この場合、build_spec.ymlには少し工夫が必要になります。 ビルド済みのImageをpullしてきた後、docker runをする際にENTRY_POINTを空にして、実行したいコマンドを渡します。 こうすることでENTRY_POINTを無視して、ビルド済みのアプリケーションに対してコマンドを実行することができます。
version: 0.2 env: parameter-store: YOUR_ENV: "your.env" phases: pre_build: commands: - echo Logging in to Amazon ECR... - aws --version - $(aws ecr get-login --no-include-email --region ap-northeast-1) - REPOSITORY_URI=${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/${IMAGE_NAME} build: commands: - echo started on $(date) - docker pull ${REPOSITORY_URI}:latest - echo ${BATCH_NAME} starting ... - | docker run \ -e YOUR_ENV=${YOUR_ENV} \ --entrypoint="" -i ${REPOSITORY_URI}:latest \ sh -c "${BATCH_COMMAND}" post_build: commands: - echo ${BATCH_NAME} completed on $(date)
さて、ここまで説明してきたCodeBuildでのバッチ処理のメリットをまとめます。
機能性
- ビルド済みのアプリケーションロジックを使い回せる
- VPC内で実行できるためEC2 / RDSなどへのアクセスができる
- 並列性が高い。
- リソースの範囲内であれば長時間バッチも可能
運用性
- サーバレス
- 成功・失敗のイベントフックが豊富
- Pipelineへの組み込みが容易なため前後関係の定義ができる
コスト
- 従量課金のため、とにかく安い。
デメリットを挙げるとすれば以下のとおりです。
デメリット
- プロビジョニングに30秒ほどかかる(ただしFargateのプロビジョニングよりかは速い)
- 使える最大リソースは8vCPU、15GBメモリなので、これ以上のパワーが必要な処理ではリソースが足りない。
まとめ
CodeBuildを使ったバッチ処理を紹介してきました。 CodeBuildをバッチ環境として使い始めたプロジェクトでは、バッチサーバという概念がなくなりました。 興味がある人は、ぜひ使ってみてください!