DockerImageにCLI実行モードを実装してECSでバッチ運用する

ECSで稼働するphp:fpm-alpineベースのPHP/Phalconで構築したアプリケーションイメージを流用して、ECSでバッチ処理をスケジュール実行するためのアーキ。

要件

  • 運用中のアプリケーションのイメージを流用できること。
  • スケジュール実行したあと、exit終了すること。
  • ログはCloudWatchLogに出力すること。

PhalconでCLI実行

エントリポイントとバッチ処理を実装する。下記のコードスニペットがそのまま使えました。 【PHP Phalcon】バッチ処理の実装とcronの設定

実行

# cliモードで起動
php /path/to/CLIエントリポイント.php \
&& className(→ ClassNameTaskクラス) \
&& action_name(→ actionNameActionメソッド) \
&& param1(→ メソッドの引数1) \
&& param2(→ メソッドの引数2)

Dockerイメージのフレキシブル運用

ENTRYPOINTCMDに応じて サーバー起動 or CLI実行 を切り替えるシェルスクリプトを用意して、docker run イメージ {コマンド上書き}あるいは、ecsのコマンド上書き設定で起動モードをスイッチ出来るようにする。



Dockerfile

...

# エントリポイントスクリプトをコピー
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

# エントリポイントを設定
ENTRYPOINT ["/entrypoint.sh"]

# デフォルトでは FPM を起動
CMD ["fpm"]

entrypoint.sh

#!/bin/sh
set -e  # エラー時に停止

# 引数がない場合、または "fpm" の場合は PHP-FPM を起動
if [ "$1" = "fpm" ] || [ -z "$1" ]; then
    echo "Starting PHP-FPM..."
    exec php-fpm
fi

# "cli" の場合は、引数を `php` に渡して実行(CLIモード)
if [ "$1" = "cli" ]; then
    shift # `cli` を削除
    echo "Running PHP CLI..."
    exec php "$@"
fi

# それ以外のコマンドは直接実行
exec "$@"

実行

# サーバー起動モード
docker run -d イメージ
#=> php-fpm

# CLI実行でワンショット起動
docker run --rm イメージ cli /path/to/cli.php arg1 arg2
#=> php /path/to/cli.php arg1 arg2

# メンテコマンドでワンショット起動
docker run --rm イメージ ls -l
#=> ls -l

ECS

初見はいろいろと問題が出るので、新しいタスクの実行でデバッグしてからスケジュールされたタスクを構築することを推奨。



  • バッチ用のタスク定義を作成して、上記のイメージを指定する。
    • 起動タイプ(Fagate?EC2?)クラスタレイヤーの話なのにここで選ぶのかい?と混乱していたら、フォームを想定環境に最適化してくる親切設計であった。Amazon ECS 起動タイプ - Amazon Elastic Container Service
    • デバッグ、モニタリングためCloudWatchLogは必ずオン
  • クラスタ > スケジュールされたタスクでは主にタスク定義cron式および、コンテナの上書き > コマンドの上書きでCMDをCLI起動用に上書きする。
# だめ ひとつのコマンドとして認識される
# cli /path/to/cli.php className action_name
#=> /entrypoint.sh: exec: line X: cli /path/to/cli.php className action_name: not found

# OK カンマでCMD引数としてプロットしてくれる(説明どおり)
cli,/path/to/cli.php,className,action_name
#=> exec php /path/to/cli.php className action_name


つまずき

ResourceInitializationError

クラスター > タスクの状態より ResourceInitializationError: unable to pull secrets or registry auth: The task cannot pull registry auth from Amazon ECR: There is a connection issue between the task and Amazon ECR. Check your task network configuration. RequestError: send request failed caused by: Post "https://api.ecr.ap-northeast-1.amazonaws.com/": dial tcp 99.77.58.41:443: i/o timeout

イメージのpull → イメージのECRエンドポイントに接続できていない。 → タスクのサブネットがプライベートサブネットだったのでNAT/IGWの準備がなくECRに接続できない。パブリックサブネットに設置して通過。

Essential container in task exited

クラスター > タスクの状態より Essential container in task exited

プロセスが終了してexit状態なのでOK。CloudWatchLogで実行結果を確認する。

Gmailでカスタムドメインのメールアドレスを送受信する

自ドメインをメールサーバー無しで送受信するためのアーキ


登場人物

  1. Gmail カスタムドメイン宛メールの受信メールボックスおよび、SMTPクライアントとして
  2. → GoogleWorkspaceは使わない。一般アカウント。

  3. improvmx カスタムドメイン宛メールをGmail転送するためのMXサーバーとして
  4. SES カスタムドメインでメール送信するためのSMTPサーバーとして(GMailでもよい)
  5. Route53 カスタムドメインのDNSとして

カスタムドメインから受信

Gmail


カスタムドメイン宛メールの受信メールボックスとして

  • 受信専用であれば追加設定は不要。

Improvmx


https://improvmx.com/

カスタムドメインのMXサーバーとして

  • Improvmxは転送専用であり、メールボックス実態は存在しない。転送先メアドのエイリアスとして利用する。
  • SMTP機能は有料オプションなので使わない。

カスタムドメインと転送先メアドを設定して登録開始


転送先メアドで認証プロセスを終えると、ひとまずドメイン未検証の状態でダッシュボードがオープンし、転送ルールと、MX・SPFレコードが払い出される。


上記、SETTINGS > DNS settingsに表示されるるMX・SPFレコードをDNSに登録後、DNS検証が完了するとセッティングがアクティベートされる。 ※なお、SMTPは使わないんだけど、アクティベートにはSPF(TXT)レコードの定義も必須。


  • Email forwarding activeになったらTESTで検証メールを送信させて、無事にGmailメールボックスに届いたら完了。


カスタムドメインから送信

Gmail


SMTPクライアントとして

1. プロフィールとSMTPサーバーを追加する

Gmailアカウントに以下の設定を組んだら、カスタムドメインをFROMアドレスとして利用可能となる。

  1. カスタムドメインのFROM送信者名とメアド
  2. SMTPサーバ

Gmail > 設定 > アカウントとインポート > 名前 | 他のメールアドレスを追加

を開いて、カスタムドメインの送信者名とメアドを指定する。 ここに追加したメールアドレスはGASのMailAppでもメールFROMとして利用可能となる


2. SMTPサーバーの指定

SMTPサーバーとしてGmailとSESを比較

最も手軽なのは検索でよく出てくる自GmailアカウントのSMTPを利用する方法。 カスタムドメインの名前&メアドをFROMにしつつ、SMTPはGmailドメインで送信者認証もサポートしてくれるのでカジュアルな送信基盤としては問題はなさそう。

一方、SESをSMTPにする場合は、カスタムメールFROM機能でカスタムドメイン(サブドメイン)から送信されるため、 GmailではエンベロープFROMヘッダにGmailメアドが露出するのに対して、SESはカスタムドメイン(サブドメイン)となる。 (厳密にはGmailの追加ヘッダX-Gmail-Original-Message-IDなど付記される。)

その他にも、送信クウォータとかレピュテーション要件などそれぞれ要件が異なるため、適宜選択というイメージ。今回はSESで構築する。

SMTPサーバーにGMailを使う場合


FROM(表示名)=mail@domain.to.path エンベロープFROM(実際送信元)={アカウント}@gmail.com

  • カスタムドメインをメールFROMにして、GmailアカウントのSMTPから送信者認証つきで送信できる。
  • もっとも簡単だけど、ヘッダのエンベロープFROMにGmailアドレスが受信者に露出する。

注意事項としては、SMTPサーバーのパスワードはGoogleアカウントのパスワードではなく、アプリパスワードだ。 アプリパスワードの発行のためにはアカウントに二段階認証プロセスの設定が必要。

SMTPサーバーにSESを使う場合

FROM(表示名)=mail@domain.to.path エンベロープFROM(実際送信者)=XXXXXXXXXXXXXXXX-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX-XXXXXX@mail.domain.to.path

  • カスタムメールFROMで指定したサブドメイン(mail.domain.to.path)から送信者認証つきで送信できる。
  • カスタムメールFROMのセッティングは必要だけど、Gmailアドレスが受信者に露出することはない。

SESでは以下、設定済のこと

  • カスタムドメインのID検証、SPF、DKIM、DMARCレコード
  • サンドボックス解除
  • SMTP設定


送信テスト

カスタムドメインからメールを送信し、SPF、DKIM認証をパスできているか確認する。 テスターmail-tester.comを使うと、問題点をリスト化してくれるのでさらに捗る。