24 Container auf einem einzelnen VPS: Hochdichte technische Architektur für Middle/Senior DevOps
Ein Server, 6 GB RAM, 24 Container — und alles läuft stabil seit über einem Jahr. Dies ist kein Experiment, sondern Produktionsinfrastruktur, die sieben aktive Projekte unterstützt: einen EdTech-Bot, einen Anti-Spam-Dienst, ein AI-Backend, eine Meta-Suchmaschine, lokale LLM-Inferenz und zwei Utility-Dienste. Keines davon generiert Einnahmen, doch alle sind kritisch für den Betriebsablauf. Das Ziel war nicht Kosteneinsparung um ihrer selbst willen, sondern ein bewusster Kompromiss: Manuelles Management statt Managed Services, vorhersehbarer Lastanstieg statt Cloud-Elastizität und tiefe Integration statt Abstraktion.
Architektonische Prinzipien: Isolation, Beobachtbarkeit, Resilienz
Der Kernansatz basiert auf strenger Netzwerkisolation. Jedes Projekt befindet sich in seinem eigenen Docker-Netzwerk (bridge), was Cross-Abhängigkeiten eliminiert und die Angriffsfläche minimiert. Ein gemeinsames shared-Netzwerk wird nur für Systemdienste genutzt (z. B. Monitoring oder gemeinsames Redis). Interne Ports (9200, 8088, 9222) sind ausschließlich über 127.0.0.1 erreichbar; externer Zugriff wird auf iptables- und nginx-Konfigurationsebene blockiert.
Resilienz wird durch Healthcheck-Abhängigkeiten in docker-compose.yml gewährleistet. Beispielsweise starten PHP-Dienste im EdTech-Projekt erst, wenn ein SSH-Tunnel zur Remote-Datenbank einen nc -z 127.0.0.1 3306-Check besteht. Dies garantiert, dass die Anwendung nicht versucht, sich mit einer nicht verfügbaren Datenbank zu verbinden und die Warteschlange mit Fehlern zu überfluten.
Beobachtbarkeit wurde aufgrund von RAM-Einschränkungen ohne Prometheus/Grafana implementiert. Stattdessen führt ein Bash-Skript alle 15 Minuten folgende Aktionen aus:
- Prüft HTTP-Statuscodes öffentlicher Endpunkte (
/health,/webhook/advert) - Identifiziert ungesunde Container via
docker ps --filter "health=unhealthy" - Überwacht Festplattenspeicher und Swap-Nutzung
- Sendet Warnungen an einen Telegram-Chat via curl an die Bot-API
Dies bietet minimale, aber ausreichende Sichtbarkeit für einen einzelnen Administrator.
Kritische Komponenten: Elasticsearch, Chrome Headless und SSH-Tunnel
Drei Komponenten definieren die Leistungsgrenzen des VPS:
- Elasticsearch 8.12.2 verbraucht 1,47 GB RAM. Ohne explizites
memory: 2g-Limit indeploy.resources.limitsbeginnt die JVM, benachbarte Prozesse auszulagern, was Instabilität verursacht. Für einen Index von ca. 50.000 Dokumenten und Logging bietet dies das optimale Gleichgewicht zwischen Funktionalität und Last. Eine Alternative wie Meilisearch würde erfordern, bestehende Indizes zu migrieren und Client-Code zu ändern, was als Aufgabe mit hohem Aufwand und geringem ROI in diesem Stadium eingestuft wird.
- Chrome Headless (zenika/alpine-chrome) belegt 636 MB RAM, wird jedoch nur einmal pro Stunde für JS-Rendering-Scraping genutzt. Das Ausführen
on-demandviadocker run --rmwürde Speicher sparen, aber pro Aufruf eine Verzögerung von 5–10 Sekunden hinzufügen. Für Dienste mit strengen SLAs ist dies inakzeptabel; hier ist es ein akzeptabler Kompromiss.
- SSH-Tunnel werden in separaten Alpine-Containern mit
autosshimplementiert. Sie bieten sicheren Zugriff auf MySQL auf einem Shared-Host, ohne externe Ports zu öffnen. Die Konfiguration umfasstServerAliveInterval=30,AUTOSSH_GATETIME=0und einen Healthcheck vianc. Diese Lösung funktioniert mit allen Hosts, die SSH unterstützen, und ersetzt vollständig bezahlte Managed-Datenbanken.
Nginx und PHP-Optimierung: Latenz um Millisekunden reduzieren
Nginx ist ausschließlich für Webhooks und APIs konfiguriert — keine statischen Dateien außer /health. Die Schlüsseloptimierung besteht darin, die Verarbeitung von Telegram-Webhooks auf eine separate PHP-Datei (webhook-advert.php) auszulagern, die das Laravel-Bootstrap umgeht. Dies reduziert die Antwortzeit von 80–120 ms auf 15–25 ms, was während massiver Webhook-Aufrufe von der Telegram-API (maximale Timeout — 60 Sekunden) kritisch ist.
Die Konfiguration umfasst zudem:
- Erzwingende HTTP → HTTPS-Umleitung
- Verweigerung der Verarbeitung aller Pfade außer
/health,/webhook/und/api/telegram/ - Erhöhung von
fastcgi_read_timeoutauf 120 Sekunden für langlaufende Aufgaben - Verwendung von
realpath_rootfür korrekteindex.php-Behandlung
SSL und Sicherheit: Drei Strategien auf einem Host
SSL-Terminierung ist hybrid organisiert:
- getssl + Let's Encrypt — für Domänen, die Ende-zu-Ende-Verschlüsselung ohne Cloudflare benötigen
- Cloudflare Proxy — für die meisten Domänen: SSL terminiert am Edge, Traffic zum VPS verläuft über HTTP, ist aber durch IP-Filterung geschützt (nur Cloudflare ASN)
- Manuelles Update — eine temporäre Maßnahme für das AI-Backend, derzeit unter Automatisierung via Caddy
Das Cloudflare-Setup beinhaltet obligatorisches Always Use HTTPS, WAF und Rate Limiting (100 Anfragen/Min pro IP), um das Fehlen eines WAF auf der VPS-Seite auszugleichen.
Fazit
- Strenge Netzwerkisolation via separater Docker-Netzwerke verhindert Kaskadenausfälle und vereinfacht Sicherheitsaudits.
- Healthcheck-Abhängigkeiten in docker-compose stellen sicher, dass Dienste erst starten, nachdem ihre Abhängigkeiten bereit sind (z. B. SSH-Tunnel zur DB).
- Elasticsearch erfordert ein hartes Speichergrenzwert (2 GB); andernfalls lagert die JVM benachbarte Container aus und löst den OOM-Killer aus.
- Das Halten von Chrome Headless im Always-On-Modus ist bei niedriger Nutzungsfrequenz gerechtfertigt, wenn Startlatenz für die Geschäftslogik kritisch ist.
- Bash-Monitoring mit Telegram-Warnungen ist ein funktionierender Ersatz für Prometheus/Grafana unter RAM-Einschränkungen, sofern SLOs klar definiert sind.
— Editorial Team
Noch keine Kommentare.