From aefcd90119e3e8071a963c9d52c1e9defb57e2f5 Mon Sep 17 00:00:00 2001 From: Stefan Koelle Date: Fri, 7 Aug 2026 13:13:41 +0200 Subject: [PATCH] heathcheck / README.md / webhook_port --- .env.example | 3 +-- Dockerfile | 3 +++ README.md | 12 +++++++----- app/config.py | 4 +--- app/main.py | 6 ++---- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.env.example b/.env.example index 7b324d6..dc1f13f 100644 --- a/.env.example +++ b/.env.example @@ -13,8 +13,7 @@ CLOUDFLARE_RECORDS=sub1.example.org,sub2.example.org,sub3.example.org # FreeDNS (Fallback/Parallelbetrieb) FREEDNS_UPDATE_URL=https://freedns.afraid.org/dynamic/update.php?TOKEN -# Webhook (kein Auth, rein intern) -WEBHOOK_PORT=8090 +# Webhook (kein Auth, rein intern, Port fest 8090) # Fallback Polling POLL_INTERVAL_MINUTES=15 diff --git a/Dockerfile b/Dockerfile index bac6d86..a375a0f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,4 +17,7 @@ ENV PYTHONUNBUFFERED=1 EXPOSE 8090 +HEALTHCHECK --interval=5m --timeout=3s --retries=3 \ + CMD python3 -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8090/healthz', timeout=2)" || exit 1 + CMD ["python", "-m", "app.main"] diff --git a/README.md b/README.md index c456053..d468e57 100644 --- a/README.md +++ b/README.md @@ -35,16 +35,18 @@ Alle Einstellungen erfolgen ausschließlich über Umgebungsvariablen (siehe | `CLOUDFLARE_ZONE_ID` | Cloudflare-Zone | | `CLOUDFLARE_RECORDS` | Kommagetrennte A-Record-Namen (DNS-only) | -Optional: `FREEDNS_UPDATE_URL`, `WEBHOOK_PORT`, `POLL_INTERVAL_MINUTES`, +Optional: `FREEDNS_UPDATE_URL`, `POLL_INTERVAL_MINUTES`, `HEALTHCHECK_PING_URL`, `TZ`, `LOG_LEVEL`. Die IP kommt ausschließlich aus der FritzBox, nie aus Webhook-Parametern. ## Betrieb -- **State:** zuletzt bekannte IP liegt als `data/last-known-ip.json` (Volume) – - Änderung wird erst nach erfolgreichem Update gespeichert. -- **Webhook:** `GET /webhook/update` (kein Auth, intern) löst einen Zyklus aus. -- **Healthz:** `GET /healthz` für Container-Healhcheck. +- **State (bewusst ohne Volume, ephemeral):** zuletzt bekannte IP liegt als + `data/last-known-ip.json` im Container. Bei jedem neu erstellten Container ist + der State wieder leer, wodurch beim Start ein Force-Push erzwungen wird. + Die Änderung wird erst nach erfolgreichem Update gespeichert. +- **Webhook:** `GET /webhook/update` (kein Auth, intern, Port fest 8090) löst einen Zyklus aus. +- **Healthz:** `GET /healthz` für den Container-Healthcheck (genutzt vom Docker-`HEALTHCHECK`). - **Healthchecks:** Healthy Ping bei jedem Poll, Failure Ping bei IP-Fehler bzw. Teilfehler. - **Logs/Wartung:** `docker compose logs -f`; Updates via Watchtower (Label im Compose). diff --git a/app/config.py b/app/config.py index e6c0dc5..d8f8f57 100644 --- a/app/config.py +++ b/app/config.py @@ -33,7 +33,6 @@ class Config: self.freedns_update_url = os.environ.get("FREEDNS_UPDATE_URL", "") - self.webhook_port = int(os.environ.get("WEBHOOK_PORT", "8090")) self.poll_interval_minutes = int(os.environ.get("POLL_INTERVAL_MINUTES", "15")) self.healthcheck_ping_url = os.environ.get("HEALTHCHECK_PING_URL", "") @@ -49,10 +48,9 @@ class Config: log.error("CLOUDFLARE_RECORDS ist leer - mindestens eine Subdomain angeben.") sys.exit(1) log.info( - "Konfiguration geladen: %d Cloudflare-Record(s), Poll-Intervall=%d Min, Webhook-Port=%d", + "Konfiguration geladen: %d Cloudflare-Record(s), Poll-Intervall=%d Min", len(self.cloudflare_records), self.poll_interval_minutes, - self.webhook_port, ) diff --git a/app/main.py b/app/main.py index acda1a2..e7b44b1 100644 --- a/app/main.py +++ b/app/main.py @@ -108,11 +108,9 @@ def main(): scheduler.start() log.info("Fallback-Polling gestartet (alle %d Minuten).", config.poll_interval_minutes) - log.info( - "Webhook-Server startet auf Port %d (GET /webhook/update, kein Auth).", config.webhook_port - ) + log.info("Webhook-Server startet auf Port 8090 (GET /webhook/update, kein Auth).") logging.getLogger("werkzeug").setLevel(logging.ERROR) - app.run(host="0.0.0.0", port=config.webhook_port) + app.run(host="0.0.0.0", port=8090) if __name__ == "__main__":