Aim: Serve “Hello world” using a docker container of a flask app, managed by kubernetes using minikube.
Main source: I have mildly modified this tutorial to run the app on minikube.
Prereqs
Installation of:
- minikube
- kubectl
- VM driver
- docker
The flask app requires python3 (used with version 3.7) with flask (obviously) and gunicorn.
A working knowledge of setting up a simple flask app in a docker container is probably helpful.
Code structure
$tree
.
├── app
│ ├── app.py
│ ├── config.py
│ ├── Dockerfile
│ └── requirements.txt
└── kubes
└── app.yaml
Flask app
The app.py
:
from flask import Flask
import config
= Flask(__name__)
app
@app.route("/")
def hello():
return "Hello World!! 🎉"
if __name__ == "__main__":
="0.0.0.0", port=config.PORT, debug=config.DEBUG_MODE) app.run(host
The config.py
:
import os, multiprocessing
# Get enviornment variables or use defaults if not set
= int(os.environ.get("PORT", 8080))
PORT = int(os.environ.get("DEBUG_MODE", 1))
DEBUG_MODE
# Gunicorn config
= ":" + str(PORT)
bind = multiprocessing.cpu_count() * 2 + 1
workers = 2 * multiprocessing.cpu_count() threads
The requirements.py
:
==1.1.1
Flask==20.0.3 gunicorn
Dockerfile
The Dockerfile
:
FROM python:3.7-alpine
WORKDIR /app
ADD requirements.txt /app/requirements.txt
RUN pip install -r /app/requirements.txt
ADD . /app
ENV PORT 8080
CMD ["gunicorn", "app:app", "--config=config.py"]
Kubenetes config
The app.yaml
:
apiVersion: apps/v1
kind: Deployment
metadata:
name: flask-app
labels:
name: flask-app
spec:
replicas: 1
selector:
matchLabels:
name: flask-app
template:
metadata:
name: flask-app
labels:
name: flask-app
spec:
containers:
- name: flask-app
image: flask-app
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
resources:
requests:
memory: 256Mi
limits:
memory: 512Mi
env:
- name: DEBUG_MODE
value: "0"
---
apiVersion: v1
kind: Service
metadata:
name: flask-app-service
spec:
selector:
name: flask-app
ports:
- port: 80
targetPort: 8080
type: LoadBalancer
Setting up on minikube
Launch
Start minikube and the dashboard:
$minikube start && minikube dashboard
😄 minikube v1.5.2 on Ubuntu 18.04
🔥 Creating kvm2 VM (CPUs=2, Memory=2000MB, Disk=20000MB) ...
🐳 Preparing Kubernetes v1.16.2 on Docker '18.09.9' ...
🚜 Pulling images ...
🚀 Launching Kubernetes ...
⌛ Waiting for: apiserver
🏄 Done! kubectl is now configured to use "minikube"
🤔 Verifying dashboard health ...
🚀 Launching proxy ...
🤔 Verifying proxy health ...
🎉 Opening http://127.0.0.1:40661/api/v1/namespaces/kubernetes-dashboard/services/http:kubernetes-dashboard:/proxy/ in your default browser...
Build image
In one terminal, establish environment variables that ensures docker builds images where minikube can see them:
$eval $(minikube docker-env)
Note that this is only relevant to the terminal in which it is run.
Build the docker image in the apps/
directory
$docker build -t flask-app .
Sending build context to Docker daemon 5.12kB
Step 1/7 : FROM python:3.7-alpine
3.7-alpine: Pulling from library/python
...
Successfully tagged flask-app:latest
Deploy and service
In the kubes/
directory, we have the app.yaml
which describes the deployment of the app,
as well as the LoadBalancer service that exposes the deployment on the correct ports.
This is setup by running the following (within the kubes/
directory):
$kubectl apply -f app.yaml
deployment.apps/flask-app created
service/flask-app-service created
Tunnelling
The exposed ports still aren’t available to the host machine (outside the VM). By either consulting the dashboard, or by running the following command:
$kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
flask-app-service LoadBalancer 10.96.229.250 <pending> 80:31748/TCP 97s
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4m23s
We see no external IP is yet available.
Last step is to open a tunnel:
$minikube tunnel
Status:
machine: minikube
pid: 16289
route: 10.96.0.0/12 -> 192.168.39.77
minikube: Running
services: [flask-app-service]
errors:
minikube: no errors
router: no errors
loadbalancer emulator: no errors
...
Now:
$kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
flask-app-service LoadBalancer 10.96.229.250 10.96.229.250 80:31748/TCP 2m44s
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 5m30s
And finally we have:
$curl 10.96.229.250
Hello World!! 🎉
Yay!!