Docker-Image-with-GitHub-Actions

This repo provides a guide πŸ“— to creating a simple pipeline to build βš™ and upload docker 🐳 image artifacts to the docker hub 🐳 repo with the help of GitHub actions as changes take place in GitHub main branch.

Prerequisites

  1. Docker 🐳 basics.
  2. Yaml πŸ“„ basics

Note

If you are viewing this page from GitHub pages then some code for GitHub secrets will not be shown, you will only see a $ sign. Check out GitHub repo from πŸ‘‰Here for the correct code.

Scanario

For this demo, the application is to be dockerized 🐳 in a static site. It is a simple game of guessing πŸ€” numbers between 0 0️⃣ to 9 9️⃣. You access this page Here

Screenshot

Repository structure

   /src
     index.html
     index.css
   /Dockerfile
   /README.md

Let’s take a look πŸ‘€ at source files πŸ—ƒ

  1. /src - This folder contains source files πŸ“ for our static site.
  2. Dockerfile - This is a basic Dockerfile 🐳 which specifies how the image should be built from the source. We will take a look πŸ‘€ at this in a moment.
  3. README.md - This is the file πŸ“„ you currently reading.

Dockerfile

Dockerfile 🐳 provides a set of instuctions to build βš™ the image. For this repo it looks πŸ‘€ like the following.

    FROM nginx
    ADD src/* /usr/share/nginx/html/
    EXPOSE 80
    CMD ["nginx","-g","daemon off;"]

Let’s take a look πŸ‘€ at each instruction to understand what these instructions mean

  1. FROM nginx - This line tells docker 🐳 to use Nginx πŸ†– as the official image as a base image for our new image.
  2. ADD src/* /usr/share/nginx/html/ - This line copies files πŸ—ƒ of src folder inside the /usr/share/nginx/html/ folder of image. /usr/share/nginx/html/ folder is used by the nginx πŸ†– server as entry point of website.
  3. EXPOSE 80 - This line tells docker 🐳 to open port 80 inside the docker 🐳 container so that we can communicate with the nginx πŸ†– server inside.
  4. CMD ["nginx","-g","daemon off;"] - This last cline starts Nginx πŸ†– server and runs β–Ά every time the docker 🐳 container start.

It’s all about what we need to get started. Let’s take a look πŸ‘€ how to setup GitHub actions to create a pipeline.

Steps to create πŸ›  a pipeline

Create github workflow

  1. Open github repo and click on actions option.
  2. Search πŸ” for Simple workflow and select the most suitable from the option that appeared.
  3. Rename the file πŸ“„ with the name you want for your workflow.

Writing Code πŸ‘¨β€πŸ’» for workflow

  1. Replace πŸ” the code πŸ‘©β€πŸ’» of workflow with the following.

     name:
     on:
     jobs:
    
    

    Let’s understand πŸ€” what this code πŸ‘©β€πŸ’» means

    • The lines starting with # represents comments in YAML.
    • name: - Defines the name for your workflow.
    • on: - When we create a GitHub action, we want the action to run β–Ά on certain events i.e push to a branch. This on parameter describes the events in which the action is going to run β–Ά.
    • jobs: - jobs work section is the place where we define what going to happen when events specified in the on: section occurs.
  2. Give a name to your workflow

     name: Docker image CI
    
  3. Specify the event of our interest in this case push event

    • Add event

       name: Docker image CI
       on:
         push:
      
    • Add branches for which we going to perform some action

       name: Docker image CI
       on:
         push:
           branches: [ "main" ]
      
  4. Now the remaining part of the workflow is jobs section. Jobs define the set of actions (Tasks) to be performed. These action can run β–Ά sequentially or in parallel. for this demo we are just going to create one job named docker-build.

       name: Docker image CI
       on:
          push:
            branches: [ "main" ]
       jobs:
          docker-build:
    
  5. Each job section has following things

    • runs-on: - Tells πŸ“’ the platform on which this job(Task) is going to run β–Ά on. For this demo, we are taking β€˜ubuntu-latest’.
    • steps: - This defines a set of steps to be performed to get the job done.
    • Now our code πŸ‘©β€πŸ’» will look πŸ‘€ like this.

         name: Docker image CI
         on:
            push:
              branches: [ "main" ]
         jobs:
            docker-build:
                 runs-on: ubuntu-latest
                 steps: 
      

    Each step in steps walk section has the following parts

    • name: - name of the step
    • run: - set of command πŸ’» to run β–Ά
    • uses: - rerefence of any other action which is used by the current steps. The referenced action will run β–Ά first.optional
    • Apart from these, there are many other optional parts.
  6. Add the following code πŸ‘¨β€πŸ’» in the steps section

    - uses: actions/checkout@v3
    - name: Build the Docker image
      run: |
    
    • notice | after run: this allows us to write multiple commands πŸ’».
  7. To build βš™ image add the following command πŸ’» to the Build the Docker image step and replace <image_name> With the name of your choice.

     docker build . --file Dockerfile --tag <image_name>
    
    • Now our file πŸ“„ look πŸ‘€ something like this

         name: Docker image CI
         on:
           push:
             branches: [ "main" ]
         jobs:
             docker-build:
                runs-on: ubuntu-latest
                  steps:
                    - uses: actions/checkout@v3
                    - name: Build the Docker image
                      run: |
                         docker build . --file Dockerfile --tag hackthenumber
      
    • Notice πŸ“ the uses part specifies actions/checkout@v3. This action checks if our repository is present and if we have access to it.
    • Command πŸ’» docker build . --file Dockerfile --tag hackthenumber builds βš™ an image named hackthenumber it is currently stored at the local system at which the job is running.
  8. Login πŸ” to dockerhub 🐳

    To make push the image to the docker hub 🐳 we need to login to the docker hub 🐳. Docker recommends creating access tokens for logins πŸ” instead of using password πŸ”‘.

    • Creating dockerhub 🐳 Access token πŸ—

      • To create an access token login πŸ” to docker hub 🐳. Goto access Account Setting > Security > New Access token.

        acc

        security

      • Enter description for the token and click generate βš™.
      • Select Copy and Close.
    • Adding passwords πŸ”‘ directly to workflow files πŸ“„ can be a potential threat. To make this secure GitHub provides secrets to store passwords πŸ”‘ etc. To add the secrets to GitHub repo do the following.

      • Goto your repo.
      • Open Settings πŸ”§.
      • Select Secret > Actions > New Repository Secret
      • Add DOCKERHUB as name and your docker hub username as secret.
      • Add another secret for TOKEN as the name and paste the access token you generated βš™ in the previous step.
    • Login πŸ” to Dockerhub 🐳

      • Add the following lines to run part to login πŸ”

        docker login -u $ -p $
        
  9. Tag image to refer to Dockerhub 🐳 repo

    • To tag image add the following command πŸ’» by replacing <image_name> with the image name you have choosen for your image

      docker tag <image_name> $/<image_name> 
      
    • With our choosen name it will look πŸ‘€ like something this

      docker tag hackthenumber $/hackthenumber
      
  10. Add the following line to push image to dockerhub 🐳

     docker push $/hackthenumber
    

Now our file πŸ“„ will look πŸ‘€ like something this

   name: Docker image CI
   on:
     push:
       branches: [ "main" ]
   jobs:
       docker-build:
           runs-on: ubuntu-latest
              steps:
                 - uses: actions/checkout@v3
                 - name: Build the Docker image
                   run: |
                      docker build . --file Dockerfile --tag hackthenumber
                      docker login -u $ -p $
                      docker tag hackthenumber $/hackthenumber
                      docker push $/hackthenumber

Similarly, you can add code to test if everything is working fine in the same script. After adding the testing code, our final code will look like this.

  name: Docker image CI
  on:
    push:
      branches: [ "main" ]
  jobs:
    docker-build:
          runs-on: ubuntu-latest
          steps:
            - uses: actions/checkout@v3
            - name: Build the Docker image
              run: |
                    docker build . --file Dockerfile --tag hackthenumber
                    docker login -u $ -p $
                    docker tag hackthenumber $/hackthenumber
                    
    docker-test:
          runs-on: ubuntu-latest
          needs: docker-build
          steps:
            - uses: actions/checkout@v3
            - name: Test image presence
              run: |
                docker image inspect $/hackthenumber
                if [[ $? -eq 1 ]]; then
                    echo "❌ Image not found!"
                    exit 1  # This will cause the job to fail
                else
                    echo "βœ… Image Build Successfully!"
                fi
            - name: Test image response
              run: |
                    docker run -itdp 8000:80 --rm $/hackthenumber
                    response=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8000)
                    if [[ $response -ne 200 ]]; then
                      echo "❌ Image response is not 200!"
                      exit 1
                    else
                      echo "βœ… Image response is 200!"
                    fi
    docker-deploy:
          runs-on: ubuntu-latest
          needs: docker-test
          steps:
              - uses: actions/checkout@v3
              - name: Deploy to docker hub
                run: |
                  docker push $/hackthenumber && echo "βœ… Deployed to Docker Hub" || echo "❌ Deployment to Docker Hub failed"

Quick Test deployed image