Cel ćwiczenia Współczesny administrator systemów nie zajmuje się już ani jednym, ani pięcioma systemami dzięki wirtualizacji sprzętu liczba instalacji systemów rośnie lawinowo. Pojawia się potrzeba standaryzacji i opisu konfiguracji systemu *nixowego. Narzędziem, które rozwiązuje ten problem i zdobywa coraz większą popularność, jest Puppet [1]. Opisanie konfiguracji Najbardziej charakterystyczną cechą Puppeta jako języka programowania jest jego deklaratywność. Opis konfiguracji ( manifest ) jest opisem stanu systemu: ma być zainstalowany pakiet foo, nie może być pliku /var/lib/bar/baz, urządzenie /dev/drbd ma być zamontowane jako /srv. Atomową cegiełką opisu jest zasób, który jest podstawową częścią języka. Najczęściej używane typy zasobów: File: plik lub katalog. Service: usługa systemowa (w sensie LSB) lub proces. Cron: zadanie okresowe. Mount: system plików.
3A Package: pakiet oprogramowania. User: użytkownik systemowy. Group: grupa systemowa. Ssh_authorized_key: klucz ssh wpuszczający na konto użytkownika. Yumrepo: repozytorium Yum (nie ma analogicznego APT-repo). Host: wpis w /etc/hosts. Exec: polecenie systemowe. W bardziej zaawansowanych zastosowaniach wykorzystuje się jeszcze zasoby Augeas (klauzula języka modyfikacji plików konfiguracyjnych), Nagios_* (elementy konfiguracji systemu Nagios), czy Zfs. Pełną listę zasobów dostępnych w aktualnej wersji Puppeta można znaleźć w [2]. Do stworzenia manifestu konfiguracji Puppet potrzebuje informacji o systemie. Zbieraniem zajmuje się osobny program: facter. Uruchomiony samodzielnie wypisuje zebrane fakty, po uruchomieniu przez puppeta fakty dostępne są manifestom w postaci zmiennych. Przykładowy zbiór faktów wygląda tak (fragment): architecture => i386 facterversion => 1.3.5 hardwareisa => unknown hardwaremodel => i686 hostname => hell id => alex ipaddress => 193.219.28.162 kernel => Linux kernelrelease => 2.6.18-6-xen-686 macaddress => 00:1D:60:86:41:EA 00:1D:60:86:41:EA 00:1D:60:86:41:EA 00:1D:60:86:41:EA memoryfree => 544.46 MB memorysize => 1.85 GB operatingsystem => Debian operatingsystemrelease => 2.6.18-6-xen-686 processor0 => AMD Athlon(tm) 64 X2 Dual Core Processor 5000+ processor1 => AMD Athlon(tm) 64 X2 Dual Core Processor 5000+ processorcount => 2 ps => ps -ef...
3B Hello, I am JanB! Najprostszy manifest Puppeta wygląda tak: Po zapisaniu jej w pliku hello.pp i wykonaniu polecenia puppet hello.pp jednak nic nie zostanie wykonane. Brakuje tu jeszcze przypisania zasobu do właściwego komputera: node hell { Tym razem po wykonaniu puppet hello.pp zostanie (w Debianie) zainstalowany pakiet hello, zawierający ogólnie znany program przykładowy. Gdyby hello wymagało konfiguracji, można by ją utworzyć tak: node hell { file { "/etc/hello.conf": content => # empty config file\n, mode => 755, Duplikowanie konfiguracji pomiędzy hostami mija się z celem, na szczęście zasoby można grupować w klasy i łączyć je powiązaniami: class helloworld { file { /etc/hello.conf : content => "# empty config file\n",
3C mode => 755, node hell { node heaven { Tak sformułowany manifest zainstaluje hello zarówno na hoście heaven jak i na hell. Jeśli jednak piekło i niebo wywodzą się z jednej pierwszej przyczyny, można wprowadzić hierarchię bytów: class helloworld { file { /etc/hello : ensure => directory, mode => 775, file { /etc/hello/hello.conf : content => # empty config file\n, mode => 775, require => [ Package[ hello ], File[ /etc/hello ]], node prime { node hell inherits prime { node heaven inherits prime {
3D Widzimy, że manifest rozbija się na dwa niezależne wątki: klasę opisu fragmentu konfiguracji niezależnego od hosta, i wątek przypisania takich elementów do grup hostów. Aby je rozdzielić w pełni, trzeba uruchomić Puppeta w pełnej architekturze klient/serwer. Chmura Pełnię swojej lalkarskiej władzy Puppet pokazuje dopiero przy uruchomieniu w środowisku składającym się z wielu podobnych maszyn. Na wyznaczonym systemie należy uruchomić usługę lalkarza (puppetmaster), a na pozostałych demony lalek (puppetd), które należy zarejestrować w lalkarzu. Lalki co określony czas (standardowo 30 minut) łączą się ze swoim lalkarzem, wysyłają swoje fakty, odbierają skompilowane z użyciem faktów manifesty i modyfikują swój stan, aby uzyskać zgodność. Środowisko sieciowe ma zdefiniowaną strukturę katalogów, do której odwołuje się lalkarz. Standardowo katalog referencyjny to /etc/puppet, ale można to na kilka sposobów zmienić w konfiguracji:./manifests/site.pp główny plik konfiguracji./manifests/nodes.pp konfiguracja lalek./modules/<nazwa> moduł funkcjonalny./modules/<nazwa>/manifests/init.pp manifest modułu./modules/<nazwa>/files pliki do których odwołuje się manifest modułu./modules/<nazwa>/templates szablony ERB [4], do których odwołuje się manifest modułu Definicję klasy z hello.pp przenosimy do./modules/helloworld/manifests/init.pp, a definicje wszystkich trzech node ów do./manifests/nodes.pp, z jednym niewielkim uzupełnieniem: w nodes.pp znajduje się już pusta definicja węzła default, musimy włączyć ją do hierarchii dziedziczenia: node default { node prime inherits default { node hell inherits prime { node heaven inherits prime { Node default ma taką specjalną właściwość, że jest aplikowany do lalek, które nie mają innej pasującej definicji, dlatego też w tym węźle umieszczamy zasoby, które mają wpływać na wszystkie systemy zarządzane Lalkarzem, a włączenie go do hierarchii dziedziczenia zapewnia utrzymanie tego wpływu po pojawieniu się definicji odpowiedniego node a.
3E Trzeba spojrzeć w oczy faktom Nie wykorzystywaliśmy jeszcze informacji zawartej w faktach o systemie. To znaczy nie wykorzystywaliśmy jej jawnie, niejawnie Puppet sam wybiera odpowiedni system pakietów do instalacji na podstawie faktu operatingsystem. Jednak do faktów możemy odwołać się bezpośrednio jak do innych zmiennych: file { /etc/hello/hello.conf : source => puppet:///helloworld/$hostname.conf, mode => 775, require => [ Package[ hello ], File[ /etc/hello ]], W powyższej definicji mamy kilka zmian: zamiast atrybutu content zawierającego docelową treść pliku mamy atrybut source definiujący URL źródłowy treści wykorzystujący własny protokół puppet:. Brak hosta w URL-u to nie błąd, brak hosta oznacza odwołanie do domyślnego hosta puppet. Dalsza część ścieżki URL-a to ścieżka modułu i odwołanie do faktu (czy zmiennej) hostname. Tak zadany URL znajdujący się w manifeście hosta hell będzie pytał lalkarza o plik./modules/helloworld/files/hell.conf i jego treść zostanie umieszczona na hell w /etc/hello/hello.conf Jeśli plik ten zależałby nie tylko od systemu, na którym ma się znaleźć, ale też od innych zmiennych czy faktów, należałoby go sformułować jako szablon ERB [3], umieścić w podkatalogu templates modułu i odwoływać się do niego przez: source => template( helloworld/hello.conf ), Puppet oczywiście nie nadpisuje zadanych plików w ciemno, wszelkim zmianom towarzyszy archiwizacja poprzedniej treści w wiadrze lokalnym (/var/lib/puppet/clientbucket) lub globalnym, zadanym w konfiguracji lalkarza. To rozkaz! Jedynym zasobem wyłamującym się z zasady opisu statycznego stanu jest Exec, w najprostszej wersji jest to polecenie systemowe, które zostanie wykonane w trakcie każdej sesji konfiguracyjnej lalki: exec { "helloworld": command => "hello", Jednak nie taki jest cel execa. Prawdziwa jego moc ukazuje się, kiedy wykorzystamy dodatkowe atrybuty onlyif i unless. onlyif to komenda wykonywana za każdym
3F odwołaniem do exec, ale właściwy exec zostanie uruchomiony, jeśli ta komenda zwróci 0, status prawidłowego zakończenia. Podobnie, lecz odwrotnie działa unless, exec zostanie wykonany jeśli komenda sprawdzająca NIE zwróci zera. exec { "helloworld": command => "hello", unless => "ps -Af grep -q hello", Możemy wreszcie powiązać exec z innym zasobem, np. plikiem konfiguracyjnym, wówczas zostanie wykonany tylko po zmianie w tym zasobie exec { "helloworld": command => "hello", subscribe => File[ /etc/hello/hello.conf ], Analogiczną funkcjonalność ma Service, zmiana subskrypcji powoduje restart usługi. Dość sznurków aby się powiesić Powyższy zarys możliwości i funkcjonalności Puppeta nie wyczerpuje jego możliwości i ma na celu prezentację paradygmatu opisowej konfiguracji serwerów i przejście od zarządzania systemami jako grzebactwa do wyabstraktowanego programowania obiektowego. Wdrożenie Puppeta w istniejącym środowisku jest zadaniem czasochłonnym i skomplikowanym, nie jest to projekt na dzień, tydzień czy miesiąc. Należy zinwentaryzować serwery, opracować podział na środowiska, napisac i PRZETESTOWAĆ manifesty, wreszcie włączyć poszczególne hosty w zarządzaną Lalkarzem grupę, co w przypadku usług produkcyjnych wymaga wielkiej ostrożności i oczekiwania. Jednak dwa główne pożytki z wprowadzenia Puppeta rekompensują i te wysiłki, i większy nakład pracy na tworzenie nowych rozwiązań: wszystkie serwery mają zuniformizowaną konfigurację, w której zmiany wprowadza się raz, a nie kilka/naście/dziesiąt/set czy tysięcy razy, i wszelkie nowe rozwiązania po stworzeniu manifestu mogą być uruchamiane na dowolnej liczbie maszyn bez dalszego nakładu pracy. Bibliografia [1] http://reductivelabs.com/ [2] http://docs.reductivelabs.com/references/stable/type.html [3] James Turnbull Pulling Strings with Puppet: Configuration Management Made Easy, Apress, 2008, ISBN 1590599780. [4] http://ruby-doc.org/stdlib/libdoc/erb/rdoc/classes/erb.html