AWS Elastic Beanstalk `.ebextensions` の `commands` と `container_commands` でハマった

どうもこんにちは。

AWS Elastic Beanstalk を使ってアプリケーションをデプロイしているときに、`.ebextensions` ディレクトリ内の設定ファイルで **`commands`** と **`container_commands`** の違いによってデプロイに失敗したので、備忘録として残しておきます。


# `commands` でエラーが発生

Elastic Beanstalk のデプロイでエラーが発生しました。`commands` セクションに記述していた `pecl install` コマンドがうまく実行されていないようでした。

当初の設定は以下の通りです。

```config
packages:
  yum:
    git: []
    libyaml-devel: []
    ImageMagick: []
    ImageMagick-devel: []
commands:
  01-wget:
    command: "wget -O /tmp/ffmpeg.tar.xz https://johnvansickle.com/ffmpeg/old-releases/ffmpeg-6.0-amd64-static.tar.xz"
  02-mkdir:
    command: "if [ ! -d /opt/ffmpeg ] ; then mkdir -p /opt/ffmpeg; fi"
  03-tar:
    command: "tar xvf /tmp/ffmpeg.tar.xz -C /opt/ffmpeg"
  04-ln:
    command: "if [[ ! -f /usr/bin/ffmpeg ]] ; then ln -sf /opt/ffmpeg/ffmpeg-6.0-amd64-static/ffmpeg /usr/bin/ffmpeg; fi"
  05-ln:
    command: "if [[ ! -f /usr/bin/ffprobe ]] ; then ln -sf /opt/ffmpeg/ffmpeg-6.0-amd64-static/ffprobe /usr/bin/ffprobe; fi"
  06-pecl:
    command: "if [ `pecl list | grep imagick` ] ; then pecl install -f imagick; fi"
```


# `container_commands` に変更したら通った！

`commands` セクションを **`container_commands`** に変更したところ、無事にデプロイが成功しました。

変更後の設定はこちらです。

```config
packages:
  yum:
    git: []
    libyaml-devel: []
    ImageMagick: []
    ImageMagick-devel: []
container_commands: # ここが変わった！
  01-wget:
    command: "wget -O /tmp/ffmpeg.tar.xz https://johnvansickle.com/ffmpeg/old-releases/ffmpeg-6.0-amd64-static.tar.xz"
  02-mkdir:
    command: "if [ ! -d /opt/ffmpeg ] ; then mkdir -p /opt/ffmpeg; fi"
  03-tar:
    command: "tar xvf /tmp/ffmpeg.tar.xz -C /opt/ffmpeg"
  04-ln:
    command: "if [[ ! -f /usr/bin/ffmpeg ]] ; then ln -sf /opt/ffmpeg/ffmpeg-6.0-amd64-static/ffmpeg /usr/bin/ffmpeg; fi"
  05-ln:
    command: "if [[ ! -f /usr/bin/ffprobe ]] ; then ln -sf /opt/ffmpeg/ffmpeg-6.0-amd64-static/ffprobe /usr/bin/ffprobe; fi"
  06-pecl:
    command: "if [ `pecl list | grep imagick` ] ; then pecl install -f imagick; fi"
```

# なぜ `commands` ではダメで `container_commands` ならOKだったのか？

この現象の鍵は、Elastic Beanstalk のデプロイプロセスにおける **`commands` と `container_commands` の実行タイミングの違い**にありました。

## `commands` と `container_commands` の違いを理解する

AWS Elastic Beanstalk のドキュメントによると、これらのコマンドはデプロイの異なるフェーズで実行されます。

### `commands`

* **実行タイミング**: アプリケーションとウェブサーバーがセットアップされる**前**、**かつ**アプリケーションコードが展開される**前**に実行されます。デプロイプロセスの非常に初期段階です。
* **用途**: **OSレベルのパッケージインストール** (`yum`, `apt-get` など)、**OS設定の変更**、**ユーザーの作成**といった、アプリケーションコードに依存しない低レベルなシステム設定に適しています。
* **注意点**: アプリケーションのコードがまだ展開されていないため、`commands` 内でアプリケーションのソースコードや、アプリケーションが依存するディレクトリ（例: `/var/app/current`）にアクセスしようとすると失敗します。また、環境変数も利用できない場合があります。

### `container_commands`

* **実行タイミング**: アプリケーションとウェブサーバーがセットアップされ、**アプリケーションバージョンアーカイブが抽出された後**、ただしアプリケーションバージョンがデプロイされる**前**に実行されます。つまり、**アプリケーションコードが `/var/app/staging` (または `current`) に展開された後**に実行されます。
* **用途**: **アプリケーションコードに依存する操作**、例えばデータベースマイグレーションの実行、アプリケーションのキャッシュディレクトリの作成、PHPの `pecl install`、Node.js の `npm install`、Python の `pip install` といった、**アプリケーションのセットアップに必要なコマンド**に適しています。
* **注意点**: アプリケーションのソースコードにアクセスできます。環境変数も利用できます。

## まとめ

Elastic Beanstalk の `.ebextensions` でコマンドを実行する際は、以下の点を覚えておきましょう。

* **`commands`**: システムの初期設定やOSレベルのパッケージインストールなど、**アプリケーション環境が整う前の処理**に使う。
* **`container_commands`**: アプリケーションコードのデプロイ後に実行されるため、**アプリケーション固有のセットアップや依存関係のインストール**に適している。

特に、特定の言語のパッケージマネージャ（PHPの`pecl`、Node.jsの`npm`、Pythonの`pip`など）を利用する場合は、そのランタイム環境が利用可能になっている必要があるため、**`container_commands` を使用するのが正しいアプローチ**です。

もし、`.ebextensions` のコマンドでエラーが発生した場合は、そのコマンドが実行されるべきタイミングと、`commands` または `container_commands` のどちらがそのタイミングに合致しているかを改めて確認してみてください。