Jak zbudować pełnoprawny rozwój wewnętrzny z wykorzystaniem DevOps - doświadczenie VTB

Praktyki DevOps działają. Sami się o tym przekonaliśmy, gdy 10-krotnie skróciliśmy czas instalacji wersji. W systemie FIS Profile, którego używamy w VTB, instalacja zajmuje teraz 90 minut, a nie 10. Czas tworzenia wersji skrócił się z dwóch tygodni do dwóch dni. Liczba utrzymujących się błędów wdrożeniowych spadła niemal do minimum. Aby uciec od „pracy fizycznej” i wyeliminować zależność od dostawcy, musieliśmy pracować o kulach i znajdować nieoczekiwane rozwiązania. Poniżej cięcia znajduje się szczegółowa historia o tym, jak zbudowaliśmy pełnoprawny rozwój wewnętrzny.

Jak zbudować pełnoprawny rozwój wewnętrzny z wykorzystaniem DevOps - doświadczenie VTB
 

Prolog: DevOps to filozofia

W ciągu ostatniego roku wykonaliśmy wiele pracy, aby zorganizować wewnętrzny rozwój i wdrożenie praktyk DevOps w VTB:

  • Zbudowaliśmy wewnętrzne procesy rozwojowe dla 12 systemów;
  • Uruchomiliśmy 15 rurociągów, z czego cztery zostały wprowadzone do produkcji;
  • Zautomatyzowane scenariusze testowe 1445;
  • Z sukcesem wdrożyliśmy szereg wydań przygotowanych przez wewnętrzne zespoły.

Jednym z najtrudniejszych do zorganizowania wewnętrznie rozwoju i wdrożenia praktyk DevSecOps okazał się system FIS Profile – procesor produktów detalicznych na nierelacyjnym systemie DBMS. Niemniej jednak udało nam się zbudować rozwój, uruchomić potok, zainstalować na produkcie indywidualne pakiety inne niż wydanie i nauczyć się montować wydania. Zadanie nie było łatwe, ale ciekawe i pozbawione oczywistych ograniczeń w realizacji: oto system - trzeba zbudować własne rozwinięcie. Jedynym warunkiem jest użycie płyty CD przed środowiskiem produktywnym.

Algorytm implementacji wydawał się początkowo prosty i przejrzysty:

  • Rozwijamy wstępną wiedzę specjalistyczną w zakresie rozwoju i osiągamy akceptowalny poziom jakości od zespołu programistycznego bez rażących wad;
  • W miarę możliwości integrujemy się z istniejącymi procesami;
  • Aby przenieść kod pomiędzy oczywistymi etapami, przecinamy potok i wpychamy jeden z jego końców w kontynuację.

W tym czasie zespół programistów odpowiedniej wielkości musi rozwinąć umiejętności i zwiększyć udział swojego wkładu w wydania do akceptowalnego poziomu. I tyle, możemy uznać zadanie za zakończone.

Wydawać by się mogło, że jest to całkowicie energooszczędna droga do wymaganego rezultatu: tu jest DevOps, tu są wskaźniki wydajności zespołu, tu jest zgromadzona wiedza specjalistyczna... Jednak w praktyce otrzymaliśmy kolejne potwierdzenie, że DevOps to wciąż filozofia , a nie „dołączony do procesu gitlab, ansible, nexus i dalej na liście”.

Po ponownej analizie planu działania zdaliśmy sobie sprawę, że budujemy w sobie pewnego rodzaju dostawcę zewnętrznego. Dlatego do opisanego powyżej algorytmu dodano reengineering procesów, a także rozwój wiedzy specjalistycznej na całej drodze rozwoju, aby osiągnąć wiodącą rolę w tym procesie. Nie jest to najłatwiejsza opcja, ale jest to droga ideologicznie prawidłowego rozwoju.
 

Gdzie zaczyna się rozwój wewnętrzny? 

Nie był to najbardziej przyjazny system do pracy. Architektonicznie był to jeden duży, nierelacyjny system DBMS, składający się z wielu oddzielnych obiektów wykonywalnych (skryptów, procedur, wsadów itp.), które wywoływano w miarę potrzeb i działał na zasadzie czarnej skrzynki: odbiera żądanie i wydaje odpowiedź. Inne trudności, na które warto zwrócić uwagę, to:

  • Język egzotyczny (ŚWINKA);
  • Interfejs konsoli;
  • Brak integracji z popularnymi narzędziami i frameworkami automatyzującymi;
  • Ilość danych w dziesiątkach terabajtów;
  • Obciążenie ponad 2 milionów operacji na godzinę;
  • Znaczenie — krytyczne dla biznesu.

Jednocześnie po naszej stronie nie było repozytorium kodu źródłowego. W ogóle. Była dokumentacja, ale cała kluczowa wiedza i kompetencje znajdowały się po stronie organizacji zewnętrznej.
Zaczęliśmy doskonalić rozwój systemu niemal od zera, biorąc pod uwagę jego możliwości i niską dystrybucję. Rozpoczęło się w październiku 2018 r.:

  • Przestudiowałem dokumentację i podstawy generowania kodu;
  • Przestudiowaliśmy krótki kurs rozwoju otrzymany od dostawcy;
  • Opanowane umiejętności początkowego rozwoju;
  • Opracowaliśmy podręcznik szkoleniowy dla nowych członków zespołu;
  • Zgodziliśmy się włączyć drużynę w tryb „bojowy”;
  • Rozwiązano problem z kontrolą jakości kodu;
  • Zorganizowaliśmy stoisko rozwojowe.

Spędziliśmy trzy miesiące na rozwijaniu wiedzy specjalistycznej i zanurzaniu się w systemie, a od początku 2019 roku wewnętrzny rozwój rozpoczął swój marsz w kierunku świetlanej przyszłości, czasem z trudem, ale pewnie i celowo.

Migracja repozytorium i autotesty

Pierwszym zadaniem DevOps jest repozytorium. Szybko zgodziliśmy się na udostępnienie dostępu, jednak konieczna była migracja z obecnego SVN z jedną gałęzią trunk do naszego docelowego Gita wraz z przejściem na model kilku gałęzi i rozwojem Git Flow. Posiadamy również 2 zespoły z własną infrastrukturą oraz część zespołu dostawcy za granicą. Musiałem żyć z dwoma Gitami i zapewnić synchronizację. W takiej sytuacji było to mniejsze zło.

Migracja repozytorium była wielokrotnie odkładana i zakończyła się dopiero w kwietniu, dzięki pomocy kolegów z pierwszej linii frontu. W przypadku Git Flow postanowiliśmy na początek uprościć wszystko i zdecydowaliśmy się na klasyczny schemat z poprawką, rozwojem i wydaniem. Postanowili porzucić mistrza (znanego również jako prod). Poniżej wyjaśnimy dlaczego ta opcja okazała się dla nas optymalna. Jako worker wykorzystano zewnętrzne repozytorium należące do dostawcy, wspólne dla dwóch zespołów. Synchronizował się z wewnętrznym repozytorium zgodnie z harmonogramem. Teraz dzięki Git i Gitlab możliwa była automatyzacja procesów.

Zagadkę autotestów rozwiązano zaskakująco łatwo – otrzymaliśmy gotowy framework. Biorąc pod uwagę specyfikę systemu, wywołanie osobnej operacji było zrozumiałą częścią procesu biznesowego i jednocześnie służyło jako test jednostkowy. Pozostało jedynie przygotować dane testowe i ustalić pożądaną kolejność wywoływania skryptów i oceny wyników. W miarę uzupełniania się listy scenariuszy, utworzonej na podstawie statystyki operacyjnej, krytyczności procesów i dotychczasowej metodologii regresji, zaczęły pojawiać się automatyczne testy. Teraz moglibyśmy rozpocząć budowę rurociągu.

Jak to było: model przed automatyzacją

Istniejący model procesu wdrożeniowego to osobna historia. Każda modyfikacja została przeniesiona ręcznie jako oddzielny, przyrostowy pakiet instalacyjny. Następnie przyszła kolej na ręczną rejestrację w Jira i ręczną instalację na środowiskach. W przypadku poszczególnych pakietów wszystko wydawało się jasne, jednak wraz z przygotowaniem wydania sprawy się skomplikowały.

Montaż odbywał się na poziomie pojedynczych dostaw, które stanowiły niezależne obiekty. Każda zmiana to nowa dostawa. Do 60-70 pakietów głównego składu wydania dodano między innymi 10–15 wersji technicznych – wersje uzyskane w wyniku dodania lub wykluczenia czegoś z wydania i odzwierciedlające zmiany w sprzedaży poza wydaniami.

Obiekty w ramach dostaw nakładały się na siebie, szczególnie w kodzie wykonywalnym, który był unikalny w mniej niż połowie. Zależności było wiele zarówno na już zainstalowanym kodzie, jak i na tym, którego instalacja była właśnie planowana. 

Aby uzyskać wymaganą wersję kodu, należało ściśle przestrzegać kolejności instalacji, podczas której obiekty były wielokrotnie przepisywane fizycznie, około 10–12 razy.

Po zainstalowaniu partii pakietów musiałem ręcznie postępować zgodnie z instrukcjami, aby zainicjować ustawienia. Wersja została zmontowana i zainstalowana przez dostawcę. Skład wydania został doprecyzowany niemal przed momentem wdrożenia, co wiązało się ze stworzeniem pakietów „odsprzęgających”. W rezultacie znaczna część dostaw przemieszczała się z wydania na wydanie z własnym ogonem „oddzielenia”.

Teraz jest jasne, że przy takim podejściu – składaniu łamigłówki na poziomie pakietu – pojedyncza gałąź główna nie miała praktycznego znaczenia. Instalacja na produkcji trwała od półtorej do dwóch godzin pracy fizycznej. Dobrze, że przynajmniej na poziomie instalatora została określona kolejność przetwarzania obiektów: pola i struktury zostały wprowadzone przed danymi do nich i procedurami. Jednak działało to tylko w oddzielnym pakiecie.

Logicznym skutkiem takiego podejścia były obowiązkowe defekty instalacyjne w postaci krzywych wersji obiektów, niepotrzebnego kodu, brakujących instrukcji i nieuwzględnionych wzajemnych wpływów obiektów, które po wydaniu gorączkowo eliminowano. 

Pierwsze aktualizacje: zatwierdzenie montażu i dostawy

Automatyzacja rozpoczęła się od przesłania kodu potokiem następującą trasą:

  • Odbierz gotową dostawę z magazynu;
  • Zainstaluj go w dedykowanym środowisku;
  • Uruchom autotesty;
  • Oceń wynik instalacji;
  • Wywołaj następujący potok po stronie polecenia testowania.

Kolejny potok powinien zarejestrować zadanie w Jira i poczekać na rozesłanie poleceń do wybranych pętli testowych, od których zależy czas realizacji zadania. Trigger - pismo o gotowości do dostarczenia pod wskazany adres. To oczywiście była oczywista pomoc, ale od czegoś trzeba było zacząć. W maju 2019 transfer kodu rozpoczął się od sprawdzenia naszych środowisk. Proces się rozpoczął, pozostaje tylko doprowadzić go do przyzwoitego kształtu:

  • Każda modyfikacja wykonywana jest w osobnej gałęzi, która odpowiada pakietowi instalacyjnemu i łączy się z docelową gałęzią główną;
  • Wyzwalaczem uruchomienia potoku jest pojawienie się nowego zatwierdzenia w gałęzi głównej poprzez żądanie połączenia, które jest zamykane przez opiekunów z wewnętrznego zespołu;
  • Repozytoria są synchronizowane raz na pięć minut;
  • Rozpoczyna się montaż pakietu instalacyjnego - przy pomocy asemblera otrzymanego od sprzedawcy.

Następnie istniały już kroki mające na celu sprawdzenie i przesłanie kodu, uruchomienie rury i montaż po naszej stronie.

Opcja ta została uruchomiona w lipcu. Trudności związane z przejściem spowodowały pewne niezadowolenie wśród dostawcy i na linii frontu, ale w ciągu następnego miesiąca udało nam się usunąć wszystkie ostre krawędzie i ustalić proces między zespołami. Mamy teraz montaż poprzez zatwierdzenie i dostawę.
W sierpniu udało nam się zakończyć pierwszą instalację osobnego pakietu na produkcji przy użyciu naszego potoku, a od września bez wyjątku wszystkie instalacje poszczególnych pakietów niepublikowanych wcześniej odbywały się za pośrednictwem naszego narzędzia CD. Dodatkowo udało nam się osiągnąć udział zadań wewnętrznych w 40% składu wydania przy mniejszym zespole niż sprzedawca - to zdecydowany sukces. Pozostało najpoważniejsze zadanie - zmontowanie i zainstalowanie wydania.

Ostateczne rozwiązanie: zbiorcze pakiety instalacyjne 

Doskonale rozumieliśmy, że pisanie skryptów według instrukcji dostawcy to taka automatyzacja i musieliśmy przemyśleć sam proces. Rozwiązanie było oczywiste - zebrać zbiorczą dostawę z gałęzi wydania ze wszystkimi obiektami wymaganych wersji.

Zaczęliśmy od sprawdzenia koncepcji: ręcznie zmontowaliśmy pakiet wersji zgodnie z zawartością poprzedniej implementacji i zainstalowaliśmy go w naszych środowiskach. Wszystko się udało, koncepcja okazała się realna. Następnie rozwiązaliśmy problem skryptowania ustawień inicjalizacji i włączania ich do zatwierdzenia. Przygotowaliśmy nowy pakiet i przetestowaliśmy go w środowiskach testowych w ramach aktualizacji konturu. Instalacja przebiegła pomyślnie, aczkolwiek ze strony zespołu wdrożeniowego pojawiło się wiele komentarzy. Ale najważniejsze jest to, że otrzymaliśmy zgodę na rozpoczęcie produkcji w listopadowej wersji wraz z naszym zespołem.

Gdy pozostał nieco ponad miesiąc, ręcznie wybrane materiały wyraźnie sugerowały, że czas ucieka. Zdecydowali się na kompilację z gałęzi wydania, ale dlaczego miałaby być oddzielona? Nie mamy wersji Prod, a istniejące gałęzie nie są dobre – jest dużo niepotrzebnego kodu. Musimy pilnie ograniczyć liczbę polubień, a to oznacza ponad trzy tysiące zatwierdzeń. Ręczny montaż w ogóle nie wchodzi w grę. Naszkicowaliśmy skrypt, który uruchamia dziennik instalacji produktu i zbiera zatwierdzenia w gałęzi. Za trzecim razem zadziałało poprawnie i po „skończeniu z plikiem” gałąź była gotowa. 

Napisaliśmy własnego kreatora pakietu instalacyjnego i ukończyliśmy go w ciągu tygodnia. Następnie musieliśmy zmodyfikować instalator z podstawowej funkcjonalności systemu, ponieważ jest to oprogramowanie typu open source. Po serii kontroli i modyfikacji wynik uznano za udany. W międzyczasie ukształtował się skład wydania, dla którego prawidłowej instalacji konieczne było zrównanie obwodu testowego z produkcyjnym, i napisano do tego osobny skrypt.

Oczywiście było wiele komentarzy na temat pierwszej instalacji, ale ogólnie kod zadziałał. I po mniej więcej trzeciej instalacji wszystko zaczęło wyglądać dobrze. Kontrolę składu i kontrolę wersji obiektów monitorowano oddzielnie w trybie ręcznym, co na tym etapie było w pełni uzasadnione.

Dodatkowym wyzwaniem była duża liczba niewydań, które należało wziąć pod uwagę. Ale dzięki gałęzi podobnej do Prod i Rebase zadanie stało się przejrzyste.

Pierwszy raz, szybko i bez błędów

Podeszliśmy do wydania z optymistycznym nastawieniem i kilkunastu udanych instalacji na różnych obwodach. Jednak dosłownie dzień przed terminem okazało się, że sprzedawca nie dokończył prac nad przygotowaniem wydania do instalacji w przyjęty sposób. Jeśli z jakiegoś powodu nasza kompilacja nie będzie działać, wydanie zostanie zakłócone. Co więcej, poprzez nasze wysiłki, co jest szczególnie nieprzyjemne. Nie mieliśmy jak się wycofać. Dlatego przemyśleliśmy alternatywne opcje, przygotowaliśmy plany działania i rozpoczęliśmy montaż.

Co zaskakujące, całe wydanie, składające się z ponad 800 obiektów, rozpoczęło się poprawnie, za pierwszym razem iw zaledwie 10 minut. Spędziliśmy godzinę sprawdzając logi w poszukiwaniu błędów, ale żadnego nie znaleźliśmy.

Przez cały następny dzień na czacie dotyczącym wydania panowała cisza: żadnych problemów z implementacją, krzywych wersji czy „nieodpowiedniego” kodu. To było nawet w jakiś sposób niezręczne. Później pojawiły się pewne uwagi, jednak w porównaniu z innymi systemami i wcześniejszymi doświadczeniami ich liczba i priorytet były zauważalnie mniejsze.

Dodatkowym efektem efektu skumulowanego był wzrost jakości montażu i testowania. Ze względu na wielokrotne instalacje pełnej wersji, defekty kompilacji i błędy wdrożeniowe zostały zidentyfikowane w odpowiednim czasie. Testowanie w konfiguracjach pełnego wydania pozwoliło dodatkowo zidentyfikować defekty we wzajemnym oddziaływaniu obiektów, które nie pojawiły się podczas instalacji przyrostowych. Zdecydowanie był to sukces, szczególnie biorąc pod uwagę nasz 57% wkład w wydanie.

Wyniki i wnioski

W niecały rok udało nam się:

  • Zbuduj pełnoprawny rozwój wewnętrzny, korzystając z egzotycznego systemu;
  • Wyeliminuj krytyczną zależność od dostawcy;
  • Uruchom CI/CD dla bardzo nieprzyjaznego dziedzictwa;
  • Podnieś procesy wdrożeniowe na nowy poziom techniczny;
  • Znacząco skrócić czas wdrożenia;
  • Znacząco zmniejszyć liczbę błędów wdrożeniowych;
  • Z całą pewnością deklaruj się jako wiodący ekspert ds. rozwoju.

Oczywiście wiele z tego, co opisano, wygląda jak bzdury, ale takie są cechy systemu i istniejące w nim ograniczenia procesów. Na chwilę obecną zmiany dotyczyły produktów i usług IS Profile (konta główne, karty plastikowe, rachunki oszczędnościowe, depozyty, pożyczki gotówkowe), ale potencjalnie podejście to można zastosować w przypadku każdego IS, dla którego postawiono zadanie wdrożenia praktyk DevOps. Model skumulowany można bezpiecznie replikować dla kolejnych wdrożeń (w tym niepublikowanych) z wielu dostaw.

Źródło: www.habr.com

Dodaj komentarz