Powrót do strony głównej

Blue-Green deployment na Docker Compose bez przestoju

Artykuł opisuje realizację blue-green deploymentu na Docker Compose bez użycia Kubernetes. Omówiono aspekty zarządzania usługami webowymi i workerami w tle, graceful shutdown, kompatybilność migracji bazy danych i automatyzację przez skrypty deploymentowe.

Blue-Green deployment bez Kubernetes: praktyka na Docker Compose
Advertisement 728x90

Blue-Green deployment bez Kubernetes: realizacja na Docker Compose z zerowym downtime'em

Realizacja blue-green deploymentu jest możliwa nawet bez orkiestratorów pokroju Kubernetes — wystarczy Docker Compose, nginx i dopracowana logika graceful shutdown. Takie podejście jest szczególnie aktualne dla zespołów pracujących na dedykowanych serwerach lub maszynach wirtualnych, gdzie użycie pełnego klastra jest nieopłacalne z powodów ekonomicznych lub architektonicznych. W artykule omawiana jest techniczna implementacja ciągłego deploymentu dla web services i background workerów z uwzględnieniem specyfiki przetwarzania wiadomości i zarządzania stanem.

Architektura blue-green na Docker Compose

W bazowej konfiguracji każde aplikacja jest dublowana: istnieją dwa niezależne instancje — blue i green. Oba uruchamiane są jako oddzielne serwisy w docker-compose.prod.yml, ale tylko jeden z nich obsługuje ruch w danym momencie. Do routingu używany jest reverse proxy (w przykładzie — nginx), który przełącza upstream-adres podczas deploymentu. Kluczowa zasada: nowa instancja jest w pełni uruchamiana i przechodzi health-check, zanim stary instancja zostanie zatrzymana.

Konfiguracja wymaga jawnego wskazania portów dla każdej instancji:

Google AdInline article slot
web-blue:
  ports:
    - "127.0.0.1:8002:8000"

web-green:
  ports:
    - "127.0.0.1:8003:8000"

To pozwala nginxowi na odwoływanie się do odpowiedniej instancji poprzez localhost i konkretny port. Taki schemat eliminuje kolizje i upraszcza zarządzanie cyklem życia.

Zarządzanie background workerami

Procesy w tle (workery), konsumujące wiadomości z kolejki (np. RabbitMQ), wymagają szczególnego podejścia. Proste zatrzymanie kontenera doprowadzi do utraty wiadomości lub ich ponownego przetwarzania w przypadku nieidempotentnych operacji. Rozwiązanie — wdrożenie mechanizmu graceful shutdown na poziomie aplikacji.

Realizowane jest to poprzez klasę singleton z flagą is_accepting. Przed przetworzeniem każdej zadania worker sprawdza tę flagę:

Google AdInline article slot
  • Jeśli is_accepting == True — zadanie jest wykonywane.
  • Jeśli is_accepting == False — wiadomość jest zwracana do kolejki z requeue=True.

Po otrzymaniu sygnału SIGTERM flaga jest przełączana na False, po czym aplikacja czeka na zakończenie wszystkich bieżących zadań (z zapasem 5–10 sekund) i dopiero wtedy kończy pracę. To gwarantuje, że żadna wiadomość nie zostanie utracona i nie dojdzie do niespójności danych.

Ważne niuanse:

  • W przypadku wieloprocesowych workerów (np. ProcessPoolExecutor) stan is_accepting musi być dostępny we wszystkich procesach potomnych. W przeciwnym razie część konsumentów będzie kontynuować przetwarzanie nowych zadań.
  • Nieidempotentne operacje wymagają dodatkowej ochrony: ponowne przetwarzanie może naruszyć integralność danych.
  • Użycie rozproszonej flagi (np. w Redis) komplikuje architekturę, ale może być uzasadnione w systemach o dużym obciążeniu.

Skrypt deploymentu: logika krok po kroku

Skrypt deploymentu automatyzuje przełączanie między instancjami. Logika jest taka sama dla web services i workerów, ale z różnymi punktami wejścia.

Google AdInline article slot

Główne etapy:

  • Określenie aktywnej instancji (poprzez analizę konfiguracji nginx lub listy uruchomionych kontenerów).
  • Uruchomienie nieaktywnej instancji (docker compose up -d).
  • Sprawdzenie gotowości nowej instancji poprzez health-check (do 30 prób co 2 sekundy).
  • Aktualizacja konfiguracji nginx i wykonanie nginx -s reload.
  • Oczekiwanie na zakończenie wszystkich aktywnych żądań HTTP (zazwyczaj 5 sekund).
  • Zatrzymanie starej instancji (docker compose stop).

Przykład dla web service:

echo "server 127.0.0.1:${INACTIVE_PORT};" > "$UPSTREAM_CONF"
nginx -t && nginx -s reload
sleep 5
$COMPOSE stop web-${ACTIVE}

Taki porządek działań gwarantuje, że ruch nigdy nie jest kierowany na niegotowy serwis, a stara instancja kończy pracę dopiero po pełnym przełączeniu.

Kompatybilność schematu bazy danych i migracje

Jedna z głównych trudności blue-green deploymentu — jednoczesna praca dwóch wersji aplikacji z jedną bazą danych. Aby uniknąć błędów, wszystkie migracje muszą być backward compatible.

Zalecane podejście — dwuetapowa aktualizacja:

  • Pierwszy deployment: usuwane jest użycie przestarzałego pola/tabeli w kodzie, ale struktura pozostaje w bazie danych. Nowa i stara wersja działają poprawnie.
  • Drugi deployment (po pełnym przełączeniu ruchu): wykonywana jest migracja usuwająca nieużywane elementy schematu.

To zwiększa liczbę operacji, ale eliminuje ryzyko awarii starej instancji z powodu niekompatybilności schematu.

Co ważne

  • Blue-green deployment na Docker Compose jest możliwy bez Kubernetes, ale wymaga ręcznego zarządzania instancjami i proxy.
  • Graceful shutdown dla workerów jest kluczowy — bez niego możliwa jest utrata lub duplikacja wiadomości.
  • Wszystkie migracje bazy danych muszą być backward compatible; usuwanie pól wymaga dwuetapowego podejścia.
  • Health-check nowej instancji jest obowiązkowy przed przełączeniem ruchu.
  • Konfiguracja nginx musi być aktualizowana atomowo i weryfikowana przez nginx -t.

— Editorial Team

Advertisement 728x90

Czytaj dalej