Ansible w Enterprise Linuksie – cz. II: teoria i zastrzyk praktyki

W poprzednim materiale o Ansible wyjaśniłem, czym jest to rozwiązanie, skąd się wzięło i do czego służy. W tym miesiącu zgodnie z propozycjami czytelników opiszę teoretyczne, często traktowane po macoszemu, aspekty Ansible i konfigurowania infrastruktury. Na samym końcu pokażę, jak postawić serwer Apache.

Na wstępie chciałbym podziękować wszystkim czytającym poprzedni tekst, a w szczególności tym, którzy udzielili cennych rad i zwrócili uwagę, na aspekty warte rozwinięcia. Efektem tych sugestii jest teoretyczna część tego artykułu oraz opis instalacji bez repozytorium EPEL.

Czym jest idempotentność?

Idempotentność jest pojęciem z matematyki. Upraszając, można powiedzieć, że wielokrotne wywoływanie danej operacji (funkcji) na obiekcie nie zmienia zwracanej wartości.

Idealnym przykładem jest tutaj funkcja zwracająca wartość bezwzględną. Funkcję tę często zapisuje się przy pomocy pionowych kresek |x| = abs(x).
Można ją jednak zapisać tak:

x: jeśli x >= 0,
-x: jeśli x < 0.

Wywołania dla wartości większych niż 0 są trywialne, więc od razu popatrzmy, co się stanie, gdy wielokrotnie wywołamy tę funkcję dla argumentu mniejszego od 0 np. -1.

|-1| = 1
||-1|| = |1| = 1
|||-1||| = ||1|| = |1| = 1

Nie ważne, ile razy wywołamy funkcję, zawsze dostaniemy piękną jedynkę. Matematyczni formaliści mogą przeprowadzić tutaj prosty dowód oparty na indukcji matematycznej.

Przenieśmy jednak z pozoru daleki świat matematyki na impulsy elektryczne w naszym komputerze. Potraktujmy teraz 1 i -1 jako zmienne boolowskie (1 w naszym przypadku to wartość True [Prawda], a -1 to False [Fałsz]). Osoby mające doświadczenie z wieloma językami mogą delikatnie uśmiechnąć się pod nosem, gdyż to, w jaki sposób definiować wartości boolowskie, jest jedną z podstawowych kości niezgody między twórcami języków, a także nawet całych układów scalonych. Zdarza się również, że zmienne boolowskie nazywa się flagami.

Wyobraźmy teraz sobie następującą funkcję przyjmującą jako argument stan naszego systemu:

EuroMan (system):
1: System jest zarejestrowany w EuroManie,
-1: System nie jest zarejestrowany w EuroManie.

Wykorzystując nasz pierwszy playbook, z poprzedniego artykułu, za pierwszym razem dostaniemy: EuroMan(-1) = 1
Każde następne wywołanie da nam jednak: EuroMan(EuroMan(-1)) = EuroMan(1) = 1
O fakcie tym poinformuje nas output ansible-playbook:

Jeśli system został zarejestrowany (pierwszy raz uruchamiamy tego playbooka): 
db.local :    ok=2 changed=1 unreachable=0 failed=0 
Za każdym kolejnym razem dostaniemy: 
db.local :    ok=2 changed=0 unreachable=0 failed=0

Idempotentność jest bardzo pożądaną cechą w przypadku konfiguracji infrastruktury. Istnieją nawet książki opisujące tzw. idempotentną infrastrukturę i to, jak ją uzyskać. Korzystając z Ansible, nie chcemy, by dobrze skonfigurowany serwer/maszyna wirtualna/kontener po wywołaniu na nim tych samych komend, przestał w sposób właściwy działać. Niestety jest to jednak bardzo często spotykane w przypadku skryptów administracyjnych pisanych ad-hoc. Podsumowując: idempotentność jest jedną z największych wartości korzystania z Ansible.

Czym jest YAML?

YAML z angielskiego jest rekursywnym akronimem. Ludzie ze świata IT często tworzą takie skrótowce. Jest to swoisty rodzaj żartu, w którym rozwinięcie nazwy zawiera swoją nazwę. Idealnym przykładem jest tutaj skrót GNU. Oznacza on – GNU’s not Unix. Jak widać, pytanie, czym jest GNU, można zadawać rekursywnie (czyli odwołując się do samego siebie w nieskończoność). W poniższym przypadku tłumaczymy 3 razy, czym jest GNU. Nawiasy pokazują wywołania rekursywne.

(((GNU’s not UNIX)’s not Unix)’s not Unix)

Jak wcześniej pisałem, YAML też należy do rekursywnych akronimów. YAML – YAML Ain’t Markup Language. W bardzo swobodnym tłumaczeniu na język polski, otrzymamy: Yaml nie jest znacznikowym językiem. Znowu pytając 3 razy, czym jest YAML, otrzymamy:

(((YAML Ain’t Markup Language) Ain’t Markup Language) Ain’t Markup Language)

Osoby związane z szeroko pojętym wytwarzaniem oprogramowania znają i rozumieją koncepcje języków służących do serializacji danych (opisywania w sposób strukturyzowany). Jednym z najpopularniejszych takich języków jest JSON. Jako ciekawostkę należy dodać, iż YAML jest nadzbiorem języka JSON. Parser YAML-a bez problemów odczyta JSON-a.

Zapiszmy teraz kilka podstawowych typów danych w tych językach.

{ 
   "zmienna": "To jest deklaracja zmiennej", 
   "lista": [ 
      "obiekt1", 
      "obiekt2", 
      "obiekt3" 
   ], 
   "zwierzatka": [ 
      { 
         "imie": "Cezar", 
         "rodzaj": "Pies", 
         "urodziny": "10-10-2010" 
      }, 
      {
         "imie": "Fafik", 
         "rodzaj": "Pies", 
         "urodziny": "20-12-2014" 
      } 
   ] 
}
---
# YAML Wspiera komentarze! 
zmienna: 'To jest deklaracja zmiennej' 

lista: 
    - 'obiekt1' 
    - 'obiekt2' 
    - 'obiekt3' 

zwierzatka: 
       - imie: Cezar 
         rodzaj: Pies 
         urodziny: 10-10-2010 
       - imie: Fafik 
         rodzaj: Pies 
         urodziny: 20-12-2014

Wyobraźmy sobie teraz, że potrzebujemy zapisać naszą konfigurację, w którymś z formatów. Nie muszę chyba nikogo przekonywać, że YAML jest bardziej, o ile nie najbardziej czytelnym formatem do serializacji danych.

Ansible został napisany w Pythonie, który tak samo, jak YAML strukturyzuje się przy pomocy białych znaków (spacji, tabulacji). Pythona uznaje się za jeden z najczytelniejszych języków, który swoją budową wymusza na programistach pisanie składnego kodu. Wybór YAML-a nie jest więc absolutnie przypadkowy.

Czym są playbooki?

W poprzednim artykule mieliśmy już okazję napisać własnego playbooka. Została tam podana krótka definicja, którą postaram się teraz rozwinąć.

Playbook jest listą sztuk do odegrania (ang. plays), składających się z zadań, które Ansible powinien na danej maszynie wykonać. Każde zadanie informuje o tym, jaki moduł winien zostać wykonany. Ansible wykona zapisane w ten sposób zadania według kolejności zapisu. Jest to bardzo istotny fakt, o którym nie należy zapominać. Dla przykładu konfiguracja usługi przed jej instalacją może się udać! Niefortunnie osoba pisząca playbooka wykonała go na maszynie, na której dana usługa była już przedtem zainstalowana. Zgodnie z Prawami Murphiego błąd ten najprawdopodobniej odkryjemy w najgorszej dla siebie sytuacji. Np. po przypadkowym skasowaniu środowiska, kilka minut przed spotkaniem, na którym mamy pokazać produkt działający na skasowanym środowisku. Ilość stresu w czasie takiej wpadki jest już wystarczająca, byśmy popełniali następne błędy :-)

Struktura playbooka wygląda następująco:
-> sztuki do odegrania (plays)
-> zadania (tasks)
-> moduły (modules)

Należy jeszcze dodać tzw. uchwyty (handlery), czyli fragmenty kodu, które są wywoływane (ang. triggered) przez taski. Różnią się one jednak tym, że są wykonywane pod koniec playbooka.

Nie mogę używać zewnętrznych repozytoriów rpm! Jak zainstalować Ansible?

Czasami zdarza się, że różne regulaminy, umowy, skrypty audytujące itp. nie pozwalają na podpinanie repozytoriów trzecich. Ansible możemy jednak zainstalować na minimalnej instalacji z kilkoma pakietami dodatkowymi. Wciąż jednak niezbędny jest dostęp do internetu. W przypadku jego braku sprawa staje się bardziej skomplikowana. Instalacja jest wciąż możliwa, jednak taki przypadek jest dużo bardziej szczegółowy. Pozwolę sobie go sobie pominąć. Na początku zainstalujmy easy_install, który jest dostarczony standardowo w EuroLinuxie (oraz innych systemach z rodziny Enterprise Linux).

Poniższe instrukcje zostały wykonane na minimalnej instalacji zarejestrowanego EuroLinuxa w wersji 7.

Większość z nas nie ma w zwyczaju pamiętania nazw wszystkich pakietów. Wykorzystamy więc yum-a do znalezienia odpowiedniego pakietu:

sudo yum whatprovides '*/easy_install'

Output zwróci nam pakiet python-setuptools:

sudo yum install python-setuptools

Mając teraz easy_install, możemy zainstalować pip-a, który, jak easy_install, jest menadżerem pakietów Pythona, jednak znacząco bardziej rozbudowanym:

sudo easy_install pip

Próba zainstalowania Ansible przez pip-a bez odpowiednich bibliotek zwróci nam błędy:

Brak komendy gcc, następnie o braku źródeł openssl, by w trzecim wywołaniu przypomnieć nam o braku źródeł Pythona. Instalujemy więc niezbędne pakiety:

sudo yum install python-devel openssl-devel gcc

Teraz jesteśmy gotowi na instalację Ansible:

sudo pip install ansible

Po wykonaniu tej komendy możemy sprawdzić, czy Ansible rzeczywiście znajduje się w systemie:

ansible --version

Zwróci na output:

ansible 2.3.0.0
config file =

Jak widzimy, nie mamy w tym momencie stworzonego pliku konfiguracyjnego. Po stworzeniu pustego pliku:

sudo mkdir -p /etc/ansible/ && sudo touch /etc/ansible/ansible.cfg

dostaniemy:

ansible 2.3.0.0
config file = /etc/ansible/ansible.cfg

Postawmy Apache

Apache jest najpopularniejszym na świecie serwerem WWW, który jest tworzony przez znaną fundację o tej samej nazwie.
W pliku inventory zdefiniowaliśmy apps.local. Będzie to serwer, z pracującym Apache, udostępniający naszą aplikację. Zanim jednak będziemy w stanie ją zainstalować, zainstalujmy samego Apache i otwórzmy go na świat zewnętrzny.

Piszemy więc następującego playbooka.

---
- hosts: apps
  remote_user: root
  tasks:

# Install
  - name: Install httpd
    yum:
    name: httpd
    state: latest

# Enable
  - name: ensure httpd is running (and enable it at boot)
    service: name=httpd state=started enabled=yes
  - name: ensure firewalld is running (and enable it at boot)
    service: name=firewalld state=started enabled=yes

# Config
  - name: Open http on firewall
    firewalld:
    service: http
    permanent: true
    state: enabled

# Not used - waiting for better times
 handlers:
  - name: restart apache
    service: name=httpd state=restarted
  - name: restart firewalld
    service: name=firewalld state=restarted

Wykonujemy teraz:

ansible-playbook setup_httpd.yaml

W celu przetestowania naszego rozwiązania proponuję użyć trywialnego skryptu:

curl -s http://apps.local >/dev/null || echo "HTTP TO apps.local FAILED"

Jeśli widzimy komunikat:

"HTTP TO apps.local FAILED"

oznacza to, że coś poszło nie tak. Należy zajrzeć do wyjścia Ansible i zobaczyć, który fragment naszego playbooka się nie wykonał.

Zakończenie

Po przeczytaniu artykułu czytelnik powinien wiedzieć, czym jest idempotentność oraz język służący do serializacji, czy YAML jest czytelnym językiem, a także, z czego składa się playbook. Czytelnik powinien móc napisać także kolejny prosty playbook.

Idąc za sugestiami czytających, materiał o Ansible Galaxy, gdzie jest wiele tzw. gotowców, będzie kończył nasz cykl. Chodzi o to, by nie zepsuć całej poznawczej zabawy. W kolejnej 3 części będziemy pisać następne playbooki, aż do momentu, kiedy zobaczymy, że jeśli czegoś jest za dużo, to zazwyczaj jest źle. Wtedy zrefaktorujemy nasze playbooki, zobaczymy, czym są role i jak można lepiej zarządzać naszym kodem.

blank Autorzy

Artykuły na blogu są pisane przez osoby z zespołu EuroLinux. 80% treści zawdzięczamy naszym developerom, pozostałą część przygotowuje dział sprzedaży lub marketingu. Dokładamy starań, żeby treści były jak najlepsze merytorycznie i językowo, ale nie jesteśmy nieomylni. Jeśli zauważysz coś wartego poprawienia lub wyjaśnienia, będziemy wdzięczni za wiadomość.