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
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!

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.