
#23_PHP8のマルチアーキテクチャコンテナイメージをビルドするのが大変だった話
こんにちは、WFSでサーバエンジニアをしている藤田です。
まずはじめに、これはWFSではもう使われなくなった技術についての記事になります。
冒頭から大変申し訳ありませんが、もう使っていないとはいえ、そこまでに紆余曲折をたどりましたので、記録として残しておきたいと思います。
ではあらためて。
以前の記事で、PHPのマルチアーキテクチャ対応コンテナイメージをビルドするための工夫について紹介しました。
その時はPHP 7.4だったのですが、PHP 8へのバージョンアップを試みて、コンテナイメージのPHPをバージョンアップするとビルドに失敗してしまいました。原因はビルド時間のタイムアウトです。そのため、Github Actionsからよりタイムアウト時間の長いGCP Cloud Buildへ移行することになりました。
今回は、このことについて詳しくお伝えしていきたいと思います。
前回のおさらい
サーバエンジニアの業務PCにApple SiliconのMacが増えてきました。
(以前の記事では書いていませんでしたが、WFSでは自分のPCをWindowsかMacか業務に差し支えなければ自由に選択することができます。)Apple SiloconのマシンではPHPUnitや静的解析の実行時間が数倍に増える事象が確認されました。
調査の結果、IntelアーキテクチャのDockerコンテナイメージをArmアーキテクチャのマシンで実行した場合に問題が発生することがわかりました。
問題解決のためArmアーキテクチャのコンテナイメージをビルドする必要がありましたが、サーバがIntelアーキテクチャであるため、両方を含んだマルチアーキテクチャのコンテナイメージをビルドすることになりました。
ビルドはGithub Actionsで実行しましたが、ビルド時間が数時間に及んだため、いくつかの工夫が必要でした。
問題の発生
PHP 7.4は一昨年サポートが終了しました。弊社でもPHP 8へ移行する必要がありますので、コンテナイメージもPHP 8以降のものをビルドしなければなりませんでした。
すでにPHP 7.4でビルドする体制はととのっていたため、Dockerfileのベースイメージのタグを7.4から8に変えるだけでスムーズに移行できると考えていました。
しかし、実際にはうまくいきませんでした。
きっかけは、PHP 8への移行に伴い、gRPC拡張のバージョンアップを行ったことでした。このバージョンアップによって、ビルド時間が大幅に増加しました。
もともとPHP 7.4では4〜5時間程度かかっていたビルド時間が、6時間かかってもおわらず、Github Actionsのビルドの最大時間に到達してしまいました。(https://docs.github.com/ja/actions/learn-github-actions/usage-limits-billing-and-administration#usage-limits)。
ビルドは強制的に終了させられ、失敗しました。
Cloud Buildへの移行
Github Actionsにおけるビルド時間をすぐに短くする方法が思いつきませんでしたので、ビルドの最大時間が12時間であるCloud Buildでのビルドをためしてみることにしました。
ビルド構成ファイルはこちらになります
steps:
- name: gcr.io/cloud-builders/docker
args:
- run
- '--privileged'
- 'linuxkit/binfmt:v0.7'
id: initialize-qemu
- name: gcr.io/cloud-builders/docker
args:
- buildx
- create
- '--name'
- mybuilder
id: create-builder
- name: gcr.io/cloud-builders/docker
args:
- buildx
- use
- mybuilder
id: select-builder
- name: gcr.io/cloud-builders/docker
args:
- buildx
- inspect
- '--bootstrap'
id: show-target-build-platforms
- name: gcr.io/cloud-builders/docker
args:
- buildx
- build
- '--platform'
- $_DOCKER_BUILDX_PLATFORMS
- '-t'
- '${_IMAGE_NAME}:${_IMAGE_PHP_VERSION}-${_IMAGE_HASH}'
- '--push'
- '--build-arg'
- '${_IMAGE_PHP_VERSION}'
- .
id: build-multi-architecture-container-image
options:
env:
- DOCKER_CLI_EXPERIMENTAL=enabled
substitutions:
_DOCKER_BUILDX_PLATFORMS: 'linux/amd64,linux/arm64'
_IMAGE_NAME: image-name
_IMAGE_PHP_VERSION: 8.1.26
_IMAGE_HASH: latest
前回、buildxでビルドとプッシュを同時にすると、ビルドに時間がかかりすぎて認証トークンの期限が最後のプッシュまで間に合わず、認証エラーで失敗してしまいました。
その経験から、今回もビルドとプッシュを同時に行うと失敗する可能性が高いことは予想できましたが、それでもとりあえずやってみました。結果はやはり失敗でした。
ビルド時間は7時間で、エラーメッセージは以下になります。
failed to authorize: failed to fetch oauth token: unexpected status: 401 Unauthorized
まったく同じビルド構成ファイルで、ビルド時間が数分のイメージをビルドするとこのようなエラーは出ませんでしたので、前回と同様、認証トークンのタイムアウトと推測できます。
よって、対応方法も前回に倣いたいところですが、前回のGithub Actionsの時と違い、認証はCloud Buildサービスアカウントを使っているため、明確な認証タイミングがありません。
ステップごとにトークンがリフレッシュされていることを期待して分けてみます。
steps:
- name: gcr.io/cloud-builders/docker
args:
- run
- '--privileged'
- 'linuxkit/binfmt:v0.7'
id: initialize-qemu
- name: gcr.io/cloud-builders/docker
args:
- buildx
- create
- '--name'
- mybuilder
id: create-builder
- name: gcr.io/cloud-builders/docker
args:
- buildx
- use
- mybuilder
id: select-builder
- name: gcr.io/cloud-builders/docker
args:
- buildx
- inspect
- '--bootstrap'
id: show-target-build-platforms
- name: gcr.io/cloud-builders/docker
args:
- buildx
- build
- '--platform'
- $_DOCKER_BUILDX_PLATFORMS
- '-t'
- '${_IMAGE_NAME}:${_IMAGE_PHP_VERSION}-${_IMAGE_HASH}'
- '--build-arg'
- '${_IMAGE_PHP_VERSION}'
- .
id: pre-build
- name: gcr.io/cloud-builders/docker
args:
- buildx
- build
- '--platform'
- $_DOCKER_BUILDX_PLATFORMS
- '-t'
- '${_IMAGE_NAME}:${_IMAGE_PHP_VERSION}-${_IMAGE_HASH}'
- '--push'
- '--build-arg'
- '${_IMAGE_PHP_VERSION}'
- .
id: build-multi-architecture-container-image
options:
env:
- DOCKER_CLI_EXPERIMENTAL=enabled
substitutions:
_DOCKER_BUILDX_PLATFORMS: 'linux/amd64,linux/arm64'
_IMAGE_NAME: image-name
_IMAGE_PHP_VERSION: 8.1.26
_IMAGE_HASH: latest
“--push”がついていないpre-buildステップを追加しました。
結果は成功です。
これで、PHP 8になっても、マルチアーキテクチャに対応したdockerイメージをビルドできるようになりました。
そして、冒頭にも書きましたが、これらは使われなくなりました。
Docker Desktop 4.25.0
そもそもの話の始まりは、Apple Siliconを使っているエンジニアがIntelアーキテクチャのdockerイメージを使うと、実行速度が極めて遅いという問題が発生したことでした。
弊社ではDocker環境としてDocker Desktopを使っていますが、Apple Siliconを使っている場合、PHPの静的解析が以前の5〜10倍の時間がかかるようになりました。
詳しく調べると、Apple SiliconのマシンでIntelアーキテクチャのdockerイメージを実行した場合、実行速度がかなり遅くなることがわかりました。
実行速度の詳細については以前の記事で詳しく説明しています。
ところが、2023年10月にリリースされたDocker Desktop 4.25.0において、Rosetta for LinuxがGAになりました。
以下が、Rosetta for Linuxを有効にして実際にPHPの静的解析をした結果です。

同等とは言えませんが、1.3倍から1.8倍弱程度の実行速度増加で済んでいます。
比較として、以前計測した、Rosetta for Linuxが入る前の結果も載せておきます。

対象コードや各ツールのバージョンも違うので参考程度にはなりますが、分単位で増加していた処理時間が20秒程度の増加時間で収まっています。
チームと相談して、これは我慢できる範囲内だろうということになりました。
現在はふたたびGithub Actionsにもどし、Intelアーキテクチャのみのコンテナイメージをビルドしています。
まとめ
ということで、PHP8のマルチアーキテクチャ コンテナイメージを自動ビルドしようとしていろいろ頑張ったけれど、結局使わなかった話でした。
結果としては残念な形になりましたが、Cloud Buildのトークン更新タイミングがステップごとであることがわかりました。
ちなみに、本文では書きませんでしたが、ビルド時間を短縮する努力としてCloud BuildのCPUとメモリを増やすというのも試みましたが、ビルド速度に影響はみられないことがわかりました。