Today, I set up a complete CI/CD pipeline using GitLab for a Python FastAPI application, and published the Docker image to GitLab’s Container Registry.
🐳 Dockerfile for Python FastAPI App
# Base Python image
FROM python:3.12-slim
# Set working directory inside the container
WORKDIR /usr/local/app
# Copy requirements and install dependencies
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
# Copy application code
COPY src ./src
# Expose the application port
EXPOSE 8000
# Create and switch to non-root user
RUN useradd app
USER app
# Run the app
CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000"]
🛠 GitLab CI/CD Setup
☸️ Kubernetes Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: mypythonapp
spec:
replicas: 1
selector:
matchLabels:
app: mypythonapp
template:
metadata:
labels:
app: mypythonapp
spec:
containers:
- name: mypythonapp
image: gitlab.hassandevops.site:5050/ramdanbootcamp/multistage-python-cicd:1.2.0
ports:
- containerPort: 8000
imagePullSecrets:
- name: gitlab-registry-secret
🌐 Ingress with TLS
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: mypythonapp-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: "/"
spec:
rules:
- host: python.hassandevops.site
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: mypythonapp-svc
port:
number: 80
tls:
- hosts:
- python.hassandevops.site
secretName: hassandevops-tls
✅ FastAPI containerized, published, and deployed to Kubernetes with GitLab CI/CD. SSL configured and domain routing set. Solid progress!
🚀 Day 9 – Blue-Green Deployment with GitLab CI/CD (Spring Boot)
Today was all about zero-downtime deployments. I implemented a Blue-Green Deployment strategy for a Spring Boot app using GitLab CI/CD and Kubernetes.
🐳 Multistage Dockerfile (Java App)
# Stage 1: Build
FROM eclipse-temurin:17-jdk-alpine AS builder
WORKDIR /usr/src/app
COPY . .
RUN chmod +x mvnw
RUN ./mvnw clean package -DskipTests
# Stage 2: Runtime
FROM eclipse-temurin:17-jdk-alpine
WORKDIR /usr/src/app
COPY --from=builder /usr/src/app/target/*.jar app.jar
EXPOSE 8080
CMD ["java", "-jar", "app.jar"]
🔁 GitLab CI/CD Pipeline with Kaniko
stages:
- build
- deploy
build-Blue:
stage: build
image: gcr.io/kaniko-project/executor:v1.23.2-debug
script:
- /kaniko/executor --context "${CI_PROJECT_DIR}" --dockerfile "${CI_PROJECT_DIR}/Dockerfile" --destination "${CI_REGISTRY_IMAGE}:Blue"
build-Green:
stage: build
image: gcr.io/kaniko-project/executor:v1.23.2-debug
script:
- /kaniko/executor --context "${CI_PROJECT_DIR}" --dockerfile "${CI_PROJECT_DIR}/Dockerfile" --destination "${CI_REGISTRY_IMAGE}:Green"
deploy-job:
stage: deploy
image: alpine:latest
before_script:
- apk add --no-cache openssh sshpass
script:
- sshpass -p "$MICROK8S_PASSWORD" scp -o StrictHostKeyChecking=no k8s/*.yml ${MICROK8S_USERNAME}@${MICROK8S_IP_ADDRESS}:/path/
- sshpass -p "$MICROK8S_PASSWORD" ssh -o StrictHostKeyChecking=no ${MICROK8S_USERNAME}@${MICROK8S_IP_ADDRESS} "cd /path && microk8s.kubectl apply -f ."
☸️ Blue & Green Deployments
Both versions are deployed, but only one serves traffic at a time.
bankapp-blue Deployment
metadata:
name: bankapp-blue
spec:
selector:
matchLabels:
app: bankapp
version: blue
template:
metadata:
labels:
app: bankapp
version: blue
spec:
containers:
- image: ...:Blue
bankapp-green Deployment
metadata:
name: bankapp-green
spec:
selector:
matchLabels:
app: bankapp
version: green
template:
metadata:
labels:
app: bankapp
version: green
spec:
containers:
- image: ...:Green
💡 Switching Traffic
The service selector decides which version is live:
selector:
app: bankapp
version: blue # Change to green when needed
🔐 MySQL Setup
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
spec:
containers:
- image: mysql:8
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
key: MYSQL_ROOT_PASSWORD
✅ Now I can deploy, test, and switch versions in Kubernetes without downtime. All thanks to GitLab CI/CD + Blue-Green Strategy 💙💚