Co to jest canary deployment? Przewodnik dla devów
Summary
Canary deployment wysyła nową wersję na mały ułamek prawdziwego ruchu, zwykle 1 do 5 procent, zanim dostanie ją każdy. Porównujesz wskaźnik błędów i opóźnienia z wersją stabilną, poszerzasz wycinek, jeśli wyniki są dobre, i ustawiasz wagę canary na zero, jeśli nie. Ogranicza to szkody złego wydania, ale wymaga metryk per wersja i wystarczającego ruchu, żeby wynik coś znaczył.
Co to jest canary deployment? To wydanie, w którym nowa wersja aplikacji trafia najpierw na mały ułamek prawdziwego ruchu, na przykład 1 do 5 procent, a reszta użytkowników dalej korzysta ze starej wersji. Na tym wycinku obserwuje się wskaźnik błędów i czas odpowiedzi. Jeśli wyniki są dobre, wycinek się poszerza. Jeśli są złe, ruch wraca do wersji stabilnej i prawie nikt tego nie zauważa. O to chodzi w całym pomyśle, a reszta artykułu pokazuje, jak to zrobić w praktyce.

Dlaczego to się nazywa canary?
Górnicy zabierali kanarki pod ziemię, bo ptaki reagowały na trujący gaz szybciej niż ludzie. Kanarek, który przestał śpiewać, był sygnałem, żeby wyjść. Z wdrożeniem jest podobnie: mała grupa użytkowników uderza w nowy kod jako pierwsza i działa jak wczesne ostrzeżenie. Glosariusz CNCF opisuje to jako dwa środowiska: jedno obsługuje ruch produkcyjny, drugie działa na zaktualizowanym kodzie, a ruch przechodzi między nimi stopniowo, zaczynając od małego udziału, na przykład 1%.
Problem w praktyce jest taki, że użytkownicy z canary to prawdziwi użytkownicy, a staging tego nie odda. Na produkcji są dziwne przeglądarki, nieaktualne cache i jeden klient z plikiem importu o rozmiarze 40 MB. Canary pozwala spotkać ich wcześniej, zanim błąd spotka wszystkich.
Jak działa canary deployment w praktyce?
Mechanizm ma cztery elementy i nie ma w nim nic egzotycznego.
Dwie wersje działające równolegle. Stara (stabilna) i nowa (canary). Obie muszą jednocześnie obsługiwać żądania.
Rozdzielacz ruchu. Load balancer, ingress, service mesh albo reguła CDN, która kieruje skonfigurowany procent ruchu na canary.
Metryki, którym można ufać. Wskaźnik błędów, opóźnienia i przynajmniej jeden sygnał biznesowy, na przykład udane płatności albo rejestracje.
Reguła przejścia dalej lub przerwania. Spisana przed wdrożeniem, a nie wymyślana o drugiej w nocy przy patrzeniu na wykresy.
Typowy harmonogram to 1%, 5%, 25%, 50%, 100%, z przerwą na każdym kroku. Google Cloud Deploy opisuje ten sam schemat: definiujesz serię procentów, a narzędzie rozkłada ruch. W przykładzie są fazy 25%, 50% i 75% przed stabilną wersją końcową. Dokumentacja canary w Cloud Deploy pokazuje też, że każda faza może uruchomić zadanie weryfikacyjne i krok analizy, który decyduje o przejściu dalej.
To nie jest idealne rozwiązanie, ale da się je wdrożyć. Kubernetes nie jest potrzebny. Wystarczy ważony upstream w nginx, reguła w Cloudflare albo dwie maszyny Fly.io za jednym proxy.
Jak wygląda podział ruchu w konfiguracji?
Najmniejszy sensowny przykład to upstream w nginx z wagami. Na dziesięć żądań dziewięć trafia do wersji stabilnej, jedno do canary.
upstream app {
server 10.0.0.10:3000 weight=9; # stabilna
server 10.0.0.11:3000 weight=1; # canary
}Ten przykład ma wadę, o której warto wiedzieć: podział działa per żądanie, więc jeden użytkownik może w kolejnych kliknięciach przeskakiwać między wersjami. Jeśli nowa wersja zmienia kształt odpowiedzi API albo kolumnę w bazie, taki użytkownik będzie miał problemy. Routing z przypisaniem po cookie albo ID użytkownika to naprawia i jest pierwszą rzeczą wartą dodania, gdy podstawowy podział już działa.

Co obserwować, gdy canary działa?
Canary porównuje się z wersją stabilną w tej samej chwili, a nie z zeszłym tygodniem, bo zeszły tydzień miał inny ruch. Pytanie zawsze brzmi: czy canary działa gorzej niż wersja, która obsługuje pozostałe 95% ruchu właśnie teraz?
Wskaźnik błędów. HTTP 5xx i nieobsłużone wyjątki, podzielone po etykiecie wersji.
Opóźnienia. p95 i p99, a nie średnia. Średnia ukrywa wolny ogon, w którym siedzą regresje.
Nasycenie. CPU, pamięć, połączenia z bazą. Wyciek widać jako nachylenie wykresu, więc trzeba mu dać czas.
Jedna metryka biznesowa. Jeśli na canary spada skuteczność płatności, a błędy są płaskie, to znaczy, że wdrożyłeś ciche błędy.
Najczęstszy błąd w małych zespołach to przechodzenie dalej według zegara: pięć minut na 1%, potem 25%, niezależnie od tego, co pokazują wykresy. Przy małym ruchu pięć minut na 1% może oznaczać sześć żądań, a to nic nie mówi. Czekaj na minimalną liczbę żądań, a nie na minimalną liczbę minut.
Co zrobić, gdy wyniki się rozjeżdżają? Najpierw sprawdź, czy różnica nie wynika z samego ruchu. Canary dostaje inne żądania niż reszta: częściej trafiają na nie testerzy, boty albo użytkownicy jednej przeglądarki. Jeśli błędy rosną tylko w jednym endpoincie, zacznij od logów właśnie tego endpointu. Jeśli rośnie opóźnienie wszystkich żądań, podejrzewaj zmianę w zapytaniach do bazy albo w konfiguracji cache. Dopiero gdy różnica zostaje po takim przeglądzie, wycofuj ruch. Wycofanie kosztuje minutę, a ciągłe debugowanie na produkcji przy kilku procentach ruchu kosztuje cierpliwość całego zespołu.
Narzędzia, które robią to mniej ręcznie, warto rozważyć, gdy masz już prawdziwy ruch.
Canary czy blue-green: co wybrać?
Blue-green utrzymuje dwa pełne środowiska i przełącza cały ruch naraz. Wycofanie to przełączenie z powrotem, szybkie i czyste. Koszt jest taki, że przez chwilę 100% użytkowników jest na nowej wersji, a w trakcie przełączenia płacisz za podwójną infrastrukturę.
Canary wystawia na nową wersję tylko wycinek, więc zasięg problemu jest mniejszy. Cena to złożoność: potrzebujesz ważenia ruchu, metryk per wersja i reguły decyzyjnej. Wycofanie oznacza ustawienie wagi canary na zero. Jest szybkie, ale nie natychmiastowe, jeśli połączenia muszą się najpierw domknąć.
Canary ma sens, gdy błąd na produkcji jest drogi, gdy masz dość ruchu, żeby mały wycinek coś znaczył, i gdy umiesz porównać metryki per wersja. Pomiń go, gdy masz kilkadziesiąt użytkowników dziennie: wycinek będzie statystycznie pusty i będziesz zgadywać. Przy takiej skali uczciwą odpowiedzią są blue-green albo zwykłe wdrożenie ze skryptem szybkiego wycofania.
Dla małego projektu koszt podwójnej infrastruktury bywa decydujący. Dwie identyczne instancje na prostym VPS to często dwukrotnie wyższy rachunek za coś, czego używasz przez kilka minut w miesiącu. Canary na jednej flocie instancji z wagami to tańsze wyjście, choć wymaga wspomnianego wyżej routingu.
Rolling update leży pośrodku. Wymienia instancje jedna po drugiej, co jest łagodne, ale nie jest kontrolowanym eksperymentem: nie wybierasz, kto zobaczy nową wersję, i nie porównujesz kohort.
Gdzie pasują feature flagi?
Canary dzieli ruch między wersje kodu. Feature flaga dzieli zachowanie wewnątrz jednej wersji. Ludzie to mieszają, a pomyłka bywa droga.
Wdrażasz nowy kod wszystkim, ukryty za flagą wyłączoną. Potem włączasz flagę dla 1% użytkowników, następnie dla 10%, a na końcu dla wszystkich. Często nazywa się to canary release na poziomie funkcji. Zaleta: wycofanie to wyłączenie flagi, bez ponownego wdrożenia. Wada: martwa ścieżka kodu jest na produkcji dla wszystkich, a zostawione flagi zamieniają się w dług techniczny.
Dla solo deva flagi są zwykle tańszym pierwszym krokiem. Wartość w konfiguracji i hash ID użytkownika dają jakieś 80% korzyści. Gdy to przestanie wystarczać, zarządzana usługa obsłuży targetowanie, logi audytu i stopniowe wdrażanie procentowe.

Co psuje się w prawdziwych projektach?
Przetestowane. Nieoptymalne. Oto dlaczego i tak to robimy. To są awarie, które naprawdę gryzą.
Migracje bazy danych. Obie wersje uderzają w tę samą bazę naraz. Jeśli canary usunie albo przemianuje kolumnę, stabilna wersja przestaje działać. Rozwiązaniem jest wzorzec expand and contract: najpierw dodajesz nową kolumnę, wypuszczasz kod obsługujący oba kształty, a starą usuwasz dopiero w późniejszym wydaniu. Pominięcie tego i canary położy wersję stabilną.
Sesje w pamięci. Jeśli sesje żyją w pamięci jednej instancji, użytkownik przeskakujący między wersjami zostaje wylogowany. Przenieś stan sesji do wspólnego magazynu, zanim zaczniesz robić canary.
Zatrucie cache. Nowa wersja zapisująca inny format do cache może popsuć dane, które czyta stara. Wersjonuj klucze cache.
Zadania w tle. Canary konsumuje też wiadomości z kolejek. Wadliwy worker może zjadać zadania pełną prędkością, bo procent ruchu nie ogranicza konsumpcji kolejki. Daj canary osobną kolejkę albo wagę konsumenta.
Alerty, których nikt nie podpiął. Canary może zawodzić głośno na dashboardzie, którego nikt nie otwiera. Automatyczne przerwanie po przekroczeniu progu jest lepsze niż człowiek patrzący w ekran, zwłaszcza o drugiej w nocy, kiedy jedyną osobą, która nie śpi, jest ta, która wdrażała.
Jak zacząć na side projekcie i czy robić canary wszystkiego?
Zacznij mniejszy, niż ci się wydaje. Jesteś jednym devem, a nie zespołem platformy.
Oznacz każdą metrykę wersją. Wstaw hash gita do nagłówka i logów. Bez tego nie porównasz canary ze stabilną wersją.
Ustal jedną regułę przerwania. Na przykład: wskaźnik błędów na canary powyżej dwukrotności wersji stabilnej przez pięć minut oznacza wycofanie. Jedna reguła, spisana.
Dodaj ważoną trasę. Wagi w nginx, reguła w Cloudflare albo funkcja podziału ruchu u twojego hostingu. Większość zarządzanych platform ma już routing procentowy bez dodatkowych narzędzi.
Najpierw wypuść coś nudnego. Zmianę tekstu, a nie przeróbkę strony z cennikiem. Ćwicz rampę i wycofanie, gdy nic nie jest na szali.
Zmierz czas wycofania. Uruchom je przynajmniej raz. Jeśli zajmuje dziesięć minut kombinowania, to jest rzecz do naprawy, a nie rampa.
Jeśli twój projekt wciąż jest na etapie szukania, kto za niego zapłaci, canary to prawdopodobnie przedwczesna optymalizacja. Wdrażaj zwyczajnie, miej skrypt wycofania i wróć do canary, gdy awaria kosztowałaby cię prawdziwych klientów. Jeśli już tam jesteś, ważona trasa plus jedna reguła przerwania to weekend pracy, który zwraca się za pierwszym razem, gdy zła wersja padnie przy 1% zamiast przy 100%.
Nie, nie warto robić canary wszystkiego. Zmiany konfiguracji, poprawki tekstów i literówki w stopce nie potrzebują rampy. Canary stosuj tam, gdzie błąd kosztuje pieniądze albo zaufanie: płatności, logowanie, modele danych, wszystko na gorącej ścieżce. Pipeline, który przez godzinę przepuszcza każdy commit, zostanie obejściony za pierwszym razem, gdy ktoś się spieszy, a obejście procesu jest gorsze niż jego brak.
Jaka jest najbardziej ryzykowna zmiana w twoim następnym wydaniu i co trzeba by zrobić, żeby najpierw trafiła do 1% użytkowników?