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.
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.
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}
# ...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With