Automate Docker Image Builds and Push to Docker Hub Using GitHub Actions

Automate Docker Image Builds and Push to Docker Hub Using GitHub Actions

Use Github Actions to automate Docker builds and push to Docker Hub

ยท

8 min read

New month new blog post.... ๐Ÿš€๐Ÿš€

Introduction

In this tutorial, we will be building a Docker image of an existing project and pushing it to Docker Hub using GitHub Actions. We will be using the following technologies:

Overview

The Problem โ˜ข๏ธ

We want to build a Docker image and push it to Docker Hub whenever we push to the main branch. We also want to build a Docker image and push it to GitHub Packages whenever we push to the dev branch. We want to do this automatically using GitHub Actions. We want to be able to do this without exposing our Docker Hub credentials.

The Solution โšก

We will use GitHub Actions to build and push Docker images to Docker Hub. We will use the following steps:

  1. Create a workflow file.
  2. Add a job to build and push the Docker image to Docker Hub.
  3. Commit and push the changes to the dev branch.
  4. Specify our docker crenentials as secrets in the repository settings.
  5. Open a pull request to merge the dev branch into the main branch.
  6. Watch the workflow run and verify that the Docker image is pushed to Docker Hub.

Prerequisites ๐Ÿ“ƒ

  • A GitHub account.
  • A Docker Hub account.
  • Docker installed on your machine. You can follow the official documentation to install Docker on your machine.
  • A GitHub repository with a Dockerfile and a project that you want to build and push to Docker Hub.

Step 1: Create a workflow file ๐ŸŠ

All GitHub Actions workflows are defined in YAML files in the .github/workflows directory of your repository.

We'll stary by creating a workflow file for building and pushing the Docker image to Docker Hub.

Create a file named .github/workflows/docker-hub.yml in your repository. This file will contain the workflow that we will use to build and push the Docker image. The workflow will be triggered whenever we push to the main or dev branch. We will also specify the Docker image name and tag. We will use the following workflow:

name: Build and Push Docker Image to Docker Hub

on:
  push:
    branches: [ "dev", "main" ]
  pull_request:
    branches: [ "dev", "main" ]

env:
  # Use docker.io for Docker Hub if empty
  REGISTRY: docker.io
  # github.repository as <account>/<repo>
  IMAGE_NAME: ${{ github.repository }}

jobs:

  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v3
    - name: Build the Docker image
      run: docker-compose build --no-cache --force-rm 
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Test the Docker image
        run: docker-compose up -d 
  push_to_registry:
    name: Push Docker image to Docker Hub
    runs-on: ubuntu-latest
    steps:
      - name: Check out the repo
        uses: actions/checkout@v3

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

      - name: Log in to Docker Hub
        uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: Extract metadata (tags, labels) for Docker
        id: meta
        uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}

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

Lets break down the workflow file:

  • name: The name of the workflow.
  • on: The events that trigger the workflow. We will trigger the workflow whenever we push to the main or dev branch.
  • env: The environment variables that we will use in the workflow. We will use the following environment variables:

    • REGISTRY: The registry that we will use to push the Docker image. We will use docker.io for Docker Hub.
    • IMAGE_NAME: The name of the Docker image. We will use the name of the repository as the name of the Docker image.
  • jobs: The jobs that will run in the workflow. We will use the following jobs:

    • build: The job that will build the Docker image. We will use the following steps:
    • Build the Docker image: We will use the docker-compose build command to build the Docker image. We will use the --no-cache and --force-rm flags to ensure that we are building the Docker image from scratch. This will ensure that we are building the Docker image with the latest changes before pushing it to Docker Hub.
  • test: The job that will test the Docker image. We will use the following steps:

    • Checkout: We will use the actions/checkout action to checkout the repository.
    • Test the Docker image: We will use the docker-compose up -d command to test the Docker image. We will use the -d flag to run the Docker image in the background.
  • push_to_registry: The job that will push the Docker image to Docker Hub. We will use the following steps:

    • Check out the repo: We will use the actions/checkout action to checkout the repository.
    • Set up Docker Buildx: We will use the docker/setup-buildx-action action to set up Docker Buildx.
    • Log in to Docker Hub: We will use the docker/login-action action to log in to Docker Hub. We will use the following inputs:
      • username: The username of the Docker Hub account. We will use the DOCKER_USERNAME secret.
      • password: The password of the Docker Hub account. We will use the DOCKER_PASSWORD secret.
    • Extract metadata (tags, labels) for Docker: We will use the docker/metadata-action action to extract the metadata for the Docker image. We will use the following inputs:
    • images: The name of the Docker image. We will use the REGISTRY and IMAGE_NAME environment variables.
  • Build and push Docker image: We will use the docker/build-push-action action to build and push the Docker image to Docker Hub. We will use the following inputs:
    • context: The context of the Docker image. We will use the defaultContext variable.
    • push: A boolean value that specifies whether to push the Docker image to Docker Hub. We will set this to true.
    • tags: The tags of the Docker image. We will use the tags output of the docker/metadata-action action.
    • labels: The labels of the Docker image. We will use the labels output of the docker/metadata-action action.

Step 2: Create a dev branch and commit the changes to the dev branch ๐ŸŒฟ

Create a dev branch and commit the changes to the dev branch. This will allow us to test the workflow before merging the changes to the main branch. We will use the following commands:

git checkout -b dev
git add .
git commit -m "Add workflow to build and push Docker image to Docker Hub"

Step 3: Push the changes to the dev branch ๐Ÿง‘โ€๐Ÿ’ป

Create a Github Repository if you dont already have one and push the changes to the dev branch. We will use the following commands:

git add .
git commit -m "Add workflow to build and push Docker image to Docker Hub"
git push -u origin dev

Step 4: Specify our docker credentials as secrets in the repository settings ๐Ÿ”

We will specify our Docker Hub credentials as secrets in the repository settings. We will use the following secrets:

  • DOCKER_USERNAME: The username of our Docker Hub account.
  • DOCKER_PASSWORD: The password of our Docker Hub account.

This will allow us to use the credentials in the workflow file without exposing them. Thus keeping sensitive information secure. This gives the workflow access to the Docker Hub account. As well ability to use the workflow across multiple repositories.

Step 5: Open a pull request to merge the dev branch into the main branch ๐Ÿ›ฃ๏ธ

Once the workflow successfully runs on the dev branch, we can merge the changes to the main branch. Open a pull request to merge the dev branch into the main branch. This will trigger the workflow. The workflow will run on the main branch and push the Docker image to Docker Hub. Example of successful workflow run on open pull request:

Successful workflow run on open pull request

Step 6: Watch the workflow run ๐Ÿ“บ

Watch the workflow run and verify that the Docker image is pushed to Docker Hub. Login to Docker Hub and check if the image is successfully pushed to Docker Hub. The image should be pushed to Docker Hub with the appropriate tags. for example kenmwaura1/fastapi-vue:main and kenmwaura1/fastapi-vue:sha-<sha>. Example of successful workflow run on merge pull request:

Successful workflow run on merge pull request

Screenshot of workflow running on merge pull request:

Workflow running on merge pull request

Step 7: Verify that the Docker image is pushed to Docker Hub ๐Ÿ‹

Login to Docker Hub and check if the image is successfully pushed to Docker Hub. The image should be pushed to Docker Hub with the appropriate tags. for example kenmwaura1/fastapi-vue:main and kenmwaura1/fastapi-vue:sha-<sha>. Example of image pushed to Docker Hub:

Image pushed to Docker Hub

GitHub Repo for this tutorial ๐Ÿ™

Find the sample code for this workflow in the following GitHub repository: Fastapi-Vue.

Find the docker image on Docker Hub: Fastapi-Vue

Conclusion ๐Ÿซ”

In this tutorial, we learned how to build and push Docker images to Docker Hub and GitHub Packages using GitHub Actions. We also learned how to specify our Docker Hub credentials as secrets in the repository settings.

Next Steps

On the next tutorial, we will learn how to build and push Docker images to GitHub Packages using GitHub Actions. Stay tuned!

Next time gif

References ๐Ÿ“–

ย