Nazywam się Wiktor Jagofarow i zajmuję się rozwijaniem platformy Kubernetes w firmie DomKlik jako lider techniczny zespołu ds. rozwoju w zespole Ops (operacje). Chciałbym opowiedzieć o struktury naszych procesów Dev Ops, o szczególnych cechach eksploatacji jednego z największych klastrów k8s w Rosji oraz o praktykach DevOps/SRE, które stosuje nasz zespół.

Zespół Ops
Obecnie w zespole Ops pracuje 15 osób. Trzy z nich odpowiadają za biuro, dwie pracują w innym strefie czasowej i są dostępne, w tym również w nocy. Dzięki temu zawsze ktoś z Ops jest przy monitorze i gotowy do reakcji na incydent o dowolnej złożoności. Nie mamy nocnych dyżurów, co chroni naszą psychikę i pozwala wszystkim się wyspać oraz spędzać czas wolny nie tylko przed komputerem.

Wszyscy mamy różne kompetencje: sieciowcy, DBA, specjaliści od stosu ELK, administratorzy/deweloperzy Kubernetes, specjaliści od monitorowania, wirtualizacji, sprzętu itp. Łączy nas jedno — każdy może w jakimś stopniu zastąpić kogoś innego: na przykład, wprowadzić nowe węzły do klastra k8s, zaktualizować PostgreSQL, napisać pipeline CI/CD + Ansible, zautomatyzować coś w Pythonie/Bash/Go, podłączyć sprzęt w centrum danych. Silne kompetencje w danej dziedzinie nie przeszkadzają w zmianie kierunku działalności i rozpoczęciu nauki w innej dziedzinie. Na przykład, zatrudniłem się w firmie jako specjalista ds. PostgreSQL, a teraz moim głównym obszarem odpowiedzialności są klastry Kubernetes. W zespole jakikolwiek rozwój jest mile widziany, a poczucie wspólnoty jest bardzo rozwinięte.
Zresztą rekrutujemy. Wymagania wobec kandydatów są dość standardowe. Osobiście ważne jest dla mnie, aby osoba wpasowała się w zespół, była niekonfliktowa, ale także potrafiła bronić swojego zdania, chciała się rozwijać i nie bała się robić czegoś nowego, proponowała swoje pomysły. Umiejętności programowania w językach skryptowych są również konieczne, tak samo jak znajomość podstaw Linuxa i angielskiego. Angielski jest potrzebny, aby osoba w przypadku problemu mogła znaleźć rozwiązanie w ciągu 10 sekund, a nie 10 minut. Specjaliści z głęboką wiedzą o Linuxie są teraz bardzo rzadkością: to śmieszne, ale dwóch na trzech kandydatów nie potrafi odpowiedzieć na pytanie „Czym jest Load Average? Z czego się składa?”, a pytanie „Jak zebrać zrzut pamięci z programu w C” uważają za coś z świata superbohaterów… albo dinozaurów. Musimy się z tym pogodzić, ponieważ zwykle ludzie mają dobrze rozwinięte inne kompetencje, a „linuksa” ich nauczymy. Odpowiedź na pytanie „po co to wszystko wiedzieć inżynierowi DevOps w dzisiejszym świecie chmur” musimy zostawić poza zakresem artykułu, ale w kilku słowach: wszystko to jest potrzebne.
Zespół Tools
Zespół Tools odgrywa znaczącą rolę w automatyzacji. Ich głównym zadaniem jest tworzenie wygodnych narzędzi graficznych i CLI dla programistów. Na przykład nasze wewnętrzne narzędzie Confer pozwala na dosłownie kilka kliknięć zainstalować aplikację w Kubernetes, skonfigurować zasoby, klucze z vault itd. Wcześniej używano Jenkins + Helm 2, ale musieliśmy opracować własne narzędzie, aby wyeliminować kopiowanie i wprowadzić jednorodność w cyklu życia oprogramowania.
Zespół Ops nie pisze pipeline'ów za programistów, ale może doradzić w każdej kwestii dotyczącej ich pisania (niektórzy nadal korzystają z Helm 3).
DevOps
Jeśli chodzi o DevOps, widzimy go w ten sposób:
Zespoły Dev piszą kod, wdrażają go przez Confer w dev -> qa/stage -> prod. Odpowiedzialność za to, aby kod nie zawieszał się i nie generował błędów, spoczywa na zespołach Dev i Ops. W ciągu dnia na incydent związany ze swoim aplikacją powinien reagować w pierwszej kolejności dyżurny z zespołu Ops, a w czasie wieczornym i nocnym dyżurny admin (Ops) powinien obudzić dyżurnego programistę, jeśli ma pewność, że problem nie leży w infrastrukturze. Wszystkie metryki i alerty w monitoringu pojawiają się automatycznie lub półautomatycznie.
Obszar odpowiedzialności Ops zaczyna się w momencie wdrożenia aplikacji w produkcji, ale odpowiedzialność Dev się na tym nie kończy — pracujemy nad tym razem i jesteśmy w jednej łódce.
Programiści konsultują administratorów, gdy potrzebna jest pomoc przy pisaniu mikroserwisu dla adminów (na przykład backend w Go + HTML5), a administratorzy doradzają programistom w sprawach związanych z infrastrukturą lub pytaniach dotyczących k8s.
Przy okazji, w ogóle nie mamy monolitu, tylko mikroserwisy. Ich liczba oscyluje obecnie między 900 a 1000 w produkcyjnym klastrze k8s, jeśli mierzyć pod względem ilości. deploymentsLiczba podów waha się między 1700 a 2000. Podów w klastrze produkcyjnym jest obecnie około 2000.
Nie mogę podać dokładnych liczb, ponieważ monitorujemy niepotrzebne mikroserwisy i usuwamy je w trybie półautomatycznym. Śledzenie niepotrzebnych bytów w k8s wspomaga nam , co znacząco oszczędza zasoby i pieniądze.
Zarządzanie zasobami
Monitoring
Fundamentem w eksploatacji dużego klastra staje się odpowiednio zbudowany i informacyjny system monitorowania. Na razie nie znaleźliśmy uniwersalnego rozwiązania, które pokryłoby 100% wszystkich "życzeń" dotyczących monitorowania, dlatego okresowo tworzymy różne niestandardowe rozwiązania w tej dziedzinie.
- Zabbix. Stary, dobry system monitorowania, który jest przeznaczony przede wszystkim do śledzenia ogólnego stanu infrastruktury. Informuje nas, kiedy węzeł umiera z powodu CPU, pamięci, dysków, sieci i tym podobnych. Nic nadzwyczajnego, ale mamy również osobny DaemonSet z agentami, za pomocą których na przykład monitorujemy stan DNS w klastrze: sprawdzamy opóźnione pody coredns, kontrolujemy dostępność zewnętrznych hostów. Można by się zastanawiać, po co się w to bawić, ale przy dużych wolumenach ruchu ten komponent jest poważnym punktem awarii. Już wcześniej mówiłem , jak radziłem sobie z wydajnością DNS w klastrze.
- Operator Prometheus. Zestaw różnych eksporterów daje szeroki przegląd wszystkich komponentów klastra. Następnie wizualizujemy to wszystko na dużych pulpitach nawigacyjnych w Grafanie, a do powiadomień używamy alertmanager.
Kolejnym przydatnym narzędziem dla nas stał się . Napisaliśmy go po tym, jak kilkukrotnie spotkaliśmy się z sytuacją, w której jeden zespół blokował ścieżki Ingress innego zespołu, co prowadziło do błędów 50x. Teraz przed wdrożeniem na produkcję deweloperzy sprawdzają, czy nikogo nie urazi, a dla mojego zespołu to dobre narzędzie do wstępnej diagnostyki problemów z Ingressami. Ciekawe, że pierwotnie został stworzony dla administratorów i wyglądał dość „topornie”, ale po tym, jak narzędzie zyskało popularność w zespołach deweloperskich, przeszedł znaczną transformację i przestał wyglądać jak „admin stworzył interfejs dla administratorów”. Wkrótce zrezygnujemy z tego narzędzia, a takie sytuacje będą weryfikowane jeszcze przed wdrożeniem pipeline’a.
Zasoby zespołów w „Kube”
Zanim przejdziemy do przykładów, warto wyjaśnić, jak u nas działa przydzielanie zasobów dla mikroserwisów.
Aby zrozumieć, jakie zespoły i w jakich ilościach wykorzystują swoje zasoby (procesor, pamięć, lokalny SSD), przydzielamy każdemu zespołowi jego własny namespace w „Kube” i ograniczamy maksymalne możliwości pod względem procesora, pamięci i dysku, wcześniej omawiając potrzeby zespołów. W konsekwencji jeden zespół, w ogólnym przypadku, nie zablokuje dla wdrożenia całego klastra, przydzielając sobie tysiące rdzeni i terabajty pamięci. Dostępy do namespace’u są wydawane przez AD (używamy RBAC). Namespace’y i ich limity są dodawane poprzez pull request do repozytorium GIT, a następnie za pomocą pipeline’a Ansible wszystko automatycznie się wdraża.
Przykład przydzielania zasobów dla zespołu:
namespaces:
chat-team:
pods: 23
limits:
cpu: 11
memory: 20Gi
requests:
cpu: 11
memory: 20Gi
Requests i limity
W „Kube” Request — to liczba gwarantowanych zarezerwowanych zasobów dla pod (jeden lub więcej kontenerów Docker) w klastrze. Limit — to niegwarantowany maksimum. Często można zobaczyć na wykresach, jak jakiś zespół przydzielił sobie zbyt wiele requests dla wszystkich swoich aplikacji i nie może wdrożyć aplikacji w „Kube”, ponieważ w ich namespace wszystkie requesty zostały już „wydane”.
Poprawnym wyjściem z takiej sytuacji jest obserwowanie rzeczywistego zużycia zasobów i porównanie go z zadanym poziomem (Request).


Na zrzutach ekranu powyżej widać, że „zadane” (Requested) CPU zbliżają się do rzeczywistej liczby wątków, a Limity mogą przekraczać rzeczywistą liczbę wątków procesorów centralnych =)
Teraz szczegółowo przyjrzymy się jakiemuś namespace'owi (wybrałem namespace kube-system — systemowy namespace dla komponentów samego „Kuba”) i zobaczymy stosunek rzeczywiście wykorzystanego czasu CPU i pamięci do żądanej:

Oczywiście, że pamięci i CPU zarezerwowane dla usług systemowych jest znacznie więcej, niż jest to rzeczywiście używane. W przypadku kube-system jest to uzasadnione: zdarzało się, że nginx ingress controller czy nodelocaldns w szczycie sięgały na maksymalny limit CPU i konsumowały bardzo dużo RAM-u, dlatego taki zapas jest uzasadniony. Ponadto, nie możemy polegać na wykresach z ostatnich 3 godzin: warto widzieć historyczne metryki przez dłuższy okres czasu.
Opracowano system „zalecenia”. Na przykład, tutaj można zobaczyć, które zasoby najlepiej by było „podnieść limity” (górna dopuszczalna granica), aby uniknąć „tłumienia” (throttling): momentu, kiedy już zużyto CPU lub pamięć w przydzielonym mu czasie i czeka na „odmrożenie”:

A oto podów, którym należałoby ograniczyć apetyt:

O tłumienie + monitorowanie zasobów można napisać niejedną artykuł, dlatego proszę zadawać pytania w komentarzach. W kilku słowach mogę powiedzieć, że zadanie automatyzacji podobnych metryk jest dość złożone i wymaga dużo czasu oraz gimnastyki z „okiennymi” funkcjami i „CTE” Prometheus / VictoriaMetrics (te terminy są w cudzysłowie, ponieważ w PromQL prawie nie ma nic podobnego, więc trzeba tworzyć skomplikowane zapytania na kilka ekranów tekstu i zajmować się ich optymalizacją).
W rezultacie, programiści mają narzędzia do monitorowania swoich namespace'ów w „Kubie”, i mogą sami decydować, gdzie i o jakiej porze można „przyciąć” zasoby, a jakim podom można oddać cały CPU na całą noc.
Metodologie
W firmie, jak teraz modnie, trzymamy się praktyk DevOps-owych i SRE- praktyk. Kiedy w firmie jest 1000 mikroserwisów, około 350 programistów i 15 administratorów na całą infrastrukturę, trzeba „być modnym”: za wszystkimi tymi „buzzwordami” kryje się ostra potrzeba automatyzacji wszystkiego i wszędzie, a administratorzy nie powinni być wąskim gardłem w procesach.
Jako Ops, dostarczamy różne metryki i pulpitów dla programistów, związanych z czasem odpowiedzi usług i ich błędami.
Używamy takich metodologii jak: , i , łącząc je razem. Staramy się zminimalizować liczbę pulpitów nawigacyjnych, aby z jednego spojrzenia było widać, który serwis właśnie ma problemy (na przykład kody odpowiedzi na sekundę, czas odpowiedzi w 99 percentylu) i tak dalej. Gdy tylko pojawią się nowe metryki potrzebne do wspólnych pulpitów, natychmiast je rysujemy i dodajemy.
Nie rysowałem wykresów od miesiąca. To chyba dobry znak: to oznacza, że większość 'chciejstw' została już zrealizowana. Czasami zdarzało się, że w tygodniu przynajmniej raz dziennie rysowałem jakiś nowy wykres.


Otrzymany wynik jest cenny, ponieważ teraz programiści rzadko przychodzą do administratorów z pytaniami 'gdzie można zobaczyć jakąś metrykę'.
Wdrożenie Service Mesh Nie ma co się obawiać, powinno to znacznie ułatwić życie wszystkim, koledzy z działu narzędzi są już blisko wdrożenia abstrakcyjnego 'Istio zdrowego człowieka': cykl życia każdego zapytania HTTP(s) będzie widoczny w monitorowaniu, i zawsze będzie można zrozumieć 'na którym etapie wszystko się zepsuło' podczas interakcji między serwisami (i nie tylko). Subskrybujcie nowości z hubu firmy DomClick. =)
Wsparcie dla infrastruktury Kubernetes
Historycznie przyjęło się, że używamy spatchowanej wersji Kubespray — rola Ansible do wdrażania, powiększania i aktualizacji Kubernetes. W pewnym momencie z głównej gałęzi wycięto wsparcie dla instalacji non-kubeadm, a proces przejścia na kubeadm nie został zaproponowany. W rezultacie firma Southbridge stworzyła swojego forka (z wsparciem kubeadm i szybkim rozwiązaniem krytycznych problemów).
Proces aktualizacji wszystkich klastrów k8s wygląda tak:
- Bierzemy Kubespray od Southbridge, porównujemy z naszą gałęzią, łączymy.
- Wdrażamy aktualizację w Stress- 'Kuba'.
- Wdrażamy aktualizację po jednej nodzie (w Ansible to 'serial: 1') w Dev- 'Kuba'.
- Aktualizujemy Prod w sobotę wieczorem po jednej nodzie.
W przyszłości planujemy zastąpić Kubespray czymś szybszym i przejść na kubeadm.
Mamy trzy 'Kuby': Stress, Dev i Prod. Planujemy uruchomić jeszcze jeden (hot standby) Prod-'Kubę' w drugim centrum danych. Stress i Dev żyją w 'wirtualkach' (oVirt dla Stress i VMWare cloud dla Dev). Prod- 'Kuba' działa na 'gołym metalu': to identyczne węzły z 32 wątkami CPU, 64-128 GB pamięci i 300 GB SSD RAID 10 — mamy ich w sumie 50. Trzy 'cienkie' węzły są przeznaczone na 'mastery' 'Kuby': 16 GB pamięci, 12 wątków CPU. ProdDla produkcji wolimy używać 'gołego metalu' i unikamy zbędnych warstw jak
: nie potrzebujemy 'hałaśliwych sąsiadów' i czasu 'kradzieży CPU' OpenStacksteal time kradnij czasA trudność administracji wzrasta w przybliżeniu dwukrotnie w przypadku open-source OpenStack.
Dla CI/CD „Kube” i innych komponentów infrastrukturalnych używamy oddzielnego serwera GIT, Helm 3 (przeszliśmy z Helm 2 dość boleśnie, ale cieszymy się z możliwości atomic), Jenkins, Ansible i Docker. Uwielbiamy feature-branching i wdrożenia do różnych środowisk z jednego repozytorium.
Podsumowanie

Tak w skrócie wygląda proces DevOps w firmie DomKlick z perspektywy inżyniera operacyjnego. Artykuł okazał się mniej techniczny niż się spodziewałem, więc bądźcie na bieżąco z nowościami DomKlick na Habrze: będą bardziej „hardcorowe” artykuły o Kubernetes i nie tylko.
Źródło: habr.com
