Co dowiedziałem się testując 200 000 linii kodu infrastrukturalnego

Co dowiedziałem się testując 200 000 linii kodu infrastrukturalnego

Podejście IaC (Infrastructure as Code) składa się nie tylko z kodu, który jest przechowywany w repozytorium, ale także z ludzi i procesów, które go otaczają. Czy można wykorzystać podejścia z rozwoju oprogramowania w zarządzaniu i opisywaniu infrastruktury? Nie zaszkodzi mieć tę ideę na uwadze podczas czytania artykułu.

Wersja angielska

To jest transkrypcja mojego wystąpienia na DevopsConf 2019-05-28.

Prezentacje i filmy

Infrastruktura jako historia bash

Co dowiedziałem się testując 200 000 linii kodu infrastrukturalnego

Załóżmy, że przychodzisz do nowego projektu, a mówią ci: „mamy Infrastruktura jako kod„. W rzeczywistości okazuje się, Infrastruktura jako historia bash lub na przykład Dokumentacja jako historia bash. To całkiem realistyczna sytuacja, na przykład, podobny przypadek opisał Denis Łysenko w swoim wystąpieniu Jak wymienić całą infrastrukturę i zacząć spokojnie spać, opowiedział, jak z historii bash otrzymali zorganizowaną infrastrukturę w projekcie.

Przy pewnej dozie chęci, można powiedzieć, że Infrastruktura jako historia bash to jak kod:

  1. odtwarzalność: możesz wziąć historię bash, wykonać polecenia stamtąd, możliwe, że w ten sposób otrzymasz działającą konfigurację na wyjściu.
  2. wersjonowanie: wiesz, kto się logował i co robił, ponownie nie ma pewności, że to doprowadzi cię do działającej konfiguracji na wyjściu.
  3. historię: historia kto i co zrobił. tylko nie będziesz mógł z niej korzystać, jeśli zgubisz serwer.

Co robić?

Infrastruktura jako kod

Co dowiedziałem się testując 200 000 linii kodu infrastrukturalnego

Nawet taki dziwny przypadek jak Infrastruktura jako historia bash można jakoś połączyć z Infrastruktura jako kod, ale gdy zechcemy zrobić coś bardziej skomplikowanego niż stary, dobry serwer LAMP, dojdziemy do wniosku, że ten kod trzeba w jakiś sposób modyfikować, zmieniać, rozwijać. Następnie chcielibyśmy rozważyć paralele między Infrastruktura jako kod a rozwojem oprogramowania.

D.R.Y.

Co dowiedziałem się testując 200 000 linii kodu infrastrukturalnego

W projekcie rozwoju systemów magazynowych, istniał podprojekt cyklicznie konfigurować SDS: wydajemy nową wersję - trzeba ją wdrożyć do dalszych testów. Zadanie jest proste:

  • wejdź tutaj przez ssh i wykonaj polecenie.
  • skopiuj tam plik.
  • tu popraw konfigurację.
  • tam uruchom usługę
  • ZYSK!

Dla opisanej logiki więcej niż wystarczy bash, szczególnie na wczesnych etapach projektu, gdy dopiero się rozpoczyna. To nie źle, że używasz bash, ale z czasem pojawia się potrzeba rozwinięcia czegoś podobnego, ale nieco różniącego się. Pierwsze, co przychodzi na myśl, to copy-paste. I już mamy dwa bardzo podobne skrypty, które robią niemal to samo. Z czasem liczba skryptów wzrosła, i napotkaliśmy na pewną logikę biznesową wdrożenia instalacji, którą trzeba zsynchronizować pomiędzy różnymi skryptami, co jest dość skomplikowane.

Co dowiedziałem się testując 200 000 linii kodu infrastrukturalnego

Okazuje się, że istnieje praktyka D.R.Y. (Nie Powtarzaj Się). Idea polega na ponownym wykorzystaniu istniejącego kodu. Brzmi prosto, ale nie doszliśmy do tego od razu. W naszym przypadku była to prosta idea: oddzielenie konfiguracji od skryptów. Tzn. logika biznesowa dotycząca wdrożenia instalacji osobno, konfiguracje osobno.

S.O.L.I.D. dla CFM

Co dowiedziałem się testując 200 000 linii kodu infrastrukturalnego

Z czasem projekt rósł i naturalnym rozwinięciem stało się pojawienie Ansible. Głównym powodem jego pojawienia się była obecność ekspertyzy w zespole oraz fakt, że bash nie jest przeznaczony do złożonej logiki. Ansible także zaczęło zawierać złożoną logikę. Aby złożona logika nie przerodziła się w chaos, w rozwoju oprogramowania istnieją zasady organizacji kodu. S.O.L.I.D. Także, na przykład, Grigorij Pietrow w swoim referacie "Dlaczego IT-owiec potrzebuje osobistej marki" poruszył kwestię, że człowiek, tak jest skonstruowany, że łatwiej mu operować pewnymi bytami społecznymi, w rozwoju oprogramowania są to obiekty. Jeśli połączyć te dwie idee i rozwijać je dalej, można zauważyć, że w opisie infrastruktury również można używać S.O.L.I.D. aby w przyszłości było łatwiej utrzymywać i modyfikować tę logikę.

Zasada pojedynczej odpowiedzialności

Co dowiedziałem się testując 200 000 linii kodu infrastrukturalnego

Każda klasa wykonuje tylko jedno zadanie.

Nie należy mieszać kodu i tworzyć monolitycznych boskich makaronowych potworów. Infrastruktura powinna składać się z prostych klocków. Okazuje się, że jeśli podzielić Ansible playbook na małe kawałki, czytaj Ansible role, to łatwiej je utrzymywać.

Zasada otwarte/zamknięte

Co dowiedziałem się testując 200 000 linii kodu infrastrukturalnego

Zasada otwartości/zamkniętości.

  • Otwarte na rozszerzenia: oznacza, że zachowanie bytu może być rozszerzone poprzez tworzenie nowych typów bytów.
  • Zamknięte na zmiany: w wyniku rozszerzenia zachowania bytu nie powinny być wprowadzane zmiany w kodzie, który korzysta z tych bytów.

Początkowo rozwijaliśmy testową infrastrukturę na maszynach wirtualnych, ale dzięki temu, że logika biznesowa implementacji była oddzielona od realizacji, bez problemu dodaliśmy rozkładanie na baremetal.

Zasada podstawienia Liskov

Co dowiedziałem się testując 200 000 linii kodu infrastrukturalnego

Zasada podstawienia Barbary Liskov. Obiekty w programie powinny być wymienialne na instancje ich podtypów bez zmiany poprawności działania programu.

Jeśli spojrzeć szerzej, to nie jest to szczególna cecha konkretnego projektu, można to zastosować wszędzie. S.O.L.I.D., w ogólnym ujęciu chodzi o CFM, na innym projekcie konieczne jest wdrażanie aplikacji Java na różnych serwerach aplikacyjnych, bazach danych, systemach operacyjnych itd. Na tym przykładzie rozważę dalsze zasady. S.O.L.I.D.

W naszym przypadku w ramach zespołu infrastruktury istnieje ustalenie, że jeśli zainstalujemy rolę imbjava lub oraclejava, to mamy binarny plik wykonywalny java. Jest to konieczne, ponieważ wyższe role zależą od tego zachowania, oczekują obecności java. Jednocześnie pozwala nam to na wymianę jednej implementacji/wersji java na inną, nie zmieniając logiki wdrażania aplikacji.

Problem polega na tym, że w Ansible nie można zaimplementować takiej funkcji, w ramach zespołu pojawiają się jakieś ustalenia.

Zasada segregacji interfejsów

Co dowiedziałem się testując 200 000 linii kodu infrastrukturalnego

Zasada segregacji interfejsów: "Wiele interfejsów, specjalnie zaprojektowanych dla klientów, jest lepsze niż jeden interfejs ogólnego przeznaczenia."

Początkowo próbowaliśmy zbierać całą wariantowość wdrażania aplikacji w jednym playbooku Ansible, ale było to trudne do utrzymania, a podejście, w którym mamy sprecyzowany interfejs na zewnątrz (klient oczekuje portu 443), pozwala na komponowanie infrastruktury dla konkretnej realizacji z oddzielnych bloków.

Zasada inwersji zależności

Co dowiedziałem się testując 200 000 linii kodu infrastrukturalnego

Zasada inwersji zależności. Moduły wyższych poziomów nie powinny zależeć od modułów niższych poziomów. Oba typy modułów powinny zależeć od abstrahacji. Abstrahacje nie powinny zależeć od szczegółów. Szczegóły powinny zależeć od abstrahacji.

Ten przykład będzie oparty na antywzorcach.

  1. Jeden z naszych klientów miał prywatne chmurę.
  2. Wewnątrz chmury zamawialiśmy maszyny wirtualne.
  3. Jednak z powodu szczególnych cech chmury wdrażanie aplikacji było powiązane z tym, na jaki hypervisor trafiła VM.

To. wysoko poziomowa logika wdrażania aplikacji, zależności przeszły na niższe poziomy hypervisora, co oznaczało problemy z ponownym wykorzystaniem tej logiki. Nie rób tak.

Interakcja

Co dowiedziałem się testując 200 000 linii kodu infrastrukturalnego

Infrastruktura jako kod to nie tylko kod, ale także relacje między kodem a człowiekiem, interakcje między twórcami infrastruktury.

Bus factor

Co dowiedziałem się testując 200 000 linii kodu infrastrukturalnego

Załóżmy, że na twoim projekcie jest Wania. Wania wie wszystko o twojej infrastrukturze. Co się stanie, jeśli Wania nagle zniknie? To całkiem realna sytuacja, ponieważ może go przejechać autobus. Czasami tak się zdarza. Jeśli wiedza na temat kodu, jego struktury, tego jak działa, loginów i haseł nie jest rozproszona w zespole, możesz napotkać wiele nieprzyjemnych sytuacji. Aby zminimalizować te ryzyka i rozłożyć wiedzę w zespole, można zastosować różne podejścia.

Pair Devopsing

Co dowiedziałem się testując 200 000 linii kodu infrastrukturalnego

To nie jest jak w żarcie, że administratorzy pili piwo, zmieniali hasła, a to odpowiednik programowania w parach. To znaczy dwóch inżynierów siada za jednym komputerem, na jednej klawiaturze i zaczyna wspólnie konfigurować twoją infrastrukturę: konfigurować serwer, pisać rolę Ansible itd. Brzmi pięknie, ale u nas się nie sprawdziło. Jednak niektóre przypadki tej praktyki działały. Nowy pracownik, jego mentor razem z nim bierze rzeczywiste zadanie, pracują — przekazują wiedzę.

Inny przypadek to incident call. Podczas problemu zbiera się grupa dyżurnych i zainteresowanych, wyznaczany jest jeden prowadzący, który udostępnia swój ekran i na głos przedstawia tok myślenia. Inni uczestnicy śledzą myśli prowadzącego, podglądają triki z konsoli, sprawdzają, czy nie pominęli jakiejś linii w logach, poznają nowe informacje o systemie. Takie podejście działało raczej lepiej niż gorzej.

Code Review

Co dowiedziałem się testując 200 000 linii kodu infrastrukturalnego

Subiektywnie, bardziej efektywne przekazywanie wiedzy o infrastrukturze i tym, jak jest zbudowana, odbywało się za pomocą code review:

  • Infrastruktura opisana jest kodem w repozytorium.
  • Zmiany zachodzą w oddzielnej gałęzi.
  • Podczas żądania mergowania można zobaczyć różnicę w zmianach infrastruktury.

Unikalnym elementem było to, że recenzenci byli wybierani na zmianę, według harmonogramu, to znaczy z pewnym prawdopodobieństwem trafiłeś w nowy obszar infrastruktury.

Styl kodu

Co dowiedziałem się testując 200 000 linii kodu infrastrukturalnego

Z biegiem czasu zaczęły się pojawiać napięcia podczas przeglądów, ponieważ recenzenci mieli swoje style, a rotacja recenzentów sprawiała, że spotykali się z różnymi stylami: 2 spacje lub 4, camelCase czy snake_case. Wdrożenie tego nie było łatwe.

  • Pierwszą myślą było zarekomendowanie użycia lintera, wszak inżynierowie to inteligentni ludzie. Ale różne edytory oraz systemy operacyjne były problematyczne.
  • To ewoluowało w bota, który przy każdym problematycznym commicie pisał na slacku i dołączał wynik lintera. Jednak w większości przypadków znajdowały się ważniejsze zadania i kod pozostawał nieskorygowany.

Green Build Master

Co dowiedziałem się testując 200 000 linii kodu infrastrukturalnego

Czas mija, a doszliśmy do wniosku, że nie można wpuszczać do mastera commitów, które nie przechodzą pewnych testów. Voilà! wynaleźliśmy Green Build Master, który już od dawna jest praktykowany w tworzeniu oprogramowania:

  • Rozwój odbywa się w osobnej gałęzi.
  • W tej gałęzi uruchamiane są testy.
  • Jeśli testy nie przechodzą, kod nie trafi do mastera.

Podjęcie tej decyzji było dość bolesne, ponieważ wywołało wiele sporów, ale było warto, ponieważ na przeglądach zaczęły się pojawiać prośby o scalanie bez różnic w stylu, a z biegiem czasu liczba problematycznych miejsc zaczęła się zmniejszać.

IaC Testing

Co dowiedziałem się testując 200 000 linii kodu infrastrukturalnego

Oprócz sprawdzania stylu można zastosować inne elementy, na przykład sprawdzić, czy Twoja infrastruktura rzeczywiście może się wdrożyć. Lub sprawdzić, czy zmiany w infrastrukturze nie doprowadzą do strat finansowych. Dlaczego może to być potrzebne? To skomplikowane i filozoficzne pytanie, najlepiej odpowiedzieć anegdotą, że kiedyś istniał auto-scaler na Powershell, który nie sprawdzał warunków brzegowych => utworzono więcej VM-ów niż potrzeba => klient wydał więcej pieniędzy, niż planował. To nieprzyjemne, ale ten błąd można było wykryć na wcześniejszych etapach.

Można zapytać, dlaczego skomplikowaną infrastrukturę uczynić jeszcze bardziej skomplikowaną? Testy dla infrastruktury, tak jak dla kodu, nie mają na celu uproszczenia, a raczej uzyskania wiedzy na temat tego, jak Twoja infrastruktura powinna działać.

IaC Testing Pyramid

Co dowiedziałem się testując 200 000 linii kodu infrastrukturalnego

IaC Testing: Analiza statyczna

Jeśli od razu wdrożysz całą infrastrukturę i sprawdzisz, czy działa, może się okazać, że zajmuje to mnóstwo czasu i wymaga dużych zasobów. Dlatego w podstawie powinno być coś, co działa szybko, jest tego dużo i pokrywa wiele podstawowych miejsc.

Bash jest skomplikowany

Rozważmy banalny przykład. Wybierz wszystkie pliki w bieżącym katalogu i skopiuj je w inne miejsce. Pierwsza myśl, która przychodzi do głowy:

for i in * ; do 
    cp $i /some/path/$i.bak
done

A co jeśli w nazwie pliku jest spacja? No dobrze, jesteśmy inteligentni, umiemy używać cudzysłowów:

for i in * ; do cp "$i" "/some/path/$i.bak" ; done

Brawo? Nie! A co jeśli w katalogu nie ma nic, tzn. globbing nie zadziała.

find . -type f -exec mv -v {} dst/{}.bak ;

Teraz brawo? Nie… Zapomnieliśmy, że w nazwie pliku może być n.

touch x
mv x  "$(printf "foonbar")"
find . -type f -print0 | xargs -0 mv -t /path/to/target-dir

Narzędzia do analizy statycznej

Problem z poprzedniego kroku można było wychwycić, gdy zapomnieliśmy o cudzysłowach, i w tym celu istnieje wiele narzędzi Shellcheck, w rzeczywistości jest ich wiele i prawdopodobnie znajdziesz linter dla swojego IDE, odpowiedni do twojego stosu technologicznego.

Język
Narzędzie

bash
Shellcheck

Ruby
RuboCop

python
Pylint

ansible
Ansible Lint

Testowanie IaC: Testy jednostkowe

Co dowiedziałem się testując 200 000 linii kodu infrastrukturalnego

Jak się przekonaliśmy z poprzedniego przykładu, lintery nie są wszechmocne i nie mogą wskazać wszystkich problematycznych miejsc. W tym kontekście warto przypomnieć o testach jednostkowych, które pojawiają się od razu: shunit, junit, rspec, pytest. Ale co zrobić z ansible, chef, saltstack i im podobnymi?

Na początku mówiliśmy o S.O.L.I.D. i tym, że nasza infrastruktura powinna się składać z małych elementów. Nadszedł ich czas.

  1. Infrastruktura jest podzielona na małe elementy, na przykład role Ansible.
  2. Wdraża się jakieś środowisko, czy to docker, czy VM.
  3. Na to testowe środowisko stosujemy naszą rolę Ansible.
  4. Sprawdzamy, czy wszystko działa tak, jak oczekiwaliśmy (uruchamiamy testy).
  5. Decydujemy, czy jest OK, czy nie.

Testowanie IaC: Narzędzia do testowania jednostkowego

Pytanie, co to są testy dla CFM? Można po prostu uruchomić skrypt, ale można też użyć gotowych rozwiązań:

CFM
Narzędzie

Ansible
Testinfra

Chef
Inspec

Chef
Serverspec

saltstack
Goss

Przykład dla testinfra, sprawdzamy, czy użytkownicy test1, test2 istnieją i są w grupie sshusers:

def test_default_users(host):
    users = ['test1', 'test2' ]
    for login in users:
        assert host.user(login).exists
        assert 'sshusers' in host.user(login).groups

Co wybrać? To skomplikowane pytanie, oto przykład zmian w projektach na githubie w latach 2018-2019:

Co dowiedziałem się testując 200 000 linii kodu infrastrukturalnego

Frameworki do testowania IaC

Jak to wszystko zebrać razem i uruchomić? Można po prostu zrobić wszystko samemu przy odpowiedniej liczbie inżynierów. Można też wziąć gotowe rozwiązania, choć nie ma ich zbyt wiele:

CFM
Narzędzie

Ansible
Molecule

Chef
Test Kitchen

Terraform
Terratest

Przykład zmian w projektach na githubie w latach 2018-2019:

Co dowiedziałem się testując 200 000 linii kodu infrastrukturalnego

Molecule vs. Testkitchen

Co dowiedziałem się testując 200 000 linii kodu infrastrukturalnego

Początkowo próbowaliśmy używać testkitchen:

  1. Stworzyć VM równolegle.
  2. Zastosować role Ansible.
  3. Uruchomić inspec.

Dla 25-35 ról zajmowało to 40-70 minut, co było zbyt długo.

Co dowiedziałem się testując 200 000 linii kodu infrastrukturalnego

Kolejnym krokiem było przejście na jenkins / docker / ansible / molecule. Ideologicznie to to samo.

  1. Przeprowadzić lintowanie playbooków.
  2. Przeprowadzić lintowanie ról.
  3. Uruchomić kontener.
  4. Zastosować role Ansible.
  5. Uruchomić testinfra.
  6. Sprawdzić idempotencję.

Co dowiedziałem się testując 200 000 linii kodu infrastrukturalnego

Lintowanie dla 40 ról oraz testy dla kilku zajmowały około 15 minut.

Co dowiedziałem się testując 200 000 linii kodu infrastrukturalnego

Co wybrać, zależy od wielu czynników, takich jak używany stack, ekspertyza w zespole itd. Każdy sam decyduje, jak załatwić kwestie testów jednostkowych.

Testowanie IaC: testy integracyjne.

Co dowiedziałem się testując 200 000 linii kodu infrastrukturalnego

Na następnej warstwie piramidy testowania infrastruktury pojawiają się testy integracyjne. Są podobne do testów jednostkowych:

  1. Infrastruktura dzieli się na małe elementy, na przykład role Ansible.
  2. Wdraża się jakieś środowisko, czy to docker, czy VM.
  3. Na to środowisko testowe stosuje się wiele role Ansible.
  4. Sprawdzamy, czy wszystko działa zgodnie z oczekiwaniami (przeprowadzamy testy).
  5. Decydujemy, czy jest OK, czy nie.

Mówiąc ogólnie, nie sprawdzamy funkcjonalności pojedynczego elementu systemu jak w testach jednostkowych, sprawdzamy, jak serwer jest skonfigurowany w całości.

Testowanie IaC: testy end-to-end.

Co dowiedziałem się testując 200 000 linii kodu infrastrukturalnego

Na szczycie piramidy czekają testy end-to-end. Tzn. nie sprawdzamy funkcjonalności pojedynczego serwera, pojedynczego skryptu, pojedynczego elementu naszej infrastruktury. Sprawdzamy, czy wiele serwerów, połączonych w całość, działa zgodnie z naszymi oczekiwaniami. Niestety, nie miałem okazji zobaczyć gotowych rozwiązań, pewnie dlatego, że infrastruktura jest często unikalna i trudno ją ztemplate'ować oraz stworzyć framework do jej testowania. W efekcie każdy tworzy swoje własne rozwiązania. Istnieje popyt, ale brak odpowiedzi. Dlatego opowiem, co istnieje, aby zainspirować innych do zdrowych myśli lub przekonać mnie, że wszystko zostało wynalezione przed nami.

Co dowiedziałem się testując 200 000 linii kodu infrastrukturalnego

Projekt z bogatą historią. Używany w dużych organizacjach i prawdopodobnie każdy z Was miał z nim do czynienia. Aplikacja obsługuje wiele baz danych, integracji itd. Wiedza o tym, jak może wyglądać infrastruktura, to wiele plików docker-compose, a wiedza o tym, jakie testy uruchomić w jakim środowisku — to jenkins.

Co dowiedziałem się testując 200 000 linii kodu infrastrukturalnego

Ten schemat działał dość długo, dopóki w ramach badań nie spróbowaliśmy przenieść tego do Openshift. Kontenery pozostały te same, a środowisko uruchomieniowe zmieniło się (cześć D.R.Y. znowu).

Co dowiedziałem się testując 200 000 linii kodu infrastrukturalnego

Myśl badawcza poszła dalej i w openshift znalazła się taka rzecz jak APB (Ansible Playbook Bundle), która pozwala zapakować wiedzę na temat rozwoju infrastruktury w kontener. Tzn. istnieje powtarzalny, testowalny punkt wiedzy, jak rozwinąć infrastrukturę.

Co dowiedziałem się testując 200 000 linii kodu infrastrukturalnego

To wszystko brzmiało dobrze, dopóki nie napotkaliśmy na heterogeniczną infrastrukturę: potrzebujemy Windows do testów. W efekcie wiedza o tym, gdzie i jak rozwinąć oraz przetestować, znajduje się w jenkins.

Podsumowanie

Co dowiedziałem się testując 200 000 linii kodu infrastrukturalnego

Infrastructure as Code to

  • Kod w repozytorium.
  • Interakcja międzyludzkie.
  • Testowanie infrastruktury.

linki

Źródło: habr.com

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster