Większość zespołów, które „mają GitOps”, ma w rzeczywistości pipeline wykonujący kubectl apply po każdym commicie. To dobry pierwszy krok, ale nie jest to GitOps, a różnica nie jest akademicka. Ujawnia się w najgorszym możliwym momencie: podczas awarii, gdy ktoś poprawił coś ręcznie na klastrze i o tym zapomniał.
Skrypt: sekwencja kroków
Skrypt deployu, niezależnie od tego, czy jest to Bash, zadanie w pipeline czy playbook Ansible, działa imperatywnie. Wykonuje określone kroki w określonej kolejności i kończy pracę. Jeśli po jego zakończeniu stan środowiska zmieni się z innego powodu, skrypt tego nie zauważy. Wie tylko to, co zrobił w chwili uruchomienia.
To ma konsekwencje:
- Zmiana wykonana ręcznie na klastrze zostaje tam do następnego uruchomienia pipeline’u, czyli często do następnej zmiany w kodzie aplikacji, która może nastąpić za tygodnie.
- Repozytorium mówi jedno, klaster drugie, a nikt nie ma narzędzia, które to zestawi.
- Wycofanie zmiany wymaga ponownego uruchomienia skryptu ze starszej wersji, co nie zawsze jest tym samym co przywrócenie starego stanu.
GitOps: pętla uzgadniania
W GitOps repozytorium jest jedynym źródłem prawdy o pożądanym stanie, a w klastrze działa kontroler, który w pętli porównuje ten stan z rzeczywistością i usuwa różnice. Nie ma „uruchomienia deployu”. Jest ciągłe uzgadnianie.
Praktycznie oznacza to trzy rzeczy, których skrypt nie da:
- Wykrywanie dryfu. Ręczna zmiana na klastrze jest widoczna w ciągu minut jako rozjazd między repozytorium a stanem faktycznym. Można ją automatycznie cofnąć albo przynajmniej zobaczyć, że istnieje.
- Samonaprawa. Jeśli ktoś usunie zasób, kontroler go odtworzy. Jeśli ktoś zmieni liczbę replik ręcznie, wróci ona do wartości z repozytorium.
- Wycofanie jako operacja na Gicie. Powrót do poprzedniej wersji to
git revert. Kontroler zajmie się resztą, a historia zmian jest kompletna, bo nic nie działo się poza repozytorium.
Gdzie kończy się rola pipeline’u
GitOps nie eliminuje CI. Pipeline nadal buduje obraz, uruchamia testy i publikuje artefakt. Zmienia się jednak granica odpowiedzialności: pipeline nie dotyka klastra. Zamiast tego modyfikuje manifesty w repozytorium, na przykład podnosi tag obrazu, i na tym kończy pracę. Wdrożeniem zajmuje się kontroler w klastrze.
Ta granica upraszcza uprawnienia. Pipeline nie potrzebuje dostępu do API klastra, a kontroler nie potrzebuje dostępu do systemu CI. Kompromitacja jednego z nich nie daje automatycznie kontroli nad drugim.
# Fragment manifestu, który pipeline modyfikuje po zbudowaniu obrazu.
# Kontroler GitOps zauważy zmianę i wdroży nową wersję.
spec:
template:
spec:
containers:
- name: app
image: registry.example.internal/team/app:1.4.2
Kiedy skrypt wystarczy
Nie każde środowisko potrzebuje pętli uzgadniania. Jeśli deploy odbywa się na pojedynczy serwer, kilka razy w roku, i nikt nie ma do niego dostępu poza pipeline’em, skrypt jest prostszy i wystarczający. GitOps zaczyna się opłacać, gdy spełniony jest co najmniej jeden z warunków:
- więcej niż jedna osoba może zmieniać środowisko,
- wdrożenia są częste albo dotyczą wielu komponentów,
- wymagany jest audyt tego, kto i kiedy zmienił konfigurację,
- środowisko musi dać się odtworzyć od zera w przewidywalnym czasie.
Podsumowanie
Skrypt odpowiada na pytanie „co zrobić”. GitOps odpowiada na pytanie „jak ma wyglądać stan docelowy” i pilnuje odpowiedzi w sposób ciągły. Pierwsze podejście jest prostsze w uruchomieniu, drugie w utrzymaniu. Wybór zależy od tego, na którym etapie środowisko spędzi więcej czasu, a odpowiedź niemal zawsze brzmi: w utrzymaniu.