たねやつの木

Photographs, Keyboards and Programming

【CI/CD勉強編-3】x86とARMの壁を越える!Docker buildxでマルチプラットフォームイメージをビルド

こんにちは、たねやつです。

前回は、GitLab CI/CDを使ってDockerイメージのビルドとプッシュを自動化しました。しかし、そのイメージは開発PC(x86_64アーキテクチャ)でビルドされたもので、そのままでは最終目標であるRaspberry Pi(ARMアーキテクチャ)では動作しません。

今回はこのアーキテクチャの壁を乗り越えるため、docker buildx という強力なツールを使います。目標は、「1回のビルドで、x86とARMの両方で動作するマルチプラットフォームイメージを作成する」ことです。

前の記事

この記事でできること

  • CPUアーキテクチャ(x86_64とARM)の違いが開発にどう影響するか理解できる。
  • docker buildxの役割と基本的な使い方がわかる。
  • GitLab CI/CDパイプラインを修正し、マルチプラットフォーム対応のDockerイメージをビルドできるようになる。

CPUアーキテクチャの壁とは?

CPUには様々な設計思想(アーキテクチャ)があり、PCで一般的なのはx86_64(またはamd64)、Raspberry Piや多くのスマートフォンで使われているのがARMarm64またはaarch64)です。

これらは互換性がないため、x86_64向けに作られたプログラム(Dockerイメージも含む)は、ARMのCPU上では原則として動きません。

この問題を解決せずにx86用のイメージをRaspberry Piで動かそうとすると、`exec format error` というエラーが発生します。

この問題を解決するのが、マルチプラットフォームビルドです。1つのイメージ名(例: my-app:latest)の中に、複数のアーキテクチャに対応したプログラムを格納しておくことで、Dockerが実行環境のCPUアーキテクチャを自動で判別し、適切なプログラムを実行してくれます。

Docker Buildxとは?

docker buildxは、このマルチプラットフォームビルドを簡単に行うためのDocker公式の拡張機能です。内部ではQEMUというエミュレータを利用して、例えばx86のCPU上でARMのプログラムをビルドする、といったことを可能にしています。

マルチプラットフォームビルド対応のパイプライン構築手順

GitLab CI/CDでdocker buildxを使うために、.gitlab-ci.ymlを修正していきます。

1. QEMUのセットアップ

docker buildxが異なるアーキテクチャのコードをビルドするためには、エミュレータであるQEMUが必要です。幸いなことに、これを簡単に行うためのDockerイメージが公開されています。

2. .gitlab-ci.ymlの修正

前回の.gitlab-ci.ymlを、docker buildxを使うように以下のように変更します。

  • .gitlab-ci.yml (変更後):
stages:
  - build

variables:
  IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
  # ビルド対象のプラットフォームを定義
  PLATFORMS: linux/amd64,linux/arm64

build_multiarch_image:
  stage: build
  image: docker:latest
  services:
    - docker:dind

  before_script:
    # buildxを有効化し、QEMU(エミュレータ)をセットアップする
    - docker context create builder-context
    - docker buildx create --name mybuilder --driver docker-container --use builder-context
    - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY

  script:
    # docker buildx を使ってマルチプラットフォームビルドを実行
    - docker buildx build --platform $PLATFORMS -t $IMAGE_TAG --push .

  only:
    - main

変更点のポイント:

  • ジョブ名の変更:
    • 分かりやすくbuild_multiarch_imageに変更しました。
  • PLATFORMS変数:
    • ビルド対象のプラットフォームをlinux/amd64 (x86_64) と linux/arm64 の両方を指定するようにしました。
  • before_script:
    • docker buildx create で、buildx用のビルダーインスタンスを作成しています。
    • multiarch/qemu-user-static イメージを実行し、QEMUをセットアップしています。これにより、異な るCPUアーキテクチャのバイナリを実行できるようになります。
    • script:
      • コマンドがdocker buildからdocker buildx build に変わりました。
      • --platform $PLATFORMS オプションで、ビルド対象のプラットフォームを指定します。
      • --pushオプションを付けています。docker buildx では、複数のイメージをまとめて出力するために、ビルドと同時にレジ ストリにプッシュする必要があります。

3. パイプラインの実行と確認

この修正を加えた.gitlab-ci.ymlをコミットし、mainブランチにプッシュします。

git add .gitlab-ci.yml
git commit -m "Update .gitlab-ci.yml for multi-platform build"
git push

GitLabでパイプラインの実行状況を確認しましょう。buildxのセットアップと2つのプラットフォーム向けのビルドが実行されるため、前回よりも少し時間がかかります。

これで、1つのイメージで開発PC(WSL)とRaspberry Piの両方で動く準備が整いました!

最後に

今回は、docker buildxを導入し、CI/CDパイプラインでマルチプラットフォームイメージをビルドする方法を学びました。これで、開発環境と本番環境のCPUアーキテクチャの違いという大きな壁を乗り越えることができました。

いよいよ次は、このイメージを最終目標であるRaspberry Piにデプロイします。次回は、Raspberry Piに軽量Kubernetesである「K3s」をインストールし、手動でアプリケーションをデプロイする手順を解説します。

次の記事