Git initial commit

This commit is contained in:
Lars Hahn 2023-08-19 23:37:46 +02:00
commit 126041847e
7 changed files with 310 additions and 0 deletions

11
Dockerfile Executable file
View File

@ -0,0 +1,11 @@
FROM python
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
EXPOSE 8000
ENTRYPOINT python app.py

54
README.md Executable file
View File

@ -0,0 +1,54 @@
# K8sPy
A simple test how to get a py flask app run on kubernetes.
---
## 1. For Docker
### Instructions
1. docker build -t flaskapp .
2. docker images -a
3. docker run -d -p 80:8000 flaskapp
-> Open Browser: http://localhost
Endpoints:
- /start-thread/<ID>
- /start-thread
- /stop-thread/<ID>
- /stop-thread
- /content/<ID>
- /content
---
## 2. For Kubernetes
Here this is just testing with minikube!!!!
(Adjust the imagePullPolicy to Never when using Minikube)
This will create a cluster with 5 nodes and 10 replica pods;
On average(!) 2 pods per node, but does not have to be!
0. $ minikube start --nodes=5
1. $ minikube nodes list
2. $ minikube image load flaskapp
3. $ minikube kubectl -- apply -f deployment.yaml -f service.yaml
4. $ minikube kubectl -- get pods
5. $ minikube kubectl -- get pods -o json
6. $ minikube service list -> open URL in Browser and have fun! :)
7. $ minikube delete

146
app.py Executable file
View File

@ -0,0 +1,146 @@
from flask import Flask, request, abort
from tthread import RandomThread
from threading import Lock
import os
app = Flask(__name__)
thread_pool = {}
thread_id_max = 0
sleep_default = 0.5
mutex = Lock()
POD_NAME_VAR = os.environ.get("POD_NAME_VAR")
NODE_NAME_VAR = os.environ.get("NODE_NAME_VAR")
info = f" on Pod '{POD_NAME_VAR}' on Node '{NODE_NAME_VAR}'" if POD_NAME_VAR is not None and NODE_NAME_VAR is not None else ""
@app.route("/")
def root_main():
welcome = f"Heyho{info} &#128540;"
thread_status = [
f"{thread_id}: active? {thread.is_active()}"
for thread_id, thread in thread_pool.items()
]
response = (
'<br>'.join(status for status in thread_status)
if thread_status
else
"< no active thread>"
)
return (
f"<h1> Thread Status </h1> {welcome} <br><br> Current thread status: <br>"
f"{response}"
)
@app.route("/start-thread/<threadid>")
def start_thread_id(threadid):
threadid = str(threadid)
if threadid in thread_pool and thread_pool[threadid].is_active():
abort(409, "Thread already started.")
sleep_time = int(request.args.get('sleep-time') or sleep_default)
_start_thread(threadid, sleep_time)
return f"Starting thread with id '{threadid}'{info}."
@app.route("/start-thread")
def start_thread():
global thread_id_max
sleep_time = request.args.get('sleep-time') or sleep_default
threadid = request.args.get('thread-id')
if threadid is None:
mutex.acquire()
threadid = str(thread_id_max)
thread_pool[threadid] = None
thread_id_max += 1
mutex.release()
if threadid in thread_pool and thread_pool[threadid] is not None and thread_pool[threadid].is_active():
abort(409, "Thread already started.")
_start_thread(threadid, sleep_time)
return f"Starting thread with ID '{threadid}'{info}."
@app.route("/stop-thread/<threadid>")
def stop_thread_id(threadid):
if threadid not in thread_pool:
abort(404, "Thread ID is unknown.")
elif thread_pool[threadid] is None or not thread_pool[threadid].is_active():
abort(404, "Thread is not active.")
_stop_thread(threadid)
return f"Stopping thread with id '{threadid}'{info}."
@app.route("/stop-thread")
def stop_threads():
active_threads = [
thread
for thread in thread_pool.values()
if thread is not None and thread.is_active()
]
if not active_threads:
abort(404,"No active thread!")
for thread in active_threads:
_stop_thread(thread.id())
id_list = [f"Thread '{thread.id()}'" for thread in active_threads]
response = f"Stopping threads{info}: <br>{'<br>'.join(id_list)}"
return response
@app.route("/content/<threadid>")
def get_content_id(threadid):
if threadid not in thread_pool:
abort(404, "Thread ID is unknown.")
elif thread_pool[threadid] is None or not thread_pool[threadid].is_active():
abort(404, "Thread is not active.")
return f"<h1>Thread '{threadid}'{info}</h1><br>Content: '{thread_pool[threadid].get_content()}'"
@app.route("/content")
def get_content():
active_threads = [
thread
for thread in thread_pool.values()
if thread is not None and thread.is_active()
]
if not active_threads:
abort(404,"No active thread!")
content = [
f"Thread '{thread.id()}': {thread.get_content()}"
for thread in active_threads
]
response = (
"<h1>Thread Content{info}</h1><br>"
f"{'<br>'.join(content)}"
)
return response
def _start_thread(threadid, sleep_time):
if not threadid in thread_pool or thread_pool[threadid] is None:
thread_pool[threadid] = RandomThread(threadid, sleep_time)
thread_pool[threadid].start()
def _stop_thread(threadid, join=False):
if not join:
thread_pool[threadid].stop()
else:
thread_pool[threadid].stopjoin()
if __name__ == "__main__":
#DO NOT RUN IN PROD!!!!!!!!!!!!!!
app.run(host="0.0.0.0", port=8000, debug=True)

30
deployment.yaml Executable file
View File

@ -0,0 +1,30 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: flask-app
labels:
app: flask-app
spec:
replicas: 10
selector:
matchLabels:
app: flask-app
template:
metadata:
labels:
app: flask-app
name: flask-app
spec:
containers:
- name: flask-app
image: flaskapp
imagePullPolicy: Never
env:
- name: NODE_NAME_VAR
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: POD_NAME_VAR
valueFrom:
fieldRef:
fieldPath: metadata.name

1
requirements.txt Executable file
View File

@ -0,0 +1 @@
flask

15
service.yaml Executable file
View File

@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
labels:
app: flask-app
name: flask-app
spec:
type: NodePort
ports:
- port: 80
protocol: TCP
targetPort: 8000
nodePort: 30080
selector:
app: flask-app

53
tthread.py Executable file
View File

@ -0,0 +1,53 @@
from random import randint
from threading import Thread, Event
from time import sleep
class RandomThread:
def executor(event, dynamic_content, sleep_time):
while not event.is_set():
dynamic_content["random"] = randint(0,10000)
sleep(sleep_time)
def __init__(self, my_id, sleep_time=0.5):
self._my_id = my_id
self._event = Event()
self._content = {"random":None}
self._sleep_time = sleep_time
def start(self):
self._event.clear()
self._thread = Thread(
target=RandomThread.executor,
args=(
self._event,
self._content,
self._sleep_time
)
)
self._thread.start()
def stop(self):
self._event.set()
def stopjoin(self):
self.stop()
self._thread.join()
def id(self):
return self._my_id
def get_content(self):
return self._content['random']
def is_active(self):
return not self._event.is_set()
if __name__ == "__main__":
sleep_time = 0.25
tthread = RandomThread(0x32a, sleep_time*2)
tthread.start()
for i in range(20):
print(f"{i+1}: tthread has content {tthread.get_content()}")
sleep(sleep_time)
tthread.stopjoin()