Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/ruanbekker/python-flask-app-kubernetes
Python Flask App Deploy to Kubernetes
https://github.com/ruanbekker/python-flask-app-kubernetes
docker docker-compose k3d k8s kubernetes mysql python python-flask terraform
Last synced: 3 months ago
JSON representation
Python Flask App Deploy to Kubernetes
- Host: GitHub
- URL: https://github.com/ruanbekker/python-flask-app-kubernetes
- Owner: ruanbekker
- Created: 2022-06-04T22:59:46.000Z (over 2 years ago)
- Default Branch: main
- Last Pushed: 2024-04-06T22:16:21.000Z (10 months ago)
- Last Synced: 2024-04-06T23:23:45.688Z (10 months ago)
- Topics: docker, docker-compose, k3d, k8s, kubernetes, mysql, python, python-flask, terraform
- Language: Python
- Homepage:
- Size: 286 KB
- Stars: 8
- Watchers: 3
- Forks: 6
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# flask-app-kubernetes
Deploy Python Flask app to Kubernetes (k3d)
## About
This project is a demonstration how to build, test and run a [Python Flask](https://flask.palletsprojects.com/en/2.1.x/) application locally using [docker-compose](https://docs.docker.com/get-started/08_using_compose/), then provision a local [Kubernetes](https://kubernetes.io/docs/home/) cluster using [K3d](https://k3d.io/stable/) which runs a lightweight kubernetes distribution called [K3s](https://k3s.io/) on [Docker](https://docs.docker.com/get-docker/).
I've included 2 methods to deploy k3d, either with the k3d binary or using [Terraform](https://www.terraform.io/) with the [terraform k3d provider](https://registry.terraform.io/providers/pvotal-tech/k3d/latest).
The config included uses 3 replicas for the python flask application, which is a basic application that returns the hostname of the runtime it runs on, and when it's deployed to the 3 node kubernetes cluster, we will test it by making 3 requests, and the traffic will be round-robin'd so we should see 3 different responses.
## Dependencies
The following is required:
- [docker](https://docs.docker.com/get-docker/)
- [docker-compose](https://docs.docker.com/get-started/08_using_compose/)
- [kubectl](https://kubernetes.io/docs/tasks/tools/)
- [terraform](https://www.terraform.io/)
- [k3d](https://k3d.io/stable/)## Build and Run Locally
Build the application locally:
```
$ docker-compose up -d --build
```Test locally:
```
$ curl http://localhost:8080/hostname
hello from 32c76a0d3784
```View the logs locally:
```
$ docker-compose logs -f
Attaching to flask-app
flask-app | [2022-06-04 23:04:59 +0000] [8] [INFO] Starting gunicorn 20.0.4
flask-app | [2022-06-04 23:04:59 +0000] [8] [INFO] Listening at: http://0.0.0.0:8080 (8)
flask-app | [2022-06-04 23:04:59 +0000] [8] [INFO] Using worker: threads
flask-app | [2022-06-04 23:04:59 +0000] [10] [INFO] Booting worker with pid: 10
flask-app | [2022-06-04 23:04:59 +0000] [11] [INFO] Booting worker with pid: 11
flask-app | 172.19.0.1 - - [04/Jun/2022:23:05:13 +0000] "GET /hostname HTTP/1.1" 200 23 "-" "curl/7.54.0"
```Terminate the application locally:
```
$ docker-compose down
Stopping flask-app ... done
Removing flask-app ... done
```## Build and Push Container Image
Builld and push the images to your registry:
```
$ docker-compose build
$ docker-compose push
```## Kubernetes
Provision a Kubernetes cluster either via [k3d](https://k3d.io/) or [terraform](https://www.terraform.io/)
### Kubernetes Cluster Option 1: Using k3d directly
Create a k3d (kubernetes on docker using k3s) kubernetes cluster:
```
$ k3d cluster create --config k3d-cluster.yml
INFO[0061] Cluster 'kubernetes-cluster' created successfully!
```### Kubernetes Cluster Option 2: Using terraform
Create the k3d Kubernetes cluster with terraform:
```
terraform -chdir=infra init
terraform -chdir=infra plan
terraform -chdir=infra apply -auto-approve
```Configuration such as the [k3s version](https://hub.docker.com/r/rancher/k3s/tags) can be configured under `./infra/variables.tf`.
## Overview of our Infrastructure
Check if the nodes are visible:
```
$ kubectl get nodes --output wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
k3d-kubernetes-cluster-agent-0 Ready 78s v1.20.4+k3s1 172.20.0.4 Unknown 5.10.25-linuxkit containerd://1.4.3-k3s3
k3d-kubernetes-cluster-agent-1 Ready 77s v1.20.4+k3s1 172.20.0.5 Unknown 5.10.25-linuxkit containerd://1.4.3-k3s3
k3d-kubernetes-cluster-server-0 Ready control-plane,master 99s v1.20.4+k3s1 172.20.0.3 Unknown 5.10.25-linuxkit containerd://1.4.3-k3s3
```Because we are using k3d, the "nodes" runs as containers and that can be identified using the docker client:
```
docker ps -f name=k3d
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
75ed4bf094b2 ghcr.io/k3d-io/k3d-tools:latest "/app/k3d-tools noop" 2 minutes ago Up 2 minutes k3d-kubernetes-cluster-tools
992bfa04a7b7 ghcr.io/k3d-io/k3d-proxy:latest "/bin/sh -c nginx-pr…" 2 minutes ago Up 2 minutes 0.0.0.0:80->80/tcp, 127.0.0.1:6445->6443/tcp k3d-kubernetes-cluster-serverlb
21a2679b5500 rancher/k3s:v1.22.9-k3s1 "/bin/k3d-entrypoint…" 2 minutes ago Up 2 minutes k3d-kubernetes-cluster-agent-1
68510b7d1bbb rancher/k3s:v1.22.9-k3s1 "/bin/k3d-entrypoint…" 2 minutes ago Up 2 minutes k3d-kubernetes-cluster-agent-0
00d20e9443f3 rancher/k3s:v1.22.9-k3s1 "/bin/k3d-entrypoint…" 2 minutes ago Up 2 minutes k3d-kubernetes-cluster-server-0
```## Deploy the flask app to kubernetes
Create the deployment:
```
$ kubectl apply -f kubernetes/
service/flask-app-service created
ingress.networking.k8s.io/flask-app-ingress created
deployment.apps/flask-app created
deployment.apps/flask-db created
secret/flask-secrets created
```View the deployment:
```
$ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
flask-db 1/1 1 1 11m
flask-app 3/3 3 3 11m
```View the pods:
```
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
flask-db-6c7f76489-nvw5b 1/1 Running 0 12m
flask-app-8485fb56dd-rzq4q 1/1 Running 0 12m
flask-app-8485fb56dd-jzpj8 1/1 Running 0 12m
flask-app-8485fb56dd-vjdhw 1/1 Running 0 12m
```View the ingress:
```
$ kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
flask-app-ingress flask-app.127.0.0.1.nip.io 172.20.0.3 80 63s
```View the services:
```
$ kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.43.0.1 443/TCP 2m57s
flask-app-service ClusterIP 10.43.142.13 80/TCP 67s
flask-db ClusterIP 10.43.104.104 3306/TCP 64s
```## Test application on Kubernetes
Test the application on the kubernetes cluster:
```
$ curl http://flask-app.127.0.0.1.nip.io/hostname
hello from flask-app-8485fb56dd-rzq4q
```View the logs from the pod:
```
$ kubectl logs -f pod/flask-app-8485fb56dd-rzq4q
[2022-06-04 23:13:19 +0000] [7] [INFO] Starting gunicorn 20.0.4
[2022-06-04 23:13:19 +0000] [7] [INFO] Listening at: http://0.0.0.0:8080 (7)
[2022-06-04 23:13:19 +0000] [7] [INFO] Using worker: threads
[2022-06-04 23:13:19 +0000] [9] [INFO] Booting worker with pid: 9
[2022-06-04 23:13:19 +0000] [10] [INFO] Booting worker with pid: 10
10.42.0.3 - - [04/Jun/2022:23:14:45 +0000] "GET /hostname HTTP/1.1" 200 37 "-" "curl/7.54.0"
```As we have set the replica count to 3, we should see 3 different responses from 3 requests:
```
$ for each in $(seq 1 3) ; do curl http://flask-app.127.0.0.1.nip.io/hostname; echo "" ; done
hello from flask-app-8485fb56dd-rzq4q
hello from flask-app-8485fb56dd-jzpj8
hello from flask-app-8485fb56dd-vjdhw
```## View the database
You can use kubectl to run a mysql pod and connect to it over its service name, first decode the password secret:
```bash
# on macosx
$ password=$(kubectl get secrets/flask-secrets --template={{.data.db_password}} | base64 -D)
# or on linux
$ password=$(kubectl get secrets/flask-secrets --template={{.data.db_password}} | base64 -d)
```The connect to the database:
```bash
$ kubectl run -it --rm --image=mysql:8.0 --restart=Never mysql-client -- mysql --host flask-db.default.svc.cluster.local --user=ruan --password=$passwordmysql>
```## Seed the database
Seed the database with sample data, for docker:
```
docker run --rm -i --network=public -e API_HOST=flask-app:8080 loadimpact/k6 run --quiet - < k6lib/post.js
```And for kubernetes:
```
docker run --rm -i --net=host -e API_HOST=flask-app.127.0.0.1.nip.io loadimpact/k6 run --quiet - < k6lib/post.js
```## Clean up
Terminate the cluster via k3d:
```
k3d cluster delete --all
```Or terminate the cluster via terraform:
```
terraform -chdir=infra destroy -auto-approve
```## Resources
Docker:
- https://docs.docker.com/get-docker/K3d:
- https://k3d.io/stable/Terraform k3d Provider:
- https://registry.terraform.io/providers/pvotal-tech/k3d/latestK6:
- https://k6.io/docs/using-k6/http-requests/