Introduction (Scroll-down to Step-by-Step Video)

In this blog, we’ll walk through the complete process of developing, containerizing, and deploying a simple Python web server on a Kubernetes cluster. This DevOps pipeline involves writing the server code, containerizing the application with Docker, pushing the image to Docker Hub using GitHub Actions, and finally deploying the application on a Kubernetes cluster using Helm. We’ll cover each step in detail, highlighting the tools used and best practices to follow.

Tools Used

  • Python: For developing the web server.
  • Docker: For containerizing the application.
  • GitHub Actions: For automating the CI/CD pipeline.
  • Docker Hub: As the Docker image registry.
  • Helm: For packaging and deploying the application on Kubernetes.
  • Kubernetes: For orchestrating the containerized application.

1. Writing a Simple Python Web Server

Let’s start by writing a simple Python web server using the http.server module (Warning: Do not use for production).

from http.server import SimpleHTTPRequestHandler, HTTPServer
class MyHandler(SimpleHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write(b"Hello, World!")
def run(server_class=HTTPServer, handler_class=MyHandler, port=8000):
server_address = ('', port)
httpd = server_class(server_address, handler_class)
print(f"Starting server on port {port}...")
httpd.serve_forever()
if __name__ == '__main__':
run()

This code creates a simple web server that listens on port 8000 and responds with “Hello, World!” to any GET requests.

2. Containerizing the Web Server with Docker

Next, we’ll create a Dockerfile to containerize our Python web server.

Dockerfile:

# Use the official Python image from the Docker Hub
FROM python:3.11-slim
# Set the working directory inside the container
WORKDIR /app
# Copy the Python script into the container
COPY . /app
# Expose the port the server will run on
EXPOSE 8000
# Command to run the web server
CMD ["python", "main.py"]

To build the Docker image, run the following command:

docker build -t my-webserver:latest .

You can test the image locally by running:

docker run -p 8000:8000 my-webserver:latest

3. Automating Image Push to Docker Hub with GitHub Actions

To automate the build and push process to Docker Hub, we’ll create a GitHub Actions workflow.

GitHub Actions Workflow (.github/workflows/docker-publish.yml):

name: Docker Image CI 
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{secrets.DOCKER_USERNAME}}
password: ${{secrets.DOCKER_PASSWORD}}
- name: Build and Push Docker Image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{secrets.DOCKER_USERNAME}}/pythonserver:v1
platforms: linux/amd64,linux/arm64
- name: Image digest
id: image_digest
run: echo "Digest = ${{steps.build.outputs.digest}}"
- name: Log out from Docker Hub
run: docker logout
  • DOCKER_USERNAME and DOCKER_PASSWORD are stored as GitHub Secrets.
  • The workflow triggers on pushes to the main branch, builds the Docker image, and pushes it to Docker Hub.

4. Packaging the Application with Helm

Helm simplifies Kubernetes application deployment by packaging multiple Kubernetes resources into a single chart. Let’s create a Helm chart to deploy our web server.

Helm Chart Directory Structure:

webserver-chart/
├── Chart.yaml
├── values.yaml
└── templates/
├── deployment.yaml
├── service.yaml
└── ingress.yaml

Chart.yaml:

apiVersion: v2
name: webserver
description: A Helm chart for deploying a Python web server with NGINX Ingress
version: 0.1.0

values.yaml:

replicaCount: 1
image:
repository: ristabel/pythonserver
tag: v1
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 80
ingress:
enabled: true
name: ""
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
hosts:
- host: example.com
paths: /
tls: []

templates/deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-{{ .Chart.Name }}-webserver
labels:
app: {{ .Release.Name }}-{{ .Chart.Name }}-webserver
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ .Release.Name }}-{{ .Chart.Name }}-webserver
template:
metadata:
labels:
app: {{ .Release.Name }}-{{ .Chart.Name }}-webserver
spec:
containers:
- name: {{ .Release.Name }}-{{ .Chart.Name }}-webserver
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
ports:
- containerPort: 8000

templates/service.yaml:

apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-{{ .Chart.Name }}-webserver
labels:
app: {{ .Release.Name }}-{{ .Chart.Name }}-webserver
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: 8000
protocol: TCP
selector:
app: {{ .Release.Name }}-{{ .Chart.Name }}-webserver

templates/ingress.yaml:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ .Release.Name }}-{{ .Chart.Name }}-webserver
spec:
ingressClassName: nginx
rules:
- host: {{ .Values.ingress.host }}
http:
paths:
{{.Values.ingress.host}}
- path: /
pathType: Prefix
backend:
service:
name: {{ .Release.Name }}-{{ .Chart.Name }}-webserver
port:
number: {{ $.Values.service.port }}

5. Deploying the Application on Kubernetes

To deploy the Helm chart to your Kubernetes cluster:

  1. Add the NGINX ingress controller:helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx helm repo update helm install nginx ingress-nginx/ingress-nginx
  2. Deploy the web server: helm install webserver ./webserver-chart
  3. Verify the deployment: kubectl get all kubectl get ingress

Conclusion

This blog has guided you through a complete DevOps process, from writing a simple Python web server to deploying it on a Kubernetes cluster. By using Docker, GitHub Actions, Helm, and Kubernetes, you can automate and streamline your application deployment process. Each of these tools plays a crucial role in modern DevOps practices, helping teams deliver software more efficiently and reliably.

Leave a Reply

Your email address will not be published. Required fields are marked *