1. コンテナはカーネルを持たない

コンテナイメージに入っているのは、カーネル以外のすべて(ユーザー空間)です。カーネルは、コンテナを動かすホスト側のものを使います。

層 例 どこから来るか
ベースの OS bash、coreutils、glibc、apt、tzdata debian:bookworm など
PHP 本体 php、php-fpm、docker-php-ext-* スクリプト php:8.x の公式イメージ
追加パッケージ default-mysql-client、libzip-dev、libicu-dev Dockerfile の apt-get install
ビルドした拡張機能 pdo_mysql.so、intl.so docker-php-ext-install
カーネル ネットワーク、メモリ管理、プロセス管理 ホスト側(イメージには含まれません)

そのため、カーネルの脆弱性はイメージを更新しても直りません。ホスト側のカーネルを更新する必要があります。

2. 実行環境ごとのカーネル

実行環境 使われるカーネル 更新するのは
AWS Fargate AWS が用意した Linux カーネル AWS(利用者は新しいタスクに入れ替えるだけ)
ECS on EC2 / Ubuntu サーバー そのホストの Linux カーネル 利用者(カーネルを更新して再起動)
Mac の Docker Desktop Docker Desktop が動かす Linux 仮想マシンのカーネル(例: 6.10.14-linuxkit) Docker Desktop をアップデート

Mac のカーネル(Darwin)は Linux ではないので、Linux コンテナを直接動かせません。
Docker Desktop が裏で軽量な Linux 仮想マシンを起動し、その中でコンテナを動かしています。

Mac(Darwin カーネル)
 └─ Docker Desktop の Linux 仮想マシン(linuxkit カーネル、arm64)
     └─ コンテナ(ユーザー空間)

次のコマンドで確認できます。

uname -srm                                   # Mac のカーネル
docker info --format ''    # コンテナが使うカーネル

3. Fargate は Debian でも Alpine でも動く

ベースのディストリビューションは関係ありません。
動くかどうかを決めるのは次の 2 点です。

  • Linux 用のイメージであること
  • CPU アーキテクチャがタスク定義と合っていること

アーキテクチャに注意

Apple Silicon の Mac で普通に docker build すると、arm64 用のイメージになります。
タスク定義が既定の x86_64 のままだと、exec format error で起動に失敗します。

動かしたい環境 対応
x86_64 docker build --platform linux/amd64 ... でビルドする
arm64(Graviton) タスク定義に RuntimePlatform: { CpuArchitecture: ARM64, OperatingSystemFamily: LINUX } を指定する

Mac でも --platform linux/amd64 を付ければ amd64 用のイメージを動かせます。
ただし命令を変換しながら実行するので遅く、まれに挙動が違うことがあります。最終確認は実環境で行うのが確実です。

4. Debian 版と Alpine 版の違い

観点 Debian(php:8.5) Alpine(php:8.5-alpine)
イメージサイズ 大きい 小さい
Fargate での起動 イメージ取得に時間がかかる 速くなりやすい
C ライブラリ glibc musl
互換性 高い musl が原因の差が出ることがある(iconv の //TRANSLIT、一部の拡張機能や外部バイナリ、DNS の細かい挙動など)
パッケージ管理 apt-get apk

既存の構成が Debian 系なら、Debian 版を選ぶのが無難です。

5. docker-php-ext-* とパッケージ名

docker-php-ext-configure と docker-php-ext-install は、PHP 公式イメージが Debian 版にも Alpine 版にも入れている共通のスクリプトです。ただし、事前に入れるビルド用パッケージは名前が違います。

用途 Debian(apt) Alpine(apk)
zlib zlib1g-dev zlib-dev
libzip libzip-dev libzip-dev
ICU(intl 用) libicu-dev icu-dev
MySQL クライアント default-mysql-client mysql-client または mariadb-client

-dev パッケージはビルド用のヘッダーです。実行時に必要なライブラリ本体(libzip4、libicu72 など)は依存関係として一緒に入ります。

6. ビルドした拡張機能は、そのイメージでしか動かない

intl.so などの拡張機能は、そのイメージの PHP、C ライブラリ、ICU のバージョンと CPU アーキテクチャに合わせてコンパイルされます。次の組み合わせでは動きません。

  • Debian でビルドしたものを Alpine で使う(glibc と musl の違い)
  • bookworm でビルドしたものを trixie で使う(ICU のバージョンの違い)
  • PHP 8.3 用のものを PHP 8.5 で使う(PHP の内部 API の違い)
  • amd64 でビルドしたものを arm64 で使う

Docker ではビルドと実行が同じイメージの中で行われるので、通常は問題になりません。PHP のバージョンを上げるときは、新しいイメージの中で拡張機能をビルドし直します。

7. -fpm イメージと待ち受けポート

php:8.x-fpm イメージは、何も指定しないと php-fpm を起動してポート 9000 で待ち受けます。ECS のタスク定義で Command を上書きすると、そのコマンドだけが実行されます。

起動方法 動くもの 待ち受け
Command の指定なし php-fpm ポート 9000
Command: php bin/console ... そのコマンドだけ なし

ENTRYPOINT の docker-php-entrypoint は、指定されたコマンドをそのまま実行します。アプリ側の Dockerfile で ENTRYPOINT を変えている場合は、裏で別のプロセスが起動していないか確認が必要です。

コンテナ内で待ち受けているポートは、次のコマンドで確認できます。何も表示されなければ、待ち受けはありません。

for f in /proc/net/tcp /proc/net/tcp6; do
  awk '$4=="0A"{split($2,a,":"); print a[2]}' "$f"
done | while read -r p; do echo "LISTEN port: $((0x$p))"; done