Introduction
In this article you will learn how to create a simple “hot-cold” app in Python, use docker for containerazation, and finally deploy your app using kubernetes.
We will use Kind to build our k8s cluster, we will learn how to do save our docker image inside Harbor image registry, and how to install our app’s k8s files using helm chart.
This artictle demonstrates the way from building the app, all the way to deployment.
Perequisites
- Python (Check out Python installation guide)
- Kubectl (Check out Kubectl installation guide)
- Kind (Check out Kind installation guide)
- Docker (Check out Docker installation guide)
Hot-Cold Python app
For this demonstration we will create a simple hot-cold up using Python. Our app will contain two simple html pages: hot and cold. Each time we will get a different page. To create this app we will use Flask library from Python, this library is useful to build websites using Python language.
So let’s get started by creating the app.
Setting up a virtual environment
Before diving into our source code, it is best practice to develop the app inside a virtual environment, so it will be easy for us to deal with our app’s dependicies later.
# To create a new environment run the command:
python -m venv .venv
# To activate and enter the environment run:
source .venv/bin/activate
Source code:
# Importing Flask and rndom libreries
from flask import Flask, render_template
import random
# Creating the app
app = Flask(__name__)
@app.route("/")
def home():
template = random.randint(1,2)
if template == 1:
return render_template("hot.html")
return render_template("cold.html")
if __name__ == '__main__':
app.run(debug=True)
To be able to randomly show a different page each time, we will use the help of the random library. We will generate a random number: 1 or 2, and according to each resault we respond with hot or cold page
Docker
Docker Engine is an open source containerization technology for building and containerizing your applications.
Docker has two main attributes, images and containers.
We can imagine that an image is like a recepy and the container is like the cake. We create containers out of images.
Dockerfile:
FROM python:slim
WORKDIR /app
RUN apt-get update
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 5000
CMD gunicorn --bind 0.0.0.0:5000 app:app
Now we need to build the image.
# Run the command:
docker build -t hot-cold-image .
# To check the new image run:
docker images
If everytrhing went according to plan you will see the next screen:
Creating K8S Cluster Using Kind
Now we are getting into the interesting part, setting up our k8s cluster. To perform that we will use kind.
Kind is an open-source, CNCF-certified Kubernetes installer used by developers to quickly and easily create Kubernetes clusters using Docker container.
Creating the k8s cluster
To download the cluster configuration run:wget https://gist.githubusercontent.com/purushothamkdr453/39e097ce8ea62efbf28d8badebcbf5dd/raw/eab80fba4afdab26a3a9398d5ba59d383aad27ae/kind-single-controlplane-multiple-worker.yaml
To create the cluster run:kind create cluster --config kind-single-controlplane-multiple-worker.yaml
Helm
Helm is a package manager for k8s. This is our way to package all the yaml files we need to run our app, insert everything to one place, and install it. This is the helm chart.
Let’s start by downloading helm.sudo dnf install helm
Note: we will use helm later on, we need it to be installed for our next services.
Harbor
So what is Harbor? Harbor is a local registry that can be installed inside our k8s cluster. It can contain docker images and podman images as well.
So let’s start our journey with harbor.
Installing MetalLB
What is metallb? MetalLB assigns IP addresses to services (applications) within Kubernetes, providing load balancing functionality similar to what cloud providers offer.
# To install run:
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.13.7/config/manifests/metallb-native.yaml
# Run this command to check pods and wait untill they are all up:
k get pods -n metallb-system
# Now we need to check our docker kind cidr, so metalb will use a pool to give us ip.
# Run the command:
docker network inspect -f '{{.IPAM.Config}}' kind
In my case the output will contain a cidr such as 172.18.0.0/16. You need to apy attention to the output and create a metallb-config.yaml accordingly.
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: example
namespace: metallb-system
spec:
addresses:
- 172.18.255.200-172.18.255.250
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: empty
namespace: metallb-system
Apply the above manifest file:kubectl apply -f metallb-config.yaml
Installing Harbor
First we need to download the harbor helm repo:
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
Next, create a values.yaml file as following:
externalURL: https://harbor-ds.octopus.lab
adminPassword: "admin"
nginx:
tls:
commonName: harbor-ds.octopus.lab
Note: change the domain to yours.
# Download the helm chart:
helm install harbor bitnami/harbor -n harbor -f values.yaml --create-namespace
# Run the following command to get the external IP:
kubectl get svc -n harbor
# In my case harbor service loadbalancer ip is -> 172.18.255.200.
# Go to your /etc/hosts and assign the domain from earlier to the ip you got.
vi /etc/hosts
# Add the following line
172.18.255.200 harbor-ds.octopus.lab
Try to get to the harbor wesite with your domain, enter the username: admin and the password you chose in the values.yaml file.
Harbor Project
After you reached the website, create a new project and name it as you like.
Now tag your image:docker tag dshwartzman5/hot-cold-app:latest harbor-ds.octopus.lab/hot-cold-registry/hot-cold-app:latest
If we try to push the image at this stage, we will get an error. This is due to the fact that docker expects registries to have valid SSL certificates signed by a trusted Certificate Authority (CA). However, when using self-signed certificates, Docker considers the registry as insecure.
Create daemon.json file:
mkdir -p /etc/docker/
vi /etc/docker/daemon.json
{
"insecure-registries" : [ "harbor-ds.octopus.lab" ]
}
Restart Docker and reload Deamon:
systemctl daemon-reload
systemctl restart docker
Login to harbor from CLI:docker login harbor-ds.octopus.lab
Note: use the username and password from before
Push the Image:docker push harbor-ds.octopus.lab/hot-cold-registry/hot-cold-app
To access harbor we need to enter via HTTPS. We have a CA certificate inside our registry. Download from the registry and copy the CA certificate to all of our nodes:
# Control Plane
docker cp /home/ortuser19/Downloads/ca.crt cluster1-control-plane:/etc/ssl/certs
docker exec cluster1-control-plane update-ca-certificates
docker exec cluster1-control-plane systemctl restart containerd
# Worker1
docker cp /home/ortuser19/Downloads/ca.crt cluster1-worker:/etc/ssl/certs
docker exec cluster1-worker update-ca-certificates
docker exec cluster1-worker systemctl restart containerd
# Worker2
docker cp /home/ortuser19/Downloads/ca.crt cluster1-worker2:/etc/ssl/certs
docker exec cluster1-worker2 update-ca-certificates
docker exec cluster1-worker2 systemctl restart containerd
# Worker3
docker cp /home/ortuser19/Downloads/ca.crt cluster1-worker3:/etc/ssl/certs
docker exec cluster1-worker3 update-ca-certificates
docker exec cluster1-worker3 systemctl restart containerd
Nginx Ingress Controller
So what is ingress? The purpose of ingress is to control the traffic from outside to inside our cluster. Ingress has rules, who can enter and who doesn’t and ingress controller is the thing that enforces those rules and helps to manage the traffic, ensuring traffic get smoothly to the right destination.
# First we need to download the nginx controller helm chart:
helm pull oci://ghcr.io/nginxinc/charts/nginx-ingress --untar --version 1.1.3
# Change your working directory to nginx-ingress:
cd nginx-ingress
# Download CRDs:
kubectl apply -f crds/
# Install the helm chart:
helm install my-release oci://ghcr.io/nginxinc/charts/nginx-ingress --version 1.1.3
Now, after nginx ingress controllwe is installed, we can modify /etc/hosts file again, but this time we need to add the domain for our website.
# Run the following command to get the extertnal IP of our app:
kubectl get svc
# Open the /etc/hosts file :
vi /etc/hosts
# Add the following :
172.18.255.201 ds-hot-cold-app.octopus.lab
Note: The IP address above can be different for each user.
Helm Chart
After a successfull installation we need to create an helm chart.
Run the command:helm create app
This will create us a new folder by the name app. If you would like you can see the default content of the folder.
Next, we will edit the values.yaml file. This file helps us to store repeated values along the files and we can access those values from one central place. Afterwards we will create 4 crucial files the help us deploy our app.
values.yaml:
# Default values for app.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
namespace:
name: hot-cold-app # The name of the namespace to deploy the app into
app:
replicaCount: 1 # Number of replicas to deploy
image:
name: harbor-ds.octopus.lab/hot-cold-registry/hot-cold-app # The name of the image to deploy
tag: latest # The tag of the image to deploy
namespace.yaml:
This file creates as a namespace. Namespace is a logical environment that we can use to grant rules and centerlize our k8s services that are related.
apiVersion: v1 # Setting the API version
kind: Namespace # Defining the kind of resource
metadata:
name: {{ .Values.namespace.name }} # Setting the name of the namespace from the values file
deployment.yaml:
A deployment in Kubernetes manages a set of identical pods, ensuring availability and scaling according to defined specifications.
apiVersion: apps/v1 # Setting the API version
kind: Deployment # Setting the kind of the resource
metadata:
name: hot-cold-deployment # Setting the name of the deployment
namespace: {{ .Values.namespace.name }} # Setting the namespace of the deployment
labels:
app: hot-cold-app # Setting the label of the deployment
spec:
replicas: {{ .Values.app.replicaCount }} # Setting the number of replicas
selector:
matchLabels:
app: hot-cold-app # Ensuring the selector matches the label of the deployment
template: # Setting the template of the deployment
metadata:
labels:
app: hot-cold-app # Ensuring the label of the pod matches the label of the deployment
spec:
containers:
- name: hot-cold-app # Setting the name of the container
image: "{{ .Values.app.image.name }}:{{ .Values.app.image.tag }}" # Setting the image of the container
ports:
- containerPort: 5000 # Setting the port of the container
service.yaml:
A service in Kubernetes is enabling network communication, holds one static IP address to all the pods with the same label.
apiVersion: v1 # Setting the version of the API
kind: Service # The kind of resource we are creating
metadata:
namespace: {{ .Values.namespace.name }} # The namespace where the service will be created (from the values.yaml file)
name: hot-cold-service # The name of the service
labels:
app: hot-cold-app # The label of the service (used to match the service with the deployment)
spec:
ports:
- port: 80 # The port that the service will listen on
targetPort: 5000 # The port that the service will forward requests to
selector:
app: hot-cold-app # The label of the service (used to match the service with the deployment)
ingress.yaml:
This file helps us to define the rules for the ingress. For example: the accessable domain, the port the traffic will be sent to and etc.
apiVersion: networking.k8s.io/v1 # Setting the API version
kind: Ingress # Setting the kind
metadata:
namespace: {{ .Values.namespace.name }} # Setting the namespace
name: hot-cold-ingress # Setting the name of the ingress
annotations:
kubernetes.io/ingress.class: "nginx" # Setting the ingress class
spec:
rules:
- host: ds-hot-cold-app.octopus.lab # Setting the domain (taken from /etc/hosts)
http:
paths:
- path: / # Setting the path(homepage)
pathType: Prefix
backend:
service:
name: hot-cold-service # Ensuring the service matches the service name
port:
number: 80 # Setting the port number traffic will be sent to
Now we have everything set up and we can install our own app!!!
Are you ready?
Run the command:helm install app ./app
Now you should see the following:
If you want to see the created resources run:kubectl get all -n hot-cold-app
Now you can access your app through the domain.
Bounus Tip
If you are tired of typing kubectl every time, there is an easy way to make it slightly easier.
Go to .bachrc file in your home directory and add an alias to the kubectl command, and that is it.
vi ~/.bachrc
alias k='kubectl'
# restart your terminal after that
So there is a big relief, huh?
Summary
In this tutorial we learned how to create a simple python app, containerize it, and deply it using kubernetes and helm. In addition, we saw how to install kind and deploy our app with kind instead of dockerhub.
I hope this article was very helpful and helm you gain the basics of app deployment using k8s.