Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Build docker image locally in GitHub Actions using docker/build-push-action

I have several Dockerfiles in my project. One is for building basic image, which contains some business-level abstractions. Others are building services, based on the basic image.

So in my services' Dockerfiles I have something like

FROM my-project/base
# Adding some custom logic around basic stuff

I am using GitHub Actions as my CI/CD tool. At first I had a step to install docker into my workers, and then ran something like:

- name: Build base image
  working-directory: business
  run: docker build -t my-project/base .

- name: Build and push service
  working-directory: service
  run: |
    docker build -t my-ecr-repo/service .
    docker push my-ecr-repo/service

But then I've found docker/build-push-action and decided to use it in my pipeline:

- name: Build business-layer container
  uses: docker/build-push-action@v2
  with:
    load: true
    tags: my-project/base
    context: business
    file: business/Dockerfile

- name: Build service
  uses: docker/build-push-action@v2
  with:
    push: true
    tags: my-ecr-repo/service
    context: service
    file: service/Dockerfile

As for now, the second step tries to download docker.io/my-project/base, and obviously cannot do it, because I never push base image:

ERROR: pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed

The question is: What is the correct way to build an image, so it is accessible by the following building steps locally?

PS: I don't want to push my naked basic image anywhere.

like image 283
MartinSolie Avatar asked Sep 14 '20 15:09

MartinSolie


People also ask

Can GitHub Actions build Docker image?

GitHub Action to build and push Docker images with Buildx with full support of the features provided by Moby BuildKit builder toolkit. This includes multi-platform build, secrets, remote cache, etc. and different builder deployment/namespacing options.


1 Answers

I believe you'll need to set load: true in both your base image and the final image. This changes the behavior to use the local docker engine for images. I believe you'll need to run a separate push if you do this, e.g.:

- name: Build business-layer container
  uses: docker/build-push-action@v2
  with:
    load: true
    tags: my-project/base
    context: business
    file: business/Dockerfile

- name: Build service
  uses: docker/build-push-action@v2
  with:
    load: true
    tags: my-ecr-repo/service
    context: service
    file: service/Dockerfile

- name: push service
  run: |
    docker push my-ecr-repo/service

The other option is to use a local registry. This has the advantage of supporting multi-platform builds. But you'll want to switch from load to push with your base image, and I'd pass the base image as a build arg to make it easier for use cases outside of Github actions, e.g.:

jobs:
  local-registry:
    runs-on: ubuntu-latest
    services:
      registry:
        image: registry:2
        ports:
          - 5000:5000
    steps:
      - name: Login to DockerHub
        uses: docker/login-action@v1 
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}
      # qemu should only be needed for multi-platform images
      - name: Set up QEMU
        uses: docker/setup-qemu-action@v2
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v1
        with:
          driver-opts: network=host
      - name: Build business-layer container
        uses: docker/build-push-action@v2
        with:
          push: true
          tags: localhost:5000/my-project/base
          context: business
          file: business/Dockerfile
      - name: Build service
        uses: docker/build-push-action@v2
        with:
          push: true
          tags: my-ecr-repo/service
          context: service
          file: service/Dockerfile
          build-args: |
            BASE_IMAGE=localhost:5000/my-project/base

And then your Dockerfile would allow the base image to be specified as a build arg:

ARG BASE_IMAGE=my-project/base
FROM ${BASE_IMAGE}
# ...
like image 116
BMitch Avatar answered Sep 19 '22 21:09

BMitch