Hallo
Ich nutze mal einen docker container mit einem webserver, der eine einfache Seite zur Verfügung stellt, welche die Besucher inkl. IP Adresse protokolliert. D.h. wenn ich die WEB Seite aufrufe, wird meine IP (lokal oder auch extern), sowie Datum und zeit zuoberst auf der Liste aller bisherigen Aufrufe gesetzt.
War super-praktisch für Tests, leider habe ich den Namen des Containers vergessen, ich Depp.......
Kennt jemand einen Container, der mir sowas anbietet?
docker container gesucht
-
domipo -
May 4, 2025 at 4:54 PM -
Thread is Resolved
-
-
docker container gesucht? Schau mal ob du hier fündig wirst!
-
- Best Answer
Wenn es nur um Tests geht, dann würde ja auch der Python http.server dafür ausreichend sein.
Dazu ein Verzeichnis erstellen und darein eine simple index.html erstellen, z.B.
HTML
Display More<!DOCTYPE html> <html lang="de"> <head> <title> Test </title> </head> <body> Test </body> </html>Dann im Terminal in das Verzeichnis wechseln und dort python -m http.server aufrufen.
Nun hast Du mit jedem Aufruf des Servers im Browser mit http://IP_des_RPi:8000 eine Ausgabe der IP und Zeit am Terminal.
//Edit
Mit dem Container kann ich aber leider nicht helfen!
-
... habe Container gefinden, es ist https://hub.docker.com/r/yeasy/simple-web
-
Ui, der hat schon 10 Jahre auf dem Buckel und verwendet noch das veraltete Python 2. Den würde ich schon aus Sicherheitsgründen nicht mehr verwenden. Am Ende verwendet der auch nur "den" Python-Webserver.
-
Ist das nicht ein bisschen overkill für ein kleines Python-Programm einen kompletten Docker-Container zu verwenden? Python ist ja standardmässig installiert und das kleine Programm verwendet nur die Standardbibliothek.
Mal davon abgesehen, dass man das dringend mal von Python 2 weg portieren sollte, hat das auch ein paar Probleme. Pickle-Daten sind eigentlich Binärdaten, da können Textdateien Probleme machen. Dateien sollte man auch sauber wieder schliessen. Das gilt auch für Sockets.
struct.pack() braucht man nicht wirklich um die 256 Bytes mit dem Interface-Namen zu erstellen.
Dieser and/or-Hack war auch unter Python 2 nicht mehr nötig, seit es den bedingten Ausdruck in der Syntax gibt. OrderedDict ist ebenfalls nicht mehr nötig — das normale Wörterbuch verhält sich in aktuellen Python 3 Versionen genau so.
Der Zusatz „Class“ hat in Klassennamen nichts zu suchen. Dass das eine Klasse ist, sieht man ja schon an der Schreibweise in PascalCase.
request wäre ein passender Name für einen Wert der eine Anfrage repräsentiert, eher nicht für ein Wörterbuch das mehrere Anfragen enthält.
Noch schlimmer ist der Name host in log_message(), denn der enthält genau das Gegenteil: die Adresse vom Client.
Das Aktualisieren des Eintrags kann man kürzer mit pop() und neu eintragen erledigen.
Es wird pickle benutzt, also muss man den Zeitstempel dafür gar nicht in eine Zeichenkette umwandeln, nur um die dann später wieder in ein datetime-Objekt zu parsen um damit rechnen zu können. Das ist nur zur Darstellung sinnvoll, nicht wenn man mit dem Wert arbeiten und rechnen möchte. Der Vergleich mit seconds der Differenz ist kaputt, weil das ja nur der Sekundenanteil ist. Man möchte hier total_seconds() vergleichen. Sonst greift das beispielsweise auch nach 14 Tagen und 2 Sekunden, weil die 2 Sekunden kleiner als 3 sind und die Anzahl der Tage überhaupt nicht berücksichtigt wird.
In der Schleife in der der Inhalt in HTML gewandelt wird, werden letztlich alle Bestandteile von Schlüssel und Werten verwendet. Das mit den ganzen magischen Indexzugriffen mit 0 und 1 zu lösen ist nicht so wirklich leicht zu lesen, da sollte man gleich im Schleifenkopf alles an sinnvolle Namen binden.
Das ganze in log_message() zu machen ist ein bisschen gruselig und ein Hack. Das HTML als Datei zu schreiben ist auch unnötig, denn das könnte man ja problemlos beim GET erzeugen und gleich ausliefern.
Dieses Zusammenstückeln von Werten und Zeichenketten mit str() und + ist eher BASIC denn Python, und auch in Python 2 gab es schon eine format()-Methode auf Zeichenketten.
Die beiden Varianten bei denen entschieden wird was bei unter 3 Sekunden und was bei älteren Verbindungen in das HTML eingebaut wird, unterscheiden sich auch nur durch Farben im HTML, sonst ist das alles gleich, sollte also nur einmal im Quelltext stehen, mit den Farben als Parameter. Oder vielleicht auch besser mit einem CSS-Klassennamen, denn die Formatierung im HTML mit <font> und <center> passt nicht dazu, dass das als HTML 5 ausgewiesen wird, denn dort gibt es die beiden Elemente gar nicht mehr.
Mal so komplett ungetestet:
Python
Display More#!/usr/bin/env python3 import fcntl import pickle import socket import sys from datetime import datetime as DateTime from http import HTTPStatus from http.server import HTTPServer, SimpleHTTPRequestHandler from string import Template PICKLE_FILENAME = "requests.bin" TEMPLATE = Template( """\ <!DOCTYPE html> <html lang="en"> <head> <title>Real Visit Results</title> <style type="text/css"> h1 { text-align: center; } h1 em { color: blue; font-family: Georgia, Arial, sans-serif; font-size: larger; } .request { font-size: 150%; } .request.hot .count { color: red; } .request.hot .ip { color: blue; } .request.normal .count { color: maroon; } .request.normal .ip { color: navy; } </style> </head> <body> <h1><em>Real</em> Visit Results</h1> $requests </body> </html> """ ) REQUEST_TEMPLATE = Template( """\ <p class="request $class_"> #$timestamp: <span class="count">$count</span> requests from <<span class="ip">$client</span>> to WebServer <<span class="ip">$server</span>> </p> """ ) def get_ip_address(interface_name): with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock: return socket.inet_ntoa( fcntl.ioctl( sock.fileno(), 0x8915, # SIOCGIFADDR interface_name.encode("ascii")[:15].ljust(256, b"\x00"), )[20:24] ) class RequestHandler(SimpleHTTPRequestHandler): def _update_requests_db(self): try: with open(PICKLE_FILENAME, "rb") as file: requests = pickle.load(file) except (IOError, pickle.UnpicklingError): requests = {} now = DateTime.now() address_pair = (self.address_string(), get_ip_address("eth0")) count, _ = requests.pop(address_pair, (1, None)) requests[address_pair] = (count + 1, now) with open(PICKLE_FILENAME, "wb") as file: pickle.dump(requests, file) return requests def _get_index(self, requests): now = DateTime.now() return TEMPLATE.safe_substitute( requests="".join( REQUEST_TEMPLATE.safe_substitute( class_=( "hot" if (now - timestamp).total_seconds() < 3 else "normal" ), timestamp=timestamp, count=count, client=( f"LOCAL: {client}" if client == self.address_string() else client ), server=server, ) for ((client, server), (count, timestamp)) in requests.items() ) ).encode("ascii") def _do_common(self, base_method): requests = self._update_requests_db() if self.path not in ["/", "/index.html"]: base_method(self) return None index_body = self._get_index(requests) self.send_response(HTTPStatus.OK) self.send_header("Content-Type", "text/html") self.send_header("Content-Length", str(len(index_body))) self.end_headers() return index_body def do_HEAD(self): _ = self._do_common(SimpleHTTPRequestHandler.do_HEAD) def do_GET(self): index_body = self._do_common(SimpleHTTPRequestHandler.do_GET) if index_body is not None: self.wfile.write(index_body) def send_response(self, code, message=None): if code is HTTPStatus.OK: try: with open(PICKLE_FILENAME, "rb") as file: requests = pickle.load(file) except (IOError, pickle.UnpicklingError): requests = {} now = DateTime.now() current_client = self.address_string() address_pair = (current_client, get_ip_address("eth0")) count, _ = requests.pop(address_pair, (1, None)) requests[address_pair] = (count + 1, now) with open(PICKLE_FILENAME, "wb") as file: pickle.dump(requests, file) SimpleHTTPRequestHandler.send_response(self, code, message) def main(): address = "0.0.0.0" if len(sys.argv) < 2 else sys.argv[1] port = 80 if len(sys.argv) < 3 else int(sys.argv[2]) httpd = HTTPServer((address, port), RequestHandler) address, port = httpd.socket.getsockname() print("Serving HTTP on", address, "port", port, "...") httpd.serve_forever() if __name__ == "__main__": main()Das mit pickle ist unschön und wird immer langsamer werden, je mehr Daten sich da ansammeln. Das würde man eher mit einer Datenbank lösen — und wenn es nur sqlite ist. Und das funktioniert so auch nur bei einem single-threaded Webserver. Skaliert also nicht wirklich.
-
oh vielen Dank für die wertvollen Hinweise.
Ich fand "als dummer Anwender" den Container spannend und hilfreich, habe ihn aber ungeschaut einfach verwendet. Werde mich mal an die / eine Python Lösung machen um die Zugriffe mit eine Zeitstempel zu versehen. Danke für das Mitdenken. Suche nun einen Weg, einen Python Container mit dem passenden Code und Volume als Stack für Portainer "herzustellen", das hilft mir dann auch das Wirken der Container in Portainer und den Volume besser zu verstehen.
Participate now!
Don’t have an account yet? Register yourself now and be a part of our community!