定時バッチをECS scheduled task + ecscheduleでお手軽管理する

メディアサービス開発部モバイルアプリケーション開発課のtukiyo(id: tukiyo320)です。現在はニコニコ漫画のバックエンド開発を担当しています。

本記事では、Webサービスに付き物の定時バッチについて、ニコニコ漫画では現在どのような方針で管理・実行しているかをご紹介します。

ニコニコ漫画の構成おさらい

以下の記事に詳しいですが、ニコニコ漫画のバックエンドは4系統存在しています。どれも現在はAWS上に乗っており、PHPの現行システム以外はECS(fargate)で管理されています。

現行PHP(独自フレームワーク)

新バックエンド(Ruby on Rails)

React向けBFF(Nest.js)

課金サブシステム(Ruby on Rails)

developers.bookwalker.jp

本記事で扱うのは、ロジックの書き直しを目的とした新バックエンドが持つバッチとなります。現行PHPのバッチは、温かみのあるcron運用がメインとなっており、ロジックの移設と共に改善したいと考えています。

新バックエンドにおけるバッチ

Railsで書かれている新バックエンド(APIサーバ)では、バッチ処理を一つのActiveJobとして実装し、それをrake taskから呼び出す形で利用しています。rake直書きではないのは、定時バッチ以外にもAWS SQSをジョブキューとしたworker用のジョブも扱っており、責務の集約や必要に応じて実行方法を切り替えられることを見越したものです。

現在扱っているrake taskの内容は、統計データの作成やおすすめ作品抽出、大量のデータ書き換えなどですが、定時バッチとして今の所最も需要が高いのは統計データの作成でしょう。

その統計データの作成も、MySQLからデータを引いてS3経由でBigQueryにインポートし……などと行っていますが、それらの紹介はまた別の機会に譲ることにします。

ECS scheduled task

ECSの機能の一つであり、読んで字のごとく指定されたタイミングでタスクを実行します。ECSにおけるタスク定義とはコンテナの実行方法ですので、時間指定で所定のパラメータを元にコンテナを起動し、指定したコマンドを実行させるのと同義です。

docs.aws.amazon.com

ニコニコ漫画ではAPIとジョブは同居していますので、API用インスタンスとバッチで同一コンテナ同一タスクを利用することが出来ます。

そのため、既にあるタスクに時間とコマンドを指定してあげれば済む……と思いきや、scheduled taskはこの設定に少々難点がありました。

  • ecs consoleで設定する場合、常にlatest revisionのタスクを使用する設定ができない
  • event bridge側でいじると上書きしたコンテナコマンドなどの設定が吹き飛ぶ

アプリケーションに更新が入るたびに、自動でイメージ作成&タスク作成を行っているため、ジョブがどのrevisionを使っているかを気にしたくはありません。常にmaster branchのコードを元に動作してもらったほうが直感的ですし、事故も起こりにくいです(実装変えたはずなのに変わってない、など)。

現状のscheduled taskでは、残念ながら最新のタスクを使い続ける設定をうまくコンソール上で行うことはできないようです。ただ、aws api経由でなら可能なので、terraformなどのInfrastructure as Codeツールで設定することはできますが、善後策である感は否めませんでした。

ecshedule

ecscheduleは、ECS scheduled taskを管理することにのみフォーカスしたシンプルなツールです。

github.com

前述のlatest指定が可能な他、次のような特徴があります。

  • スケジュールのルールをyamlで記述
  • 既存のルールをdumpできる
  • ルールを即時実行させることができる

yamlでルールが記述されることによって、ルールをアプリケーションと同一のリポジトリで違和感なく管理することが可能になります。仮にrakeコマンドが変更になるなどした場合でも、一つのリポジトリ内を置換すれば事足りるのは嬉しいですね。

以下は新バックエンドで管理しているルールを抜粋したものです(一部値はダミー)

 
1region: ap-northeast-1 2cluster: nicomanga_dev 3aliases: 4 - &ecs_settings 5 taskDefinition: nicomanga_dev 6 launch_type: FARGATE 7 platform_version: LATEST 8 network_configuration: 9 aws_vpc_configuration: 10 subnets: 11 - subnet-hoge 12 security_groups: 13 - sg-hoge 14 assign_public_ip: DISABLED 15rules: 16- name: dev_nicomanga_daily_comic_aggregation_job 17 scheduleExpression: cron(0 19 * * ? *) 18 <<: *ecs_settings 19 containerOverrides: 20 - name: nicomanga_dev 21 command: [bundle exec rake aggregations:daily_comic_aggregation_job] 22- name: dev_nicomanga_users_favorite_count_job 23 scheduleExpression: cron(0 21 ? * SUN *) 24 <<: *ecs_settings 25 containerOverrides: 26 - name: nicomanga_dev 27 command: [bundle exec rake aggregations:users_favorite_count_data_job]

yamlなのでaliasが使えることを活かし、ネットワーク設定などのどのルールでも変わらない設定を共通化しています。問題となった最新のタスクを常に使う設定( platform_version: LATEST )もここに記載しています。

また、ルールの有効/無効もこの方法で管理できるため、メンテナンスなどの都合で一時的に止めておきたいスケジュールには disabled: true を指定することで、操作対象のルールをまとめて変更することができます。もちろん、Pull Requestを出せばレビューももらえるため、IaCの恩恵を享受することが可能です。

環境ごとに異なる設定は、単純にファイルを分けることで表現しています。新バックエンドではdevとprodといったECSクラスターの分け方をしていますので、これと同様のprod向け設定ファイルが別途存在します。

1. 2└─.ecschedule/ 3 ├── dev.yaml 4 └── prod.yaml

ecscheduleには環境変数を展開する記法が存在するため、頑張れば一つのファイルで表現できるかもしれません。しかし、もし環境が増えるとロジックが大変なことになるため、シンプルな管理方法に留めています。

ただ、一つハマりどころとしては、(ecs上の仕様の問題ですが)ルール名はアカウント単位で共通なため、環境ごとにファイル名を分けていたとしてもルール名は一意なものにしないといけなかったことです。特にエラーにはならず、後から作成されたルールに上書きされるだけなので注意が必要です。

運用方法

現在はルールの変更頻度が少ないため、手元から直接コマンドを叩く運用になっています。

しかし、頻繁にバッチが追加されたりルールが変更されるようになった場合、設定の食い違いが発生することになるため、アプリケーションと同じワークフローでのCDの整備が課題となっています。

残念ながら新バックエンドはCircleCIでのCI/CDを行っているのでそのままは使えませんが、ecschedule自体はgithub actionsに対応していますので、そちらをご利用の方はすぐに対応できると思います。

まとめ

ニコニコ漫画の新バックエンドにおける定時バッチの管理方法をご紹介しました。

ecscheduleは便利ながら非常にシンプルで分かりやすく、導入も簡単ですので、scheduled taskの管理に悩んでいる方の助けになれたのなら幸いです。