Nazywam się Sergiej, jestem z firmy ITSumma i chcę opowiedzieć, jak podchodzimy do rezerwacji w Kubernetes. Ostatnio wiele czasu poświęcam pracy doradczej nad wdrażaniem różnych rozwiązań devops dla różnych zespołów, a w szczególności intensywnie pracuję nad projektami z wykorzystaniem K8s. Na konferencji Uptime day 4, która była poświęcona rezerwacji w złożonych architekturach, wygłosiłem referat na temat rezerwacji 'kubeletu', i oto jego swobodna interpretacja. Tylko wcześniej ostrzegam, że to nie jest bezpośredni przewodnik do działania, a raczej ogólnym podsumowaniem rozważań na ten temat.

Zasadniczo monitorowanie i rezerwacja to dwa podstawowe narzędzia zwiększające odporność każdego projektu. Ale przecież w Kubernetes wszystko się samo bilansuje, powiecie, wszystko się samo skalując, a jeśli coś się wydarzy — uruchomi się samo… To znaczy, przy pierwszym powierzchownym badaniu tematu, odpowiedź internetu na pytanie, jak kto podchodzi do rezerwacji K8s, brzmiała: 'po co?'. Wielu myśli, że Kubernetes to coś magicznego, co eliminuje wszystkie problemy infrastrukturalne i sprawia, że projekt nigdy nie upadnie. Ale… świat nie jest tym, czym się wydaje.
Jak podchodziliśmy do procesu rezerwacji wcześniej? Mieliśmy identyczne platformy do hostowania – były to albo maszyny wirtualne, albo urządzenia fizyczne, serwery, do których stosowaliśmy trzy podstawowe praktyki:
- synchronizację kodu i statykę
- synchronizację konfiguracji
- replikację bazy danych
I voilà: w każdej chwili przełączamy się na zapasową platformę, wszyscy są szczęśliwi, wstajemy i się rozchodzimy.

Co nam oferują, aby zwiększyć ciągłą dostępność naszej aplikacji Kubernetes? Pierwsze, o czym mówi nieoficjalna dokumentacja, to postawić wiele maszyn, stworzyć wielu masterów — ich liczba powinna spełniać warunki osiągnięcia kworum w klastrze, a na każdym z masterów powinien być uruchomiony etcd, API, MC, scheduler… I wydaje się, że wszystko jest w porządku: przy awarii kilku węzłów roboczych lub masterów nasz klaster zostanie zrebalansowany, a aplikacja będzie kontynuować działanie. Znowu wygląda to jak magia! Ale często nasz klaster znajduje się w jednym centrum danych, co może rodzić pewne pytania. Co jeśli przyjedzie koparka i przekopie kabel, uderzy piorun, nastąpi potop? Wszystko się kończy, nasz klaster znika. Jak podejść do rezerwacji, biorąc pod uwagę ten aspekt problemu?
Przede wszystkim, powinieneś mieć jeszcze jeden klaster w gorącym rezerwie, czyli klaster, na który możesz przełączyć się w każdej chwili. Przy tym, z punktu widzenia Kubernetesa, infrastruktury muszą być całkowicie identyczne. To znaczy, że jeśli istnieją jakieś niestandardowe wtyczki do pracy z systemem plików, dostosowane rozwiązania dla ingress, muszą być w pełni identyczne na twoich dwóch (lub trzech, lub dziesięciu, tutaj już w zależności od dostępnych funduszy i siły adminów) klastrach. Należy wyraźnie określić dwa zestawy aplikacji (deploymentów, statefulsetów, daemonsetów, cronjobów itd.): które z nich mogą działać na rezerwie na stałe, a które lepiej nie uruchamiać aż do bezpośredniego przełączenia.
Czy nasz klaster rezerwowy powinien być całkowicie identyczny z naszym klastrem produkcyjnym? Nie. Jeśli wcześniej w ramach pracy z monolitycznymi projektami, z fizyczną infrastrukturą utrzymywaliśmy prawie całkowicie identyczne środowisko, to w przypadku Kubernetesa, uważam, że nie powinno tak być. Przyjrzyjmy się, dlaczego.
Na przykład, zaczniemy od podstawowych jednostek Kubernetes – deployments – które powinny być identyczne. Aplikacje muszą działać w taki sposób, aby w każdej chwili mogły przejąć obsługę ruchu, co pozwoli naszemu projektowi na dalsze funkcjonowanie. Kiedy mówimy o plikach konfiguracyjnych, musimy zastanowić się, czy powinny być one identyczne, czy nie. To znaczy, jeśli my, inteligentni ludzie, nie używamy żadnych zabronionych substancji i nie przechowujemy bazy w K8s, to w naszych configmaps muszą być ustawienia dostępu do produkcyjnej bazy (proces jej rezerwacji jest zorganizowany oddzielnie). Odpowiednio, aby zapewnić dostęp do rezerwowej wersji bazy danych, musimy mieć oddzielny plik konfiguracyjny (configmap). To samo dotyczy secretów: haseł do bazy, kluczy API; w każdej chwili może działać albo produkcyjny secret, albo rezerwowy. W sumie mamy już dwie jednostki Kubernetes, których rezerwowe wersje nie powinny być identyczne z produkcyjnymi. Kolejna jednostka, na której warto się zatrzymać, to cronjob. Cronjoby w rezerwie w żadnym wypadku nie powinny być identyczne z zestawem cronjobów klastra produkcyjnego! Jeśli uruchamiamy rezerwowy klaster i uruchamiamy go całkowicie ze wszystkimi włączonymi cronjobami, to na przykład ludzie będą otrzymywać od Ciebie dwa e-maile za jednym razem zamiast jednego. Lub jakaś synchronizacja danych z zewnętrznymi źródłami będzie odbywać się dwa razy, więc zaczynamy cierpieć, płakać, krzyczeć i się złościć.

A jak proponują zorganizować rezerwowy klaster ludzie z internetu? Drugą najpopularniejszą odpowiedzią po 'a po co?' jest wykorzystanie Kubernetes Federation.
Co to jest? To, powiedzmy, duży meta-klaster. Jeśli wyobrazimy sobie architekturę Kubernetes — gdzie mamy mastera, kilka węzłów — to z punktu widzenia federacji mamy również mastera i kilka węzłów, tylko że każdy węzeł to oddzielny klaster. Pracujemy z tymi samymi bytami, tymi samymi prymitywami, co w pojedynczym Kubernetesie, tylko obracamy nie naszymi fizycznymi maszynami, ale całymi klastrami. W ramach federacji mamy pełną synchronizację zasobów federacyjnych od rodziców do potomków. Na przykład, jeśli uruchomiliśmy jakąś implementację przez federację — zostanie ona wdrożona w każdym naszym podrzędnym klastrze. Jeśli weźmiemy dowolny configmap, sekret, i wdrożymy go w federacji — rozprzestrzeni się to we wszystkich naszych podrzędnych klastrach; przy tym federacja pozwala na dostosowanie naszych zasobów na dzieciach. To znaczy, że wzięliśmy jakiś configmap, wdrożyliśmy go przez federację, a następnie, jeśli potrzebujemy coś poprawić w konkretnych klastrach, idziemy wprowadzić poprawki w oddzielnym klastrze, a ta zmiana już nigdzie nie będzie synchronizowana.
Federacja Kubernetes — to narzędzie, które istnieje od niedawna i nie obsługuje wielu zasobów, które oferuje sam K8s: w momencie publikacji jednej z pierwszych wersji dokumentacji mówiono o wsparciu tylko dla configmapów, wdrożeń pod replika-setem i ingress. Sekrety nie były obsługiwane, praca z volume również nie była możliwa. Zbyt ograniczony zestaw. Zwłaszcza jeśli lubimy eksperymentować, na przykład przesyłając własne zasoby do Kubernetes za pomocą custom resource definition — w federacji już ich nie wprowadzimy. To znaczy… bardzo podobne do prawdy rozwiązanie, ale zmusza nas do sporadycznego strzelania sobie w stopę. Z drugiej strony, federacja pozwala elastycznie zarządzać naszym replika-setem. Na przykład chcemy uruchomić 10 replik naszej aplikacji, a domyślnie federacja podzieli tę liczbę proporcjonalnie między klasterami. I to wszystko można jeszcze konfigurować! Można wskazać, że na klastrze produkcyjnym powinno być 6 replik naszej aplikacji, a na klastrze zapasowym, dla oszczędności zasobów lub dla własnej zabawy — tylko 4 repliki naszej aplikacji. Co również jest dość wygodne. Ale z federacją musimy stosować nowe rozwiązania, coś dodatkowo wdrażać w locie, zmuszać się do większego myślenia…
Czy można podejść do procesu rezerwacji Kubernetesa w prostszy sposób? Jakie narzędzia są w ogóle dostępne?
Po pierwsze, zawsze mamy jakąś system ci/cd, co oznacza, że nie musimy ręcznie wchodzić i pisać na serwerach create/apply. System generuje pliki yaml dla naszych kontenerów.
Po drugie, mamy kilka klastrów, mamy albo jedno, albo kilka (jeśli jesteśmy mądrzy) rejestrów, które również zarezerwowaliśmy. I jest wspaniała utilita kubectl, która może współpracować z wieloma klastrami jednocześnie.

Z mojego punktu widzenia najprostszym i najpewniejszym rozwiązaniem budowy zapasowego klastra jest prosty równoległy wdrożenie. Mamy jakiś pipeline w systemie ci/cd; najpierw budujemy nasze kontenery, testujemy je i wdrażamy aplikacje za pomocą kubectl na kilka niezależnych klastrów. Możemy równocześnie budować wdrożenia na kilka klastrów. Odpowiednio, dostarczanie konfiguracji również rozwiązuje się na tym etapie. Można wcześniej określić zbiór konfiguracji dla naszego klastra produkcyjnego, zbiór konfiguracji dla zapasowego klastra i na poziomie systemu ci/cd wdrażać środowisko produkcyjne do klastra produkcyjnego, a zapasowe środowisko — do zapasowego klastra. W porównaniu do federacji nie musimy przechodzić po określeniu federacyjnego zasobu do każdego podrzędnego klastra i cokolwiek redefiniować. Zrobiliśmy to z wyprzedzeniem. Jacy jesteśmy zdolni.
Ale… jest… pisałem, że jest «korzeń wszystkich zła», ale tak naprawdę jest ich dwa. Po pierwsze, system plików. Jest jakiś PV, albo używamy zewnętrznego magazynu. Jeśli przechowujemy pliki wewnątrz klastra, to musimy postępować zgodnie ze starymi praktykami, które pozostały z czasów infrastruktur sprzętowych: na przykład synchronizować lsync'em. No, albo jakimkolwiek innym, ulubionym przez was sposobem. Wdrażamy wszystko na inne maszyny i żyjemy.
Po drugie, i to jest w rzeczywistości jeszcze ważniejszy punkt — baza danych. Jeśli jesteśmy mądrymi ludźmi i nie trzymamy bazy danych w kube, to proces tworzenia kopii zapasowej danych według tej samej starej schemy — replikacja master-slave, potem przełączenie, dokończymy replikę i będziemy żyć dobrze. Ale jeśli będziemy trzymać naszą bazę danych wewnątrz klastra, to zasadniczo jest wiele gotowych rozwiązań do zorganizowania tej samej repliki master-slave, wiele rozwiązań do uruchamiania bazy danych wewnątrz kube.
Na temat tworzenia kopii zapasowych baz danych przeczytano miliard referatów, napisano miliard artykułów, nic nowego w zasadzie tu nie ma. Ogólnie rzecz biorąc, podążaj za swoimi marzeniami, żyj tak, jak chcesz, wynajduj sobie również jakieś skomplikowane rozwiązania, ale koniecznie przemyśl, jak wszystko to będziesz rezerwować.
A teraz o tym, jak w zasadzie będzie wyglądał proces przełączania na zapasową platformę w przypadku pożaru. Po pierwsze, jednocześnie wdrażamy aplikacje stateless. Nie wpływają one na logikę biznesową naszych aplikacji, naszego projektu, możemy stale mieć dwie uruchomione instancje aplikacji, które mogą zacząć przyjmować ruch. Bardzo ważne jest, aby w procesie przełączania na zapasową platformę koniecznie sprawdzić — czy należy redefiniować konfiguracje? Na przykład mamy produkcyjny klaster Kubernetes, mamy zapasowy klaster Kubernetes, mamy zewnętrzną bazę danych master, mamy zapasową bazę danych master. Mamy cztery możliwości, jak te aplikacje w produkcji mogą zacząć ze sobą współdziałać. Może się zdarzyć przełączenie bazy, co oznacza, że trzeba przełączyć ruch w klastrze produkcyjnym na nową bazę, lub klaster może nawalić — i przechodzimy na zapas, ale nadal pracujemy z produkcyjną bazą, a trzecią możliwością jest to, że jedno i drugie zawiodło, i przełączamy obie aplikacje, redefiniując naszą konfigurację, aby nowe aplikacje mogły już pracować z nową bazą danych.
Jakie wnioski można z tego wyciągnąć?

Pierwszy wniosek: dobrze jest mieć zapas. Ale to kosztuje. W idealnej sytuacji nie powinniśmy mieć tylko jednego zapasu. Idealnie byłoby mieć kilka zapasów. Po pierwsze, zapas powinien być przynajmniej w innym centrum danych, a po drugie, najlepiej u innego dostawcy. Często miałem takie sytuacje — i w mojej praktyce to się zdarzało. Projektów nie mogę niestety nazywać, ale dokładnie w momencie, gdy wybuchł pożar w centrum danych… Mówię: przełączamy się na zapas! A serwery zapasowe były w tej samej szafie...
Albo wyobraź sobie, że Amazon został zablokowany w Rosji (co się zdarzyło). I wszystko: co z tego, że w innym amazon mamy nasz zapas? On także jest niedostępny. Dlatego powtarzam: trzymaj zapas przynajmniej w innym centrum danych, a najlepiej — u innego dostawcy.
Drugie zalecenie: jeśli w Kuberze aplikacja łączy się z jakimiś zewnętrznymi źródłami (może to być zarówno baza danych, jak i zewnętrzne API), koniecznie określ ją jako serwis z zewnętrznym punktem końcowym, aby w momencie zmiany nie redeployować 15 swoich aplikacji, które łączą się z tą samą bazą. Zdefiniuj bazę jako oddzielny serwis i łącz się z nią, jakby znajdowała się wewnątrz klastra: jeśli twoja baza się zawiesi, zmieniasz IP w jednym miejscu i dalej żyjesz szczęśliwie.
I na koniec: uwielbiam "kube", tak jak eksperymenty z nim. A także lubię dzielić się wynikami tych eksperymentów oraz swoim osobistym doświadczeniem. Dlatego nagrałem serię webinarów na temat K8s, zapraszam do po szczegóły.
Źródło: habr.com
