Przyp. tłum.: Po niedawnej publikacji na temat metod pull i push w GitOps zauważyliśmy zainteresowanie tym modelem w ogóle, jednak publikacji w języku rosyjskim na ten temat jest bardzo mało (na habrze po prostu ich nie ma). Dlatego z przyjemnością przedstawiamy Państwu tłumaczenie innego artykułu — choć sprzed prawie roku! — od firmy Weaveworks, której założyciel wymyślił termin „GitOps”. W tekście wyjaśniono istotę podejścia oraz kluczowe różnice w porównaniu do istniejących już rozwiązań.
Rok temu opublikowaliśmy . Wtedy opowiedzieliśmy, jak zespół Weaveworks uruchomił SaaS oparty całkowicie na Kubernetesie i opracował zestaw zalecanych najlepszych praktyk dotyczących wdrażania, zarządzania i monitorowania w środowisku cloud native.
Artykuł cieszył się popularnością. Inni zaczęli mówić o GitOps, publikować nowe narzędzia do , , , , itp. Na naszej stronie pojawiło się i różnych zastosowań GitOps. Ale niektórzy wciąż mieli pytania. Czym model różni się od tradycyjnego oraz ciągłego dostarczania ()? Czy konieczne jest użycie Kubernetesa?
Wkrótce zrozumieliśmy, że potrzebne jest nowe opracowanie, oferujące:
- Dużą ilość przykładów i historii;
- Konkretną definicję GitOps;
- Porównanie z tradycyjnym ciągłym dostarczaniem.
W tym artykule staraliśmy się omówić wszystkie te tematy. Znajdziesz w nim zaktualizowane wprowadzenie do GitOps oraz spojrzenie na nie z perspektywy programistów i CI/CD. Skupiamy się głównie na Kubernetesie, chociaż model można z łatwością uogólnić.
Poznajcie: GitOps
Wyobraź sobie Alicję. Zarządza firmą Family Insurance, oferującą polisy ubezpieczeniowe zdrowotne, samochodowe, na nieruchomości oraz ubezpieczenia turystyczne dla osób, które są zbyt zajęte, aby samodzielnie rozwiązywać zawirowania związane z umowami. Jej biznes rozpoczął się jako projekt poboczny, kiedy Alicja pracowała w banku jako data scientist. Pewnego dnia zdała sobie sprawę, że może wykorzystać zaawansowane algorytmy komputerowe do skuteczniejszej analizy danych i tworzenia pakietów ubezpieczeniowych. Inwestorzy sfinansowali projekt, a teraz jej firma generuje ponad 20 milionów dolarów rocznie i szybko się rozwija. W tej chwili zatrudnia 180 osób na różnych stanowiskach. Wśród nich znajduje się zespół technologiczny, który zajmuje się tworzeniem, utrzymywaniem strony internetowej, bazy danych oraz analizą bazy klientów. Zespół składający się z 60 osób prowadzi Bob – dyrektor techniczny firmy.
Zespół Boba wdraża systemy produkcyjne w chmurze. Ich główne aplikacje działają na GKE, korzystając z zalet Kubernetes w Google Cloud. Ponadto wykorzystują różne narzędzia do pracy z danymi i analityki.
Family Insurance nie planowała korzystać z kontenerów, ale zarażono ich entuzjazmem wokół Docker. Wkrótce specjaliści firmy odkryli, że GKE umożliwia łatwe i swobodne wdrażanie klastrów do testowania nowych funkcji. Dodano Jenkins do CI oraz Quay do organizacji rejestru kontenerów, napisano skrypty dla Jenkins, które przesyłały nowe kontenery i konfiguracje do GKE.
Minęło trochę czasu. Alicja i Bob byli rozczarowani wydajnością wybranej metody oraz jej wpływem na biznes. Wdrożenie kontenerów nie zwiększyło wydajności tak, jak się spodziewał zespół. Czasami wdrożenia się psuły, a nie było jasne, czy to przez zmiany w kodzie. Okazało się również trudne śledzenie zmian w konfiguracjach. Często trzeba było tworzyć nowy klaster i przenosić do niego aplikacje, ponieważ w ten sposób najłatwiej było uporządkować chaos, w który zmienił się system. Alicja obawiała się, że sytuacja pogorszy się wraz z rozwojem aplikacji (ponadto zbliżał się nowy projekt oparty na uczeniu maszynowym). Bob zautomatyzował dużą część pracy i nie rozumiał, dlaczego pipeline wciąż jest niestabilny, słabo się skalował i okresowo wymagał ręcznej interwencji?
Następnie dowiedzieli się o GitOps. To rozwiązanie okazało się dokładnie tym, czego potrzebowali, aby pewnie iść naprzód.
Alicja i Bob słyszeli o procesach opartych na Gicie, DevOps oraz infrastructure as code od wielu lat. Unikalność GitOps polega na tym, że wprowadza on szereg najlepszych praktyk — kategorycznych i normatywnych — dotyczących wdrażania tych idei w kontekście Kubernetes. Ten temat , w tym w .
Family Insurance postanawia wdrożyć GitOps. Teraz firma ma zautomatyzowany model operacyjny, kompatybilny z Kubernetes, który łączy szybkość z stabilnością, ponieważ:
- okazało się, że wydajność zespołu wzrosła dwukrotnie i nikt nie wpada w szał;
- zaprzestali utrzymywania skryptów. Zamiast tego mogą teraz skupić się na nowych funkcjach i doskonalić metody inżynieryjne — na przykład wdrożyć wypuszczenia kanaryjskie i poprawić testowanie;
- udoskonalili proces wdrażania — w tej chwili rzadko się psuje;
- uzyskali możliwość odzyskiwania wdrożeń po częściowych awariach bez ręcznej interwencji;
- zyskaliwiększąwiększą pewność w systemach dostaw. Alicja i Bob odkryli, że mogą podzielić zespół na grupy zajmujące się mikrousługami i pracujące równolegle;
- mogą wprowadzać od 30 do 50 zmian w projekcie każdego dnia dzięki wysiłkom każdej grupy i próbować nowych technik;
- łatwo przyciągają do projektu nowych programistów, którzy mają możliwość wdrażania aktualizacji na produkcję za pomocą pull requestów już w ciągu kilku godzin;
- łatwo przechodzą audyt w ramach SOC2 (na zgodność dostawców usług z wymaganiami dotyczącymi bezpiecznego zarządzania danymi; więcej informacji można znaleźć na przykład — przyp. tłum.).
Co się stało?
GitOps — to dwie rzeczy:
- Model eksploatacji dla Kubernetes i cloud native. Oferuje zbiór najlepszych praktyk do wdrażania, zarządzania i monitorowania skojarzonych w kontenerach klastrów i aplikacji. Elegancką definicją jest od :
- Ścieżka do stworzenia środowiska zorientowanego na programistów do zarządzania aplikacjami. Stosujemy workflow Git zarówno do eksploatacji, jak i do rozwoju. Zauważ, że nie chodzi tylko o Git push, ale o organizację całego zestawu narzędzi CI/CD oraz UI/UX.
Kilka słów o Git
Jeśli nie znasz systemów kontroli wersji i opartego na Git workflow, serdecznie zalecamy ich zgłębienie. Na początku praca z gałęziami i pull requestami może wydawać się czarną magią, ale korzyści przewyższają włożony wysiłek. Oto na początek.
Jak działa Kubernetes
W naszej historii, Alice i Bob zwrócili się do GitOps, pracując przez pewien czas z Kubernetes. Rzeczywiście, GitOps jest ściśle związane z Kubernetes — to model eksploatacji dla infrastruktury i aplikacji opartych na Kubernetes.
Co Kubernetes oferuje użytkownikom?
Oto niektóre z kluczowych możliwości:
- W modelu Kubernetes wszystko można opisać w formie deklaratywnej.
- Serwer API Kubernetes przyjmuje takie deklaracje jako dane wejściowe, a następnie nieustannie stara się doprowadzić klaster do stanu opisanego w deklaracji.
- Deklaracje są wystarczające do opisu i zarządzania szerokim zakresem obciążeń roboczych — "aplikacji".
- W rezultacie, wprowadzanie zmian w aplikacji i klastrze następuje z powodu:
- zmian w obrazach kontenerów;
- zmian w deklaratywnej specyfikacji;
- błędów w środowisku — na przykład, awarii kontenerów.
Wspaniałe możliwości konwergencji Kubernetes
Gdy administrator wprowadza zmiany w konfiguracji, orkiestrator Kubernetes będzie stosować je do klastra, aż jego stan zblizy się do nowej konfiguracji.. Ten model działa dla dowolnego zasobu Kubernetes i jest rozszerzany za pomocą Custom Resource Definitions (CRD). Dlatego deploye Kubernetes mają następujące wspaniałe właściwości:
- Automatyzacja: aktualizacje Kubernetes zapewniają mechanizm do automatyzacji procesu wprowadzania zmian poprawnie i na czas.
- Konwergencja: Kubernetes będzie kontynuować próby aktualizacji aż do osiągnięcia sukcesu.
- Idempotencja: wielokrotne zastosowania konwergencji prowadzą do tego samego wyniku.
- Deterministyczność: przy wystarczających zasobach stan zaktualizowanego klastra zależy tylko od żądanego stanu.
Jak działa GitOps
Wystarczająco dużo dowiedzieliśmy się o Kubernetes, aby wyjaśnić zasady działania GitOps.
Wracając do zespołów Family Insurance związanych z mikrousługami. Czym się zazwyczaj zajmują? Zobacz listę poniżej (jeśli jakieś punkty wydają się dziwne lub niezrozumiałe — prosimy o wstrzymanie się z krytyką i pozostanie z nami). To tylko przykłady procesów roboczych opartych na Jenkins. Istnieje wiele innych procesów przy pracy z innymi narzędziami.
Najważniejsze — widzimy, że każda aktualizacja kończy się wprowadzeniem zmian w plikach konfiguracyjnych i repozytoriach Git. Te zmiany w Git prowadzą do tego, że „operator GitOps” aktualizuje klaster:
1. Proces roboczy: „Build Jenkins — branch master».
Lista zadań:
- Jenkins pcha tagowane obrazy do Quay;
- Jenkins pcha konfigurację i Helm-chart do bucketa master repozytoriów;
- Funkcja w chmurze kopiuje konfigurację i chart do repozytorium Git master;
- Operator GitOps aktualizuje klaster.
2. Build Jenkins — branch release lub hotfix:
- Jenkins pcha nieotagowane obrazy do Quay;
- Jenkins pcha konfigurację i Helm-chart do bucketa staging repozytoriów;
- Funkcja w chmurze kopiuje konfigurację i chart do repozytorium Git staging;
- Operator GitOps aktualizuje klaster.
3. Build Jenkins — branch develop lub feature:
- Jenkins pcha nieotagowane obrazy do Quay;
- Jenkins pcha konfigurację i Helm-chart do bucketa develop repozytoriów;
- Funkcja w chmurze kopiuje konfigurację i chart do repozytorium Git develop;
- Operator GitOps aktualizuje klaster.
4. Dodanie nowego klienta:
- Menadżer lub administrator (LCM/ops) wywołuje Gradle do początkowego wdrożenia i konfiguracji balansujących obciążenie (NLB);
- LCM/ops commituje nową konfigurację, aby przygotować deployment do aktualizacji;
- Operator GitOps aktualizuje klaster.
Krótki opis GitOps
- Opisz pożądany stan całego systemu, używając specyfikacji deklaratywnych dla każdego środowiska (w naszej historii zespół Boba definiuje całą konfigurację systemu w Git).
- Repozytorium Git jest jedynym źródłem prawdy w odniesieniu do pożądanego stanu całego systemu.
- Wszystkie zmiany w pożądanym stanie dokonuje się poprzez commity w Git.
- Wszystkie pożądane parametry klastra są również obserwowalne w samym klastrze. W ten sposób możemy określić, czy stany pożądane i obserwowane odpowiadają sobie (konwergują, converge) czy różnią się (dywergują, diverge) od siebie.
- Jeżeli stany pożądany i obserwowany różnią się, to:
- Istnieje mechanizm konwergencji, który prędzej czy później automatycznie synchronizuje stan docelowy i obserwowany. W obrębie klastra zajmuje się tym Kubernetes.
- Proces uruchamia się niezwłocznie z powiadomieniem „zmiana zatwierdzona”.
- Po pewnym konfigurowalnym czasie może zostać wysłane powiadomienie „diff”, jeśli stany różnią się.
- W ten sposób wszystkie commity w Git powodują sprawdzalne i idempotentne aktualizacje w klastrze.
- Rollback to konwergencja do wcześniej pożądanego stanu.
- Konwergencja jest ostateczna. O jej wystąpieniu świadczą:
- Brak powiadomień „diff” przez określony czas.
- Powiadomienie „konwergentny” (np. webhook, zdarzenie Git writeback).
Czym jest dywergencja?
Powtórzmy jeszcze raz: wszystkie pożądane właściwości klastra muszą być obserwowalne w samym klastrze.
Kilka przykładów dywergencji:
- Zmiana w pliku konfiguracyjnym z powodu scalania gałęzi w Git.
- Zmiana w pliku konfiguracyjnym z powodu commita w Git, wykonanego przez klienta GUI.
- Wiele zmian w pożądanym stanie z powodu PR w Git, z późniejszym budowaniem obrazu kontenera i zmianami w konfiguracji.
- Zmiana stanu klastra z powodu błędu, konfliktu zasobów prowadzącego do „złego zachowania”, lub po prostu przypadkowego odchylenia od pierwotnego stanu.
Co stanowi mechanizm konwergencji?
Kilka przykładów:
- W przypadku kontenerów i klastrów mechanizm konwergencji zapewnia Kubernetes.
- Ten sam mechanizm można użyć do zarządzania aplikacjami i strukturami opartymi na Kubernetes (np. Istio i Kubeflow).
- Mechanizm do zarządzania współpracą roboczą między Kubernetes, repozytoriami obrazów a Gitem zapewnia , będący częścią .
- Dla podstawowych maszyn mechanizm konwergencji musi być deklaratywny i autonomiczny. Z własnego doświadczenia możemy powiedzieć, że jest najbliższy tej definicji, jednak wciąż wymaga kontroli człowieka. W tym sensie GitOps rozwija tradycje Infrastructure as Code.
GitOps łączy Git z doskonałym mechanizmem konwergencji Kubernetes, oferując model eksploatacji.
GitOps pozwala nam stwierdzić: automatyzacji i kontroli podlegają tylko te systemy, które można opisać i monitorować.
GitOps jest przeznaczony dla całego stacka cloud native (np. Terraform itp.)
GitOps to nie tylko Kubernetes. Chcemy, aby cały system był zarządzany deklaratywnie i wykorzystywał konwergencję. Pod całym systemem rozumiemy zbiór środowisk działających z Kubernetes — na przykład „dev cluster 1”, „production” itd. W każde środowisko wchodzą maszyny, klastry, aplikacje oraz interfejsy dla zewnętrznych usług dostarczających dane, monitoring itd.
Zauważcie, jak ważny w tym przypadku jest Terraform dla problemu bootstrappingu. Kubernetes musi być gdzieś wdrożony, a użycie Terraform oznacza, że możemy zastosować te same procesy robocze GitOps do stworzenia warstwy zarządzającej, leżącej u podstaw Kubernetes i aplikacji. To dobra praktyka.
Dużą uwagę poświęca się zastosowaniu konceptów GitOps do warstw nad Kubernetes. Jak na razie istnieją rozwiązania typu GitOps dla Istio, Helm, Ksonnet, OpenFaaS i Kubeflow, a także na przykład Pulumi, które tworzą warstwę do rozwoju aplikacji pod cloud native.
Kubernetes CI/CD: porównanie GitOps z innymi podejściami
Jak już wspomniano, GitOps to dwie rzeczy:
- Model eksploatacji dla Kubernetes i cloud native, jak opisano powyżej.
- Droga do organizacji zorientowanej na deweloperów w zarządzaniu aplikacjami.
Dla wielu GitOps to przede wszystkim proces roboczy oparty na pushach Git. Nam również się to podoba. Ale to nie wszystko: teraz spójrzmy na pipeline'y CI/CD.
GitOps zapewnia ciągłe wdrażanie (CD) pod Kubernetes
GitOps oferuje mechanizm ciągłego wdrażania, eliminujący potrzebę poszczególnych „systemów zarządzania wdrożeniami”. Całą pracę wykonuje za was Kubernetes.
- Aktualizacja aplikacji wymaga aktualizacji w Git. Jest to aktualizacja transakcyjna do pożądanego stanu. „Wdrażanie” następnie odbywa się wewnątrz klastra przez Kubernetes na podstawie zaktualizowanego opisu.
- Ze względu na specyfikę działania Kubernetes, te aktualizacje są zbieżne. Zapewnia to mechanizm ciągłego wdrażania, w którym wszystkie aktualizacje są atomowe.
- Uwaga: oferuje operatora GitOps, integrującego Git i Kubernetes, który pozwala na realizację CD poprzez porównanie pożądanych i bieżących stanów klastra.
Bez kubectl i skryptów
Należy unikać używania kubectl do aktualizacji klastra, a zwłaszcza skryptów do grupowania poleceń kubectl. Zamiast tego, przy użyciu pipeline'a GitOps użytkownik może zaktualizować swój klaster Kubernetes poprzez Git.
Zalety obejmują:
- Poprawność. Grupę aktualizacji można zastosować, zbiegać i w końcu zweryfikować, co przybliża nas do celu atomowego wdrażania. Przeciwnie, użycie skryptów nie daje żadnych gwarancji zbieżności (więcej o tym poniżej).
- Bezpieczeństwo. Kelsey Hightower: „Ogranicz dostęp do klastra Kubernetes narzędziom automatyzacji oraz administratorom, których obowiązkiem jest jego debugowanie lub utrzymanie sprawności”. Zobacz także na temat bezpieczeństwa i zgodności z wymaganiami technicznymi, oraz poprzez kradzież danych logowania z niedbale skonstruowanego skryptu Jenkins.
- Doświadczenie użytkownika. Kubectl ujawnia mechanizm modelu obiektowego Kubernetes, który jest dość skomplikowany. W idealnym przypadku użytkownicy powinni interaktywować z systemem na wyższym poziomie abstrakcji. Ponownie odwołam się do Kelsey i polecam zapoznać się z .
Różnica między CI a CD
GitOps poprawia istniejące modele CI/CD.
Nowoczesny serwer CI jest narzędziem do orkiestracji. W szczególności jest to narzędzie do orkiestracji pipeline'ów CI. Obejmują one kompilację, testowanie, scalanie do gałęzi głównej itd. Serwery CI automatyzują zarządzanie skomplikowanymi wieloetapowymi pipeline'ami. Powszechną pokusą jest stworzenie skryptu do zestawu aktualizacji Kubernetes i wykonanie go jako elementu pipeline'a do przesyłania zmian do klastra. Rzeczywiście, wielu specjalistów tak postępuje. Jednak to nieoptymalne, i oto dlaczego.
CI powinno być wykorzystywane do wprowadzania aktualizacji do trunk, a klaster Kubernetes powinien dostosować się na podstawie tych aktualizacji, aby zarządzać CD "wewnętrznie". Nazywamy to , w przeciwieństwie do modelu push CI. CD jest częścią orkiestracji runtime.
Dlaczego serwery CI nie powinny realizować CD poprzez bezpośrednie aktualizacje w Kubernetes
Nie używaj serwera CI do orkiestracji bezpośrednich aktualizacji w Kubernetes w formie zestawu zadań CI. To antywzorzec, o którym na naszym blogu.
Wróćmy do Alicji i Boba.
Z jakimi problemami się borykali? Serwer CI Boba stosuje zmiany w klastrze, ale jeśli w trakcie tego procesu się zawiesi, Bob nie będzie wiedział, w jakim stanie jest (lub powinien być) klaster i jak go naprawić. To samo dotyczy sukcesu.
Załóżmy, że zespół Boba zbudował nowy obraz, a następnie zastosował poprawki do swoich wdrożeń, aby wdrożyć obraz (wszystko to z pipeline CI).
Jeśli obraz zostanie zbudowany prawidłowo, ale pipeline się zawiesi, zespół będzie musiał ustalić:
- Czy aktualizacja została wdrożona?
- Czy uruchamiamy nową wersję? Czy to spowoduje niepotrzebne skutki uboczne — z możliwością uzyskania dwóch instalacji tego samego niezmiennego obrazu?
- Czy powinniśmy poczekać na kolejną aktualizację, zanim uruchomimy budowę?
- Co dokładnie poszło nie tak? Jakie kroki należy powtórzyć (i które z nich można bezpiecznie powtórzyć)?
Organizacja opartego na Gicie przepływu pracy nie gwarantuje, że zespół Boba nie napotka tych problemów. Wciąż mogą się pomylić z pchnięciem commita, tagiem lub innym parametrem; jednak to podejście wciąż jest znacznie bliższe wyraźnemu wszystkiemu lub nic.
Podsumowując, oto dlaczego serwery CI nie powinny zajmować się CD:
- Skrypty aktualizacji nie zawsze są deterministyczne; łatwo można popełnić w nich błędy.
- Serwery CI nie konwergują do deklaratywnego modelu klastra.
- Trudno jest zapewnić idempotencję. Użytkownicy muszą rozumieć głęboką semantykę systemu.
- Trudniej jest przeprowadzić odzyskiwanie po częściowym awarii.
Uwaga dotycząca Helma: jeśli chcesz korzystać z Helma, rekomendujemy połączenie go z operatorem GitOps, takim jak . To pomoże zapewnić konwergencję. Sam Helm nie jest ani deterministyczny, ani atomowy.
GitOps jako najlepszy sposób na realizację Continuous Delivery dla Kubernetes
Zespół Alice i Bob wdraża GitOps i odkrywa, że znacznie łatwiej jest pracować z produktami oprogramowania, utrzymywać wysoką wydajność i stabilność. Zakończmy ten artykuł ilustracjami, które pokazują, jak wygląda ich nowe podejście. Należy pamiętać, że głównie mówimy o aplikacjach i usługach, jednak GitOps można wykorzystać do zarządzania całą platformą.
Model eksploatacji dla Kubernetes
Zobacz poniższy diagram. Przedstawia Git i repozytorium obrazów kontenerów jako wspólne zasoby dla dwóch orkestrowanych cykli życia:
- Pipelina ciągłej integracji, która odczytuje i zapisuje pliki w Git oraz może aktualizować repozytorium obrazów kontenerów.
- Pipelina Runtime GitOps, łącząca wdrażanie z zarządzaniem i observability. Odczytuje i zapisuje pliki w Git oraz może przesyłać obrazy kontenerów.
Jakie są główne wnioski?
- Rozdzielenie problemów: Zauważ, że oba pipeline'y mogą wymieniać się danymi tylko poprzez aktualizację Gita lub repozytorium obrazów. Innymi słowy, istnieje zapora sieciowa między środowiskiem CI a runtime. Nazywamy to 'zapora niezmienności' (immutability firewall), ponieważ wszystkie aktualizacje repozytoriów tworzą nowe wersje. Po więcej informacji na ten temat, zapoznaj się ze slajdami 72-87. .
- Można używać dowolnych serwerów CI i Git: GitOps działa z dowolnymi komponentami. Możesz dalej korzystać ze swoich ulubionych serwerów CI i Git, repozytoriów obrazów i zestawów testów. Prawie wszystkie inne narzędzia do Continuous Delivery na rynku wymagają własnego serwera CI/Git lub repozytorium obrazów. Może to stanowić ograniczenie w rozwoju cloud native. W przypadku GitOps możesz korzystać z dobrze znanych narzędzi.
- Wydarzenia jako narzędzie integracji: Gdy tylko dane w Gicie są aktualizowane, Weave Flux (lub operator Weave Cloud) informuje o tym runtime. Za każdym razem, gdy Kubernetes przyjmuje zestaw zmian, Git jest aktualizowany. To zapewnia prosty model integracji do organizacji procesów roboczych dla GitOps, jak pokazano poniżej.
Podsumowanie
GitOps zapewnia istotne gwarancje aktualizacji, które są niezbędne dla każdego nowoczesnego narzędzia CI/CD:
- automatyzacja;
- konwergencja;
- idempotentność;
- deterministyczność.
Jest to istotne, ponieważ oferuje model eksploatacji dla deweloperów w obszarze chmurowym.
- Tradycyjne narzędzia do zarządzania i monitorowania systemów są powiązane z zespołami eksploatacyjnymi działającymi w ramach runbook’a. (zbioru rutynowych procedur i operacji — przyp. tłum.), powiązanym z konkretnym wdrożeniem.
- W zarządzaniu systemami chmurowymi narzędzia do monitorowania są najlepszym sposobem oceny wyników wdrożeń, aby zespół deweloperski mógł szybko na nie reagować.
Wyobraź sobie wiele klastrów rozproszonych po różnych chmurach oraz wiele serwisów z własnymi zespołami i planami wdrożeń. GitOps oferuje model zarządzania dla wszystkich tych zasobów.
P.S. od tłumacza
Przeczytaj także na naszym blogu:
- «»;
- «»;
- «».
Tylko zarejestrowani użytkownicy mogą brać udział w ankiecie. , proszę.
Czy wiedziałeś o GitOps zanim pojawiły się te dwa tłumaczenia na habrze?
Tak, wszystko wiedziałem(a).
Jedynie powierzchownie.
Nie
35 użytkowników zagłosowało. 10 użytkowników wstrzymało się od głosu.
Źródło: habr.com
