Introduction

Continuous Integration and Continuous Deployment (CI/CD) are essential for modern software development. In this tutorial, I will walk you through setting up a CI/CD pipeline for a Flask application using GitHub Actions and Docker. By the end of this guide, you will have an automated workflow that builds, tests, and pushes a Docker image to Docker Hub.

Prerequisites

Before we get started, ensure you have the following:

  • Python 3.x installed on your machine.
  • Docker installed and configured.
  • A GitHub repository for your Flask project.
  • A Docker Hub account to store container images.

Step 1: Setting Up the Flask Application

Let's start by creating a simple Flask application.

  1. Create a project directory:

    mkdir flask-cicd && cd flask-cicd
    
  2. Create a virtual environment and activate it:

    python3 -m venv venv
    source venv/bin/activate  # On macOS/Linux
    venv\Scripts\activate  # On Windows
    
  3. Install Flask:

    pip install flask
    
  4. Create an app.py file:

    from flask import Flask
    
    app = Flask(__name__)
    
    @app.route('/')
    def home():
        return "Hello, Flask CI/CD!"
    
    if __name__ == '__main__':
        app.run(host='0.0.0.0', port=5000)
    
  5. Create a requirements.txt file:

    flask
    

Step 2: Dockerizing the Flask Application

Create a Dockerfile in the project directory:

# Use official Python image
FROM python:3.10

WORKDIR /app

COPY requirements.txt requirements.txt
RUN pip install --no-cache-dir -r requirements.txt

COPY . .
EXPOSE 5000
CMD ["python", "app.py"]

To test the Docker container locally, build and run it:

docker build -t flask-cicd .
docker run -p 5000:5000 flask-cicd

Step 3: Setting Up GitHub Actions for CI/CD

Now, we will create a GitHub Actions workflow to automate building and pushing our Docker image to Docker Hub.

  1. Inside your project, create .github/workflows/main.yml and add the following:
name: Flask CI/CD

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

permissions:
  contents: read

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout Repository
        uses: actions/checkout@v4

      - name: Set up Python 3.10
        uses: actions/setup-python@v3
        with:
          python-version: "3.10"

      - name: Install Dependencies
        run: |
          python -m pip install --upgrade pip
          pip install flake8 pytest
          if [ -f requirements.txt ]; then pip install -r requirements.txt; fi

      - name: Lint with flake8
        run: |
          flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
          flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics

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

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

      - name: Build and Push Docker Image
        uses: docker/build-push-action@v5
        with:
          context: .
          file: Dockerfile
          push: true
          tags: ${{ secrets.DOCKER_USERNAME }}/flask-app:latest

Also, remember to create a .gitignore file and .dockerignore file.

Step 4: Configuring GitHub Secrets

To securely authenticate with Docker Hub, add the following GitHub Secrets in your repository:

  • DOCKER_USERNAME: Your Docker Hub username.
  • DOCKER_PASSWORD: Your Docker Hub password.

Step 5: Pushing Code and Running the Pipeline

Now, push your code to GitHub:

git init
git add .
git commit -m "Initial commit"
git branch -M main
git remote add origin 
git push -u origin main

Once pushed, GitHub Actions will automatically run the workflow. You can check the status under the Actions tab in your repository.

Conclusion

Congratulations! 🎉 You have successfully set up a CI/CD pipeline for your Flask application using GitHub Actions and Docker. Now, every time you push code, your application will be tested, built, and deployed automatically.

Feel free to share your thoughts and improvements in the comments below! 🚀