🐷

ECSでバッチを作ってみた

2021/10/04に公開

目次

  • はじめに
  • ECSを採用した理由
  • 方法
  • 終わりに

はじめに

中国語例文投稿サイトの「チャイブン」の開発を行っています。
そのサイトで投稿された例文の一部を抽出して表示するページ(今日の中国語ページ)を作成するにあたってバッチ機能が必要になりました。

毎日深夜にバッチが実行され、「今日の中国語」ページに表示する例文のidがDBに保存されます。そしてそのidを使っていくつかの例文を表示する構成になっています。

ECSを採用した理由

ECSを勉強したかったからです。仕事でもECSでバッチ処理を書いているのですが、自分で1から構築したことはなかったので一度自分でやってみたいと考え、ECSを使うことにしました。

バッチ処理ならECSの他にLambdaやAWS Batchを使って実装することもできるようです。
参考: https://zenn.dev/faycute/articles/fb310e3ccd783f

方法

バッチ処理でDBを直接操作するのではなく、バッチ処理でAPIを叩き、DBを操作するようにしました。
このようなイメージです。

作業手順は以下です

  • APIを叩くためのjsファイル作成
  • dockerファイル作成
  • ECRのレポジトリ作成
  • ECRにdockerイメージをpush
  • ECSクラスター作成
  • ECSタスク定義作成(ここで一度手動で実行できることを確認)
  • ECSタスクのスケジューリング作成

1つずつ見ていきましょう。

APIを叩くためのjsファイル作成

以下のようなものを作成しました。「APIのURL」の部分は書き換えてください

index.js
const util = require('util');
const { exec } = require('child_process');
const promisify = util.promisify(exec);

const updateDailySentences = async () => {
  const result = await promisify('curl -XPOST APIのURL', {
    timeout: 5000,
  }).catch((err) => console.log('curl failed: ', err));
  console.log('updateDailySentences result:', result?.stdout, '\n');
};

updateDailySentences();
console.log('done');

単純にこのファイルが呼ばれた時に、curlコマンドを実行するだけです。shellスクリプトでも良かったかもしれませんが、アプリ全体をJavaScriptで書いていたので合わせてJavaScriptにしました。

参考
https://www.wakuwakubank.com/posts/728-nodejs-child-process/
https://qiita.com/mazxxxry/items/eb2036b28f75eb39333c

optionsのtimeoutはつけなくても大丈夫です。最初タイムアウトエラーになっていてその原因調査の時につけました。なくても問題ないです。

またpostでリクエストした方が都合が良かったので -XPOST を付けていますがgetで良い人は -XPOST も不要です。関数名も好きに変更してもらって大丈夫です。

dockerファイル作成

dockerファイルは以下のようにしました。

Dockerfile
FROM node:14

ENV LANG ja_JP.UTF-8

WORKDIR /usr/src/app
COPY index.js /usr/src/app

CMD ["node", "/usr/src/app/index.js"]

ご覧の通りかなりシンプルです。5行しかありません。
1行ずつ簡単に説明します。

1行目: nodeのバージョン14のimageをpull
2行目: 環境変数 LANGをja_JP.UTF-8に設定
3行目: /usr/src/配下にappディレクトリを作成
4行目: localのindex.jsを/usr/src/app直下にコピー
5行目: index.jsを実行

ECRのリポジトリ作成

まずはAWSマネジメントコンソールにログインし、ECRのページに移動します。
「使用方法」をクリック。

以下のように設定。イメージスキャンの設定の箇所が画像では無効になっていますが有効にしました。

「リポジトリの作成」をクリック。
これでECRリポジトリが作成されました。

ECRにdockerイメージをpush

先ほど作成したECRリポジトリをクリックすると「プッシュコマンドを表示」というボタンがあるのでそれをクリック。

そうすると以下の4種類のコマンドが表示されます。

  • 認証のためのコマンド
  • docker imageをbuildするためのコマンド
  • タグ作成のためのコマンド
  • docker imageをECRにpushするためのコマンド

この4つのコマンドを順番に実行すると、ECRにdocer imageが登録されます。

ECSクラスター作成

ECSのページに移動し、「クラスターの作成」をクリック。

「ネットワーキングのみ」をクリック。

クラスター名を入力し、「作成」をクリック。

ECSタスク定義作成

ECSのページのサイドバーにある「タスク定義」をクリック。
そして「新しいタスクの定義」をクリック。
それから起動タイプは「Fargate」を選択。

他の設定は以下のようにする。

「コンテナの追加」をクリック。
すると以下のようなモーダルが開く。イメージの欄には先ほど作成したECRリポジトリのURIを記入します。
コンテナ名とイメージ以外は何も入力していません。

そして「追加」を押してから「作成」をクリックするとタスク定義が作成されます。

ここでいきなりECSタスクのスケジューリングを作成しても良いですが、一度手動で実行して正しく動作することを確認します。
作成したクラスターを選択し、さらにタブの「タスク」を選択してください。
そして「新しいタスクの実行」をクリックします。

以下のように設定します。
セキュリティグループは何も指定しなければ新しく作成されます。特にこだわりがなければdefaultでも良いと思います。

そして「タスクの実行」をクリック。想定通りにバッチが実行されていることを確認。

ECSタスクのスケジューリング作成

手動でバッチを実行できることが確認できたら、そのバッチを定期的に実行させるようにしていきます。
タブの「タスクのスケジューリング」を選択し「作成」をクリック。

「スケジュールルールの名前」や「スケジュールルールの説明」は何でも良いです。

「スケジュールルールタイプ」を「固定された間隔で実行」に設定すると○日に1回とか○分に1回みたいな間隔でバッチを実行できます。この方法の場合、スケジューリングを作成した時に1回目が実行されます。例えば1日に1回バッチが実行されるようにした場合、そのスケジューリング作成日が10/3の22時10分だった場合、10/3 22:10に1回目のバッチが実行され、それ以降は毎日22:10にバッチが実行されるようになります。

もう少し細かい設定をしたい場合は、Cron式を選択します。
今回はバッチが深夜に実行されるようにしたかったのでCron式を選択しました。
日本時間の深夜0時5分に毎日実行されるように以下のように設定しています。

cron(5 15 * * ? *)

UTCの時刻での設定になるので15:05に設定されています。(UTCは日本時間より9時間遅いです)

そして他の設定は以下のようにします。手動でバッチを実行した時と同じです。

最後に「作成」を押すと「ECSタスクのスケジューリング」が作成されます。

終わりに

これで定期的に実行されるバッチができたと思います。
バッチ処理があるかないかでアプリでできることが結構変わってくると思うので、バッチ処理は作成できるようにしておきたいですね。
ちなみにこの方法でバッチを作成した時の料金なのですが、ほとんどかかっておりません。ECRに4円くらいとECSに1円くらいかかっていますがそれだけです。安すぎです。

LambdaやAWS Batchを使ったバッチ処理も機会があれば作成してみたいと思います。

Discussion