Skip to main content

Command Palette

Search for a command to run...

Day 9 – GitLab CI/CD for Python + Container Registry + Kubernetes Deployment

Updated
3 min read
Day 9 – GitLab CI/CD for Python + Container Registry + Kubernetes Deployment

Welcome to Day 8 of the Ramdan DevOps Bootcamp! Today, we dove into real-world DevOps practices by implementing CI/CD pipelines for a Python API using GitLab CI, building Docker images, pushing them to the GitLab Container Registry, and deploying them via Kubernetes.

Let’s break down what we accomplished 👇


🐍 Python App Setup with Docker

We containerized a basic FastAPI application using this Dockerfile:

# Base Python image
FROM python:3.12-slim

# Set working directory inside the container
WORKDIR /usr/local/app

# Copy requirements file and install dependencies
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt

# Copy application source code
COPY src ./src

# Expose application port
EXPOSE 8000

# Switch to a non-root user for better security
RUN useradd app
USER app

# Run the application
CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000"]

✅ Lightweight
✅ Secure (non-root user)
✅ Production-ready


⚙️ GitLab CI/CD Pipeline

We configured a GitLab CI/CD pipeline to automatically:

  1. Build the Docker image.

  2. Push it to the GitLab Container Registry.

  3. Optionally trigger deployment (manual or automatic).

A sample .gitlab-ci.yml might look like this:

image: docker:latest

services:
  - docker:build

stages:
  - build
  - push

variables:
  IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG

before_script:
  - echo "$CI_JOB_TOKEN" | docker login -u gitlab-ci-token --password-stdin $CI_REGISTRY

build:
  stage: build
  script:
    - docker build -t $IMAGE_TAG .
    - docker push $IMAGE_TAG
  only:
    - tags

💡 We trigger builds on Git tags like 1.2.0 to produce versioned images.


🐳 Image Hosting with GitLab Container Registry

No need for Docker Hub or external registries. GitLab gives us a built-in container registry:

tgitlab.hassandevops.site:5050/ramdanbootcamp/multistage-python-cicd:1.2.0

You can access it under your project > Packages & Registries > Container Registry.


☸️ Kubernetes Deployment

After pushing our Docker image, we deployed it to Kubernetes using the following YAML configs:

✅ 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

🔐 imagePullSecrets helps Kubernetes authenticate with GitLab's private registry.

✅ Service

apiVersion: v1
kind: Service
metadata:
  name: mypythonapp-svc
spec:
  selector:
    app: mypythonapp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8000

✅ Ingress (with SSL)

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

🌐 Our Python app is now live at https://python.hassandevops.site


🔐 Pro Tip: Creating the GitLab Registry Secret

To pull private images in Kubernetes, create a secret:

kubectl create secret docker-registry gitlab-registry-secret \
  --docker-server=gitlab.hassandevops.site:5050 \
  --docker-username=<your-username> \
  --docker-password=<your-token> \
  --docker-email=you@example.com

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.