Möglichkeiten um Projekte "einfach" auf verschiedenen Server einzurichten
Hallo,
ich habe dieses mal keine direkte Python-Frage.
Ich arbeite teilweise recht umständlich. Wenn ich zum Beispiel eine Web-App mit Datenbankanbindung habe und die auf einem anderen System testen will, dann bin ich zwar soweit, dass ich den Code von Github laden kann, aber die Datenbankinstallation und -einrichtung, das kopieren von *.html-Dateien nach `/var/www/html/` und was sonst noch so anfällt bis es läuft, muss ich alles von Hand machen.
Wie macht man dass denn im professionellen Umfeld?
Meine Idee bis jetzt, ein Bash-Skript schreiben, dass die Befehle die ich von Hand schreibe, ausführt.
Habe schon etwas nach "Deploy" gegooglte und bin mir nicht so wirklich sicher wo ich ansetzen soll. Es geht hier aber rein um irgendwelche private Linux-Server, auf denen ich damit testen will. Ein Problem das mich in der Vergangenheit immer genervt hat ist das wenn ich beim entwickeln etwas an der Datenbank (MariaDB) geändert habe, musste ich das immer extra notieren und auf einem anderen System wieder neu von Hand ändern. Im Python-Code ist das mit Github schon angenehmer, einfach den neuen runterladen und fertig.
Danke und Grüße
Dennis
ich habe dieses mal keine direkte Python-Frage.
Ich arbeite teilweise recht umständlich. Wenn ich zum Beispiel eine Web-App mit Datenbankanbindung habe und die auf einem anderen System testen will, dann bin ich zwar soweit, dass ich den Code von Github laden kann, aber die Datenbankinstallation und -einrichtung, das kopieren von *.html-Dateien nach `/var/www/html/` und was sonst noch so anfällt bis es läuft, muss ich alles von Hand machen.
Wie macht man dass denn im professionellen Umfeld?
Meine Idee bis jetzt, ein Bash-Skript schreiben, dass die Befehle die ich von Hand schreibe, ausführt.
Habe schon etwas nach "Deploy" gegooglte und bin mir nicht so wirklich sicher wo ich ansetzen soll. Es geht hier aber rein um irgendwelche private Linux-Server, auf denen ich damit testen will. Ein Problem das mich in der Vergangenheit immer genervt hat ist das wenn ich beim entwickeln etwas an der Datenbank (MariaDB) geändert habe, musste ich das immer extra notieren und auf einem anderen System wieder neu von Hand ändern. Im Python-Code ist das mit Github schon angenehmer, einfach den neuen runterladen und fertig.
Danke und Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Im professionellen und Semi-professionellen Umfeld würde man hierfür Container-Technologien verwenden. D.h. in der Regel docker/docker-compose (oder im Enterprise Bereich dann sowas wie Kubernetes, wenn die notwendige, nicht unerhebliche Infrastruktur vorhanden ist). Die Dockerfiles richten die Images für deine Container ein (ggf. auch einschließlich Ingest bei deinen Datenbanken) und mit z.B.docker-compose koordinierst du die einzelnen Container und verschaltest sie. Aus meiner Sicht ist das heute der einfachste Weg, Python Webanwendungen replizierbar zu deployen. Die Webanwendung selbst läuft dann in einem Container (z.B. mit Gunicorn oder uWSGI), die Datenbank in einem weiteren (Datenbanken in Containern sind umstritten, aber zum Entwickeln ist das okay), das ganze läuft dann hinter einen Reverse-Proxy (z.B. nginx) der ebenfalls in einem Container laufen kann. Weitere Container für Caches, Hintergrundtasks sind ebenfalls üblich.
falls es nicht eh schon bei dne Docker-Geschichten als Begriff auftaucht, sollte meiner Meinung nach auch noch Ansible erwähnt werden
_______________________________________________________________________________
https://www.python-kurs.eu/index.php
https://learnxinyminutes.com/docs/python/ https://learnxinyminutes.com/docs/de-de/python-de/
https://quickref.me/python https://docs.python-guide.org/
Guten Morgen,
ich bin schon wieder am verzweifeln. Ich versuche ein Test-Image zu erstellen, aber ich bekomme immer den Fehler, dass er den Befehl `gunicorn` nicht findet. Ich habe es in meiner `requirements.txt`und das wird auch installiert.
Möglich dass da noch weitere Fehler in der `Dockerfile` oder `docker-compose.yaml` sind.
Projektstruktur:
Dockerfile:
docker-compose.yaml:
requirements.txt:
Dann folgender Befehl:
Läuft ohne Probleme durch.
Dann:
ergibt:
`mariadb` war mal drin, habe ich wieder entfernt, um mich auf das wesentliche zu konzentrieren.
Ich geb `--no-cache` an, muss ich nach einer Änderung vor dem erneuten `build` noch etwas beachten, damit das auch wirklich neu ist?
Bis jetzt gehe ich so vor, das ich entweder `docker-compose down` ausführe und dann oben gezeigten build-Befehl ausführe oder falls `down` einen Fehler bringt, lasse ich mit mit `docker ps` die ID anzeigen und stoppe die Container mit `docker stop <ID>`.
Danke und Grüße
Dennis
ich bin schon wieder am verzweifeln. Ich versuche ein Test-Image zu erstellen, aber ich bekomme immer den Fehler, dass er den Befehl `gunicorn` nicht findet. Ich habe es in meiner `requirements.txt`und das wird auch installiert.
Möglich dass da noch weitere Fehler in der `Dockerfile` oder `docker-compose.yaml` sind.
Projektstruktur:
Code: Alles auswählen
[dennis@dennis docker_test]$ tree -L 4
.
├── app
│ ├── __pycache__
│ │ ├── app.cpython-310.pyc
│ │ └── __init__.cpython-310.pyc
│ ├── src
│ │ ├── app.py
│ │ └── __init__.py
│ └── templates
│ ├── first_choice.png
│ ├── header.png
│ └── Test.html
├── docker-compose.yaml
├── Dockerfile
├── README.md
└── requirements.txt
5 directories, 11 files
Code: Alles auswählen
FROM python:3.11-slim
ENV INSTALL_PATH=/docker_test
RUN mkdir -p $INSTALL_PATH
WORKDIR $INSTALL_PATH
COPY requirements.txt $INSTALL_PATH/
COPY app/src ${INSTALL_PATH}/src
COPY app/templates ${INSTALL_PATH}/templates
RUN python -m venv .venv
RUN . .venv/bin/activate
RUN pip3 install --no-cache-dir -r requirements.txt
RUN rm -rf ~/.cache
RUN rm -rf /tmp/*
Code: Alles auswählen
services:
apache:
image: httpd:latest
restart: always
ports:
- 80:80
volumes:
- ./app/templates:/var/www/html
docker_test:
build:
context: .
dockerfile: Dockerfile
platform: linux/amd64
ports:
- "5000:5000"
volumes:
- type: bind
source: ./app
target: /src/app
command: gunicorn -w 4 -b 0.0.0.0:5000 --reload "src.app:app"
Code: Alles auswählen
attrs==24.2.0
cattrs==24.1.2
Flask==3.0.3
Flask-Cors==5.0.0
loguru==0.7.2
CoolProp~=6.6.0
prettyprinter~=0.18.0
gunicorn==23.0.0
Code: Alles auswählen
[dennis@dennis docker_test]$ docker-compose build --no-cache
Dann:
Code: Alles auswählen
[dennis@dennis docker_test]$ docker compose up -d
Code: Alles auswählen
[+] Running 0/1
⠙ Network docker_test_default Creating 0.2s
[+] Running 2/3d orphan containers ([docker_test-mariadb-1]) for this project. If you removed or renamed this servi ✔ Network docker_test_default Created 0.3s
✔ Container docker_test-apache-1 Started 1.2s
⠼ Container docker_test-docker_test-1 Starting 1.4s
Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "gunicorn": executable file not found in $PATH: unknown
Ich geb `--no-cache` an, muss ich nach einer Änderung vor dem erneuten `build` noch etwas beachten, damit das auch wirklich neu ist?
Bis jetzt gehe ich so vor, das ich entweder `docker-compose down` ausführe und dann oben gezeigten build-Befehl ausführe oder falls `down` einen Fehler bringt, lasse ich mit mit `docker ps` die ID anzeigen und stoppe die Container mit `docker stop <ID>`.
Danke und Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Ich benutze in Containern keine venvs, weil ich mir denke, dass es doch eh schon im Container isoliert ist. Wenn man es so macht: Activate ist ja dafür gedacht, das Leben auf einer interaktiven Shell leichter zu machen; in Skripten halte ich es für nicht so sinnvoll, da man dort doch einfach mit absoluten Pfaden arbeiten kann. Zudem starte ich Gunicorn meist aus einem Shellskript heraus, das ich als entrypoint im Dockerfile angebe; ist aber vmtl. Geschmackssache.
Solche Pfad-Probleme sind aber relativ normal. Was ich dann immer mache, ist, mich in den Container einloggen und gucken, was da los ist (bei laufenden Containern mit docker exec -it <containername> </pfad/zur/shell>; oder halt schon entsprechend starten mit docker run -it …) . Meist findet sich das Problem schnell, wenn man sich im Container etwas umguckt.
Solche Pfad-Probleme sind aber relativ normal. Was ich dann immer mache, ist, mich in den Container einloggen und gucken, was da los ist (bei laufenden Containern mit docker exec -it <containername> </pfad/zur/shell>; oder halt schon entsprechend starten mit docker run -it …) . Meist findet sich das Problem schnell, wenn man sich im Container etwas umguckt.
Danke für den Hinweis. Ohne venv bin ich den Fehler los geworden.
Ich denke ich habe aber noch ein Verständnisproblem.
`Dockerfile` sieht jetzt so aus:
Der Rest blieb gleich.
Ich dachte ich kann jetzt unter "localhost:8080/Test.html" meine Seite aufrufen, aber der Browser sagt mir, dass die Seite nicht gefunden werden kann.
Dann habe ich den Ratschlag, mich in den Container einzuloggen genutzt um zu sehen ob unter `/var/www/html` meine Seite liegt und ja die ist da:
Irgendwie dachte ich zu dem auch, dass ich zwei Container laufen haben müsste. In dem eingeloggten Container gibt es kein `gunicorn`. Irgendwas verstehe ich da noch nicht ganz.
Danke und Grüße
Dennis
Ich denke ich habe aber noch ein Verständnisproblem.
`Dockerfile` sieht jetzt so aus:
Code: Alles auswählen
FROM python:3.11-slim
ENV INSTALL_PATH=/docker_test
RUN mkdir -p $INSTALL_PATH
WORKDIR $INSTALL_PATH
COPY requirements.txt $INSTALL_PATH/
COPY app/src ${INSTALL_PATH}/src
COPY app/templates ${INSTALL_PATH}/templates
RUN pip install --no-cache-dir -r requirements.txt
RUN rm -rf ~/.cache
RUN rm -rf /tmp/*
Ich dachte ich kann jetzt unter "localhost:8080/Test.html" meine Seite aufrufen, aber der Browser sagt mir, dass die Seite nicht gefunden werden kann.
Dann habe ich den Ratschlag, mich in den Container einzuloggen genutzt um zu sehen ob unter `/var/www/html` meine Seite liegt und ja die ist da:
Code: Alles auswählen
[dennis@dennis docker_test]$ docker-compose up -d
Creating docker_test_docker_test_1 ... done
Creating docker_test_apache_1 ... done
[dennis@dennis docker_test]$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
396548814bdc httpd:latest "httpd-foreground" 22 seconds ago Up 22 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp docker_test_apache_1
[dennis@dennis docker_test]$ docker exec -it docker_test_apache_1 bash
root@396548814bdc:/usr/local/apache2# ls -l /var/www/html/
total 124
-rw-r--r--. 1 1000 1000 2716 Oct 24 17:52 Test.html
-rw-r--r--. 1 1000 1000 42611 Oct 24 17:39 first_choice.png
-rw-r--r--. 1 1000 1000 75811 Oct 24 17:36 header.png
root@396548814bdc:/usr/local/apache2#
Danke und Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Dein Container läuft nicht.
Wenn du statt "docker ps" "docker container ls --all" verwendest, werden dir alle Container angezeigt - nicht nur die laufenden. Und da wirst du feststellen, dass sich dein Container (docker_test_docker_test_1) sofort mit dem Exit-Code 0 beendet hat - denke ich.
Das Dockerfile ist der Bauplan, um dein Image zu bauen. Es ist quasi eine Abfolge von Befehlen, die dir einen bestimmten Zustand zusammenstellen.
Und am Ende gibt es in der Regel einen Befehl, der ausgeführt wird und dann das Programm startet, dass die ganze Zeit läuft.
Stichwort: Entrypoint.
Und klar: In deinem Apache-Container gibt es kein gunicorn. Woher soll das kommen? Dein Dockerfile hat erst einmal mit dem Apache-Container nichts zu tun.
Wenn du statt "docker ps" "docker container ls --all" verwendest, werden dir alle Container angezeigt - nicht nur die laufenden. Und da wirst du feststellen, dass sich dein Container (docker_test_docker_test_1) sofort mit dem Exit-Code 0 beendet hat - denke ich.
Das Dockerfile ist der Bauplan, um dein Image zu bauen. Es ist quasi eine Abfolge von Befehlen, die dir einen bestimmten Zustand zusammenstellen.
Und am Ende gibt es in der Regel einen Befehl, der ausgeführt wird und dann das Programm startet, dass die ganze Zeit läuft.
Stichwort: Entrypoint.
Und klar: In deinem Apache-Container gibt es kein gunicorn. Woher soll das kommen? Dein Dockerfile hat erst einmal mit dem Apache-Container nichts zu tun.
Danke für die Hilfe.
`Dockerfile`:
Ergibt:
Sehr schön, jetzt laufen beide.
Bei Apache steht ja auch, dass der auf Port 80, wie ich angegeben habe, hört. Wieso findet er dann die Webseite nicht? Seht ihr da zufällig noch einen Fehler?
Danke und Grüße
Dennis
`Dockerfile`:
Code: Alles auswählen
FROM python:3.11-slim
ENV INSTALL_PATH=/docker_test
RUN mkdir -p $INSTALL_PATH
WORKDIR $INSTALL_PATH
COPY requirements.txt $INSTALL_PATH/
COPY app/src ${INSTALL_PATH}/src
COPY app/templates ${INSTALL_PATH}/templates
RUN pip install --no-cache-dir -r requirements.txt
RUN rm -rf ~/.cache
RUN rm -rf /tmp/*
CMD ["gunicorn", "-b", "0.0.0.0:5000", "${INSTALL_PATH}.src.app:app"]
Code: Alles auswählen
[dennis@dennis docker_test]$ docker container ls --all
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f98f56cbfae0 httpd:latest "httpd-foreground" 5 seconds ago Up 4 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp docker_test_apache_1
4ec7f8ba07e0 docker_test_docker_test "gunicorn -w 4 -b 0.…" 5 seconds ago Up 4 seconds 0.0.0.0:5000->5000/tcp, :::5000->5000/tcp docker_test_docker_test_1
Bei Apache steht ja auch, dass der auf Port 80, wie ich angegeben habe, hört. Wieso findet er dann die Webseite nicht? Seht ihr da zufällig noch einen Fehler?
Danke und Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Das hängt davon ab, wie du den Apache konfiguriert hast.
Was erwartetst du denn zu sehen? Deine gunicorn-Projekt? Wie hast du denn konfiguriert, dass Apache das per Reverse-Proxy durchreichen soll?
Für mich endet das Bauen immer bei der Anwendung. In der Regel haben die Zielsysteme bereits etwas, an das gunicorn angekoppelt wird. Die Hyperscaler haben entsprechende Services (zum Beispiel die WebApp oder die ContainerApp bei Azure) und die meisten Hoster, geben einem oft die Möglichkeit an die Hand, einfach einen Container an einen Webport zu binden.
Was erwartetst du denn zu sehen? Deine gunicorn-Projekt? Wie hast du denn konfiguriert, dass Apache das per Reverse-Proxy durchreichen soll?
Für mich endet das Bauen immer bei der Anwendung. In der Regel haben die Zielsysteme bereits etwas, an das gunicorn angekoppelt wird. Die Hyperscaler haben entsprechende Services (zum Beispiel die WebApp oder die ContainerApp bei Azure) und die meisten Hoster, geben einem oft die Möglichkeit an die Hand, einfach einen Container an einen Webport zu binden.
Danke für die Antwort.
Im ersten Schritt erwarte ich, dass ich den Inhalt der `Test.html` sehe. Ich habe da zwar Eingabefelder drin, deren Eingabe dann in der Flask-App verarbeitet werden und da kommt dann Gunicorn ins Spiel. Das wäre aber erst mein zweiter Schritt.
Konfiguriert habe ich nichts, weil ich kann hier auf meinem Laptop eine *.html - Datei in `/var/www/html/` legen und wenn ich Apache2 bzw. httpd starte, dann kann ich die im Browser unter Angabe des Dateiennames anssehen.
Dieses Verhalten wollte ich als erstes mit Docker nachstellen.
Wie ich dass dann danach mit Gunicorn kopple, das ist für mich auch noch eine große Unbekannte.
Mein Zielsystem ist ein privater Server, auf dem das drauf ist, was ich installiere. Also es gibt so gesehen keinen Hoster für das und auch nicht für zukünftige Projekte.
Grüße
Dennis
Im ersten Schritt erwarte ich, dass ich den Inhalt der `Test.html` sehe. Ich habe da zwar Eingabefelder drin, deren Eingabe dann in der Flask-App verarbeitet werden und da kommt dann Gunicorn ins Spiel. Das wäre aber erst mein zweiter Schritt.
Konfiguriert habe ich nichts, weil ich kann hier auf meinem Laptop eine *.html - Datei in `/var/www/html/` legen und wenn ich Apache2 bzw. httpd starte, dann kann ich die im Browser unter Angabe des Dateiennames anssehen.
Dieses Verhalten wollte ich als erstes mit Docker nachstellen.
Wie ich dass dann danach mit Gunicorn kopple, das ist für mich auch noch eine große Unbekannte.
Mein Zielsystem ist ein privater Server, auf dem das drauf ist, was ich installiere. Also es gibt so gesehen keinen Hoster für das und auch nicht für zukünftige Projekte.
Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Ach na klar, das muss ja nicht `/var/www/html/` sein und war es auch nicht. Die Dateien müssen in `/usr/local/apache2/htdocs/` liegen.
Ich weis nicht ob da einen Einfluss hat, aber anstatt die conf-Datei zu ändern, habe ich den Pfad in der `docker-compose.yaml` angepasst:
Jetzt funktioniert es. 
Dann kann ich mich jetzt um die Flask-Anbindung kümmern.
Vielen Dank für die Hilfe!
Grüße
Dennis
Ich weis nicht ob da einen Einfluss hat, aber anstatt die conf-Datei zu ändern, habe ich den Pfad in der `docker-compose.yaml` angepasst:
Code: Alles auswählen
apache:
image: httpd:latest
restart: always
ports:
- 80:80
volumes:
- ./app/templates:/usr/local/apache2/htdocs/
Dann kann ich mich jetzt um die Flask-Anbindung kümmern.
Vielen Dank für die Hilfe!
Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Hallo,
die Flask-Anbindung hat funktioniert. Allerdings macht mein Test-Programm (das wirklich nichts sinnvolles macht) nicht was ich will. Ich dachte mir, ich mache 4 Eingabefelder, die Eingabe soll geprüft werden und wenn die letzten zwei Felder mit Zahlen gefüllt sind, sollen sie verrechnet werden. Ohne dass ich einen Button drücke.
Dafür habe ich jetzt `computed()` missbraucht, bzw. das ausführen mit dem prüfen in einer Funktion vermischt.
Mein Problem liegt, meiner Meinung nach, in der Flask-Anwendung. Ich verwende `flask_classful` und ich weis ja vom "letzten mal" dass ich da pro Anfrage vom Browser nicht immer die gleiche Klasseninstanz zur Verfügung habe und ich denke, dass ich deswegen, ab und zu, folgenden Fehler bekomme:
app.py:
Test.html:
requirements.txt:
docker-compose.yaml:
Dockerfile:
Struktur:
Und das führe ich der Reihe nach so aus:
Sorry, dass das jetzt wieder vom Thema abweicht.
Aber liege ich in meiner Vermutung richtig, dass das Problem in app.py liegt und wenn ja, wie würde man das machen, dass man sich Werte über die Browser-Anfrage hinweg merkt? Wo soll ich die hinschreiben?
Danke und Grüße
Dennis
P.S. Neulich dachte ich noch, dass das mit den Webanwendungen so langsam läuft und schon denke ich das nicht mehr.
die Flask-Anbindung hat funktioniert. Allerdings macht mein Test-Programm (das wirklich nichts sinnvolles macht) nicht was ich will. Ich dachte mir, ich mache 4 Eingabefelder, die Eingabe soll geprüft werden und wenn die letzten zwei Felder mit Zahlen gefüllt sind, sollen sie verrechnet werden. Ohne dass ich einen Button drücke.
Dafür habe ich jetzt `computed()` missbraucht, bzw. das ausführen mit dem prüfen in einer Funktion vermischt.
Mein Problem liegt, meiner Meinung nach, in der Flask-Anwendung. Ich verwende `flask_classful` und ich weis ja vom "letzten mal" dass ich da pro Anfrage vom Browser nicht immer die gleiche Klasseninstanz zur Verfügung habe und ich denke, dass ich deswegen, ab und zu, folgenden Fehler bekomme:
Code: Alles auswählen
docker_test_1 | [2024-11-25 16:33:10,328] ERROR in app: Exception on /transfer_user_data [GET]
docker_test_1 | Traceback (most recent call last):
docker_test_1 | File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 1473, in wsgi_app
docker_test_1 | response = self.full_dispatch_request()
docker_test_1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
docker_test_1 | File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 882, in full_dispatch_request
docker_test_1 | rv = self.handle_user_exception(e)
docker_test_1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
docker_test_1 | File "/usr/local/lib/python3.11/site-packages/flask_cors/extension.py", line 194, in wrapped_function
docker_test_1 | return cors_after_request(app.make_response(f(*args, **kwargs)))
docker_test_1 | ^^^^^^^^^^^^^^^^^^
docker_test_1 | File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 880, in full_dispatch_request
docker_test_1 | rv = self.dispatch_request()
docker_test_1 | ^^^^^^^^^^^^^^^^^^^^^^^
docker_test_1 | File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 865, in dispatch_request
docker_test_1 | return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return]
docker_test_1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
docker_test_1 | File "/usr/local/lib/python3.11/site-packages/flask_classful.py", line 314, in proxy
docker_test_1 | response = make_response(response, code, headers)
docker_test_1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
docker_test_1 | File "/usr/local/lib/python3.11/site-packages/flask/helpers.py", line 173, in make_response
docker_test_1 | return current_app.make_response(args)
docker_test_1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
docker_test_1 | File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 1174, in make_response
docker_test_1 | raise TypeError(
docker_test_1 | TypeError: The view function for 'App:data_transfer' did not return a valid response. The function either returned None or ended without a return statement.
Code: Alles auswählen
#!/usr/bin/env python
from math import pi
from attrs import define, field
from attrs.validators import gt as is_greater
from cattrs import structure
from flask import Flask, jsonify
from flask_classful import FlaskView, request, route
from flask_cors import CORS
from loguru import logger
@define
class UserInput:
start = field(validator=is_greater(0))
end = field(validator=is_greater(0))
memory = field(validator=is_greater(0))
cpu = field(validator=is_greater(0))
@define
class App(FlaskView):
user_input = field(default=None)
results = field(default=None)
@logger.catch(Exception)
@route("/transfer_user_data", methods=["POST", "GET"])
def data_transfer(self):
if request.method == "POST":
self.calculation_process(request.get_json())
return jsonify(request.get_json())
if request.method == "GET":
print(self.results)
return self.results
def calculation_process(self, input_data):
self.user_input = structure(input_data, UserInput)
result = self.user_input.memory - self.user_input.cpu
self.results = {"result": result}
app = Flask(__name__)
app.config.from_object(__name__)
CORS(app, resources={r"/*": {"origins": "*"}})
App.register(app, route_base="/")
Code: Alles auswählen
<html lang="en">
<head>
<meta name="viewport" content="width=device-width,initial-scale=1.0" charset="utf-8">
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style>
.header {
text-align: center;
max-width: 100%;
display: block;
margin: 0 auto;
}
body {
background-color: 111111;
text-align: center;
color: #fff
}
h1 {
color: #fff
}
*{
margin:0;
padding:0;
}
</style>
<title>Python-AG</title>
</head>
<body>
<header class>
<img src="header.png" class="header">
</header>
<div id="app">
<template v-if="one_stage_mode" align="center">
<br/><h1>Auslegungsdaten eintragen</h1><br/>
Start: <input v-model="start"> s<br/>
End: <input v-model="end"> s<br/>
Arbeitsspeicher: <input v-model="memory"> MB<br/>
Prozessor: <input v-model="cpu"> GHz<br/><br/>
<div v-if="is_input_valid">
Ausgabe: {{ results }} <br/><br/>
</div>
<button @click="one_stage_mode=false">Zurück</button>
</template>
<template v-else>
<br/><h1>Bitte einen PC wählen!</h1><br/>
<button @click="one_stage_mode=true"><img src="first_choice.png"></button>
</template>
</div>
<script>
const { createApp, ref, computed, onMounted } = Vue
createApp({
setup() {
const one_stage_mode = ref(false)
const start = ref(0)
const end = ref(0)
const memory = ref(0)
const cpu = ref(0)
const results = ref(0)
async function send_input() {
await fetch('http://localhost:5000/transfer_user_data', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(
{
"start": parseFloat(start.value),
"end": parseFloat(end.value),
"memory": parseFloat(memory.value),
"cpu": parseFloat(cpu.value)
}
)
})
const response = await fetch('http://localhost:5000/transfer_user_data', {method: 'GET'});
result = await response.json();
results.value = result['result'];
}
const is_input_valid = computed(() => {
if (
1 * start.value > 0 &&
1 * end.value > 0 &&
1 * memory.value > 0 &&
1 * cpu.value > 0 &&
1 * start.value < 1 * end.value
) {
send_input();
return true } else { return false }
})
return {
one_stage_mode,start, end,
memory, cpu, results, is_input_valid
}
}
}).mount('#app')
</script>
</body>
</html>
Code: Alles auswählen
attrs==24.2.0
cattrs==24.1.2
Flask==3.0.3
Flask-Cors==5.0.0
loguru
prettyprinter~=0.18.0
gunicorn==23.0.0
flask_classful
Code: Alles auswählen
services:
apache:
image: httpd:latest
restart: always
ports:
- 80:80
volumes:
- ./app/templates:/usr/local/apache2/htdocs/
docker_test:
build:
context: .
dockerfile: Dockerfile
platform: linux/amd64
ports:
- "5000:5000"
volumes:
- type: bind
source: ./app
target: /src/app
command: gunicorn -w 4 -b 0.0.0.0:5000 --reload "src.app:app"
Code: Alles auswählen
FROM python:3.11-slim
ENV INSTALL_PATH=/docker_test
RUN mkdir -p $INSTALL_PATH
WORKDIR $INSTALL_PATH
COPY requirements.txt $INSTALL_PATH/
COPY app/src ${INSTALL_PATH}/src
COPY app/templates ${INSTALL_PATH}/templates
RUN pip install --no-cache-dir -r requirements.txt
RUN rm -rf ~/.cache
RUN rm -rf /tmp/*
CMD ["gunicorn", "-b", "0.0.0.0:5000", "${INSTALL_PATH}.src.app:app"]
Code: Alles auswählen
[dennis@dennis docker_test]$ tree -L 3
.
├── app
│ ├── __pycache__
│ │ ├── app.cpython-310.pyc
│ │ └── __init__.cpython-310.pyc
│ ├── src
│ │ ├── app.py
│ │ └── __init__.py
│ └── templates
│ ├── first_choice.png
│ ├── header.png
│ └── Test.html
├── docker-compose.yaml
├── Dockerfile
├── docker_test
│ └── templates
├── README.md
└── requirements.txt
Code: Alles auswählen
docker-compose build --no-cache
docker-compose up
Aber liege ich in meiner Vermutung richtig, dass das Problem in app.py liegt und wenn ja, wie würde man das machen, dass man sich Werte über die Browser-Anfrage hinweg merkt? Wo soll ich die hinschreiben?
Danke und Grüße
Dennis
P.S. Neulich dachte ich noch, dass das mit den Webanwendungen so langsam läuft und schon denke ich das nicht mehr.
"When I got the music, I got a place to go" [Rancid, 1993]
Guten Morgen,
ich habe jetzt die Datenbank ink. anlegen von Tabellen in meinem Docker-Image und alle Container laufen.
Ich würde jetzt gerne ein Python-Skript, dass die Datenbank mit Werten füllt, starten bevor `gunicorn` gestartet wird, aber ich bekomme immer die Fehlermeldung, dass keine Verbindung zur Datenbank aufgebaut werden kann. Ich verstehe nicht, wie mein "docker_test" Container jetzt mit meinem "mariadb"-Container kommunizieren kann.
docker-compose.yaml:
Dockerfile:
Mit dem Befehl `fill_database.py` startet der Container nicht mehr, da er keine Verbindung herstellen kann, aber ohne laufen zumindest alle 3.
Die `fill_database.py` sieht so aus:
Danke und Grüße
Dennis
ich habe jetzt die Datenbank ink. anlegen von Tabellen in meinem Docker-Image und alle Container laufen.
Ich würde jetzt gerne ein Python-Skript, dass die Datenbank mit Werten füllt, starten bevor `gunicorn` gestartet wird, aber ich bekomme immer die Fehlermeldung, dass keine Verbindung zur Datenbank aufgebaut werden kann. Ich verstehe nicht, wie mein "docker_test" Container jetzt mit meinem "mariadb"-Container kommunizieren kann.
docker-compose.yaml:
Code: Alles auswählen
services:
mariadb:
image: mariadb:11.2.6-jammy
platform: linux/amd64
restart: always
ports:
- "3306:3306"
volumes:
- ./test.sql:/docker-entrypoint-initdb.d/1.sql
environment:
- MYSQL_ROOT_PASSWORD=Blabliblub
- MYSQL_PASSWORD=Bla2024!
- MYSQL_USER=dennis
- MYSQL_DATABASE=test
apache:
image: httpd:latest
restart: always
ports:
- "80:80"
volumes:
- ./app/templates:/usr/local/apache2/htdocs/
docker_test:
build:
context: .
dockerfile: Dockerfile
platform: linux/amd64
ports:
- "5000:5000"
volumes:
- type: bind
source: ./app
target: /src/app
command: >
sh -c "python3 technical_data/fill_database.py &&
gunicorn -w 4 -b 0.0.0.0:5000 --reload 'src.app:app'"
Code: Alles auswählen
FROM python:3.11-slim
ENV INSTALL_PATH=/Test
RUN mkdir -p $INSTALL_PATH
WORKDIR $INSTALL_PATH
COPY ./requirements.txt $INSTALL_PATH/
COPY ./app/src ${INSTALL_PATH}/src
COPY ./app/templates ${INSTALL_PATH}/templates
COPY ./technical_data ${INSTALL_PATH}/technical_data
RUN pip install --no-cache-dir -r requirements.txt
RUN rm -rf ~/.cache
RUN rm -rf /tmp/*
CMD ["gunicorn", "-b", "0.0.0.0:5000", "${INSTALL_PATH}.src.app:app"]
Die `fill_database.py` sieht so aus:
Code: Alles auswählen
#!/usr/bin/env python
from sqlalchemy import create_engine
from sqlalchemy.orm import Session, DeclarativeBase, Mapped, mapped_column
from json import loads
from pathlib import Path
DATABASE_URL = "mysql+pymysql://dennis:Bla2024!@0.0.0.0:3306/test"
MACHINE_DATA_FILE = Path(__file__).parent / "machines.json"
class Base(DeclarativeBase):
pass
class Cylinder(Base):
__tablename__ = "Stage"
id: Mapped[int] = mapped_column(primary_key=True)
motor_id: Mapped[int] = mapped_column()
piston_diameter: Mapped[float] = mapped_column()
class Motor(Base):
__tablename__ = "Compressor"
id: Mapped[int] = mapped_column(primary_key=True)
stroke: Mapped[float] = mapped_column()
def main():
machines = loads(MACHINE_DATA_FILE.read_text(encoding="UTF-8"))
engine = create_engine(DATABASE_URL, echo=True)
Base.metadata.create_all(engine)
with Session(engine) as session:
for machine in machines["machines"]:
motor = Motor(
stroke=machine["stroke"]
)
session.add(motor)
session.flush()
for cylinder in machine["cylinders"]:
cylinder = Cylinder(
motor_id = motor.id,
piston_diameter=cylinder["piston_diameter"]
)
session.add(cylinder)
session.commit()
if __name__ == '__main__':
main()
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
- __blackjack__
- User
- Beiträge: 13998
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
"0.0.0.0:3306" in der Datenbankurl sieht falsch aus.
“The best book on programming for the layman is »Alice in Wonderland«; but that's because it's the best book on anything for the layman.” — Alan J. Perlis
Danke für deine Antwort.
Das habe ich deswegen drin:
Ich verstehe das so, das mir hier die Adresse angezeigt wird.
Wenn ich mich in `docker_test_mariadb_1` einlogge, und mich dann mit den angegebenen Logging-Daten in MariaDB einlogge, dann sehe ich meine Datenbank mit den zwei Tabellen.
Ich habe mich auch in `docker_test_docker_test_1` eingeloggt, aber klar da habe ich kein `mariadb` muss ich das da drin auch noch irgendwie installieren? Oder wie könnte ich wenn ich in dem Container bin, rausfinden ob der MariaDB-Container erreichbar ist?
Grüße
Dennis
Das habe ich deswegen drin:
Code: Alles auswählen
[dennis@dennis Test]$ docker container ls --all
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5cf151921a24 docker_test_docker_test "gunicorn -w 4 -b 0.…" 18 seconds ago Up 17 seconds 0.0.0.0:5000->5000/tcp, :::5000->5000/tcp docker_test_docker_test_1
613dc0335354 mariadb:11.2.6-jammy "docker-entrypoint.s…" 18 seconds ago Up 17 seconds 0.0.0.0:3306->3306/tcp, :::3306->3306/tcp docker_test_mariadb_1
077760b8a6cc httpd:latest "httpd-foreground" 18 seconds ago Up 17 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp docker_test_apache_1
Wenn ich mich in `docker_test_mariadb_1` einlogge, und mich dann mit den angegebenen Logging-Daten in MariaDB einlogge, dann sehe ich meine Datenbank mit den zwei Tabellen.
Ich habe mich auch in `docker_test_docker_test_1` eingeloggt, aber klar da habe ich kein `mariadb` muss ich das da drin auch noch irgendwie installieren? Oder wie könnte ich wenn ich in dem Container bin, rausfinden ob der MariaDB-Container erreichbar ist?
Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
- __blackjack__
- User
- Beiträge: 13998
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
Die Datenbank lauscht *im* Container auf allen Schnittstellen von diesem Container (0.0.0.0) auf Port 3306, und dieser Port ist nach ”aussen” auf den Port 3306 des Rechners abgebildet auf dem der Container läuft. *Damit* muss man sich mit der Datenbank verbinden.
“The best book on programming for the layman is »Alice in Wonderland«; but that's because it's the best book on anything for the layman.” — Alan J. Perlis