One can use Github Actions as CI/CD tool to build and push your Docker images from your repositories Dockerfile and to your Docker image registry of choice. You may want to use shell commands for that but there is a more refined way to do it in Github Actions, using existing Action Modules.
There are a lot of existing modules for Github Actions. Some of them assist you in handling Docker images and pushing them to a container registry. I want to demonstrate you, what needs to be done to create a building pipeline with Github Actions and existing Modules.
Naming the modules I used, they are docker/metadata-action
, docker/login-action
and docker/build-push-action
. I created a small hello-world
Dockerfile and the Github Action manifest in my cookbooks
Github repository here. But we will have a walkthrough here in detail.
First you will need a Dockerfile you want your pipeline to build and push later. For this demo, I created a small hello-world
Dockerfile with the following content:
FROM alpine
CMD ["echo", "Hello from the databaseme!"]
This will be enough for demonstration. Coming to the Actions now, shall we? Here’s the complete manifest, we will go through step by step below:
name: Deploy Hello World
on:
push:
branches:
- master
tags:
- "v*-hello-world"
pull_request:
branches:
- 'master'
jobs:
deploy-docker-hello-world:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Docker meta
id: meta
uses: docker/metadata-action@v4
with:
images: ghcr.io/thedatabaseme/hello-world
flavor: latest=true
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
- name: Login to image repository
if: github.ref_type == 'tag'
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GHCR_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v3
with:
context: .
file: docker/dockerfile/hello-world/Dockerfile
push: ${{ github.ref_type == 'tag' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
Let’s go from top to bottom. On line 1
we give the child a name, in this case Deploy Hello World
. The on
part starting at line 3
defines, on which situation this workflow / action should get triggered. For demonstration purpose, I decided that the workflow should run every time I push directly to the master
branch, every time, I create a pull request with the target branch master
and for all Git tags / releases I create with the semantic v*-hello-world
.
The actual job definition of the workflow starts at line 13
. I specified a job name deploy-docker-hello-world
and decided on which kind of Github Action runner I want this workflow to get executed. ubuntu-latest
is fine for me in this case. See here for more information on Github runner types. The first step of the job is to checkout the actual repository in which this workflow runs. This is needed cause we want to build something with the code we use in this repository. Therefore I use the actions/checkout
module. It needs no parameters by default. After the code has been pulled to the runner host, I use the docker/metadata-action
to specify metadata information on my Docker image which is needed later when building and pushing the image. I specify the image
as ghcr.io/thedatabaseme/hello-world
. This is the reference to the container registry I want to use and the image name. I always want to have the latest
tag attached to this image, so I configured flavor: latest=true
. Also, I specified which other image tags I want to have attached to the image. This depends on the actual event that triggered the workflow. So for pushing into a branch
, I want to have the ref
specified (which is the source branch name). The same applies for the event pr
. Also, when there is a version specified by a Git tag, I use this as a tag as well. You can find more information on the metadata-action here.
Now it’s time to login to the container registry of choice. In my case, I use the Github container registry (ghcr.io). As a username
, I use the github.actor
environment variable, which always is set when using workflows. As a password
I use a Github PAT (personal access token) which I specified as a secret in the repository settings as GHCR_TOKEN
. You might have already figured it out, I specified an if
clause for this step. A login will thereby only happen, when the reference type is a tag
. So when directly pushing to master
or creating a pull request, this step will not get executed. You will understand why in the next step.
Now we come to the real show. It’s time to build and then push our self-made Docker image. I use the docker/build-push-action
for that. I reference the path to the Dockerfile under file
. This may differ in your case (most certainly). I add all the tags I specified in the metadata-action
earlier and I configure, that a push
will only happen, when the workflow was triggered because of the creation of a tag. Now it makes sense to do the login earlier also only when a tag triggers the action.
It’s time to proof that this will work. When creating a new release in my cookbook repository, the workflow will get triggered. You can find the overview of all workflows under the Actions tab on your Github repository.
You will get detailed information when clicking on the workflow. As you can see, I created a tag v0.0.1-hello-world
which triggered the action. When having a look in my Repository packages, I can see, that there is a new image now with the according tags (latest
and 0.0.1-hello-world
.
Philip