DEV Community

lajibolala
lajibolala

Posted on

Building a Complete CI/CD Pipeline for a Python Application (GitLab + Docker + SonarQube)

Modern software development relies heavily on automation. Continuous Integration and Continuous Deployment (CI/CD) pipelines help ensure that code is tested, analyzed, and deployed reliably with minimal manual intervention.

In this article, we’ll walk through how to build a simple yet effective CI/CD pipeline for a Python application using:

GitLab CI/CD
Docker
SonarQube
Pytest
🧠 Why CI/CD Matters

A well-designed CI/CD pipeline allows you to:

Automatically run tests on every code change
Ensure consistent and reproducible deployments
Detect bugs and code quality issues early
Reduce manual errors during deployment

The goal is simple: deliver reliable software faster and safer.

πŸ“ Project Structure

Here’s a typical structure for a Python project with CI/CD:

project/
β”œβ”€β”€ app.py
β”œβ”€β”€ requirements.txt
β”œβ”€β”€ tests/
β”‚ └── test_app.py
β”œβ”€β”€ Dockerfile
β”œβ”€β”€ .gitlab-ci.yml
└── sonar-project.properties
🐍 Step 1 β€” Python Application
app.py
from flask import Flask, jsonify
import os

app = Flask(name)

@app.route("/")
def home():
return jsonify({"message": "Hello from CI/CD pipeline", "status": "ok"})

@app.route("/health")
def health():
return jsonify({"service": "up"}), 200

@app.route("/config")
def config():
env = os.getenv("APP_ENV", "dev")
return jsonify({"environment": env})

if name == "main":
app.run(host="0.0.0.0", port=5000)
πŸ§ͺ Step 2 β€” Automated Tests
tests/test_app.py
from app import app

def test_home():
client = app.test_client()
response = client.get("/")
assert response.status_code == 200

def test_health():
client = app.test_client()
response = client.get("/health")
assert response.status_code == 200
requirements.txt
flask
pytest
🐳 Step 3 β€” Dockerizing the Application
Dockerfile
FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .

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

COPY . .

ENV APP_ENV=production

EXPOSE 5000

CMD ["python", "app.py"]
Why Docker?

Docker ensures that your application runs in a consistent environment, regardless of where it's deployed.

βš™οΈ Step 4 β€” GitLab CI/CD Pipeline
.gitlab-ci.yml
stages:

  • test
  • build
  • sonarqube
  • deploy

variables:
IMAGE_NAME: python-app
CONTAINER_NAME: python-app-dev

run_tests:
stage: test
script:
- pip install -r requirements.txt
- pytest

build_image:
stage: build
script:
- docker build --network=host -t $IMAGE_NAME:latest .

sonarqube_check:
stage: sonarqube
image:
name: sonarsource/sonar-scanner-cli:latest
entrypoint: [""]
script:
- sonar-scanner
allow_failure: true

deploy_dev:
stage: deploy
script:
- docker stop $CONTAINER_NAME || true
- docker rm $CONTAINER_NAME || true
- docker run -d --network host --name $CONTAINER_NAME $IMAGE_NAME:latest
needs: ["run_tests", "build_image"]
πŸ” Step 5 β€” Code Quality with SonarQube
sonar-project.properties
sonar.projectKey=python-app
sonar.projectName=python-app
sonar.sources=.
sonar.tests=tests
sonar.qualitygate.wait=true

SonarQube performs static code analysis, helping detect:

Bugs
Code smells
Maintainability issues
Potential vulnerabilities
πŸ”„ Pipeline Workflow

The pipeline follows a simple and effective flow:

Test β†’ Build β†’ Analyze β†’ Deploy

  1. Test

Runs automated tests using pytest.

  1. Build

Creates a Docker image of the application.

  1. Analyze

Uses SonarQube to evaluate code quality.

  1. Deploy

Stops the previous container and deploys a new version.

πŸ” DevSecOps Considerations

Security should be integrated early in the development lifecycle.

This pipeline introduces basic DevSecOps practices by:

Validating code through automated tests
Performing static analysis with SonarQube
Structuring the pipeline to prevent unsafe deployments

For production environments, this can be extended with:

Dependency vulnerability scanning
Docker image scanning (e.g., Trivy)
Secret management solutions
⚠️ Limitations

This setup is intentionally simple and may have limitations:

No staging environment
No rollback mechanism
Basic security checks
Single-container deployment
πŸš€ Possible Improvements

To make this pipeline production-ready, you could:

Add a linting stage (e.g., flake8)
Enforce SonarQube quality gates
Introduce multiple environments (dev / staging / prod)
Use Docker Compose or Kubernetes
Add automated security scans
Implement CI/CD variables and secret management
🧾 Conclusion

This CI/CD pipeline demonstrates how to automate the full lifecycle of a Python application:

Testing
Building
Quality analysis
Deployment

Even with a simple setup, you can significantly improve reliability, consistency, and development speed.

CI/CD is not about complexity β€” it's about automation, confidence, and repeatability.

πŸ’¬ Final Thought

Start simple. Make it work. Then improve it.

That’s the real DevOps mindset.

Top comments (0)