みーのぺーじ

みーが趣味でやっているPCやソフトウェアについて.Python, Javascript, Processing, Unityなど.

docker/setup-buildx-action で複数のイメージを扱う

GitHub Action で,docker/setup-buildx-actionを用いて複数のイメージを扱ってみます.

前提条件

簡単な例として,pip コマンドで依存関係を解決して,結果を表示する Docker イメージをビルドします.

Dockerfile

FROM python:3.11-slim-bullseye

WORKDIR /app
COPY requirements.txt .
RUN pip install --upgrade pip
RUN pip install wheel
RUN pip install -r requirements.txt
COPY . .
CMD ["pip", "freeze"]

イメージを1個ビルドする

app/requirements.txt

flask

.github/workflows/ci.yaml

name: ci
on: push

jobs:
  docker:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2
      - name: Build app
        uses: docker/build-push-action@v4
        with:
          context: app
          push: false
          load: true
          tags: app:latest
          cache-from: type=gha
          cache-to: type=gha,mode=max
      - name: Run app image
        run: docker run app:latest

これを実行すると,初回は以下のようにflaskがインストールされます.

...
#10 3.963 Collecting flask
#10 3.992   Downloading Flask-2.2.2-py3-none-any.whl (101 kB)
#10 3.999      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 101.5/101.5 kB 19.6 MB/s eta 0:00:00
...

もう一度実行すると,キャッシュにより高速になります.

#10 [4/5] RUN pip install --upgrade pip     && pip install wheel     && pip install -r requirements.txt
#10 CACHED

以下の画面でキャッシュを削除してからもう一度実行すると,flask がインストールされます.

...
#10 3.838 Collecting flask
#10 3.856   Downloading Flask-2.2.2-py3-none-any.whl (101 kB)
#10 3.865      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 101.5/101.5 kB 15.7 MB/s eta 0:00:00
...

最終的には以下のように出力されました.

click==8.1.3
Flask==2.2.2
itsdangerous==2.1.2
Jinja2==3.1.2
MarkupSafe==2.1.2
Werkzeug==2.2.2

コマンドが正常に実行されていることがわかりました.

イメージを2個ビルドする

app の他に,別のイメージである app2 をビルドしてみます.

app2/requirements.txt

django

.github/workflows/ci.yaml

name: ci
on: push

jobs:
  docker:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2
      - name: Build app
        uses: docker/build-push-action@v4
        with:
          context: app
          push: false
          load: true
          tags: app:latest
          cache-from: type=gha
          cache-to: type=gha,mode=max
      - name: Run app image
        run: docker run app:latest
      - name: Build app2
        uses: docker/build-push-action@v4
        with:
          context: app2
          push: false
          load: true
          tags: app2:latest
          cache-from: type=gha
          cache-to: type=gha,mode=max
      - name: Run app2 image
        run: docker run app2:latest

一見うまくいきそうですが,キャッシュが使用されなくなります.異なるイメージを交互にビルドしてしまい,互いにキャッシュを破棄してしまうからです.

scope を指定する

scope=<scope>: which scope cache object belongs to (default buildkit)

GitHub - moby/buildkit: concurrent, cache-efficient, and Dockerfile-agnostic builder toolkit

name: ci
on: push

jobs:
  docker:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2
      - name: Build app
        uses: docker/build-push-action@v4
        with:
          context: app
          push: false
          load: true
          tags: app:latest
          cache-from: type=gha,scope=app # Add scope
          cache-to: type=gha,mode=max,scope=app # Add scope
      - name: Run app image
        run: docker run app:latest
      - name: Build app2
        uses: docker/build-push-action@v4
        with:
          context: app2
          push: false
          load: true
          tags: app2:latest
          cache-from: type=gha,scope=app2 # Add scope
          cache-to: type=gha,mode=max,scope=app2 # Add scope
      - name: Run app2 image
        run: docker run app2:latest

これでキャッシュが使われるようになりました.最終的には以下のような出力になり,正常にビルドできたことがわかりました.

app

click==8.1.3
Flask==2.2.2
itsdangerous==2.1.2
Jinja2==3.1.2
MarkupSafe==2.1.2
Werkzeug==2.2.2

app2

asgiref==3.6.0
Django==4.1.6
sqlparse==0.4.3