Rozwijaliśmy DevOps, jak mogliśmy. Było nas 8 osób, a Wasya był najlepszy w Windows. Nagle Wasya odszedł, a ja musiałem zająć się nowym projektem, który dostarczałby rozwój Windows. Kiedy wyłożyłem na stół cały stos technologii Windows, zrozumiałem, że sytuacja to ból…
Tak zaczyna się historia Aleksandr Sinchinov na . Kiedy z firmy odszedł główny specjalista od Windows, Aleksandr zadał sobie pytanie, co teraz robić. Oczywiście przejść na Linux! Aleksandr opowie, jak udało mu się stworzyć precedens i przenieść część rozwoju Windows na Linux na przykładzie zrealizowanego projektu dla 100 000 użytkowników końcowych.

Jak łatwo i niewymuszenie dostarczyć projekt w RPM, używając TFS, Puppet, Linux .NET core? Jak utrzymać wersjonowanie BD projektu, jeśli zespół deweloperski po raz pierwszy słyszy słowa Postgres i Flyway, a deadline jest na pojutrze? Jak zintegrować z Dockerem? Jak zmotywować programistów .NET do porzucenia Windows i smoothies na rzecz Puppet i Linux? Jak rozwiązywać konflikty ideologiczne, gdy nie ma siły, woli ani zasobów do obsługi Windows w produkcji? O tym, oraz o Web Deploy, testowaniu, CI, praktykach używania TFS w istniejących projektach, a także o złamanych kościach i działających rozwiązaniach w transkrypcji wykładu Aleksandra.

Więc Wasya odszedł, zadanie spadło na mnie, programiści czekają z niecierpliwością. Kiedy w końcu zrozumiałem, że nie mogę już przywrócić Wasya - zabrałem się do pracy. Na początek oceniłem procent Win VM w naszym parku. Wynik nie był na korzyść Windows.

Ponieważ aktywnie rozwijamy DevOps, zrozumiałem, że musimy zmienić podejście do wydania nowej aplikacji. Jedynym rozwiązaniem było przenieść wszystko na Linux, jeśli to możliwe. Google mi pomógł - w tamtym czasie .Net był już portowany na Linux, więc zrozumiałem, że to jest rozwiązanie!
Dlaczego .NET core w połączeniu z Linux?
Na to było kilka powodów. Większość ludzi wybierze opcję „nie płacić”, zamiast „płacić” - tak jak ja. Licencja na MSDB kosztuje około 1000 $, a koszty utrzymania parku wirtualnych maszyn Windows liczone są w setkach dolarów. Dla dużej firmy to ogromne wydatki. Dlatego oszczędność — to pierwszy powód. Nie jest to najważniejsze, ale jeden z istotnych.
Maszyny wirtualne Windows zajmują więcej zasobów niż ich odpowiedniki w Linuxie - są ciężkie. Biorąc pod uwagę skalę dużej firmy, wybraliśmy Linux.
System wbudowuje się w istniejący CI. Uważamy się za nowoczesnych DevOps, używamy Bamboo, Jenkins i GitLab CI, dlatego większość działa na Linuxie.
Ostatni powód to wygodne wsparcie. Musieliśmy obniżyć próg wejścia dla „wsparcia” – ludzi, którzy rozumieją część techniczną, zapewniają ciągłość i obsługują usługi na drugim poziomie. Byli już zaznajomieni z ekosystemem Linux, więc łatwiej im zrozumieć nowy produkt, wspierać i go obsługiwać zamiast inwestować dodatkowe zasoby, aby zrozumieć analogiczny funkcjonalności oprogramowania dla platform Windows.
Wymagania
Pierwsze i najważniejsze to wygoda nowego rozwiązania dla programistów. Nie wszyscy byli gotowi na zmiany, zwłaszcza po wyrażonym słowie Linux. Programiści chcą swojej ulubionej Visual Studio, TFS z automatycznymi testami dla kompilacji i smoothiem. Jak odbywa się dostawa do produkcji - dla nich to nie ma znaczenia. Dlatego zdecydowaliśmy się nie zmieniać ustalonego procesu i pozostawić wszystko bez zmian dla programistów Windows.
Nowy projekt musi być wbudowany w istniejący CI. Rails już były i całą pracę należało wykonać z uwzględnieniem parametrów systemu zarządzania konfiguracją, przyjętych standardów dostarczania i systemów monitorowania.
Prostota w utrzymaniu i eksploatacji, jako warunek minimalnego progu wejścia dla wszystkich nowych uczestników z różnych działów i zespołu wsparcia.
Termin - wczoraj.
Zespół Win development
Czym wtedy zajmował się zespół Windows?

Teraz mogę spokojnie powiedzieć, że IdentityServer4 to świetna darmowa alternatywa dla ADFS z podobnymi możliwościami, czy że Entity Framework Core to raj dla programisty, gdzie nie trzeba się martwić pisaniem skryptów SQL, ale można opisywać zapytania w bazie danych w terminach OOP. Ale wtedy, podczas omawiania planu działania, patrzyłem na ten stos jak na sumeryjski klin, znając jedynie PostgreSQL i Git.
W tamtym czasie aktywnie używaliśmy Puppet jako systemu zarządzania konfiguracją. W większości naszych projektów stosowaliśmy GitLab CI, Elastic, zbalansowaliśmy wysoko obciążone usługi przy pomocy HAProxy, monitorowaliśmy wszystko przy pomocy Zabbix, zestawu Grafana i Prometheus, Jaeger, i to wszystko działało na sprzęcie HP c ESXi. na VMwareWszystkim znane - klasyka gatunku.

Zobaczmy i spróbujmy zrozumieć, co działo się przed tym, jak zaczęliśmy wszystkie te interwencje.
Co było
TFS to dość potężny system, który nie tylko dostarcza kod od programisty do finalnej maszyny produkcyjnej, ale również posiada zestaw do bardzo elastycznej integracji z różnymi usługami - w celu zapewnienia CI na poziomie wieloplatformowym.

Dawniej były to jedynie okna. TFS korzystał z kilku agentów Build, na których zbierano wiele projektów. W każdym agencie było po 3-4 workerów, aby zrównoleglić zadania i zoptymalizować proces. Następnie, zgodnie z planami wydania, TFS dostarczał świeżo upieczony Build na serwerze aplikacji Windows.
Do czego chcieliśmy dojść
Do dostarczania i rozwijania używamy TFS, uruchamiamy aplikację na serwerze aplikacji Linux, a między nimi dzieje się jakaś magia. Ta Magiczna skrzynka jest istotą nadchodzącej pracy. Zanim rozłożę ją na części, zrobię krok w bok i powiem kilka słów o aplikacji.
Projekt
Aplikacja zapewnia funkcjonalność do operowania kartami przedpłaconymi.

Client
Istniały dwa typy użytkowników. Pierwszy uzyskiwał dostęp, autoryzując się certyfikatem SSL SHA-2. Drugi posiadał dostęp za pomocą loginu i hasła. Następnie zapytanie klienta trafiało do HAProxy, które rozwiązywało następujące zadania:
HAProxy
podstawowa autoryzacja;
- terminowanie SSL;
- dostosowywanie zapytań HTTP;
- translacja zapytań.
- Weryfikacja certyfikatu klienta odbywała się w oparciu o łańcuch. My jesteśmy
authority i możemy sobie na to pozwolić, ponieważ sami wydajemy certyfikaty klientom usługi. Zwróć uwagę na trzeci punkt, wrócimy do niego za chwilę.
Backend planowano zrealizować na Linuxie. Backend współpracuje z bazą danych, ładował niezbędną listę uprawnień, a następnie, w zależności od tego, jakie uprawnienia posiada autoryzujący się użytkownik, zapewniał dostęp do podpisywania dokumentów finansowych i ich wysyłania, bądź generowania jakiegoś raportu.
Backend
Oszczędności z HAProxy
Oprócz dwóch kontekstów, w ramach których poruszał się każdy z klientów, istniał jeszcze kontekst identity.
który umożliwia autoryzację, to bezpłatny i potężny odpowiednik dla IdentityServer4 ADFS Active Directory Federation Services — Zapytanie w identity było przetwarzane w kilku krokach. Pierwszy krok -.
trafiało do backendu 客户端 trafiać do backendu, który wymieniał dane z tym serwerem i sprawdzał obecność tokenu dla klienta. Jeśli go nie znajdował, — zapytanie wracało do kontekstu, z którego przyszło, ale już z przekierowaniem, i z przekierowaniem kierowało na identity.
Drugi krok — zapytanie trafiało na stronę logowania w IdentityServer, gdzie klient się rejestrował, a w bazie danych IdentityServer pojawiał się ten długo wyczekiwany token.
Trzeci krok — klient był przekierowywany z powrotem na kontekst, z którego przyszedł.

IdentityServer4 ma pewną właściwość: odpowiedź na zapytanie zwrotne zwraca przez HTTP. Jak bardzo staraliśmy się z konfiguracją serwera, jak bardzo przestudiowaliśmy dokumentację, to za każdym razem otrzymywaliśmy początkowe zapytanie klienta z URL, który przyszedł przez HTTPS, a IdentityServer zwracał ten sam kontekst, ale przez HTTP. Byliśmy w szoku! I przekierowaliśmy to wszędzie przez kontekst identity na HAProxy, a w nagłówkach musieliśmy zmodyfikować protokół HTTP na HTTPS.
Gdzie tkwi poprawa i gdzie zaoszczędziliśmy?
Zaoszczędziliśmy pieniądze, korzystając z bezpłatnego rozwiązania do autoryzacji grupy użytkowników, zasoby, ponieważ nie wyodrębnialiśmy IdentityServer4 jako osobnej nody w oddzielnym segmencie, lecz używaliśmy go wspólnie z backendem na tym samym serwerze, gdzie działa backend aplikacji.
Jak powinno działać
A zatem, jak obiecałem — Magic Box. Już rozumiemy, że gwarantowanie poruszamy się w kierunku Linux. Formułujmy konkretne zadania, które wymagały rozwiązania.

Manifesty Puppet. Aby dostarczać i zarządzać konfiguracją usługi i aplikacji, trzeba było napisać świetne przepisy. Rolka z ołówkiem wymownie pokazuje, jak szybko i jakościowo to było zrobione.
Sposób dostawy. Standard — to RPM. Wszyscy rozumieją, że w Linuxie bez niego się nie obejdzie, ale sam projekt po zbiórce stanowił zestaw wykonywalnych plików DLL. Było ich około 150, projekt był dosyć ciężki. Jedynym harmonijnym rozwiązaniem było spakowanie tej binariów w RPM i już z niej wdrażanie aplikacji.
Wersjonowanie. Musieliśmy często wydawać nowe wersje, dlatego musieliśmy zdecydować, jak formować nazwę pakietu. To jest kwestia integracji z TFS. Nasz agent budowy działał na Linuxie. Kiedy TFS wysyła zadanie do wykonawcy - worker - na agenta budowy, przekazuje również zestaw zmiennych, które trafiają do środowiska procesu wykonawczego. W tych zmiennych przekazywana jest nazwa budowy, nazwa wersji i inne zmienne. Więcej informacji w sekcji „budowanie pakietu RPM”.
Konfiguracja TFS polegała na ustawieniu Pipeline. Wcześniej zbieraliśmy wszystkie projekty Windows na agentach Windows, a teraz pojawia się agent Linux - agent budowy, który należy włączyć do grupy budowy, wzbogacić o jakieś artefakty i określić, jakie projekty będą zbierane na tym agencie budowy oraz jak zmodyfikować Pipeline.
IdentityServer. ADFS nie jest dla nas, wspieramy Open Source.
Przejdźmy do komponentów.
Magiczna skrzynka
Składa się z czterech części.

Agent budowy Linux. Linux, ponieważ to dla niego zbieramy - logiczne. Ta część była wykonywana w trzech krokach.
- Skonfigurować workerów i nie tylko jednego, ponieważ przewidywano rozproszoną pracę nad projektem.
- Zainstalować .NET Core 1.x. Dlaczego 1.x, mimo że już dostępna jest 2.0 w standardowym repozytorium? Ponieważ, gdy zaczynaliśmy rozwój, stabilną wersją była 1.09, a projekt postanowiono zrobić pod nią.
- Git 2.x.
RPM-repozytorium. Pakiety RPM musiały być gdzieś przechowywane. Zakładano, że będziemy korzystać z tej samej korporacyjnej bazy danych RPM, która jest dostępna dla wszystkich hostów Linux. Tak właśnie zrobiliśmy. Na serwerze repozytorium ustawiono webhook który pobierał wymagany pakiet RPM z danego miejsca. Wersję pakietu przekazywał agent budowy do webhooka.
GitLab. Uwaga! GitLab jest używany nie przez programistów, a przez dział eksploatacji do kontroli wersji aplikacji, wersji pakietów, monitorowania stanu wszystkich maszyn Linux, a przechowuje w nim recepturę - wszystkie manifesty Puppet.
Puppet — rozwiązując wszystkie sporne kwestie i dostarczając dokładnie tę konfigurację, której chcemy, z Gitlab.
Zaczynamy zanurzenie. Jak odbywa się dostawa DLL w RPM?
Dostawa DLL w RPM
Załóżmy, że mamy gwiazdę rocka programowania w .NET. Używa Visual Studio i tworzy gałąź wydania. Następnie ładuje ją do Gita, a Git w tym przypadku jest bytem TFS, czyli to jest repozytorium aplikacji, z którym pracuje programista.

Po czym TFS widzi, że nadeszło nowe zatwierdzenie. Jaką aplikację? W ustawieniach TFS znajduje się etykieta, jakimi zasobami dysponuje dany agent kompilacji. W tym przypadku widzi, że kompilujemy projekt .NET Core i wybiera agenta kompilacji Linux z puli.
Agent kompilacji otrzymuje źródła, pobiera niezbędne dependencies z repozytorium .NET, npm itd. i po skompilowaniu samej aplikacji oraz następnej paczki wysyła paczkę RPM do repozytorium RPM.
Z drugiej strony dzieje się następujące. Inżynier działu eksploatacji zajmuje się bezpośrednim wprowadzeniem projektu: zmienia wersje pakietów w Hiera w repozytorium, gdzie przechowywana jest receptura aplikacji, po czym Puppet wyzwala Yum, pobiera nowy pakiet z repozytorium, a nowa wersja aplikacji jest gotowa do użycia.

Na słowach wszystko jest proste, ale co dzieje się wewnątrz samego agenta kompilacji?
Pakowanie DLL RPM
Otrzymano źródła projektu i zadanie na kompilację od TFS. Agent kompilacji uruchamia kompilację samego projektu z źródeł.Skompilowany projekt jest dostępny w postaci wielu plików DLL, które są pakowane w archiwum zip, aby zmniejszyć obciążenie systemu plików.
Archiwum ZIP jest umieszczane w katalogu kompilacji paczki RPM. Następnie skrypt Bash inicjalizuje zmienne środowiskowe, znajduje wersję Build, wersję projektu, ścieżkę do katalogu kompilacji i uruchamia RPM-build. Po zakończeniu kompilacji paczka jest publikowana w lokalnym repozytorium, które znajduje się na agencie kompilacji.
Następnie, z agenta kompilacji na serwer do repozytoriów RPM wysyłane jest żądanie JSON z określeniem nazwy wersji i numeru kompilacji. Webhook, o którym wcześniej mówiłem, pobiera ten pakiet z lokalnego repozytorium na agencie kompilacji i udostępnia nową kompilację do zainstalowania.

Dlaczego właśnie taki schemat dostarczania pakietu do repozytorium RPM? Dlaczego nie można od razu wysłać skompilowanego pakietu do repozytorium? Chodzi o to, że jest to warunek dla zapewnienia bezpieczeństwa. Taki scenariusz ogranicza możliwość nieautoryzowanego przesyłania pakietów RPM przez osoby trzecie na serwer, który jest dostępny dla wszystkich maszyn Linux.
Wersjonowanie Bazy Danych
Na konsylium z zespołem deweloperskim okazało się, że zespołowi bliżej do MS SQL, ale w większości projektów non-Windows już w pełni używaliśmy PostgreSQL. Ponieważ już postanowiliśmy zrezygnować ze wszystkiego, co płatne, zaczęliśmy używać PostgreSQL i tutaj.

W tej części chcę opowiedzieć, jak przeprowadzaliśmy wersjonowanie bazy danych i jak wybieraliśmy między Flyway a Entity Framework Core. Przyjrzymy się ich zaletom i wadom.
Minusy
Flyway działa tylko w jedną stronę, nie możemy cofnąć się wstecz — to istotna wada. Porównując z Entity Framework Core, można to ocenić pod względem wygody programisty. Pamiętacie, że to postawiliśmy na pierwszym miejscu i głównym kryterium było nie wprowadzać żadnych zmian w programowaniu dla Windows.
Do Flyway potrzebowaliśmy jakiegoś opakowania, aby chłopaki nie pisali,. Dla nich znacznie bardziej naturalne jest operowanie w terminach OOP. Przygotowaliśmy instrukcje dotyczące pracy z obiektami bazy danych, powstało zapytanie SQL i zostało wykonane. Nowa wersja bazy danych gotowa, przeszła testy — wszystko dobrze, wszystko działa. zapytania SQLEntity Framework Core ma wadę — przy dużych obciążeniach generuje
nieoptymalne zapytania SQL, a spadek wydajności bazy danych może być znaczny. Ale ponieważ nasza usługa nie jest obciążona, nie mierzymy obciążenia setkami RPS, zaakceptowaliśmy te ryzyka i przekazaliśmy problemy przyszłym nam.działa od razu i jest wygodny dla programistów,
Zalety
Entity Framework Core a Flywayłatwo integruje się z istniejącym CI. Ale robimy, aby to było wygodne dla deweloperów :)Procedura wdrożenia
Puppet widzi, że przychodzi zmiana wersji pakietów, wśród których jest ten odpowiedzialny za migrację. Najpierw instaluje pakiet, w którym znajdują się skrypty migracyjne i funkcjonalność związana z bazą danych. Następnie aplikacja, która pracuje z bazą danych, jest restartowana. Kolejne pakiety są instalowane po tym kroku. Kolejność instalacji pakietów i uruchamiania aplikacji jest opisana w manifeście Puppet.
Aplikacje wykorzystują dane wrażliwe, takie jak tokeny, hasła do bazy danych, wszystko to jest pobierane w konfiguracji z Puppet master, gdzie są przechowywane w zaszyfrowanej formie.
Problemy z TFS
Po tym, jak ustaliliśmy i zrozumieliśmy, że naprawdę wszystko działa, postanowiłem sprawdzić, co dzieje się ze składkami w TFS w ogóle dla działu Win-rozwoju w innych projektach — czy szybko się budujemy/wydajemy, i odkryłem istotne problemy z prędkością.
Jeden z głównych projektów buduje się 12-15 minut — to długo, tak żyć nie można. Szybka analiza wykazała ogromny spadek wydajności I/O, a to na macierzach.
Analizując składnikami, wyróżniłem trzy ogniska. Pierwsze —
«Kaspersky antivirus» «Antywirus Kaspersky», który skanuje źródła na wszystkich agentach Windows Build. Drugi - Windows Indeksator. Nie został wyłączony, a na agentach Build wszystko było indeksowane w czasie rzeczywistym podczas procesu wdrażania.
Trzeci - Npm install. Okazało się, że w większości Pipelines używaliśmy dokładnie tego skryptu. Co w nim jest złego? Procedura Npm install uruchamia się podczas tworzenia drzewa zależności w package-lock.json, gdzie są zapisywane wersje pakietów, które będą użyte do budowy projektu. Minusem jest to, że Npm install za każdym razem pobiera aktualne wersje pakietów z internetu, co zajmuje sporo czasu w przypadku dużego projektu.
Programiści czasami eksperymentują na lokalnej maszynie, aby sprawdzić działanie pojedynczej części lub całego projektu. Czasami zdarzało się, że lokalnie wszystko działało świetnie, ale podczas budowy, wdrażania - nic nie działało. Zaczynamy się zastanawiać, gdzie leży problem - aha, różne wersje pakietów z zależnościami.
Rozwiązanie
- Źródła w wykluczeniach AV.
- Wyłączenie indeksacji.
- Przejście na npm ci.
Zalety npm ci są takie, że budujemy drzewo zależności tylko raz, i możemy dostarczyć programiście aktualną listę pakietów, z którą może eksperymentować lokalnie. To oszczędza czas programistów, którzy piszą kod.
Konfiguracja
Teraz trochę o konfiguracji repozytorium. Historycznie używamy Nexus do zarządzania repozytoriami, w tym Internal REPO. Do tego wewnętrznego repozytorium dostarczane są wszystkie komponenty, które wykorzystujemy do celów wewnętrznych, na przykład samodzielnie napisane narzędzia monitorujące.

Używamy również NuGet, ponieważ dobrze cache'uje w porównaniu do innych menedżerów pakietów.
Wynik
Po optymalizacji agencji Build średni czas budowy skrócił się z 12 minut do 7.
Licząc wszystkie maszyny, które moglibyśmy wykorzystać dla Windows, ale w tym projekcie przenieśliśmy na Linux, zaoszczędziliśmy około 10 000 dolarów. I to tylko na licencjach, a jeśli uwzględnić utrzymanie - więcej.
Plany
Na następny kwartał w planach mamy pracę nad optymalizacją dostarczania kodu.
Przejście na prebuild Docker-image. TFS to świetna rzecz z wieloma wtyczkami, które pozwalają integrować w Pipeline, w tym budowę na podstawie triggera, na przykład, obrazu Docker. Ten trigger chcemy ustawić na ten właśnie sam. package-lock.json. Jeśli w jakikolwiek sposób zmienia się skład komponentów używanych do budowy projektu – tworzony jest nowy obraz Docker. W dalszej kolejności jest on używany do wdrożenia kontenera z zbudowaną aplikacją. Obecnie tego nie ma, ale planujemy przejść na architekturę mikrousług w Kubernetes, która aktywnie rozwija się w naszej firmie i od dawna obsługuje rozwiązania produkcyjne.
Podsumowanie
Zachęcam wszystkich do porzucenia Windows, ale nie dlatego, że nie potrafię go używać. Powód jest taki, że większość rozwiązań typu Opensource to stak Linux. Dobrze zaoszczędzicie na zasobach. Moim zdaniem przyszłość należy do rozwiązań Open Source na Linux z silną społecznością.
Profil prelegenta Aleksandra Sinczinowa .
to konferencja dotycząca integracji procesów tworzenia, testowania i eksploatacji dla profesjonalistów od profesjonalistów. Dlatego projekt, o którym opowiadał Aleksander, został zrealizowany i działa, a w dniu wystąpienia przeprowadzono dwa udane wydania. Na 27 i 28 maja będzie jeszcze więcej podobnych przypadków od praktyków. Można jeszcze wskoczyć do ostatniego wagonu i lub na spokojnie bilet. Do zobaczenia w Skolkowie!
Źródło: habr.com
