Powrót do strony głównej

Docker na VPS: 24 kontenery w produkcji

Artykuł omawia wdrożenie produkcyjne 24 kontenerów Docker na jednym VPS z 6 GB RAM. Omówiono architektoniczne zasady izolacji, zarządzanie zależnościami poprzez healthcheck, optymalizację zużycia pamięci Elasticsearch i Chrome Headless, bezpieczną pracę z zdalnymi bazami danych poprzez tunele SSH oraz lekkie monitorowanie bez Prometheus.

24 kontenery na jednym serwerze: analiza techniczna
Advertisement 728x90

24 kontenery na jednym VPS: architektura techniczna o wysokiej gęstości dla DevOpsów średniego i wyższego poziomu

Jeden serwer, 6 GB RAM, 24 kontenery — a wszystko działa stabilnie od ponad roku. To nie eksperyment, ale infrastruktura produkcyjna dla siedmiu projektów operacyjnych: bota EdTech, serwisu antyspamowego, backendu AI, metaprzeszukiwarki, lokalnej inferencji LLM oraz dwóch usług pomocniczych. Żaden z nich nie generuje przychodów, ale wszystkie są krytyczne dla codziennej działalności. Celem nie jest oszczędność dla samej oszczędności, lecz świadomy wybór kompromisu: ręczne zarządzanie zamiast usług zarządzanych, przewidywalne obciążenie zamiast elastyczności chmury, głęboka integracja zamiast abstrakcji.

Zasady architektoniczne: izolacja, obserwowalność, odporność

Kluczowe podejście to rygorystyczna izolacja sieciowa. Każdy projekt umieszczony jest w własnej sieci Docker (bridge), co eliminuje zależności między projektami i minimalizuje powierzchnię ataku. Jedyna wspólna sieć shared służy tylko do obsługi usług systemowych (np. monitoringu czy wspólnego Redis). Wewnętrzne porty (9200, 8088, 9222) dostępne są wyłącznie przez 127.0.0.1, a zewnętrzny dostęp blokowany jest na poziomie iptables i konfiguracji nginx.

Odporność zapewniona jest poprzez zależności healthcheck w pliku docker-compose.yml. Na przykład serwisy PHP w projekcie EdTech nie uruchamiają się, dopóki tunel SSH do zdalnej bazy danych nie przejdzie sprawdzenia nc -z 127.0.0.1 3306. To gwarantuje, że aplikacja nie spróbuje połączyć się z niedostępną bazą i nie zatka kolejki błędami.

Google AdInline article slot

Obserwowalność została zbudowana bez Prometheus/Grafana — ze względu na ograniczenia pamięci RAM. Zamiast tego stosowany jest skrypt bash wykonujący się co 15 minut:

  • Sprawdzanie statusów HTTP publicznych punktów końcowych (/health, /webhook/advert)
  • Poszukiwanie niezdrowych kontenerów za pomocą docker ps --filter "health=unhealthy"
  • Monitoring zużycia miejsca na dysku i swapu
  • Powiadomienia wysyłane są do czatu Telegrama za pomocą curl do API Bota

To daje minimalną, ale wystarczającą widoczność dla jednego administratora.

Krytyczne komponenty: Elasticsearch, Chrome Headless i tunel SSH

Trzy komponenty definiują granice wydajności VPS:

Google AdInline article slot
  • Elasticsearch 8.12.2 zużywa 1,47 GB RAM. Bez wyraźnego memory: 2g w deploy.resources.limits JVM zaczyna wypierać sąsiednie procesy do swapu, co prowadzi do niestabilności. Dla indeksu liczącego około 50 tys. dokumentów i logowania to optymalny balans między funkcjonalnością a obciążeniem. Alternatywa — Meilisearch — wymaga migracji istniejących indeksów i zmiany kodu klienta, co w obecnym etapie oceniamy jako zadanie o wysokim nakładzie pracy i niskim zwrocie inwestycji.
  • Chrome Headless (zenika/alpine-chrome) zajmuje 636 MB RAM, choć używany jest tylko raz na godzinę do scrapowania renderingu JS. Uruchamianie on-demand przez docker run --rm pozwoliłoby zaoszczędzić pamięć, ale dodawałoby 5–10 sekund opóźnienia na każde wywołanie. Dla usług z surowymi SLA to nie do przyjęcia; tutaj jednak jest to akceptowalny kompromis.
  • Tunel SSH realizowany jest w osobnych kontenerach alpine z autossh. Zapewniają one bezpieczny dostęp do MySQL na hostingu shared bez otwierania portów na zewnątrz. Konfiguracja zawiera ServerAliveInterval=30, AUTOSSH_GATETIME=0 oraz healthcheck za pomocą nc. Rozwiązanie to działa z każdym hostingiem obsługującym SSH i w pełni zastępuje płatne usługi zarządzanych baz.

Optymalizacja Nginx i PHP: redukcja latencji o milisekundy

Nginx skonfigurowany jest wyłącznie do obsługi webhooków i API — żadnych statycznych plików poza /health. Kluczową optymalizacją jest przeniesienie przetwarzania webhooków Telegrama do oddzielnego pliku PHP (webhook-advert.php), który nie ładuje Laravel bootstrap. Dzięki temu czas odpowiedzi skraca się z 80–120 ms do 15–25 ms, co jest kluczowe przy masowych wywołaniach webhooków z API Telegrama (maksymalny timeout — 60 sekund).

Konfiguracja obejmuje również:

  • Przymusowe przekierowanie HTTP → HTTPS
  • Odrzucenie przetwarzania wszelkich ścieżek poza /health, /webhook/ i /api/telegram/
  • Zwiększenie fastcgi_read_timeout do 120 sekund dla długotrwałych zadań
  • Użycie realpath_root dla poprawnego przetwarzania index.php

SSL i bezpieczeństwo: trzy strategie na jednym hostu

Terminacja SSL organizowana jest hybrydowo:

Google AdInline article slot
  • getssl + Let's Encrypt — dla domen wymagających end-to-end szyfrowania i niekorzystających z Cloudflare
  • Cloudflare Proxy — dla większości domen: SSL kończy się na brzegu, a ruch do VPS idzie po HTTP, ale jest chroniony filtracją IP (tylko ASN Cloudflare)
  • Ręczne aktualizacje — tymczasowe rozwiązanie dla backendu AI, który jest w trakcie automatyzacji za pomocą Caddy

Konfiguracja Cloudflare obejmuje obligatoryjne włączenie Always Use HTTPS, WAF i Rate Limiting (100 requestów/min na IP), co kompensuje brak WAF po stronie VPS.

Co jest ważne

  • Rygorystyczna izolacja sieciowa poprzez oddzielne sieci Docker zapobiega kaskadowym awariom i ułatwia audyt bezpieczeństwa.
  • Zależności healthcheck w docker-compose gwarantują, że serwisy startują tylko po uprzednim przygotowaniu swoich zależności (np. tunelu SSH do bazy danych).
  • Elasticsearch wymaga surowego limitu pamięci (2 GB), inaczej JVM wypiera sąsiednie kontenery do swapu, co prowadzi do działania OOM-killer.
  • Chrome Headless warto trzymać w trybie always-on przy niskiej częstotliwości użytkowania, jeśli opóźnienie uruchomienia jest krytyczne dla logiki biznesowej.
  • Monitorowanie bash z alertami Telegram — funkcjonalna alternatywa dla Prometheus/Grafana przy deficycie pamięci RAM, pod warunkiem jasno określonych SLO.

— Editorial Team

Advertisement 728x90

Czytaj dalej