50 Punkte von xguru 2021-04-12 | 11 Kommentare | Auf WhatsApp teilen
  • Erklärung der Architektur eines Solo-Entwicklers, der ein SaaS ohne Stress und in gemächlichem Tempo betreibt

  • Er hat eine Infrastruktur aufgebaut, mit der er mehrere Projekte gleichzeitig betreiben kann

  • Er erklärt das am Beispiel seines kürzlich entwickelten SaaS namens PanelBear
    → Start auf dem kleinsten VPS mit SQLite + Django
    → Nach 6 Monaten Iteration: Django-Monolith auf EKS + Postgres + ClickHouse (Analytics) + Redis (Caching) + Celery (geplante Jobs)
    → Weitgehend automatisiert: Autoscaling, Ingress, TLS-Zertifikate, Failover, Logging, Monitoring usw.
    → Da dieses Setup für mehrere Projekte genutzt wird, senkt es die Kosten und macht es sehr einfach, neue Experimente zu starten
    → Für die Infrastrukturverwaltung fällt fast keine Zeit an (0–2 Stunden pro Monat)
    → Die meiste Zeit fließt in Feature-Entwicklung, Kundensupport und das Wachstum des Geschäfts

  • Er verwendet Kubernetes auf AWS, aber zwingend nötig ist das nicht. Er ist damit jedoch vertraut und meint daher, es stabil betreiben zu können.
    → Er hat das Tool über mehrere Jahre in einem geduldigen Team gelernt, das Fehler gemeinsam ausgehalten und abgefangen hat
    → „Kubernetes macht Einfaches kompliziert, kann aber auch Komplexes einfach machen“

  • Automatisches DNS, SSL und Load Balancing
    → Sämtlicher Traffic wird vom CloudFlare Proxy an den AWS L4 NLB (Network Load Balancer) weitergeleitet
    → Wenn eine Request eingeht, leitet der LB sie an einen der k8s-Cluster-Nodes weiter
    → Diese Nodes befinden sich in privaten Subnetzen über mehrere AZs (Availability Zones) hinweg
    → Dafür, dass k8s den passenden Service für eine Request findet, sorgt ingress-nginx (ein Nginx-Cluster)
    → Vor dem Weiterleiten des Traffics wendet nginx Rate Limiting und Traffic-Shaping-Regeln an
    → Bei PanelBear wird der App-Container von Django bedient, das über Uvicorn ausgeliefert wird
    → Zwischen Terraform und K8s gibt es einige Konfigurationsdateien, die von den meisten Projekten gemeinsam genutzt werden
    → Ein neues Projekt lässt sich mit etwa 20 Zeilen Ingress-Konfiguration deployen

  • Automatische Rollouts und Rollbacks
    → Bei jedem Push auf den Master wird per GitHub Actions eine CI-Pipeline ausgeführt
    → Es wird die Codebasis geprüft und mit Docker-Compose eine vollständige Umgebung für End-to-End-Tests erstellt
    → Besteht der Check, wird ein neues Docker-Image gebaut und in ECR (AWS' Docker Registry) gepusht
    → Eine flux-Komponente ( https://fluxcd.io/ ) im k8s-Cluster synchronisiert die Images im Cluster automatisch
    → Flux führt dann automatisch ein inkrementelles Rollout durch

  • Horizontal Autoscaling
    → Autoscaling auf Basis von CPU- und Speicherauslastung
    → Wenn pro Node im Cluster zu viele Pods laufen, werden automatisch weitere Server erstellt, um die Cluster-Kapazität zu erhöhen und die Last zu senken; bei Leerlauf wird wieder skaliert
    → Bei PanelBear werden die Replikate der API-Pods automatisch von 2 auf bis zu 8 skaliert

  • Caching statischer Assets mit CDN
    → CloudFlare ist im DNS eingetragen, verarbeitet alle Requests und übernimmt auch DDoS-Schutz
    → Für das Ausliefern statischer Dateien wird Whitenoise ( https://github.com/evansd/whitenoise ) verwendet, sodass kein Datei-Upload zu Nginx/Cloudfront/S3 nötig ist
    → Für einige statische Websites wie die Landingpage von PanelBear wird NextJS verwendet

  • Caching von Anwendungsdaten
    → In einigen Bereichen wird das von Python bereitgestellte In-Memory-LRU-Caching verwendet
    → Die meisten Endpoints verwenden Redis innerhalb des Clusters

  • Rate Limiting pro Endpoint
    nginx-ingress setzt ein globales Rate Limiting um, aber manchmal braucht man spezifische Limits pro Endpoint oder Methode
    → Mit der Bibliothek Django Ratelimit lassen sich Limits pro Django-View deklarieren
    → Als Backend dient Redis, um Clients pro Endpoint nachzuverfolgen (Hash auf Basis eines Client-Keys statt der IP)

  • App-Administration
    → Das Admin Panel von Django unterstützt standardmäßig Funktionen zum Anzeigen und Bearbeiten von Daten
    → Hinzugefügt wurden Funktionen wie das Sperren verdächtiger Kontozugriffe, das Versenden von Benachrichtigungs-E-Mails und die Bearbeitung von Anfragen zur Kontolöschung (zunächst Soft Delete, vollständige Löschung innerhalb von 72 Stunden)

  • Ausführung geplanter Aufgaben
    → In einem SaaS laufen verschiedene geplante Jobs: tägliche Berichte für Kunden, Berechnung von Nutzungsstatistiken alle 15 Minuten, Metrik-E-Mails an Mitarbeitende usw.
    → Im Cluster laufen mehrere Celery-Worker und der Scheduler Celery beat. Redis dient als Task Queue
    → Um benachrichtigt zu werden, wenn geplante Jobs nicht korrekt laufen, wird HealthChecks.io für SMS/Slack/E-Mail-Benachrichtigungen verwendet

  • App-Konfiguration
    → Alle Konfigurationen erfolgen über Umgebungsvariablen. Altmodisch, aber portabel und gut unterstützt

  • Umgang mit Secrets
    → Verwendet wird kubeseal. Dabei werden Secrets mit asymmetrischer Verschlüsselung verschlüsselt. Entschlüsseln können nur Cluster, die Zugriff auf den Entschlüsselungsschlüssel haben
    → Zum Schutz der Secrets im Cluster werden Verschlüsselungsschlüssel von AWS KMS verwendet

  • Relationale Daten: Postgres
    → Für Experimente läuft im Cluster ein schlichtes Postgres-Container-Setup, das per K8s CronJob täglich nach S3 gesichert wird
    → Wenn ein Projekt wächst, wird die Datenbank aus dem Cluster nach RDS verschoben, sodass AWS sich um verschlüsselte Backups und Sicherheitsupdates kümmert
    → Zur Erhöhung der Sicherheit ist die AWS-Datenbank nur aus dem Private Network erreichbar

  • Spaltenorientierte Daten: ClickHouse
    → Für die effiziente Speicherung und Echtzeitabfrage der Analytics-Daten von PanelBear wird ClickHouse verwendet
    → Es ist eine hervorragende spaltenorientierte Datenbank, extrem schnell und mit hoher Kompressionsrate bei gutem Schema-Design (weniger Storage = mehr Gewinn)
    → Die ClickHouse-Instanz wird selbst im K8s-Cluster gehostet
    → Es wurde ein CronJob eingerichtet, der spaltenorientierte Daten regelmäßig nach S3 sichert
    → Für den Notfall gibt es einige Skripte, um Daten manuell nach S3 zu sichern und daraus wiederherzustellen

  • DNS-basierte Service Discovery
    → K8s verwaltet DNS-Records innerhalb des Clusters automatisch und routet den Traffic an den passenden Service
    → Auch während des Autoscalings werden DNS-Records automatisch synchronisiert, damit Verbindungen nur zu gesunden Pods aufgebaut werden

  • Versionskontrollierte Infrastruktur
    → Docker, Terraform und K8s-Manifeste werden in einem einzigen Repository (Infra Mono-Repo) verwaltet
    → Dadurch lassen sich Infrastruktur mit einfachen Befehlen erstellen und entfernen und über Versionskontrolle reproduzierbar verwalten

  • Terraform für Cloud-Ressourcen
    → Die meisten Cloud-Ressourcen werden mit Terraform verwaltet
    → Dadurch lassen sich Infrastrukturressourcen und Konfigurationen dokumentieren und nachvollziehen

  • K8s-Manifeste für App-Deployments
    → Die K8s-Manifeste stehen als YAML-Dateien im Infra-Monorepo
    → Es ist in zwei Ordner aufgeteilt: cluster und apps
    cluster enthält Konfigurationen für clusterweite Services wie nginx-ingress, verschlüsselte Secrets und Prometheus-Scraper
    → In apps werden Informationen pro Projekt in jeweils einem Namespace gespeichert

  • Abos und Bezahlung
    → Alle Zahlungen werden über Stripe Checkout abgewickelt
    → So muss man sich nicht mit den Zahlungsdaten selbst befassen und kann sich auf das Produkt konzentrieren
    → Es reicht, eine Kundensession zu erstellen, zur Stripe-Seite weiterzuleiten und das Ergebnis per Webhook zu empfangen

  • Logging
    → Statt eines Logging-Agenten werden Logs einfach nach stdout geschrieben; k8s sammelt sie automatisch ein und rotiert sie auch
    → Man könnte sie über FluentBit usw. an Elasticsearch/Kibana weiterleiten, aber um alles einfach zu halten, wird das bisher nicht gemacht
    → Für die Log-Inspektion wird das CLI-Tool stern verwendet

  • Monitoring und Alerts
    → Anfangs wurden Prometheus und Grafana selbst gehostet, aber wenn es Cluster-Probleme gab, fiel auch das Alerting-System aus, was unpraktisch war
    → Deshalb wurde auf New Relic umgestellt
    → Alle Services haben eine Prometheus-Integration, die automatisch Metriken sammelt und an Datadog, New Relic, Grafana Cloud usw. senden kann; für die Migration zu New Relic genügte die Nutzung ihres bereitgestellten Prometheus-Docker-Images

  • Fehlerverfolgung
    → Mit Sentry werden Anwendungsfehler erfasst
    → Über den Slack-Kanal #alerts werden alle Warnungen zentralisiert: Downtime, Cron-Job-Fehler, Security Alerts, Performance-Regressionen, Application Exceptions usw.

  • Profiling und weitere nützliche Dinge
    → Wenn tiefergehende Analyse nötig ist, werden Tools wie cProfile oder snakeviz verwendet
    → Auf dem lokalen Rechner kommt Django Debug Toolbar zum Einsatz

11 Kommentare

 
wellsbabo 2024-08-13

Danke.

 
admin2 2021-04-13

Gibt es große Funktionsunterschiede zwischen Sentry und New Relic?

Ich dachte, sie würden ähnliche Funktionen erfüllen, habe sie aber noch nicht verwendet.

 
kbumsik 2021-04-13

Oh, wir prüfen in unserem Unternehmen gerade die Einführung von k8s; das ist also auch dann ein ziemlich guter Artikel, wenn man kein Ein-Personen-Startup ist.

 
fortune 2021-04-12

Vielen Dank für den guten Artikel. Ich nehme viel Inspiration daraus mit.

 
khris 2021-04-12

Auch wenn es nicht unbedingt um ein Ein-Personen-Startup gehen muss, ist es ein guter Artikel.

 
yshrust 2021-04-12

Ein kleiner Tippfehler,,

  • Fehlerverfolgung

→ Mit Sentry werden Anwendungsfehler erfasst

=> Ich denke, es sollte „erfassen“ heißen

 
xguru 2021-04-12

Danke, ich habe es korrigiert~!

 
xguru 2021-04-12

Ich hoffe, dass auch in Korea mehr Solo-Entwickler oder kleine Teams entstehen, die mit ihren eigenen Services Geld verdienen.

Ich wünsche mir, dass GeekNews zu einem Ort wächst, an dem solche Services sich bekannt machen und gesundes Feedback erhalten können.

 
wellsbabo 2024-08-13

Danke.

 
reedids 2021-04-12

Da stimme ich zu. Danke :)

 
e1q88 2021-04-12

👍