Git initial commit
This commit is contained in:
commit
126041847e
11
Dockerfile
Executable file
11
Dockerfile
Executable 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
54
README.md
Executable 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
146
app.py
Executable 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} 😜"
|
||||
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
30
deployment.yaml
Executable 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
1
requirements.txt
Executable file
@ -0,0 +1 @@
|
||||
flask
|
15
service.yaml
Executable file
15
service.yaml
Executable 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
53
tthread.py
Executable 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()
|
Loading…
Reference in New Issue
Block a user