【反射神経で設定可】GitHub actionsを使ったマルチCPUアーキテクチャ対応コンテナのビルドについて


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96992/a27fcf77-e956-d6d8-a9c0-1505d2b9702f.png)

# 要約
マルチCPUアーキテクチャ対応Dockerコンテナをビルドし、DockerHubにPushする。（エラー回避の虎の巻付き!!）
クラウド上でテストが出来てハッピー！！活用できる場面が増えてみんなハッピー！！

- 0. 初回のみ。あらかじめDockerHubのシークレットを設定しておく。
   - [シークレットを設定する](#シークレットを設定する)
- 1. リポジトリのトップに`Dockerfile`を作成する。
- 2. リポジトリのトップでディレクトリを作成する。

  ```bash
  mkdir .github/workflows
  ```

- 3. アクションファイルを作成する。8割方のユースケースでは変更不要。仮称：反射神経的設定ファイル
   - [docker_build.yaml](#yamlファイル)
- 4. GitHubにpushする。

以上
詳細については、記事本文を参照ください。


# はじめに
こんにちは。RaspberryPi(armhf、arm64)と、クラウド上のサーバー(amd64)を日々反復横跳びしている者です。  

[rdbox\-intec/rdbox: GitHub](https://github.com/rdbox-intec/rdbox)で公開しているOSSをメインでメンテナンスしている通り、RaspberryPiをそこそこ使い倒している私ですが、ARMプロセッサの利用拡大は、驚くべき勢いと感じています。特に大きなトピックはやはり「Apple M1プロセッサ」を搭載した各Macが発売され、そのワットパフォーマンスの高さを示したことでしょうか。

今後は、CPUアーキテクチャによる境界線は今よりも緩いものとなり、一度書いたらどこでも動くような、かつてJの何者かが通ってきた道を再び歩み始めることが予測されます。

本記事では、マルチCPUアーキテクチャ対応のdocker imageを、GitHub actionsのテンプレを使って、思考によるダウンタイム無しでビルドすることを促すために書きました。

あなたが取るに足らないと思ったそのDockerイメージは、誰かが喉から手が出るほど欲しかったDockerイメージかもしれない。（名言風）

# 技術要素
## docker buildx
dockerイメージのビルド方法として、buildxという新しい コマンドライン・ツールが実験的に提供されています。(2021/02/16)
これで作成されたdockerイメージはマルチCPUアーキテクチャをサポートしています。つまり１つのイメージ内に、異なるアーキテクチャを含ませることが出来る仕組みが実現できます。
マルチCPUアーキテクチャをサポートしているイメージの実行時、 dockerは自動的にOSとアーキテクチャに一致した、対応しているイメージを選択します。
例えば、良く使われるbusyboxイメージがサポートするのはamd64、 arm32v6、arm32v7、arm64v8、i386、ppc64le、s390xです。
ターゲットとするアーキテクチャ向けに、dockerイメージをビルドする時にはDockerfileやソースコードに一切手を加える必要はありません。

[公式情報](https://docs.docker.com/buildx/working-with-buildx/)

## GitHub actions
GitHub actionsはGitHubが公式で提供するCI/CDパイプライン作成のための枠組みです。古より、CircleCIなどGitHubの外側でパイプライン構築を助けてくれるサービスはありました。しかし、GitHub社が提供してくれるという点は偉大であり、ジャパニーズトラディッショナル企業のように新規に請求先を追加することに一苦労するようなケースが散見される場合に大きな威力を発揮する。（なお、プライベートリポジトリは2000時間、パブリックリポジトリは無制限無料です。）
機能面でも、後発ということもあり優れた点が多数あります。最大の特徴はコミュニティが開発・管理する「アクション」です。これらは数多く提供されており、ユーザ側はこれらを組み合わせてより少ないコストでCI/CDパイプラインが作れます。
[GitHub Marketplace · Actions to improve your workflow](https://github.com/marketplace?type=actions)

なお、パイプラインの定義は`.github/workflows`ディレクトリに、YAMLファイルを置くことで定義します。

[公式情報](https://github.co.jp/features/actions)

## Docker社によって公開されているアクション
[Docker V2 Github Action is Now GA \- Docker Blog](https://www.docker.com/blog/docker-v2-github-action-is-now-ga/)
[Build and push Docker images · Actions · GitHub Marketplace](https://github.com/marketplace/actions/build-and-push-docker-images)

というわけで、GitHub actionsには、`docker buildx`を始めとする最新機能を織り交ぜたアクションが2020年10月末にGAされています。
詳細に関しては、上記公式ブログの通りなのですが、個人の見解としては、GitHub Actionsを使うことで従来のCI/CDパイプラインと大きく異なり設定ファイルベースで全てのことが解決できる点は非常に魅力的です。

# 要約の解説
## 0. 初回のみ。あらかじめDockerHubのシークレットを設定しておく。
### シークレットを設定する
Push先となるユーザ名(DOCKERHUB_USERNAME)及びトークン(DOCKERHUB_TOKEN)を設定します。GitHub上で設定します。これは、Docker HubへイメージをPushするために必要な情報です。

#### Docker Hub上での操作
本投稿では、セキュリティ上の観点からDocker HubへのアクセスにAPIトークンを払い出して使用することとします。
次の手順に従い、払い出して下さい。なお、APIトークンは、1度しか表示されないのでしっかりメモしておいて下さい。
[Managing access tokens \| Docker Documentation](https://docs.docker.com/docker-hub/access-tokens/)
成功すると、[Profile](https://hub.docker.com/settings/security)画面でこのような表示が確認できます。なお、シークレット名は任意です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96992/5ba060e1-b99a-473f-5c9c-9821190f7c88.png)


#### GitHub上での操作
次の手順に従い、登録して下さい。こちらのドキュメントの通り、シークレットの登録方法はいくつかあってリポジトリ毎だったり、環境毎だったり、組織毎だったり。用途に合わせて選択しましょう。
[暗号化されたシークレット \- GitHub Docs](https://docs.github.com/ja/actions/reference/encrypted-secrets)
成功すると、シークレット一覧画面でこのような表示が確認できます。項目名`DOCKERHUB_USERNAME`、`DOCKERHUB_TOKEN`となっていることを確認して下さい。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96992/3804f6b3-9c14-e12e-02d1-581c0c5f7136.png)

## 1. リポジトリのトップにDockerfileを作成する。
いつもどおり、作成すれば良いです。FROMとして指定するベースイメージをOfficialイメージとして使用することを推奨します。投稿されているLinuxディストリビューションのイメージの多くがマルチーアーキテクチャに対応しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96992/2c2c736a-8749-8ecf-1859-6588c1c94ec0.png)
（念の為、[Docker Hub](https://hub.docker.com/)でマルチーアーキテクチャ対応しているか確認しましょう。）

### Dockerfileでcmakeする際の注意
[compiler\_id\_detection fails for armhf when using QEMU user\-mode emulation \(\#20568\) · Issues · CMake / CMake · GitLab](https://gitlab.kitware.com/cmake/cmake/-/issues/20568)
Issueで上がっている通り、ARMv7をエミュレートしたQEMU上で`cmake v3.16`を使ってビルドしようとするとエラーになるという既知の問題があります。`Ubuntu 20.04`でapt-getで取れてくるcmakeがこれに該当します。その解決策としてARMv7時のみ、最新のcmakeをインストールするように細工する必要があります。以下の実装を参考にしてみて下さい。

```Dockerfile:Dockerfile
FROM ubuntu:20.04

COPY ./helper_script_for_armhf.sh /helper_script_for_armhf.sh  # 細工部分
RUN /helper_script_for_armhf.sh                                # 細工部分

COPY ./source_dir /source_dir

WORKDIR /source_dir
RUN cmake .
```

```bash:helper_script_for_armhf.sh
#!/bin/bash

set -xeu

architecture=$(dpkg --print-architecture)

if [ "$architecture" = "armhf" ]; then
  apt-get install -y --no-install-recommends \
                                  apt-transport-https \
                                  ca-certificates \
                                  gnupg \
                                  software-properties-common \
                                  wget
  wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | gpg --dearmor - | tee /etc/apt/trusted.gpg.d/kitware.gpg >/dev/null && \
  apt-add-repository 'deb https://apt.kitware.com/ubuntu/ focal main' && \
  apt-get update && apt-get install -y cmake=3.19.2-0kitware1ubuntu20.04.1 \
                                       cmake-data=3.19.2-0kitware1ubuntu20.04.1
fi
```


## 2. リポジトリのトップでディレクトリを作成する。
特に説明は無いですが、GitHub Actionsは`.github/workflows`にあるアクションファイルを読み込み動作します。

## 3. アクションファイルを設定する
### YAMLファイル

```yaml:docker_build.yaml
name: docker build

# 1. Trigger
on:
  push:
    branches:
      - main

jobs:
  main:
    runs-on: ubuntu-latest
    steps:
    # 2. setup variables
    - uses: FranzDiebold/github-env-vars-action@v2
    - name: Get current date
      id: date
      run: echo "::set-output name=today::$(date +'%Y%m%d')"
    # 3. Checkout code
    - name: Checkout
      uses: actions/checkout@v2
    # 4. Login DockerHub
    - name: Login to DockerHub
      uses: docker/login-action@v1
      with:
        username: ${{ secrets.DOCKERHUB_USERNAME }}
        password: ${{ secrets.DOCKERHUB_TOKEN }}
    # 5. setup buildx
    - name: Set up QEMU
      uses: docker/setup-qemu-action@v1
    - name: Setup Docker Buildx
      uses: docker/setup-buildx-action@v1
    # 6. Build and Push
    - name: Build and push
      id: docker_build
      uses: docker/build-push-action@v2
      with:
        context: .
        file: ./Dockerfile
        platforms: linux/amd64,linux/arm/v7,linux/arm64
        push: true
        # 7. nameing
        tags: |
          ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.CI_REPOSITORY_NAME }}:${{ steps.date.outputs.today }}
          ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.CI_REPOSITORY_NAME }}:latest
```

### アクションファイル解説
細かい書き方については、公式のチュートリアルなどを参考にして頂ければと思います。

1. mainブランチにpushされたら、本アクションは起動します。 (1. Trigger)
   - masterのままのリポジトリは変更。また、GitHub上で行ういろんなアクションをトリガーに指定できるので都合に合わせて書き換えましょう
   - [ワークフローをトリガーするイベント \- GitHub Docs](https://docs.github.com/ja/actions/reference/events-that-trigger-workflows)
2. このファイルの場合は、GitHubリポジトリ名と同じ名前のDockerHubリポジトリ名を作成する前提で作っています。また、Dockerのイメージタグをlatestと日付で作る想定です。そのための変数をセットアップしています。(2. setup variables)
   - Dockerイメージとして使えない文字種の場合エラーになることに注意。（記号、大文字小文字等注意）
   - 他のイメージ名としたい場合は(7. nameing)を変更して下さい。
3. GitHubからコードをチェックアウトする必要があります。GitHub公式より提供されているアクションです。
4. DockerHubへのログイン操作。先に設定したGitHubシークレットを参照します。
5. buildxを使うために、Docker社から提供されているアクションを複数宣言する必要があります。
   - docker/setup-qemu-action@v1
   - docker/setup-buildx-action@v1
6. 実際にDockerイメージをビルド&プッシュします。
   - platformsとして、ターゲットとなるアーキテクチャを指定します。注意する必要があるのは、ここに指定するアーキテクチャは`Dockerfile`の`FROM`で指定するベースイメージにも存在するアーキテクチャを選択することです。存在しないものを指定するとエラーとなります。

# おわりに
手元のマシンのCPUアーキテクチャを気にせずに開発する時代が近づいているような気がします。AWSなどのメガクラウドベンダーもARMの仮想マシンを提供しています。[Amazon EC2 A1 インスタンス \| AWS](https://aws.amazon.com/jp/ec2/instance-types/a1/)
特に、いろんなCPUアーキテクチャが絡んでくる、ロボット界隈・IoT界隈ではこの動きはより活発になるのではないでしょうか。
同じ作業の繰り返しが少しでも減ることを願いつつ、この記事を締めたいと思います。

# 追記
環境変数の参照方法が古かったので修正しました。（2021/02/18）
サンプル置いておきます。（2021年2月17日）
[rdbox\-intec/ros\-core\-catkinws: GitHub](https://github.com/rdbox-intec/ros-core-catkinws)
[rdbox/ros\-core\-catkinws: DockerHub](https://hub.docker.com/r/rdbox/ros-core-catkinws)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96992/7a8d3d54-f32a-e703-d06b-b782878108f5.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96992/bed6ef13-601b-01f0-ba6f-3f0dd0e9fc0b.png)
こんな感じでビルド結果が表示されます。