Język DSL Puppeta cz.2

Dalsza część kursu traktującego o najpopularniejszym narzędziu o zarządzania konfiguracją infrastruktury.

Git.

Aby nasz Puppet mógł współpracować z Gitem potrzebujemy mieć tam założone w pierwszej kolejności konto. Po założeniu konta generujemy klucz ssh, który później wgramy na Gita.

Wyświetlamy wygenerowany klucz publiczny.

A następnie wklejamy go na Git hubie (www.github.com) w sekcji

Settings -> SSH and GPG keys -> New SSH key.

Tworzymy także nasze repozytorium o nazwie np. puppet-nginx.

 Settings -> Repositories -> + -> New repository -> Repository name

Na naszej maszynie instalujemy gita.

Testujemy nasze repozytorium.

Gdzie GIT_ACCOUNT_NAME to nazwa naszego konta na githubie, a puppet-ssh to to nazwa repozytorium.

 

Moduły.

Aby manifest był bardziej czytelny i łatwiejszy w utrzymaniu dobrym zwyczajem jest rozbicie go na moduły.

Generowanie modułu:

Nazwa modułu powinna:

  • zawierać male litery,
  • zawierać liczby,
  • zawierać podkreślenia “_”,
  • zaczynać się od małej litery,
  • nie może zawierać dwukropek “::”.

Wejście do katalogu z modułami:

Wyświetlenie aktualnie zainstalowanych modułów:

Wygenerowanie naszego modułu o nazwie np. ssh (zamiast username wpisujemy nazwę użytownika):

W katalogu z modułami pojawił się nowy moduł:

Szkielet naszego modułu składa się z takich plików i katalogów:

W pliku metadata.json znajdują się opcje wpisywane przez nas w czasie generowania modułu. Brakuje tutaj katalogu files i templates, które musimy utworzyć ręcznie.

Wyślijmy teraz do repozytorium git nasz moduł:

 

Klasy.

To nazwane bloki kodu Puppeta, które są używane w modułach. Nie są używane dopóki nie zostaną wywołane. Mogą być dodane w katalogu węzła przez zadeklarowanie ich w manifeście. Używają zasobów do konfigurowania pakietów, plików, usług, etc. Używają parametrów do korzystania z zewnętrznych danych. Mogą być użyte na danym węźle tylko jeden raz.

Składnia klasy wygląda następująco:

Nazwa klasy składa się z segmentów, które mogą zwierać tylko małe litery, cyfry i podkreślenia. Segmenty łączyć możemy podwójnym znakiem dwukropka “::”. Pierwszy segment nazwy klasy określa moduł. Ostatni segment określa nazwę pliku. Każdy dodatkowy segment w nazwie pomiędzy pomiędzy pierwszym i ostatnim członem nazwy będzie dodatkowym podkatalogiem, np.

apache - <MODULE_DIRECTORY>/apache/manifest/init.pp

apache::mod - <MODULE_DIRECTORY>/apache/manifest/mod.pp

apache::mod::passenger - <MODULE_DIRECTORY>/apache/manifest/mod/passenger.pp

Gdzie  <MODULE_DIRECTORY> to /etc/puppetlabs/code/environments/production/modules

Po wygenerowaniu przez nas wcześniej modułu ssh definicja klasy ssh znajduje się w pliku /etc/puppetlabs/code/environments/production/modules/ssh/manifest/init.pp.

Modyfikujemy ją tak, aby wyglądała następująco:

Każda klasa powinna znajdować się w swoim własnym pliku z rozszerzeniem .pp. W przeciwnym razie Puppet zwróci komunikat o błędzie.

Zakładamy zatem plik /etc/puppetlabs/code/environments/production/modules/nginx/manifest/install.pp o treści:

oraz w tym samym katalogu plik service.pp :

Sprawdzamy składnie naszej klasy:

Na wyjściu nie ma żadnych komunikatów a zatem składnia jest poprawna. Tym samym cały kod dotyczący nginx przenieśliśmy z pliku site.pp do wygenerowanego modułu.

Plik /etc/puppetlabs/code/environments/production/manifests/site.pp możemy teraz zmodyfikować tak aby wczytywał tylko utworzony przez nas wcześniej moduł nginx:

 

Operatory.

  • = Przypisanie
  • == Równość

  • =! Nierówność

  • >  większy niż
  •  < mniejszy niż
  • >= większy lub równy niż
  • <= mniejszy lub równy niż

Operator porównania występuje również w łańcuchach, jest to operator “in“:

  • Boolean

Przykłady:

  • Arytmetyka: dodawanie (+), odejmowanie (-), mnożenie (*), dzielenie (/).

 

Wyrażenia warunkowe.

Bardzo użyteczne jest wykonywanie różnych operacji w zależności od wartości pewnych zmiennych lub wyrażeń. Puppet pozwala robić to na wiele różnych sposobów.

  • If, elsif, else

Przykład:

  • unless

  • case

Np. konstrukcję if:

można zastąpić konstrukcją case:

Inny przykład case:

 

Facter.

Facter to narzędzie, które dostarcza informacje o systemie. Wszystkie dostępne informacje o systemie mogą być wyświetlone po wpisaniu komendy:

Użyjmy factera w niedawno utworzonym module. W tym celu zakładamy i edytujemy plik /etc/puppetlabs/code/environments/production/modules/nginx/manifest/params.pp

Poza tym zmieniamy zawartość init.pp:

Zmieniamy także plik install.pp:

oraz service.pp:

Walidacja poprawności składni:

 

Typy danych.

Podstawowe typy danych używane w Puppecie to:

  • String – łańcuch
  • Integer, Float, Numeric
  • Boolean
  • Array – tablica
  • Hash
  • Regexp
  • Undef
  • Default

Abstrakcyjne typy danych używane przez Puppeta:

  • Scalar – inaczejInteger, Float, String, Boolean, Regexp
  • Collection – tablica lub hash
  • Variant – dowolna ilość różnych typów danych
  • Data – tak jak Scalar plus dodatkowo undef, tablice i hashe
  • Pattern – wzorzec
  • Enum – zawiera wcześniej określone stringi
  • Tuple – tablica, która może zawierać różne typy danych
  • Struct – zawiera hashe
  • Optional – dowolny typ danych
  • Catalogentry – zawiera zasoby i klasy
  • Any – dowolna wartość dowolnego typu danych
  • Callable – lambdy dostarczone jako argumenty funkcji

 

Wyrażenie regularne.

W Puppecie są różne sposoby na testowanie łańcuchów znaków.

Np.

lub

Jeżeli natomiast chcemy sprawdzić czy w łańcuchu występuje jakiś wzorzec to musimy się posłużyć wyrażeniami regularnymi.

Wzorzec pasuje: VALUE =~ /REGEX/
Wzorzec nie pasuje: VALUE !~ /REGEX/

Np. wyrażenie:

będzie prawdziwe jeżeli string $::hostname przyjmie dowolną poniższą wartość:

  • ‹app_staging
  • app-1-staging
  • application_staging
  • appstaging
  • my_app_staging_server

Jeżeli zachodzi potrzeba odnieść się do tekstu, który był wyszukany przez wyrażenie regularne to można to zrobić zmienną $0, np.

Jeżeli jest potrzeba odniesienia się do jakiejś części wyszukanego przez wzorzec tekstu to należy go zamknąć w nawias, np:

Odwołanie do każdego następnego nawiasu będzie się odbywać przez zmienne $2, $3 itd.

Wyrażenia regularne zaimplementowane w Puppecie wywodzą się z języka Ruby. Więcej o ich składni można poczytać w tym tutorialu.

Wyrażenia regularne mogą być stosowane w określaniu węzłów, np. zamiast zapisu:

możemy zapisać:

Przydaje się to gdy mamy pewną ilość serwerów wykonujących te same zadania o podobnych nazwach:

W wyrażeniach warunkowych także można użyć wyrażeń regularnych, np. zawartość params.pp:

 

Tablice.

Przy pomocy tablic możemy grupować zasoby, np.:

Przypisanie zmiennej tablicy:

Wywołanie:

zwróci:

Interpolacja tablicy w łańcuch:

Odniesienie się do konkretnego elementu tablicy:

Można posługiwać się także ujemnymi indeksami:

Operator in działa również w tablicach:

Odnosząc się do zmiennej w innym stringu zmienną owijamy w nawiasy klamrowe, np. ${crewmember}.

 

Hashe.

Hashe to zbiór par elementów: klucz => wartość. W Puppecie hash wygląda następująco:

Odniesienie się do konkretnej pozycji w hashu:

Kluczem w hashu musi być string ale wartości mogą być dowolnego typu:

Ponieważ wartości w hashu mogą być także innymi hashami to można konstruować wielopoziomowe hashe, np:

Sprawdzać czy dany klucz występuje w hashu możemy wyrażeniem:

 

Pliki.

Nginx jest zainstalowany i uruchomiony ale nie serwuje strony web. Tworzymy na masterze zatem strukturę katalogów i plik index.html dla serwowanej strony.

Utworzenie pliku /etc/puppetlabs/code/environments/production/modules/nginx/files/example.conf z konfiguracją strony:

Zakładamy nowy plik /etc/puppetlabs/code/environments/production/modules/nginx/manifests/config.pp:

Atrybut recurse pozwala na skopiowanie całego katalogu website wraz z zawartością do /var/www pod nazwą example. Zwróćmy uwagę, że wartość atrybutu source puppet:///modules/nginx/example.conf zaczyna się od trzech ukośników a nie dwóch co oznacza, że plik puppeta, na który wskazuje atrybut jest na lokalnym hoście, ponieważ zapis puppet:///modules/nginx/example.conf jest równoważny z puppet:///modules/nginx/example.conf.

Edytujemy plik init.pp .

Znak ~> to odpowiednik atrybutu notify => Service['nginx'], nakazuje puppetowi aby zrestartował się po każdej zmianie zawartości pliku example.conf. Kod jest równoważny z:

Co z koleji jest równoważne z:

Czyli usługa niginx będzie subskrybować plik /etc/nginx/default.d/default. Jeżeli zmieni się zawartość tego pliku to usługa się przeładuje.

Do zarządzania konfiguracją wybranego pakietu często używany jest następujący wzorzec.

W praktyce Puppet jest wolny jeżeli chodzi o pobieranie całego drzewa katalogów pod stronę web dlatego lepiej używać GITa jako repozytorium.

 

Szablony.

Użytego przez nas pliku konfiguracyjnego nginxa example.com nie możemy niestety wykorzystać ponownie ponieważ zawiera on konfigurację tylko dla ściśle określonego jednego webserwera. Aby można go było użyć wielokrotnie musimy przerobić go na szablon.

Szablony mają roszerzenie .erb i składujemy w katalogu:

ERB to język tworzenia szablonów używany przez Ruby.

Zakładamy katalog z szablonami nginxa i wgrywamy do niego nasz plik:

Edytujemy plik vhost.conf.erb :

Znaki <%= %> oznaczają, że w środku będzie zmienna w języku Ruby. W naszym przypadku będzie tu wstawiona zmienna Ruby. Zmienne te to @site_name oraz @site_domain. Istnieje także możliwość wstawienia całych wyrażeń w języku Ruby. Robi się to przy pomocy konstrukcji, np.:

Inne konstrukcje, które można używać w szablonach ERB to:

  • komentarz:
  • pętla:

     

Nasz plik config.pp wyglądać powinien teraz tak:

Dodajmy teraz tryb warunkowy do naszej konfiguracji web serwera aby mógł działać nie tylko na porcie 80. Nasz szablon erb niech wygląda tak:

Oczywiście można było wpisać tutaj po prostu:

ale chcemy wykorzystać tryb warunkowy if.

Pliki tworzące moduł nginx niech wyglądają teraz jak poniżej.

params.pp:

init.pp :

config.pp :

 

Parametry klasy.

Ponieważ używany przez nas wcześniej wzorzec przekazywania parametrów do klasy <MODULE NAME>::params został zdeprecjonowany musimy te parametry przekazać w inny sposób. Rekomendowanym sposobem jest teraz używanie funkcji lub Hiera, o której napisze w innych artykułach. Funkcja, o które mowa to Function Data Provider – <MODULE NAME>::data, podobna do używanej wcześniej params.pp.

Function Data Provider może być:

  • napisana  w języku Puppeta i zlokalizowana w pliku <MODULE ROOT>/functions/data.pp
  • napisana w języku Ruby i zlokalizowana w <MODULE ROOT>/lib/puppet/functions/<MODULE NAME>/data.rb.

Stwórzmy teraz taką funkcję napisaną w języku Puppeta.

Plik data.pp:

init.pp:

config.pp:

install.pp:

service.pp:

Zmodyfikować musimy także plik metadata.json:

data_provider został tu przestawiony na function.

Jak widać wszystkie parametry modułu nginx można ustawiawiono bezpośrednio w data.pp.

 

Definicje zasobów.

Definicje zasobów przydają się gdy chcemy grupować zasoby różnych typów. Jeżeli mamy potrzebę wykonania np. takich zadań:

To najlepiej zdefiniować taki zasób:

A później go uruchomić w manifeście:

Można także przekazywać parametry do takiego zdefiniowanego zasobu:

Uruchomienie z domyślnymi parametrami ($hour = '00', $minute = '00'):

Uruchomienie ze swoimi parametrami:

Uruchomienie z jednym domyślnym ($minute = '00') i jednym swoim parametrem (hour => "*"):

Utwórzmy taki zdefiniowany zasób w naszym module nginx, który będzie odpowiedzialny za backup plików tworzących webserver example.com.  Backup będzie wykonywał skrypt /etc/puppetlabs/code/environments/production/modules/nginx/files/scripts/backup.sh. Definicję zasobu umieszczamy natomiast w osobnym pliku np. backup.pp:

Zawartość pliku backup.pp:

Plik config.pp modyfikujemy następująco:

Po uruchomieniu na agencie:

Na agencie pojawi się owy skrypt /scripts/backup.sh a na liście zadań crona pojawi się nowe zadanie:

 

 

Konfiguracja SSH (OLD).

Jeżeli zachodzi potrzeba modyfikacji konfiguracji SSH dla systemu to również można użyć Puppeta.

Tworzymy katalogi dla konfiguracji SSH:

Zakładamy plik /puppet/modules/ssh/manifests/init.pp o treści:

Zakładamy plik /puppet/ modules/ssh/files/sshd_config z zawartością:

Do definicji węzła server1 dodajemy wczytywanie nowej klasy ssh:

Tak jak po każdej zmianie tak i teraz uruchamiamy ponownie papply:

 

Leave a Reply

Your email address will not be published.