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
- Test
Runs automated tests using pytest.
- Build
Creates a Docker image of the application.
- Analyze
Uses SonarQube to evaluate code quality.
- 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)