DEV Community

Cover image for Automate Docker Image Builds and Push to Docker Hub Using GitHub Actions 🐳🐙
Zoo Codes
Zoo Codes

Posted on

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

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 }}



Enter fullscreen mode Exit fullscreen mode

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:
    • Checkout: We will use the actions/checkout action to checkout the repository.
    • 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"


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

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:

GitHub logo KenMwaura1 / Fast-Api-Vue

Simple asynchronous API implemented with Fast-Api framework utilizing Postgres as a Database and SqlAlchemy as ORM . GiHub Actions as CI/CD Pipeline. Vue + Daisy UI for the frontend

FastAPI Vue Starter App

fastapi-0.92.0-informational CodeQL Docker Compose Actions Workflow Build and Push Docker Image to Docker Hub Docker Image Publish

Twitter

This repository contains code for asynchronous example api using the Fast Api framework ,Uvicorn server and Postgres Database to perform crud operations on notes.

Fast-api

Accompanying Article

Installation method 1 (Run application locally)

  1. Clone this Repo

    git clone (https://github.com/KenMwaura1/Fast-Api-example)

  2. Cd into the Fast-Api folder

    cd Fast-Api-example

  3. Create a virtual environment

    python3 -m venv venv

  4. Activate virtualenv

    source venv/bin/activate

    For zsh users

    source venv/bin/activate.zsh

    For bash users

    source venv/bin/activate.bash

    For fish users

    source venv/bin/activate.fish

  5. Cd into the src folder

    cd src

  6. Install the required packages

    python -m pip install -r requirements.txt

  7. Start the app

    python main.py
    Enter fullscreen mode Exit fullscreen mode

    7b. Start the app using Uvicorn

    uvicorn app.main:app --reload --workers 1 --host 0.0.0.0 --port 8002
    Enter fullscreen mode Exit fullscreen mode
  8. Ensure you have a Postgres Database running locally Additionally create a fast_api_dev database with user **fast_api** having required privileges. OR Change the DATABASE_URL variable in the .env file inside then app folder to reflect database settings (user:password/db)

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 📖

Top comments (2)

Collapse
 
hartley94 profile image
Martin Thuo

Informative ✌.

Collapse
 
ken_mwaura1 profile image
Zoo Codes

Thank you for reading through!