QEMUエミュレーションを廃止してarm64ネイティブビルドへ移行

rails/devcontainer

従来のDockerイメージビルドでQEMUエミュレーションによって発生していた速度低下を解消するため、arm64イメージのビルドをネイティブのarm64 Ubuntuランナー上で実行するよう変更されました。あわせてワークフロー定義のリファクタリングも行われ、重複の排除とGitHub Actions UIからのトリガー操作の簡略化も実現しています。

背景

QEMUによるクロスアーキテクチャビルドは、単一マシンでamd64/arm64の両イメージを生成できる手軽な手法ですが、エミュレーションのオーバーヘッドによりビルド時間が大幅に増加するという問題がありました。GitHub ActionsではUbuntu 24.04のarm64ランナー(ubuntu-24.04-arm)が利用可能になったことで、エミュレーションなしにarm64イメージをネイティブビルドできる環境が整っています。

各アーキテクチャをそれぞれネイティブランナーでビルドする方式に切り替えることで、QEMUの導入コスト自体を排除できます。ただしアーキテクチャごとに独立したイメージタグがGHCRにプッシュされることになるため、最終的にmulti-archマニフェストを作成してまとめるステップが別途必要になります。

技術的な変更

アクション定義の拡張(build-and-publish-image)

build-and-publish-image アクションに3つの新しい入力パラメータが追加され、シングルアーキテクチャビルドとマニフェスト発行の両モードを1つのアクションで扱えるようになりました。

追加されたパラメータは以下のとおりです:

  • build_platform: ビルド対象のプラットフォーム(linux/amd64 または linux/arm64
  • platform_suffix: アーキテクチャ固有タグのサフィックス(amd64 または arm64
  • publish_manifest: true を指定するとビルドを行わずmulti-archマニフェストの発行のみを実行

変更前:

- name: Set up QEMU for multi-architecture builds
  uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
  uses: docker/setup-buildx-action@v3
  with:
    platforms: linux/amd64,linux/arm64

変更後:

- name: Set up Docker Buildx
  if: ${{ inputs.publish_manifest != 'true' }}
  uses: docker/setup-buildx-action@v3
  with:
    platforms: ${{ inputs.build_platform }}

docker/setup-qemu-action のステップが完全に削除され、setup-buildx-action には単一プラットフォームのみを渡すようになっています。また publish_manifesttrue の場合はCheckoutやBuildxのセットアップを含む多くのステップが if 条件でスキップされます。入力の整合性は冒頭の Validate required inputs ステップでシェルスクリプトによりチェックされます。

再利用可能ワークフローの新設

新たに .github/workflows/publish-images-reusable.yaml が追加され、ビルドのコアロジックがここに集約されました。このワークフローはビルドジョブのmatrixにアーキテクチャ軸を追加しており、ubuntu-24.04(amd64)と ubuntu-24.04-arm(arm64)の2ランナーが並列実行されます。

matrix:
  RUBY_VERSION: ${{ fromJSON(inputs.ruby_versions_json) }}
  IMAGE_VERSION: ${{ fromJSON(inputs.image_versions_json) }}
  TARGET:
    - RUNNER: ubuntu-24.04
      BUILD_PLATFORM: linux/amd64
      PLATFORM_SUFFIX: amd64
    - RUNNER: ubuntu-24.04-arm
      BUILD_PLATFORM: linux/arm64
      PLATFORM_SUFFIX: arm64

Rubyバージョン・イメージバージョン・ターゲットアーキテクチャの3次元matrixによって、必要な全組み合わせが並列ビルドされます。このワークフローは workflow_call トリガーのみで起動し、既存の2つのワークフロー(タグ起動・手動起動)から uses: で呼び出される構成です。

入力正規化スクリプトの追加

手動トリガーワークフロー(publish-new-ruby-versions.yaml)の入力形式が改善されました。従来はJSONアレイ形式(例: ["3.3.1","3.2.4"])での入力が必要でしたが、カンマまたは改行区切りのテキスト(例: 3.3.1, 3.2.4)を受け付けるようになっています。

module Commands
  class PublishInputNormalizer
    def call
      ruby_versions = normalize_list(ruby_versions_input)
      raise Error, "Ruby versions input is empty" if ruby_versions.empty?

      images_source = image_versions_input
      if blank_list?(images_source)
        images_source = latest_tag_fetcher.call(repository)
      end

      image_versions = normalize_image_versions(normalize_list(images_source))
      raise Error, "Image versions input is empty" if image_versions.empty?

      {
        ruby_versions: ruby_versions,
        image_versions: image_versions,
        ruby_versions_json: JSON.generate(ruby_versions),
        image_versions_json: JSON.generate(image_versions)
      }
    end
  end
end

PublishInputNormalizer クラスは入力文字列の正規化、ruby- プレフィックスの付与、およびimage_versionsが空の場合にリポジトリの最新タグを自動取得する機能を持ちます。latest_tag_fetcher をDIとして外部から注入できる設計になっており、テストでモック差し替えが容易です。bin/normalize-publish-inputs スクリプト経由でGitHub Actionsのステップから呼び出され、出力は GITHUB_OUTPUT 形式でセットされます。

設計判断

アーキテクチャごとにジョブを分割してネイティブランナーで実行するアプローチ が採用されました。

代替案として単一ランナーでQEMUを使いつつ並列化する方法も存在しますが、このPRではネイティブビルドを選択することでエミュレーションオーバーヘッドを根本から排除しています。matrixのアーキテクチャ軸をワークフローYAML内に宣言的に定義することで、将来的なアーキテクチャ追加も行次元のエントリ追加だけで対応できます。

再利用可能ワークフロー(workflow_call)によるロジックの集約は、タグ起動・手動起動の2つのエントリポイントでビルド定義が重複していた問題を解消するものです。また入力正規化のロジックをRubyクラスとして実装することで、シェルスクリプトで実装するよりもテスタビリティを高めており、実際に Minitest によるユニットテストとスクリプトレベルの統合テストが追加されています。

まとめ

QEMUエミュレーションの廃止によりarm64イメージのビルド速度を改善しつつ、再利用可能ワークフローと入力正規化スクリプトの導入でワークフロー定義の重複と運用上の煩雑さを同時に解消した変更です。ネイティブランナーへの移行・宣言的なmatrix定義・テスト付きのRubyクラスへのロジック分離という3つの判断が組み合わさることで、ビルドパイプラインの速度と保守性が同時に向上しています。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
7c6220c7

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

「リード文(総論)→ 背景・技術詳細・設計判断(各論)→ まとめ(結論)」という構成が明確で、非常に分かりやすいです。

カスタムMarkdown構文 ✓ PASS

シンタックスハイライト・GitHubリンク記法の正確性

ファイル名付きのシンタックスハイライト、PR番号のリンク記法など、全てのカスタムMarkdown構文が正しく使用されています。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

GitHub ActionsやDockerイメージビルドに関する専門的な内容であり、対象読者であるエンジニアに適した技術レベルと表現で記述されています。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクション・各パラグラフが要点から始まる構成になっており、構造的で読みやすいです。1段落1トピックの原則も守られています。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事内のコードブロックは、提供されたDiffの内容を正確に、かつ要点を押さえて引用しています。ファイルパスの指定も正確です。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

QEMUエミュレーション、multi-archマニフェスト、matrixビルドなど、関連する技術用語が正確かつ適切に使用されています。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

ワークフローの変更内容やスクリプトの役割に関する説明は、Diffの内容と一致しており、技術的に正確です。

事実の突合 ⚠ WARNING

PR情報による主張の裏付け(ハルシネーション検出)

記事の主張の大部分はPR情報で裏付けられていますが、「arm64ランナーが利用可能になった」という背景説明はPRに明記されておらず、外部知識に基づく補足となっています。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR番号(#124)やファイルパス、YAML内のランナー名などがすべて正確に記載されています。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事のタイトル「QEMUエミュレーションを廃止してarm64ネイティブビルドへ移行」は、PRのタイトル「Faster image build」の具体的な方法を示しており、PRの内容を的確に表現しています。

外部知識の正確性 ⚠ WARNING

PRに記載のない外部知識(LTS、サポート状況など)の不使用

「GitHub Actionsでは...利用可能になったことで」という背景説明は、PRに明記されていない外部の時系列情報を含んでいます。ただし、Diff内のランナー名から推測可能な範囲であり、読者の理解を助ける有益な補足情報ではあります。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

「変更されました」「追加され」など、完了した変更を報告する時間表現が適切に使用されており、PR情報との矛盾はありません。