# 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:

```plaintext
# 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:

```plaintext
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:

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

You can access it under your project &gt; `Packages & Registries` &gt; `Container Registry`.

---

### ☸️ Kubernetes Deployment

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

#### ✅ Deployment

```plaintext
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

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

#### ✅ Ingress (with SSL)

```plaintext
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**](https://python.hassandevops.site)

---

### 🔐 Pro Tip: Creating the GitLab Registry Secret

To pull private images in Kubernetes, create a secret:

```plaintext
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
```
