Jetsonの為にx86_64でARM用イメージをマルチアーキテクチャビルド

毎度、ググっても出てこない小ネタを取り扱っております。
本記事は個人的な見解であり、筆者の所属するいかなる団体にも関係ございません。

# 0. はじめに

Jetson向けのDockerコンテナイメージを作りたいのですが、GitHub Actionsで作る方法をご紹介します。

# 1. マルチアーキテクチャビルドって何？
複数のCPUアーキテクチャ（例：x86_64、ARM64など）に対応したDockerイメージを、一度にビルド・配布するプロセスです

対応が必要なのはイメージをビルドする人 (開発者)のみで、イメージを利用する人はただdocker pull するだけでアーキテクチャに適したイメージをpullできます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/15176/ccdbfb30-2939-7dfe-ee83-d34a093c9647.png)

# 1-1. Jetson用のコンテナイメージをビルドする方法

x86_64とARMの両方で開発する場合以下の3つの方法が考えられます
1. Qemuエミュレーションによるマルチアーキテクチャビルド
2. x86_64とARMのRunnerによるマルチビルド
一番おすすめなのはマルチアーキテクチャビルドです

|ビルド方法|メリット|デメリット|
|--|--|--|
|Qemuエミュレーションによるマルチアーキテクチャビルド|・開発者はアーキテクチャを気にすることなくDockerfileを利用してビルドできる<br>・1回のビルドで2種類のコンテナイメージが作成できる|・1回のビルドで2つ作るのでビルド時間が長くなる|
|x86_64とARMのRunnerによるマルチビルド|・CPUに応じたコンパイルが必要な場合にも事前のコンパイルなどは不要で、それぞれのCPUで実際にビルドできる|・GitHub ActionsのデフォルトのRunnerではx86しかないため、ARMのRunnerが必要<br>・管理も必要|

## 1-2. GitHub ActionsでのQEMUでのビルド
GitHub Actionsではsetup-qemu-actionを使うことによりqemuでビルド可能です
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/15176/1cdf9076-65ac-e4c3-8bcf-5543ab018268.png)

## 1-3. x86_64とARMのRunnerでのビルド
GitHub ActionsではセルフホステッドRunnerをJetsonで動かす事によりビルド可能です
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/15176/9b3a2e1f-c57c-4cb4-8a04-eb7b1fd1e5fc.png)


# 2. マルチアーキテクチャビルドの手順

以下の手順でマルチアーキテクチャービルドを行います
1. マルチアーキテクチャー対応のベースイメージを利用する
  - ビルドするイメージが複数のアーキテクチャに対応していることを確認してください
  - 公式のイメージは、複数のアーキテクチャに対応しているものが多いです
2. Dockerfileをアーキテクチャに依存しないように記述する
3. buildxを利用したマルチアーキテクチャービルド

# 3. 手順

Dockerのバージョンを確認:Docker 19.03以降が必要です。確認するには、以下のコマンドを実行してください。
```
docker --version
```
ビルダーインスタンスの作成と初期化
```
docker buildx create --name mybuilder
docker buildx use mybuilder
docker buildx inspect --bootstrap
```

マルチアーキテクチャービルドの実行(例：amd64とarm64)
```
docker buildx build --platform linux/amd64,linux/arm64 -t yourusername/yourimagename:tag --push .
```

# 4. GitHub Actions

マルチアーキテクチャビルドするGitHub Actionsのサンプルです。

```yaml:.github/workflows/main.yml
name: docker build

# 1. Controls when the workflow will run
on:
  push:
    branches:
      - 'main'
      - 'dev'
    tags:
      - 'v*.*.*'
  pull_request:
    branches:
      - 'main'
      - 'dev'

jobs:
  main:
    runs-on: ubuntu-latest
    permissions:
      packages: write
    env:
      IMAGE_NAME: sample-app
    steps:
    # 2. setup variables
    - name: Define variables
      uses: FranzDiebold/github-env-vars-action@v2
    - name: echo Repository tags
      run: echo "$CI_REF_NAME"
    - name: Get current date
      id: date
      run: echo "today=$(date +'%Y%m%d')" >> $GITHUB_OUTPUT

    # 3. Checkout code
    - name: Checkout
      uses: actions/checkout@v3

    # 4. Login DockerHub
    - name: Login to GitHub Container Registry
      uses: docker/login-action@v2
      with:
        registry: ghcr.io
        username: ${{ github.repository_owner }}
        password: ${{ secrets.GITHUB_TOKEN }}

    # 5. setup buildx
    - name: Set up QEMU
      uses: docker/setup-qemu-action@v2
    - name: Setup Docker Buildx
      uses: docker/setup-buildx-action@v2

    # 6. Build and Push
    - name: env
      run: env | sort
    - name: Build and push
      id: docker_build
      uses: docker/build-push-action@v4
      with:
        context: .
        file: ./Dockerfile
        platforms: linux/amd64,linux/arm64
        # 7. nameing
        tags: |
          ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}/${{ github.event.repository.name }}:latest
          ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}/${{ github.event.repository.name }}:${{ steps.date.outputs.today }}
        cache-from: type=gha
        cache-to: type=gha,mode=max
        outputs: type=docker,dest=/tmp/${{ github.event.repository.name }}.tar
    - name: Upload artifact
      uses: actions/upload-artifact@v3
      with:
        name: ${{ github.event.repository.name }}
        path: /tmp/${{ github.event.repository.name }}.tar
```

重要な肝は上記の5と6です。
5でマルチアーキテクチャビルドの準備を行い、
6で実際にビルドしています。`# 7. nameing`の1行上で複数のプラットフォームを指定しています。

# 5. まとめ

Github Actionsを利用すると簡単にJetson用のコンテナイメージを作成できました。