2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

GoReleaser でマルチプラットフォーム対応の Docker イメージを作る

Last updated at Posted at 2022-09-26

Go のクロスコンパイルやらリリースやらの面倒を見てくれる便利な GoReleaser ですが、Docker イメージのビルドにも対応しています。

この記事では、GoReleaser を使用してマルチプラットフォーム (マルチ CPU アーキテクチャ) 対応の Docker イメージを作成する方法について紹介します。

記事中で使用する Go のバージョンは 1.19.1、GoReleaser のバージョンは 1.11.4 です。

サンプルプロジェクト

次のようなサンプルプロジェクトを例として扱います。

$ tree
.
├── go.mod
└── main.go
go.mod
module github.com/frozenbonito/helloworld

go 1.19
package main

import "fmt"

func main() {
	fmt.Println("Hello, world!")
}

このサンプルプロジェクトから linux/amd64, linux/arm64 両対応の Docker イメージを作成します。

Dockerfile を作る

まずは Dockerfile を作成します。
Go のビルド自体は GoReleaser がやってくれるので、ビルド後のバイナリを COPY する感じで書けば OK です。

今回のサンプルプロジェクトの場合、module 名が helloworld なのでデフォルトでは helloworld というバイナリが生成されるはずです。

そのため Dockerfile は次のようになります。

Dockerfile
FROM gcr.io/distroless/static-debian11
COPY helloworld /
ENTRYPOINT [ "/helloworld" ]

重要なのは COPY 命令の部分だけで、他は自由にカスタマイズ可能です。

GoReleaser の設定ファイルを作る

次に GoReleaser の設定ファイル .goreleaser.yaml を作成します。

今回書く設定は主に Go のビルドに関する設定、Docker イメージのビルドに関する設定、Docker マニフェスト (マルチプラットフォーム対応イメージを実現するもの) に関する設定の 3 つに分けられます。

.goreleaser.yaml
project_name: helloworld
builds:
  # Go のビルドに関する設定
  # ...
dockers:
  # Docker イメージのビルドに関する設定
  # ...
docker_manifests:
  # Docker マニフェストのビルドに関する設定
  # ...

Go のビルドに関する設定

次のように linux/amd64, linux/arm64 向けのビルドを行うように設定します。

.goreleaser.yaml
builds:
  - env:
      - CGO_ENABLED=0
    goos:
      - linux
    goarch:
      - amd64
      - arm64

Docker イメージのビルドに関する設定

linux/amd64 用と linux/arm64 用の 2 つの設定を用意します。

今回は DockerHub に push することを想定して、frozenbonito/helloworld というイメージ名を使うことにします。
また、セマンティックバージョニングに従ってイメージにタグを付けることにします。

.goreleaser.yaml
dockers:
  - image_templates:
      - frozenbonito/helloworld:latest-amd64
      - frozenbonito/helloworld:{{ .Version }}-amd64
      - frozenbonito/helloworld:{{ .Major }}-amd64
      - frozenbonito/helloworld:{{ .Major }}.{{ .Minor }}-amd64
    use: buildx
    goos: linux
    goarch: amd64
    build_flag_templates:
      - --platform=linux/amd64
  - image_templates:
      - frozenbonito/helloworld:latest-arm64
      - frozenbonito/helloworld:{{ .Version }}-arm64
      - frozenbonito/helloworld:{{ .Major }}-arm64
      - frozenbonito/helloworld:{{ .Major }}.{{ .Minor }}-arm64
    use: buildx
    goos: linux
    goarch: arm64
    build_flag_templates:
      - --platform=linux/arm64

この設定により、タグ v1.0.0 をリリースした場合に次のようなイメージが作成されます。

  • linux/amd64 向けイメージ
    • frozenbonito/helloworld:latest-amd64
    • frozenbonito/helloworld:1.0.0-amd64
    • frozenbonito/helloworld:1-amd64
    • frozenbonito/helloworld:1.0-amd64
  • linux/arm64 向けイメージ
    • frozenbonito/helloworld:latest-arm64
    • frozenbonito/helloworld:1.0.0-arm64
    • frozenbonito/helloworld:1-arm64
    • frozenbonito/helloworld:1.0-arm64

いくつかポイントがあります。

  • Docker でクロスプラットフォームビルドを行うには実験的機能である buildx を使用する必要があるため、use: buildx を指定する
  • CPU アーキテクチャ毎に適切な goarch を指定する
  • CPU アーキテクチャ毎に適切な --platform フラグを指定する
  • 各タグに -amd64, -arm64 などの分かりやすいサフィックスをつけておく

Docker マニフェストに関する設定

Docker イメージのビルドに関する設定linux/amd64 用と linux/arm64 用のイメージを作ることはできていますが、アーキテクチャごとに別のタグとなってしまっています。
これではマルチプラットフォーム対応イメージとは言えないので Docker マニフェストを作成する設定を追加します。

.goreleaser.yaml
docker_manifests:
  - name_template: frozenbonito/helloworld:latest
    image_templates:
      - frozenbonito/helloworld:latest-amd64
      - frozenbonito/helloworld:latest-arm64
  - name_template: frozenbonito/helloworld:{{ .Version }}
    image_templates:
      - frozenbonito/helloworld:{{ .Version }}-amd64
      - frozenbonito/helloworld:{{ .Version }}-arm64
  - name_template: frozenbonito/helloworld:{{ .Major }}
    image_templates:
      - frozenbonito/helloworld:{{ .Major }}-amd64
      - frozenbonito/helloworld:{{ .Major }}-arm64
  - name_template: frozenbonito/helloworld:{{ .Major }}.{{ .Minor }}
    image_templates:
      - frozenbonito/helloworld:{{ .Major }}.{{ .Minor }}-amd64
      - frozenbonito/helloworld:{{ .Major }}.{{ .Minor }}-arm64

この設定により次のようなマニフェストが作成され、DockerHub からマルチプラットフォーム対応イメージとして利用することができるようになります。

  • frozenbonito/helloworld:latest
  • frozenbonito/helloworld:1.0.0
  • frozenbonito/helloworld:1
  • frozenbonito/helloworld:1.0

ローカルで動作を確認する

.goreleaser.yaml が作成できたらローカルで動作を確認しておきます。

GoReleaser の CLI をインストールします。

$ go install github.com/goreleaser/goreleaser@latest

Docker の buildx コマンドを使用するので、実験的機能を有効にしておく必要があります。

例えば Docker Desktop for Windows (v4.12.0) の場合は次のように右上の歯車アイコンから設定を開き、Docker Engine の experimentaltrue にして Apply & Restart します。

dockerdesktop.png

Docker イメージを push せずにビルドするには --snapshot をつけて goreleaser release を実行します。

$ goreleaser release --rm-dist --snapshot

想定通りのイメージが作成されているかを確認します。

$ docker image ls
REPOSITORY                TAG                         IMAGE ID       CREATED          SIZE
frozenbonito/helloworld   0-arm64                     7a445338dc93   5 seconds ago    3.6MB
frozenbonito/helloworld   0.0-arm64                   7a445338dc93   5 seconds ago    3.6MB
frozenbonito/helloworld   0.0.0-SNAPSHOT-none-arm64   7a445338dc93   5 seconds ago    3.6MB
frozenbonito/helloworld   latest-arm64                7a445338dc93   5 seconds ago    3.6MB
frozenbonito/helloworld   0-amd64                     8408192f6a68   5 seconds ago    3.57MB
frozenbonito/helloworld   0.0-amd64                   8408192f6a68   5 seconds ago    3.57MB
frozenbonito/helloworld   0.0.0-SNAPSHOT-none-amd64   8408192f6a68   5 seconds ago    3.57MB
frozenbonito/helloworld   latest-amd64                8408192f6a68   5 seconds ago    3.57MB

ただしこの時点では Docker マニフェストは作成されません。
これは docker manifest create の制限としてマニフェストに追加するイメージが push されている必要があるためです。

また、--snapshot をつけて実行した場合スナップショット用のバージョンが振られます。
このバージョン名は snapshot 設定でカスタイマイズ可能です。

GitHub Actions で DockerHub に push する

ここまでの手順で、リリースを実行すればマルチプラットフォーム対応の Docker イメージが DockerHub に push されるようになっています。

リリースの実行方法は色々とありますが、ここでは GitHub Actions で自動化する方法を紹介します。

次のような workflow ファイルを作成します。

.github/workflows/release.yaml
name: Release

on:
  push:
    tags:
      - v**

permissions:
  contents: write

jobs:
  build:
    name: Build
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v3
        with:
          fetch-depth: 0
      - name: Setup go
        uses: actions/setup-go@v3
        with:
          go-version: "1.19"
          cache: true
      - name: Login to Docker Hub
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}
      - name: Run goreleaser
        uses: goreleaser/goreleaser-action@v3
        with:
          args: release --rm-dist
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

GoReleaser Action の一般的な使い方に従えば OK ですが、イメージを push するために事前に DockerHub にログインしておく必要があります。
これは Docker 公式の Login Action で簡単に実現可能です。

ログインに必要な情報は GitHub リポジトリの Secret に保存します。

  • DOCKERHUB_USERNAME
    • DockerHub のユーザー名
  • DOCKERHUB_TOKEN
    • DockerHub のアクセストークン

アクセストークンは次の手順に従って Read & Write 権限を持つものを作成します。

これで GitHub への tag push をトリガーに自動でリリースが実行され、マルチプラットフォーム対応の Docker イメージが作成されるようになるはずです。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?