Wdrażanie scentralizowanego systemu dystrybucji zmian

Nowoczesne rozwiązanie zarządzania platformą datacenter nie może funkcjonować bez komponentów takich jak proaktywny monitoring, audytowanie zmian zachodzących w systemach, możliwość wymuszenia polityk (np. bezpieczeństwa, dostępu do danych, backupów) oraz automatyzacja dominujących procesów (inwentaryzacja i raportowanie, planowanie pojemności, deployowanie systemów oraz aplikacji). Klasyczne podejście administratorów systemów gwarantujące rozwiązanie powyższych problemów zazwyczaj sprowadza się do implementacji systemów monitorowania z funkcjami wykrywania zmian w sieci (discovery), komponentów inwentaryzacji i zarządzania dostarczanych przez producenta sprzętu oraz własnym skryptom automatyzującym i testującym poprawność wykonania danych procesów oraz bazowaniu na dostępie do serwerów poprzez ssh. Jak łatwo zauważyć taka liczba systemów i różnych procedur, w połączeniu z dużą ilością serwerów (np. w przypadku istnienia środowisk testowych oraz wielu ośrodków produkcyjnych) oraz brak centralizacji procesów sprawia, że odpowiedzialność za ich rzetelne wykonanie spada na administratorów systemu oraz wypracowanych przez nich dobrych praktyk konfiguracji i używanych narzędzi (np. checklist, systemu śledzenia zmian, dokumentacji i kalendarza). Rodzi to jednak szereg zagrożeń, takich jak przeciążenie kadry administratorów wraz ze wzrostem ilości i złożoności systemu, nieautoryzowane zmiany (nie wykrywane bo nie audytowane), możliwe niekonsekwencje w stosowaniu określonych polityk,  wybiórcze ich stosowanie czy istnienie komponentów systemu, za których konfigurację lub utrzymanie odpowiada tylko jedna osoba, wreszcie możliwość zawodowego wypalenia pracowników, którym powierza się cykliczne wykonywanie tych samych czynności.

Rozwiązaniem tych problemów jakie proponujemy jest wykorzystanie oprogramowania do śledzenia zmian, zgodnie z filozofią infrastructure as code. Wiodącym produktem realizującym ten paradygmat jest opensource’owy projekt Puppet (istnieje możliwość wsparcia komercyjnego). Główne cechy wyróżniające to rozwiązanie to duża dynamika jego rozwoju, elastyczność w stosowaniu, duża liczba wspieranych usług i systemów oraz rozwinięta wokół projektu społeczność ułatwiająca jego naukę, wdrożenie i rozwiązywanie problemów. Oprogramowanie do zarządzania zmianami, oraz projekt Puppet w szczególności, charakteryzuje kilka cech, dzięki którym możemy uznać je za centralny system zarządzania infrastrukturą w dużej części mogący zastąpić klasyczny model w oparciu o własne skrypty i ssh lub szereg niezależnych programów realizujących różne zadania.

Scentralizowany system dystrybucji zmian

Przede wszystkim system zarządzania zmianami jest wysokopoziomowym interfejsem opisującym stan docelowy systemu (instalacja odpowiedniego oprogramowania, nadawanie uprawnień, konfiguracja usług i inne). Na podstawie takiego opisu dostarczone moduły oraz dostawcy mogą realizować zadanie na większości architektur (np. różne metody instalacji oprogramowania na systemach Linux i FreeBSD) umożliwiając zarządzanie heterogenicznym środowiskiem za pomocą tego samego zwięzłego opisu polityk. Każdy zdefiniowany zasób systemowy może być cyklicznie (typowo co 30 minut) sprawdzany przez uruchomionego agenta i w razie odstępstwa od polityk przywracany do opisanego stanu (przy zachowaniu zdefiniowanych zależności, np. zmiana konfiguracji wymusza przeładowanie usługi). Warto podkreślić, że te same reguły opisujące instalację i konfigurację środowiska odnoszą się jednocześnie do zainstalowanych oraz nowo wdrażanych elementów systemu, a więc uzyskujemy pewność, że całość zarządzanej infrastruktury funkcjonuje w sposób spójny i taki stan zostanie utrzymany w przyszłości, co jest nieocenione w przypadku konieczności wymuszenia pewnych polityk (czy to związanych z bezpieczeństwem systemu czy też wynikających z wymagań prawnych – np. ochrona danych osobowych).

Hierarchiczne definicje elementów infrastruktury

Deklaratywny skryptowy język opisu konfiguracji (DSL) nie jest płaską strukturą funkcyjną, ale obiektowym systemem zawierającym możliwości definiowania klas, metod oraz używania dziedziczenia własności obiektów nadrzędnych. Te znane z wysokopoziomowego programowania koncepty co prawda zmuszają administratorów systemu do przyswojenia nowych metod projektowania systemów, jednocześnie zwracając uwagę na występujące między nimi zależności, jednak w efekcie w dużym stopniu upraszczają konfigurację. Zwykle bowiem powstały kod opisujący konfigurację grupowany w klasy definiujące opis danej funkcjonalności czy usługi może być użyty w wielu miejscach (np. serwer Apache może być użyty jako niezależny serwis służący serwowaniu plików, ale również jako serwer proxy, np. w projektach opartych o Tomcat, jako serwer aplikacyjny dla PHP lub Ruby Passenger, albo jako serwer CGI hostujący oprogramowanie starszego typu zainstalowane na serwerach backoffice) – we wszystkich tych miejscach dziedzicząc takie wspólne elementy jak wersja zainstalowanego oprogramowania, dozwolone metody HTTP, czy konfigurację dotyczącą zakresu obsługiwanych przez serwer modułów. Również definicje poszczególnych węzłów (serwerów) systemu zwyczajowo są dziedziczone, dzięki czemu powstaje czytelny graf opisujący wspólne oraz specyficzne części architektury jakie powinny być obecne na serwerach w obrębie całego środowiska (testowe, produkcyjne), data center (np. ustawienia dotyczące serwerów DNS czy backupów), danego projektu (np. ustawienia konfiguracji klastrów), poszczególnych grup serwerów (np. aplikacyjne, bazodanowe) i w końcu indywidualne role (np. serwer aplikacji dodatkowo realizujący cykliczne funkcje raportujące). Jak łatwo zauważyć, taki centralny opis może służyć nam również jako dobry system inwentaryzacji, bowiem oprócz zawartej konfiguracji (np. hasła określające uprawnienia do baz danych) mamy możliwość włączenia mechanizmu raportowania serwerów do centralnego repozytorium YAML. Dane jakie otrzymamy będą zawierać zarówno szczegółową informację nt. stanu konfiguracji serwera (w tym alerty w przypadku kiedy założona polityka nie może być z jakiegoś powodu zastosowana – np. w przypadku brakujących plików szablonów konfiguracji) jak również informację nt. systemu takie jak adresy interfejsów sieciowych, konfiguracja sprzętowa (CPU, RAM, dyski) czy bieżące wykorzystanie zasobów (np. wolna pamięć). Te ostatnie informacje mogą być zresztą z powodzeniem użyte do opisu polityki, np. serwery MySQL, niezależnie od projektu i konfiguracji sprzętowej mogą być ustawione tak, że wielkość bufora będzie wynosić zawsze 70% dostępnej fizycznej pamięci. Proces rozbudowy serwera sprowadza się wtedy jedynie do wymiany kości pamięci, bowiem ponowne jego uruchomienie samoczynnie dostosuje konfigurację aktualizując parametr wielkości bufora optymalnie wykorzystując nowo dostępne zasoby. Hierarchiczna struktura opisu konfiguracji pozwala też na włączenie do polityk takich elementów, jakie bez jej użycia nie mogłyby zostać wdrożone ze względu na stopień komplikacji czy zbyt duży nakład prac wymagany do indywidualnej konfiguracji usług. W dużej mierze ma to wpływ na bezpieczeństwo systemów, bowiem często prace związane np. z hardeningiem systemów czy uruchomieniem host firewall czy host IDS są odsuwane na dalszy plan, ze względu na inne priorytetowe zadania (jak napięty cykl zmian w aplikacji, czy dodawanie w krótkim czasie nowych funkcjonalności) lub brak budżetu. Dzięki automatyzacji oraz wykorzystaniu wspólnych dla infrastruktury elementów (np. identyczne reguły firewalla) ich wdrożenie w ramach procesu zarządzania zmianami w konfiguracji okazuje się dużo efektywniejsze niż gdyby było traktowane jako osobny proces.

Zarządzanie zmianami

Tak jak w nowoczesnych projektach programistycznych użycie koncepcji infrastructure as code  pozwala na użycie narzędzi kontroli kodu (np. svn, git), użycie systemu ticketowego (np. trac, redmine) oraz wewnętrznych procedur dopuszczania zmian w kodzie (np. konieczność wdrażania zmian konfiguracyjnych najpierw na środowisko testowe, czy istnienie grupy doświadczonych administratorów uprawnionych do tagowania określonych zmian celem dopuszczenia ich do produkcji, np. z użyciem oprogramowania Review Board). Dzięki modularności kodu dajemy też możliwość zmian konfiguracji usług mniej doświadczonym administratorom pierwszej linii wsparcia (np. poprzez korzystanie z gotowych metod służących do dodawania konfiguracji wirtualnego serwera HTTP, bez konieczności wiedzy nt. właściwych zmian jakie zostaną wykonane w systemie poprzez jej użycie – tu np. ustawienie odpowiednich uprawnień, stworzenie konfiguracji serwera wg. szablonu, dodanie konfiguracji Load Balancerów i DNS), jednocześnie dając bardziej zaawansowanym inżynierom narzędzie, które w sposób spójny dostosowuje system do określonych polityk. Nie bez znaczenia jest też, że w trakcie pisania takiego kodu opisu infrastruktury administratorzy zdobywają doświadczenie, pozwalające im rozwiązywać coraz to bardziej złożone problemy w metodyczny i zaprogramowany sposób, w dużej mierze eliminując potrzebę indywidualnego zarządzania poszczególnymi serwerami i priorytetyzując zadania projektowe nad powtarzające się wykonywanie dających się zautomatyzować czynności.

Możliwość ponownego użycia kodu

Ze względu na powszechne użycie popularnych koncepcji oraz oprogramowania opensource oraz dużą społeczność skupioną wewnątrz projektu w ramach projektu Puppetforge istnieje wiele gotowych do użycia i dostosowania modułów umożliwiających szybką adaptację systemu. Większość prac, jakie są potrzebne do wdrożenia systemu kontroli zmian sprowadza się zatem do inwentaryzacji istniejących systemów (a więc procesu będącego częścią audytu infrastruktury) oraz projektowania nowych środowisk. Z czasem, kiedy baza kodu organizacji jest coraz bardziej złożona i zawiera więcej komponentów można zauważyć tendencję do włączania i ponownego wykorzystywania powstałych już rozwiązań do nowych projektów co znacznie może skrócić czas ich implementacji oraz przyczynić się do zwiększenia przejrzystości zachodzących w systemach zależności.

Automatyczna rejestracja w centralnych systemach

Warto również podkreślić, że dzięki możliwości eksportowania części ustawień na centralny serwer konfiguracji otrzymujemy unikalny system umożliwiający konfigurację systemów działających w architekturze klient-serwer za pomocą jednego opisu. Otwiera to wiele możliwości do zastosowań, gdzie najpopularniejszymi jest automatyczna rejestracja nowo zainstalowanych serwerów oraz usług w systemach monitoringu i zbierania danych statystycznych, rejestracja zasobów dyskowych w systemie backupowym czy ustalanie reguł firewalla poprzez rejestrację poszczególnych usług oraz adresów poszczególnych interfejsów sieciowych. Takie rozwiązanie umożliwia także łatwe skalowanie takich systemów, bowiem instalacja kolejnych elementów sprowadza się do przyznania im roli serwera danego rozwiązania oraz pobranie całości lub części konfiguracji obecnych serwerów.

Audytowalność zmian

Dzięki możliwością raportowania narzędzie konfiguracyjne staje się jednocześnie narzędziem audytu dokonywanych zmian. Istnieje wiele aspektów konfiguracji i działania serwerów, jakie powinne być stale audytowane zapewniając zgodność i jednolitość wobec przyjętych polityk, a oprogramowania zarządzające zmianami jest jedynym miejscem, które może weryfikować je wszystkie, dzięki pełnej wiedzy dotyczącej zainstalowanych komponentów oraz wykluczenie innych, nieobecnych w kodzie. Podstawowe parametry jakie powinny być monitorowane, to między innymi: uprawnienia do plików (w szczególności zawierających hasła), sumy kontrolne konfiguracji oraz rejestracja zdarzeń ich modyfikacji (nieautoryzowane  zmiany – funkcjonalność HIDS), uruchomione usługi sieciowe, poprawność zainstalowanych konfiguracji (weryfikacja ich składni zapewniająca że w razie potrzeby usługa może być zrestartowana i zostanie poprawnie ponownie uruchomiona), konfiguracja sieci i firewalla (istnienie odpowiednich interfejsów, reguł routingu, aktywności oprogramowania kierującego routingiem dynamicznym, zgodność reguł firewalla z zasadami określonymi w konfiguracji. Monitorowaniu powinno podlegać również samo wykonanie agenta Puppet, bowiem w przypadku błędów składniowych lub brakujących plików szablonów część konfiguracji może nie być prawidłowo naniesiona. To zagrożenie można również minimalizować używając środowisk przedprodukcyjnych (staging) oraz testowanie zmian (np. z użyciem odpowiednich reguł RSpec, Cucumber czy wykonanie no-op).

Proaktywny monitoring

Częstym problemem występującym w systemach monitoringu jest konieczność ręcznej rejestracji poszczególnych usług dostępnych na monitorowanych serwerach. Nawet w przypadku rozwiązań posiadających możliwość autokonfiguracji (discovery) często oprogramowania nie może działać w pełni automatycznie (np. wykryty serwer HTTP na porcie 80 jest niewystarczające aby określić jego rolę i konkretny url do monitoringu jego stanu oraz oczekiwaną wartość). Ponieważ w przypadku wykorzystania narzędzi zarządzania zmianami konfiguracja systemu monitorowania jest dziedziczona z konfiguracji danego hosta nie musi być ona definiowana w dwóch miejscach: na serwerze usługowym oraz monitorującym oraz może być bardzo szczegółowa – nie ograniczają nas możliwości zdalnego wykrywania i diagnozowania a docelowa konfiguracja monitoringu jest pochodną konfiguracji samej usługi. Co więcej możliwe jest modyfikowanie części konfiguracji w zależności od projektu bądź centrum danych w jakim znajduje się monitorowany serwer, nawet jeśli korzystamy z centralnego systemu monitoringu (np. modyfikujący dopuszczalne czasy odpowiedzi lub kontakty na które powinny być wysyłane powiadomienia). Funkcjonalności te pozwalają nam również na użycie dotychczasowych narzędzi w nowoczesny sposób, nawet pomimo tego, że same nie posiadają one takich funkcjonalności jak autokonfiguracja czy samoadaptacja.

Możliwość rozszerzania systemu o nowe komponenty

Dzięki otwartej licencji oprogramowania (Apache License) oraz wykorzystanie wysokopoziomowego języka Ruby i API opartego na formatach REST i YAML stosunkowo łatwe jest rozszerzanie możliwości oprogramowania o elementy specyficzne dla organizacji (np. integracja z istniejącymi systemami) i nieistniejące w standardowej dystrybucji. Wśród takich wprowadzonych przez nas usprawnień można wymienić udoskonalony system zarządzania monitoringiem Nagios, automatyczna rejestracja serwerów i wykresów Cacti, automatyczne tworzenie reguł firewalla w oparciu o Iptables, zarządzanie konfiguracją interfejsów sieciowych oraz reguł routingu (w tym dynamicznego w  oparciu o BIRD), autokonfiguracja reguł ACL dla wychodzącego proxy squid, raportowanie przez agentów Puppet z użyciem NSCA, rejestracja klastrowych oraz lokalnych systemów plików w systemie backupów Bacula, provider instalacyjny dla Ruby Gem i RVM, metody deployowania aplikacji dla wielu architektur (w tym PHP, Rails, J2EE), rozszerzone audytowanie stanu usług i inne. Samo oprogramowanie jest również dynamicznie rozwijane o nowe typy konfiguracji (w tym bierze udział w programie Google SoC), możliwe jest również zlecenie utworzenia nowych funckjonalności przez programistów znających wewnętrzne struktury danych i budowę oprogramowania.

Instrumentacja wprowadzania zmian

W przypadku dużych wielowęzłowych klastrów ważnym elementem konfiguracji jest procedura wdrażania zmian, która musi zakładać odpowiednią instrumentację ich wprowadzania – ze względu na technologię cache ważne jest aby nie restartować zbyt wielu elementów klastra w tym samym czasie, zmiany muszą być przeprowadzone w określonej sekwencji (np. najpierw update bazy danych, potem aplikacji), deploy powinien być wykonany w jak najkrótszym możliwym czasie, każda zmiana musi być weryfikowana (poprawność działania aplikacji po wprowadzeniu zmian i restarcie) oraz musi istnieć procedura rollback. Wszystkie te procesy są niezwykle wymagające i dlatego najczęściej są procesem angażującym odpowiedzialnego za wdrażanie zmian programistę lub administratora (RM), który wykonuje te zmiany ręcznie lub za pomocą własnych skryptów (z użyciem ssh, rsync). W przypadku Puppet poza typowym 30 minutowym oknem weryfikującym zgodność systemu z założonymi politykami mamy do dyspozycji dodatkowy serwer Mcollective pozwalający korzystać ze wszystkich cech inwentaryzacji i raportowania oprogramowania do zarządzania zmianami jednocześnie pozwalającego na grupowanie serwerów w kolektywy i tworzenie zaawansowanych technik instrumentacji zmian: kolejność wykonania zmian, możliwość stosowania filtrów na podstawie charakterystyk serwerów (np. możliwość update’u tylko danego typu serwerów aplikacyjnych lub jednoczesny update tylko ¼ serwerów znajdujących się w tym samym ośrodku) oraz natychmiastowa informacja zwrotna na temat sukcesu lub porażki przeprowadzonej operacji. Jest to oprogramowanie korzystające z wydajnego modelu publish/subscribe, napisane w Ruby, a więc również łatwo rozszerzalne o moduły realizujące wymaganą logikę biznesową.

Izolacja uprawnień

Architektura klient-serwer w jakiej jest zbudowany Puppet zapewnia, że dany konfigurowany system otrzymuje z serwera jedynie te informacje które są niezbędne do jego konfiguracji. Niemożliwa jest zatem sytuacja (oprócz świadomego eksportu danych na inne systemy) w której np. system aplikacyjny otrzymuje informacje dotyczące konfiguracji systemu bazy danych, wraz z hasłami do tej bazy (np. użytkownik root). Serwer Puppet przetwarza szablony i konfigurację opisaną językiem DSL i dostarcza je na docelowy system w postaci skompilowanej wraz z zależnościami.

Komercyjne wsparcie

Wymagający klienci mogą korzystać z wersji oprogramowania Puppet Enterprise zapewniające automatyczny instalator, priorytet w usuwaniu występujących usterek, wsparcie emailowe oraz telefoniczne producenta oraz szkolenie dla administatorów. Licencja bazuje na liczbie kontrolowanych węzłów (25-100-250-500-1000 i więcej).