Skip to main content

Command Palette

Search for a command to run...

Day 10 – GitLab CI/CD for Python & Using GitLab Container Registry

Updated
3 min read
Day 10 – GitLab CI/CD for Python & Using GitLab Container Registry
M

Hey there! I'm currently working as an Associate DevOps Engineer, and I'm diving into popular DevOps tools like Azure Devops,Linux, Docker, Kubernetes,Terraform and Ansible. I'm also on the learning track with AWS certifications to amp up my cloud game. If you're into tech collaborations and exploring new horizons, let's connect!

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

  • Built Docker image in GitLab pipeline

  • Pushed it to GitLab’s private container registry

  • Used :1.2.0 version tag

☸️ 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 💙💚

More from this blog

DevOps Journey with M Hassan

174 posts

I am writing these blogs because I recently completed a comprehensive DevOps course where I gained in-depth knowledge of the topics mentioned. As I progressed through the course, I realized the importance of having a concise and accessible resource to revise and reinforce my understanding of each topic. Therefore, I decided to create cheat sheets in the form of blog posts. These cheat sheets will not only serve as a handy reference for myself but also benefit others who are also interested in mastering DevOps concepts. By documenting each topic and providing concise explanations, I aim to create a valuable resource that simplifies complex concepts and facilitates hands-on practice. This way, I can solidify my own understanding while helping others on their DevOps journey.