0
0

AWS FargateでRakeタスクを定期実行する

Last updated at Posted at 2024-05-28

実現したいこと

  • 従来はWheneverでcron定義を自動生成し、EC2上で定期バッチを実行させていた
  • EC2からFargateへのインフラ移行に際して、定期バッチもFargateで実行できる形にしたい
  • 定義の反映はアプリケーションのCI/CDに載せて自動化したい
    (「手作業でECS Schesuled Tasksを編集する」のような形は避けたい)
  • 定義は設定ファイルで構成管理したい
    (「スケジュール追加スクリプトを実行する」ような形は避けたい)

選択肢

  1. CloudFormation でEventBridgeルールの定義を行い、CircleCI 内でaws cloudformation deploy を実行する
  2. ecschedule(ECS Scheduled Task 設定を管理する gem)を使い、circleci 内でコマンドを実行する
  3. Elastic Whenever(Whenever のように cron ジョブのタスク一覧をconfig/schedule.rbで管理できるようになる gem)を使い、circleci 内でコマンドを実行する

デファクトスタンダードな手段が存在しなかったため、できるだけライブラリには依存しない形にする、という意図で1のCloudFormationで管理する形としました。

設定

ECSタスク定義

まずバッチが起動するECSのタスク定義を作成します。今回はあらかじめ以下が存在する前提とします。

  • クラスター
    sample-app
  • コンテナイメージ(アプリケーションで使うコンテナイメージと同じもの)
    362548988451.dkr.ecr.ap-northeast-1.amazonaws.com/sample-app:rails-21f7b833babf545f86fea29239ffb34c6969177d
  EcsTaskDefinition0001:
    Type: AWS::ECS::TaskDefinition
    Properties:
      Family: sample-app # 起動させるクラスター名
      Cpu: '512'
      Memory: '1024'
      NetworkMode: awsvpc
      RequiresCompatibilities:
        - FARGATE
      ExecutionRoleArn: arn:aws:iam::362548988451:role/ecsTaskExecutionRole
      ContainerDefinitions:
        - Name: batch # コンテナ名
          Image: 362548988451.dkr.ecr.ap-northeast-1.amazonaws.com/sample-app:rails-21f7b833babf545f86fea29239ffb34c6969177d
          Essential: true
          Environment:
            - Name: RAILS_ENV
              Value: production
            - Name: RAILS_LOG_TO_STDOUT
              Value: true
          Command: ['bash'] # EventBridgeルールでOverrideするのでなんでもOK
          Secrets:
            - Name: DB_HOST
              ValueFrom: arn:aws:ssm:ap-northeast-1:362548988451:parameter/sample-app/production/DB_HOST
            - Name: DB_USER
              ValueFrom: arn:aws:ssm:ap-northeast-1:362548988451:parameter/sample-app/production/DB_USER
            - Name: DB_PASSWORD
              ValueFrom: arn:aws:ssm:ap-northeast-1:362548988451:parameter/sample-app/production/DB_PASSWORD
            - Name: DB_DATABASE
              ValueFrom: arn:aws:ssm:ap-northeast-1:362548988451:parameter/sample-app/production/DB_DATABASE
          LogConfiguration:
            LogDriver: awslogs
            Options:
              awslogs-group: /ecs/sample-app-batch
              awslogs-region: ap-northeast-1
              awslogs-stream-prefix: ecs
          PortMappings:
            - ContainerPort: 8080

定義ファイルの作成が完了したら、CloudFormationを適用します。

EventsRule定義

Railsプロジェクトのlib/tasks/schedule.ymlに以下を作成します。
今回はsitemap:refresh:no_pingを定期実行する定義とします。

Resources:
  EventsRule0001:
    Type: AWS::Events::Rule
    Properties:
      Name: sample-app-0001 # ルール名
      Description: run rake sitemap:refresh:no_ping
      # 日本時間の毎日4時(JST)に実行する
      ScheduleExpression: cron(0 19 * * ? *)
      Targets:
        - Id: !Sub 'sample-app-0001'
          Arn: !Sub 'arn:aws:ecs:ap-northeast-1:362548988451:cluster/sample-app'
          RoleArn: arn:aws:iam::362548988451:role/ecsEventsRole
          Input: '{"containerOverrides":[{"name":"batch","command":["bundle","exec","rake","sitemap:refresh:no_ping"]}]}'
          EcsParameters:
            TaskDefinitionArn: !Sub 'arn:aws:ecs:ap-northeast-1:362548988451:task-definition/sample-app-batch'
            TaskCount: 1
            NetworkConfiguration:
              AwsVpcConfiguration:
                SecurityGroups:
                  - sg-xxx
                Subnets:
                  - subnet-xxx # private-1a
                  - subnet-xxx # private-1c
            LaunchType: FARGATE
            PlatformVersion: LATEST

注意点

  • RoleはマネージドなRoleArn: arn:aws:iam::362548988451:role/ecsEventsRoleを使う
    (自前のルールだと起動しなくてハマった)
  • JSTでの時間指定はできないのでUTCで指定する必要がある
  • TaskDefinitionArnのバージョンを記載しないことにより、最新のタスク定義が実行される

CircleCI定義

.circleci/config.yml

version: 2.1

orbs:
  aws-ecs: circleci/aws-ecs@4.0.0
  aws-ecr: circleci/aws-ecr@9.0.3

としてaws-ecsのorbsを使える状態にしつつ、jobs内で以下と定義します

jobs:
  build_and_deploy:
    <<: *defaults
    machine:
      image: ubuntu-2204:2024.01.1
    environment:
      DOCKER_BUILDKIT: 1
    steps:
      - checkout
      - run:
          name: Create master.key
          command: |
            touch ./config/master.key
            echo $RAILS_MASTER_KEY > ./config/master.key
      - aws-cli/setup
      - aws-ecr/build_and_push_image:
          auth:
            - aws-cli/setup
          account_id: '${AWS_ACCOUNT_ID}'
          region: '${AWS_DEFAULT_REGION}'
          dockerfile: Dockerfile
          repo: sample-app
          tag: 'rails-${CIRCLE_SHA1}'
      - aws-ecs/update_task_definition:
          container_image_name_updates: 'container=batch,tag=rails-${CIRCLE_SHA1}'
          family: 'sample-app-batch'
      - run:
          name: Update ECS Scheduled Tasks
          command: aws cloudformation deploy --template-file lib/tasks/schedule.yml --stack-name ecs-scheduled-tasks-sample-app
workflows:
  version: 2
  build-test-and-deploy:
    jobs:
      - build_and_deploy:
          filters:
            branches:
              only:
                - master

これにより、

  1. masterブランチへのプッシュ駆動でbuild_and_deployが実行される
  2. aws-ecr/build_and_push_imageでコンテナイメージのビルドとECRへのデプロイが行われる
  3. aws-ecs/update_task_definitionでタスク定義にビルドしたイメージを指定する
  4. aws cloudformation deploy --template-file lib/tasks/schedule.yml --stack-name ecs-scheduled-tasks-sample-appでCloudFormationの反映を実行する

という流れでEventBridgeルールを最新化が行われます。
CircleCIジョブの実行後、Amazon EventBridgeのルール管理画面sample-app-0001が作成されていればOKです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0