{"id":19393199,"url":"https://github.com/ruanbekker/python-flask-app-kubernetes","last_synced_at":"2025-04-24T02:31:31.644Z","repository":{"id":139707038,"uuid":"499954009","full_name":"ruanbekker/python-flask-app-kubernetes","owner":"ruanbekker","description":"Python Flask App Deploy to Kubernetes","archived":false,"fork":false,"pushed_at":"2024-04-06T22:16:21.000Z","size":293,"stargazers_count":8,"open_issues_count":0,"forks_count":6,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-04-06T23:23:45.688Z","etag":null,"topics":["docker","docker-compose","k3d","k8s","kubernetes","mysql","python","python-flask","terraform"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ruanbekker.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2022-06-04T22:59:46.000Z","updated_at":"2024-04-06T23:23:48.048Z","dependencies_parsed_at":"2024-04-06T23:23:46.964Z","dependency_job_id":"5c447333-b70f-4130-8927-855c6c542a1f","html_url":"https://github.com/ruanbekker/python-flask-app-kubernetes","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ruanbekker%2Fpython-flask-app-kubernetes","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ruanbekker%2Fpython-flask-app-kubernetes/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ruanbekker%2Fpython-flask-app-kubernetes/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ruanbekker%2Fpython-flask-app-kubernetes/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ruanbekker","download_url":"https://codeload.github.com/ruanbekker/python-flask-app-kubernetes/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223941388,"owners_count":17229014,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["docker","docker-compose","k3d","k8s","kubernetes","mysql","python","python-flask","terraform"],"created_at":"2024-11-10T10:32:33.685Z","updated_at":"2024-11-10T10:34:38.050Z","avatar_url":"https://github.com/ruanbekker.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# flask-app-kubernetes\n\nDeploy Python Flask app to Kubernetes (k3d)\n\n## About\n\nThis 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/).\n\nI'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).\n\nThe 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.\n\n## Dependencies\n\nThe following is required:\n\n- [docker](https://docs.docker.com/get-docker/)\n- [docker-compose](https://docs.docker.com/get-started/08_using_compose/)\n- [kubectl](https://kubernetes.io/docs/tasks/tools/)\n- [terraform](https://www.terraform.io/)\n- [k3d](https://k3d.io/stable/)\n\n## Build and Run Locally\n\nBuild the application locally:\n\n```\n$ docker-compose up -d --build\n```\n\nTest locally:\n\n```\n$ curl http://localhost:8080/hostname\nhello from 32c76a0d3784\n```\n\nView the logs locally:\n\n```\n$ docker-compose logs -f\nAttaching to flask-app\nflask-app    | [2022-06-04 23:04:59 +0000] [8] [INFO] Starting gunicorn 20.0.4\nflask-app    | [2022-06-04 23:04:59 +0000] [8] [INFO] Listening at: http://0.0.0.0:8080 (8)\nflask-app    | [2022-06-04 23:04:59 +0000] [8] [INFO] Using worker: threads\nflask-app    | [2022-06-04 23:04:59 +0000] [10] [INFO] Booting worker with pid: 10\nflask-app    | [2022-06-04 23:04:59 +0000] [11] [INFO] Booting worker with pid: 11\nflask-app    | 172.19.0.1 - - [04/Jun/2022:23:05:13 +0000] \"GET /hostname HTTP/1.1\" 200 23 \"-\" \"curl/7.54.0\"\n```\n\nTerminate the application locally:\n\n```\n$ docker-compose down\nStopping flask-app ... done\nRemoving flask-app ... done\n```\n\n## Build and Push Container Image\n\nBuilld and push the images to your registry:\n\n```\n$ docker-compose build\n$ docker-compose push\n```\n\n## Kubernetes\n\nProvision a Kubernetes cluster either via [k3d](https://k3d.io/) or [terraform](https://www.terraform.io/)\n\n### Kubernetes Cluster Option 1: Using k3d directly\n\nCreate a k3d (kubernetes on docker using k3s) kubernetes cluster:\n\n```\n$ k3d cluster create --config k3d-cluster.yml\nINFO[0061] Cluster 'kubernetes-cluster' created successfully!\n```\n\n### Kubernetes Cluster Option 2: Using terraform\n\nCreate the k3d Kubernetes cluster with terraform:\n\n```\nterraform -chdir=infra init\nterraform -chdir=infra plan\nterraform -chdir=infra apply -auto-approve\n```\n\nConfiguration such as the [k3s version](https://hub.docker.com/r/rancher/k3s/tags) can be configured under `./infra/variables.tf`.\n\n## Overview of our Infrastructure\n\nCheck if the nodes are visible:\n\n```\n$ kubectl get nodes --output wide\nNAME                              STATUS   ROLES                  AGE   VERSION        INTERNAL-IP   EXTERNAL-IP   OS-IMAGE   KERNEL-VERSION     CONTAINER-RUNTIME\nk3d-kubernetes-cluster-agent-0    Ready    \u003cnone\u003e                 78s   v1.20.4+k3s1   172.20.0.4    \u003cnone\u003e        Unknown    5.10.25-linuxkit   containerd://1.4.3-k3s3\nk3d-kubernetes-cluster-agent-1    Ready    \u003cnone\u003e                 77s   v1.20.4+k3s1   172.20.0.5    \u003cnone\u003e        Unknown    5.10.25-linuxkit   containerd://1.4.3-k3s3\nk3d-kubernetes-cluster-server-0   Ready    control-plane,master   99s   v1.20.4+k3s1   172.20.0.3    \u003cnone\u003e        Unknown    5.10.25-linuxkit   containerd://1.4.3-k3s3\n```\n\nBecause we are using k3d, the \"nodes\" runs as containers and that can be identified using the docker client:\n\n```\ndocker ps -f name=k3d\nCONTAINER ID   IMAGE                             COMMAND                  CREATED         STATUS         PORTS                                            NAMES\n75ed4bf094b2   ghcr.io/k3d-io/k3d-tools:latest   \"/app/k3d-tools noop\"    2 minutes ago   Up 2 minutes                                                    k3d-kubernetes-cluster-tools\n992bfa04a7b7   ghcr.io/k3d-io/k3d-proxy:latest   \"/bin/sh -c nginx-pr…\"   2 minutes ago   Up 2 minutes   0.0.0.0:80-\u003e80/tcp, 127.0.0.1:6445-\u003e6443/tcp   k3d-kubernetes-cluster-serverlb\n21a2679b5500   rancher/k3s:v1.22.9-k3s1          \"/bin/k3d-entrypoint…\"   2 minutes ago   Up 2 minutes                                                    k3d-kubernetes-cluster-agent-1\n68510b7d1bbb   rancher/k3s:v1.22.9-k3s1          \"/bin/k3d-entrypoint…\"   2 minutes ago   Up 2 minutes                                                    k3d-kubernetes-cluster-agent-0\n00d20e9443f3   rancher/k3s:v1.22.9-k3s1          \"/bin/k3d-entrypoint…\"   2 minutes ago   Up 2 minutes                                                    k3d-kubernetes-cluster-server-0\n```\n\n## Deploy the flask app to kubernetes\n\nCreate the deployment:\n\n```\n$ kubectl apply -f kubernetes/\nservice/flask-app-service created\ningress.networking.k8s.io/flask-app-ingress created\ndeployment.apps/flask-app created\ndeployment.apps/flask-db created\nsecret/flask-secrets created\n```\n\nView the deployment:\n\n```\n$ kubectl get deployments\nNAME        READY   UP-TO-DATE   AVAILABLE   AGE\nflask-db    1/1     1            1           11m\nflask-app   3/3     3            3           11m\n```\n\nView the pods:\n\n```\n$ kubectl get pods\nNAME                         READY   STATUS    RESTARTS   AGE\nflask-db-6c7f76489-nvw5b     1/1     Running   0          12m\nflask-app-8485fb56dd-rzq4q   1/1     Running   0          12m\nflask-app-8485fb56dd-jzpj8   1/1     Running   0          12m\nflask-app-8485fb56dd-vjdhw   1/1     Running   0          12m\n```\n\nView the ingress:\n\n```\n$ kubectl get ingress\nNAME                CLASS    HOSTS                        ADDRESS      PORTS   AGE\nflask-app-ingress   \u003cnone\u003e   flask-app.127.0.0.1.nip.io   172.20.0.3   80      63s\n```\n\nView the services:\n\n```\n$ kubectl get service\nNAME                TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE\nkubernetes          ClusterIP   10.43.0.1      \u003cnone\u003e        443/TCP   2m57s\nflask-app-service   ClusterIP   10.43.142.13   \u003cnone\u003e        80/TCP    67s\nflask-db            ClusterIP   10.43.104.104  \u003cnone\u003e        3306/TCP  64s\n```\n\n## Test application on Kubernetes\n\nTest the application on the kubernetes cluster:\n\n```\n$ curl http://flask-app.127.0.0.1.nip.io/hostname\nhello from flask-app-8485fb56dd-rzq4q\n```\n\nView the logs from the pod:\n\n```\n$ kubectl logs -f pod/flask-app-8485fb56dd-rzq4q\n[2022-06-04 23:13:19 +0000] [7] [INFO] Starting gunicorn 20.0.4\n[2022-06-04 23:13:19 +0000] [7] [INFO] Listening at: http://0.0.0.0:8080 (7)\n[2022-06-04 23:13:19 +0000] [7] [INFO] Using worker: threads\n[2022-06-04 23:13:19 +0000] [9] [INFO] Booting worker with pid: 9\n[2022-06-04 23:13:19 +0000] [10] [INFO] Booting worker with pid: 10\n10.42.0.3 - - [04/Jun/2022:23:14:45 +0000] \"GET /hostname HTTP/1.1\" 200 37 \"-\" \"curl/7.54.0\"\n```\n\nAs we have set the replica count to 3, we should see 3 different responses from 3 requests:\n\n```\n$ for each in $(seq 1 3) ; do curl http://flask-app.127.0.0.1.nip.io/hostname; echo \"\" ; done\nhello from flask-app-8485fb56dd-rzq4q\nhello from flask-app-8485fb56dd-jzpj8\nhello from flask-app-8485fb56dd-vjdhw\n```\n\n## View the database\n\nYou can use kubectl to run a mysql pod and connect to it over its service name, first decode the password secret:\n\n```bash\n# on macosx\n$ password=$(kubectl get secrets/flask-secrets --template={{.data.db_password}} | base64 -D)\n# or on linux\n$ password=$(kubectl get secrets/flask-secrets --template={{.data.db_password}} | base64 -d)\n```\n\nThe connect to the database:\n\n```bash\n$ kubectl run -it --rm --image=mysql:8.0 --restart=Never mysql-client -- mysql --host flask-db.default.svc.cluster.local --user=ruan  --password=$password\n\nmysql\u003e \n```\n\n## Seed the database\n\nSeed the database with sample data, for docker:\n\n```\ndocker run --rm -i --network=public -e API_HOST=flask-app:8080 loadimpact/k6 run --quiet - \u003c k6lib/post.js\n```\n\nAnd for kubernetes:\n\n```\ndocker run --rm -i --net=host -e API_HOST=flask-app.127.0.0.1.nip.io loadimpact/k6 run --quiet - \u003c k6lib/post.js\n```\n\n## Clean up\n\nTerminate the cluster via k3d:\n\n```\nk3d cluster delete --all\n```\n\nOr terminate the cluster via terraform:\n\n```\nterraform -chdir=infra destroy -auto-approve\n```\n\n## Resources\n\nDocker:\n- https://docs.docker.com/get-docker/\n\nK3d:\n- https://k3d.io/stable/\n\nTerraform k3d Provider:\n- https://registry.terraform.io/providers/pvotal-tech/k3d/latest\n\nK6:\n- https://k6.io/docs/using-k6/http-requests/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fruanbekker%2Fpython-flask-app-kubernetes","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fruanbekker%2Fpython-flask-app-kubernetes","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fruanbekker%2Fpython-flask-app-kubernetes/lists"}