ひつじTips

技術系いろいろつまみ食います。

Docker Hub無課金勢(Personal Plan)でAutomated Buildが使えなくてもgithubからDockerイメージを自動でBuild & Pushしたい!

Docker Hubを見ていると,「Source Repository」にGithubリポジトリのリンクがあるものが多いです.

ということで,githubにあるDockerfileを自動ビルドしてDocker Hubに登録してくれる機能とかあるんだろうなと思って「github dockerhub 連携」とかでググると,みんなDocker Hubの「Automated Build」なるものを使っていることがわかります.

各記事見てると,まず「Docker Hubのアカウント設定からLinked Accountsを選んでGithubを連携させましょう」とか書いてるのですが,僕のDocker HubにはLinked Accountsなんてありません......🤔

よくよく公式ドキュメントを読むと,Automated BuildはDocker Hub課金勢(Pro, Teams, Business)のみの機能と書かれているではないですか...!😩😩

f:id:mu-777:20220108164506p:plain
Set up Automated Builds | Docker Documentation より


Docker Hubに課金してもよいのですが,一番安いProプランでも月5ドルという...

高くはないですが,エンジョイ趣味勢で月額課金はツラいです.ここは無課金で乗り切りたいところ.


前置きが長くなりましたが,こういうことで,Docker Hub無課金勢(Personal Plan)でもgithubからDockerイメージを自動でBuild & Pushする方法についてまとめます.

方針としては,Github Actionsを使ってCI/CD的に実施します.が,筆者はGithub Actionsを使うのは今回が初めて(お仕事はGitLab...)なので,問題あるかもですが責任は取りません.あくまでご参考にどうぞ(免責)

ほぼここに書かれてる内容になるので,分かる人はこちらを見てください.

docs.github.com

もしくは,今回使用するyamlファイルをご覧ください.

github.com

前提

以下はすでにあるものとします

  • Githubのアカウント
  • Docker HubにPushしたいDockerイメージのDockerfile
  • そのDockerfileを含むGithubリポジトリ
  • Docker Hubのアカウント

今回の記事では,以下のリポジトリを使ってます

github.com

ちなみに,Docker Hub側にリポジトリを用意する必要は必ずしもありません

Docker Hubのアカウント情報をGithubに設定する

Github ActionsでDocker Hubへログインする都合上,GithubリポジトリにDocker Hubのユーザ名とパスワードを登録します.ここでは,GithubEncrypted secrets機能を使います.

Githubのレポジトリの「Settings > Secrets」から設定できます.

f:id:mu-777:20220108180114p:plain
Encrypted secretsの登録


ここに,「DOCKERHUB_USERNAME」というNameでDocker Hubのユーザ名を,「DOCKERHUB_PASSWORD」というNameでDocker Hubのパスワードを登録します.

f:id:mu-777:20220108175924p:plain
ユーザ名の登録

Github Actionsのワークフローの設定

githubリポジトリのルートから.github/workflowsというディレクトリを切って,build-and-push-image.ymlというファイルを作り(ファイル名は任意です),以下を記述します.

name: Build and Push image to Docker Hub

on:
  push:
    branches:
      - master

jobs:
  build_and_push:
    runs-on: ubuntu-latest
    steps:
      - name: Check out the repo
        uses: actions/checkout@v2

      - name: Log in to Docker Hub
        uses: docker/login-action@v1
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_PASSWORD }}

      - name: Extract metadata for Docker
        id: meta
        uses: docker/metadata-action@v3
        with:
          images: mu777/odrive-cli

      - name: Build and push Docker image
        uses: docker/build-push-action@v2
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}


これはこちらのドキュメントから持ってきたものです.

docs.github.com

ここでは,masterブランチでpushされた場合にこれが発火するように設定しています.適宜onの項目を変更してください.

使用しているアクションは以下です

「Extract metadata for Docker」ステップのwith: imagesのところは適宜ご自身のDocker Hubの「namespace/repository」に変更してください.

GithubへPush(から,自動でDockerイメージのBuild & Push)

追加したワークフローをコミットして,GithubへPushしましょう.

$ git add .github
$ git commit -m "Add github actions"
$ git push origin master


Githubリポジトリの「Action」から実行の結果を確認できます.

f:id:mu-777:20220108181948p:plain
Github Actionsの実行確認

Docker Hubを見ると,イメージがpushされていることがわかります.事前にDocker Hub側にリポジトリを作っていなかった場合は自動でリポジトリも作成されます.

f:id:mu-777:20220109025146p:plain
Docker Hubのリポジトリ

ローカルでdocker pullすると,ちゃんとDocker Hubから持ってくることができました!🎉🎉

f:id:mu-777:20220108182917p:plain
docker pullした様子
(開発時にdocker buildしているのでだいたい「Already exists」になってます)

その他

上記手順が目的への最小ステップと思われますが,更に追加でやりたいことを以下に挙げます.

Dockerイメージにlatestタグをつけたい!

上記手順だと,ブランチ名がタグになってしまいます.

f:id:mu-777:20220109025425p:plain
masterというtagになっている

手順

「Docker Metadata action」にflavorパラメータを追加します.

      - name: Extract metadata (tags, labels) for Docker
        id: meta
        uses: docker/metadata-action@v3
        with:
          images: mu777/odrive-cli
+         flavor: latest=true


「Docker Metadata action」ではもっと様々な条件でtag名を変更させることができるようですが,ここでは深入りしません.

詳細はドキュメントをご覧ください.

github.com

GithubのReadmeをDocker Hub側にも表示させたい!

上記手順だけだと,Docker HubのReadmeはDocker Hub側で作成する必要があります.

さすがにGithubとDocker Hubのダブルメンテは面倒なので,共有できるようにしましょう.
(これも,Automated Buildだと自動でできるみたいです... 金が欲しい......)

手順

Docker Hub Description アクションを使います.

github.com

以下のStepをgithub/workflows/build-and-push-image.ymlの一番最後に追加します.

      - name: Docker Hub Description
        uses: peter-evans/dockerhub-description@v2
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_PASSWORD }}
          repository: mu777/odrive-cli
          readme-filepath: ./README.md
          short-description: ${{ github.event.repository.description }}


このアクションは個人の方が作られているものなので,気になる方はご注意ください.

ソースを見る感じパスワードを抜いたりしているわけではないと思われますが,改悪が起こる可能性も無きにしもあらず......

(まぁ他で使っているDocker公式のアクションも「not certified by GitHub」ではあるのですが......)

複数CPUアーキテクチャ対応のイメージを自動ビルドしたい!

上記手順のruns-on: ubuntu-latest環境だと,AMD64(x86-64)でDockerビルドが実行されます. 実際に確認したわけではないですが,これだとARMなど他の環境だと動かないはずです.

f:id:mu-777:20220109010422p:plain
Docker Hubでも「amd64」だけになっています


これを他のCPUアーキテクチャでも動くイメージもbuild & pushしておくようにしましょう.

手順

Docker Setup Buildx アクションで,複数CPUアーキテクチャでDockerビルドするための機能であるDocker Buildxを使います.

github.com

以下の3つのStepを,github/workflows/build-and-push-image.ymlの「Build and push Docker image」の前のどこかに追加します.

      - name: Set up QEMU
        uses: docker/setup-qemu-action@v1

      - name: Set up Docker Buildx
        id: buildx
        uses: docker/setup-buildx-action@v1

      - name: Available platforms
        run: echo ${{ steps.buildx.outputs.platforms }}


  • 1つ目のQEMUは,CPUのエミュレータとのことです
  • 2つ目は上で説明したDocker Buildxの設定になります
  • 3つ目は単にBuild可能な環境をログ上で確認するためのStepです

さらに,「Build and push Docker image」ステップのwith項にplatforms: linux/amd64,linux/arm64,linux/386,linux/arm/v7,linux/arm/v6を追加し,対応させるアーキテクチャを指定します.

      - name: Build and push Docker image
        uses: docker/build-push-action@v2
        with:
          context: .
          push: true
+         platforms: linux/amd64,linux/arm64,linux/386,linux/arm/v7,linux/arm/v6
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}


これらのStepを加えたアクションを実行後,Docker Hubで複数アーキテクチャが登録されていることを確認できます.

f:id:mu-777:20220109012111p:plain
対応アーキテクチャの確認@Docker Hub

注意: ベースイメージの対応アーキテクチャ

ベースイメージが対応していないアーキテクチャではビルドできないのでご注意ください.

今回の例で言うと,ベースイメージのpythonイメージが対応しているアーキテクチャは以下です.

f:id:mu-777:20220109002403p:plain
pythonイメージの対応アーキテクチャ


もちろんこれ以外のアーキテクチャではベースイメージが使えないので,以下のようなエラーでGithub Actionsがコケます.

Error: buildx failed with: error: failed to solve: python:3.7: no match for platform in manifest sha256:d9abbc0737ff8d23a546859c85903f1b8235a1495a405d5a47cbc55747f27b20: not found

f:id:mu-777:20220109002721p:plain
ベースイメージが非対応のCPUアーキテクチャでビルドしようとしたときのエラーメッセージ

まとめ

結局一番言いたかったのは,最初のAutomated BuildはDocker Hub課金勢(Pro, Teams, Business)のみということだけでした(地味にその情報にたどり着けずにハマってしまった).

とりあえず,「その他」の項を含めて全ての内容をまとめたbuild-and-push-image.ymlの中身を最後に載せておきます.

name: Build and Push image to Docker Hub

on:
  push:
    branches:
      - master

jobs:
  build_and_push:
    runs-on: ubuntu-latest
    steps:
      - name: Check out the repo
        uses: actions/checkout@v2

      - name: Log in to Docker Hub
        uses: docker/login-action@v1
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_PASSWORD }}

      - name: Set up QEMU
        uses: docker/setup-qemu-action@v1
      - name: Set up Docker Buildx
        id: buildx
        uses: docker/setup-buildx-action@v1
      - name: Available platforms
        run: echo ${{ steps.buildx.outputs.platforms }}

      - name: Extract metadata (tags, labels) for Docker
        id: meta
        uses: docker/metadata-action@v3
        with:
          images: mu777/odrive-cli
          flavor: latest=true

      - name: Build and push Docker image
        uses: docker/build-push-action@v2
        with:
          context: .
          push: true
          platforms: linux/amd64,linux/arm64,linux/386,linux/arm/v7,linux/arm/v6
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}

      - name: Docker Hub Description
        uses: peter-evans/dockerhub-description@v2
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_PASSWORD }}
          repository: mu777/odrive-cli
          readme-filepath: ./README.md
          short-description: ${{ github.event.repository.description }}