CI/CD with GitHub Actions: Multi-Registry Docker Builds, Image Scanning & Email Notifications

CI/CD with GitHub Actions: Multi-Registry Docker Builds, Image Scanning & Email Notifications

✨ Introduction

In modern DevOps workflows, automation is the key to speed, security, and scalability. GitHub Actions allows us to build robust CI/CD pipelines tailored to our needs without introducing additional infrastructure overhead. This article guides you through setting up an end-to-end GitHub Actions workflow that:

Github Repo: https://github.com/codewithmuh/docker-ci-cd-github-actions.git

Youtube Video

  • Builds a Docker image
  • Pushes it to DockerHub and AWS ECR
  • Scans the image for vulnerabilities
  • Generates a scan report
  • Sends the report via email

Let’s build a professional-grade pipeline step-by-step.


🔧 Prerequisites

  1. GitHub Repository
  2. DockerHub Account
  3. AWS Account with ECR Repository
  4. GitHub Secrets Setup:

  • DOCKER_USERNAME & DOCKER_PASSWORD
  • AWS_ACCESS_KEY_ID & AWS_SECRET_ACCESS_KEY
  • AWS_REGION
  • AWS_ACCOUNT_ID
  • EMAIL_USER (e.g., Gmail address)
  • EMAIL_PASS (App-specific password for Gmail or SMTP)
  • EMAIL_TO (Where the report will be sent)


📦 Create a Simple Dockerfile

In your GitHub repository root, create a Dockerfile for a simple example app. For demonstration, we’ll use a basic Python web server:

# Dockerfile
FROM python:3.12-slim

WORKDIR /app

COPY app.py .

RUN pip install flask

CMD ["python", "app.py"]
        

And a minimal app.py file:

# app.py
from flask import Flask
app = Flask(__name__)

@app.route('/')
def home():
    return "Hello, GitHub Actions!"

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=80)
        

Commit both files to your repo.


⚖️ Step-by-Step GitHub Actions Workflow

Step 1: Define Workflow File

Create .github/workflows/docker-multi-registry.yml

name: Multi-Registry Docker CI/CD

on:
  push:
    branches:
      - main

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    outputs:
      sha_tag: ${{ steps.set_tag.outputs.sha_tag }}

    steps:
      - name: Checkout Code
        uses: actions/checkout@v3

      - name: Set SHA Tag
        id: set_tag
        run: echo "sha_tag=${GITHUB_SHA::7}" >> $GITHUB_OUTPUT

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

      - name: Log in to AWS ECR
        uses: aws-actions/amazon-ecr-login@v1

      - name: Build Docker Image
        run: |
          docker build -t myapp:${{ steps.set_tag.outputs.sha_tag }} .
          docker tag myapp:${{ steps.set_tag.outputs.sha_tag }} ${{ secrets.DOCKER_USERNAME }}/myapp:${{ steps.set_tag.outputs.sha_tag }}
          docker tag myapp:${{ steps.set_tag.outputs.sha_tag }} ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_REGION }}.amazonaws.com/myapp:${{ steps.set_tag.outputs.sha_tag }}

      - name: Push to DockerHub
        run: docker push ${{ secrets.DOCKER_USERNAME }}/myapp:${{ steps.set_tag.outputs.sha_tag }}

      - name: Push to AWS ECR
        run: docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_REGION }}.amazonaws.com/myapp:${{ steps.set_tag.outputs.sha_tag }}

  scan-and-email:
    needs: build-and-push
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/checkout@v3

      - name: Install Trivy
        run: |
          sudo apt-get install wget apt-transport-https gnupg lsb-release -y
          wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add -
          echo deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main | sudo tee -a /etc/apt/sources.list.d/trivy.list
          sudo apt-get update
          sudo apt-get install trivy -y

      - name: Docker Login to DockerHub
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: Pull Image for Scanning
        run: docker pull ${{ secrets.DOCKER_USERNAME }}/myapp:${{ needs.build-and-push.outputs.sha_tag }}

      - name: Scan Docker Image with Trivy
        run: |
          trivy image --format json -o trivy-report.json ${{ secrets.DOCKER_USERNAME }}/myapp:${{ needs.build-and-push.outputs.sha_tag }}

      - name: Send Email Notification
        uses: dawidd6/action-send-mail@v3
        with:
          server_address: smtp.gmail.com
          server_port: 465
          username: ${{ secrets.EMAIL_USER }}
          password: ${{ secrets.EMAIL_PASS }}
          subject: "Docker Image Scan Report"
          to: ${{ secrets.EMAIL_TO }}
          from: GitHub CI/CD <${{ secrets.EMAIL_USER }}>
          body: "The Trivy scan report is attached."
          attachments: trivy-report.json
        

🔁 Optional Improvements

  • Run scan jobs on a cron schedule (e.g., weekly security scans)
  • Send alerts to Slack or Discord in addition to email
  • Store reports in Amazon S3 or artifact storage for compliance
  • Use Grype or Dockle for alternative scanning tools


🎉 Conclusion

With this GitHub Actions pipeline, you've built a production-grade automation flow:

  • Docker image is built on every push to main
  • Pushed to DockerHub and AWS ECR
  • Scanned using Trivy
  • A report is generated and emailed to your team

This setup is scalable, reusable, and a perfect base for your DevSecOps journey. Happy shipping! 🚀


Wow please we are waiting. May God give you strength to develop Us in this field. Your explanation is second to non

Like
Reply

I have been waiting for this your lecture sir. Indeed you are doing well many aspiring DevOps engineers. Thanks so much brother and may God keep you. Sir is like you didn't post video for some time now or am I the one missing it? how can I be getting access to all your video?

Like
Reply

To view or add a comment, sign in

More articles by Muhammad Rashid

Explore content categories