はじめに
Laravelで構築したphpアプリをAWS Elastic Beanstalkにデプロイする際に、スケジューラが使いたかったので、cronを仕込むことにしました。インスタンスはイミュータブルにデプロイされることを想定し、.ebextensionsを使用して設定します。このやり方は公式ドキュメントに記載があります。
https://aws.amazon.com/jp/premiumsupport/knowledge-center/cron-job-elastic-beanstalk/
しかしながら、cronで実行されるスクリプト内では、環境プロパティで設定した環境変数が取得出来ないようで、"artisan schedule:run"がうまく動いてくれずハマりました。
解決策
ヒントは公式ドキュメントにありました。
https://aws.amazon.com/jp/premiumsupport/knowledge-center/view-environment-properties-elastic-beanstalk/
ここでは、cron実行のスクリプトではなく、ログインシェル内での環境変数のことが説明されています。さらに、phpの環境では問題ないので、このような対策は不要との趣旨でした。
PHP を実行するスタックを除き、環境プロパティは、インスタンス内にある場合でも、自動的にはシェルにエクスポートされません。代わりに、環境プロパティはアプリケーションで使用可能になります。使用しているプラットフォームに応じて、アプリケーションの実行元のスタックを通じて利用できます。該当するプラットフォームでの環境プロパティの場所は以下のとおりです。
実際はphp環境でも、cronスクリプトには環境変数が渡りません。そこで、Rubyのやり方を参考に /opt/elasticbeanstalk/support/envvars を source すれば大丈夫です。envvarsの中身はこんな感じになっていて、環境プロパティの変更に同期して自動的に更新されるようです。
export APP_ENV="production"
export APP_NAME="Laravel"
export APP_KEY="base64:********************************************"
export APP_URL="http://*********"
export APP_DEBUG="true"
export PHP_MEMORY_LIMIT="256M"
export PHP_MAX_EXECUTION_TIME="60"
export PHP_DISPLAY_ERRORS="Off"
export PHP_COMPOSER_OPTIONS=""
export PHP_ALLOW_URL_FOPEN="On"
export PHP_ZLIB_OUTPUT_COMPRESSION="Off"
export PHP_DOCUMENT_ROOT="/public"
export PHP_DATE_TIMEZONE="UTC"
(以下省略)
最終的なconfigファイルはこんな感じです。ウェブ側の実行ユーザーであるwebappユーザーの権限で実行させたかったので、そのホームディレクトリを作ったり、storage配下ををNFTマウントしたEFSにリンクしたりしていますが、その辺は不要であれば無視して構いません。本トピックで必須なのは、filesとcontainer_commandsの02_set_cronのみです。
files:
"/tmp/laravel":
mode: "000644"
owner: root
group: root
content: |
* * * * * webapp source /opt/elasticbeanstalk/support/envvars; cd /var/www/html; php artisan schedule:run >> /dev/null 2>&1
commands:
make_webapp_home:
command: |
mkdir -p /home/webapp
chown webapp:webapp /home/webapp
chmod 700 /home/webapp
container_commands:
laravel_configs:
command: |
ln -s ../storage/app/public ./public/storage
rm -rf ./storage/app/public
ln -s /mnt/efs ./storage/app/public
01_migrate:
command: |
source /opt/elasticbeanstalk/support/envvars; sudo -u webapp -E php artisan migrate --force
leader_only: true
02_set_cron:
command: |
cp -f /tmp/laravel /etc/cron.d/
leader_only: true
なお、leader_onlyを付けているのは、複数インスタンスで実行する際に、スケジューラを一台だけで動かしたかったからです。単一サーバーのみで動かす場合や、memcachedかredisを使ってonOneServer() で制御するのであれば不要かと思います。