Ansible kontra Puppet

   Ansible Automatyzacja Puppet 0 Comments

Mam kolegę, którego niekoniecznie chciałbym w tym momencie zrobić sławnym, ale niedawno zaczął swoją podróż przez świat automatyzacji, pracując w jednej z tych firm, które wymagają transformacji, ponieważ wiele rzeczy wymknęło się spod kontroli, co często dzieję się w firmach, które odnoszą sukces w bardzo szybkim czasie, przez co rozrastają się z firmy zatrudniającej 10 osób do firmy zatrudniającej kilkaset osób. Ważne jest to też w szczególności, kiedy współpracuje się z ludźmi raczej nie żywiącymi zbytniego entuzjazmu ze swojej pracy.. przychodzą, bo im za to płacą. Próba zachęcenia takich ludzi do jakiejkolwiek zmiany jest bardzo ciężka, szczególnie jak nie ma się wsparcia ze strony managerów, którzy de facto nie interesują się tym co się dzieje, dopóki się coś nie stanie, a nawet jak się stanie to nie wyciąga się z tego żadnych wniosków. W każdym bądź razie, post jest o czym innym i nie chcę się zagłębiać w temat zmian kulturowych w danych firmach…

Zawsze kiedy rozmawiamy (ja i kolega wspomniany wyżej), zadaje mi milion pytań na temat jak ja coś robię u siebie w pracy, czy jak rozwiązuje dane problemy albo po prostu pyta o sugestie jak on by mógł rozwiązać dane problemy.

Zdałem sobie wtedy sprawę, że ludzie ciągle maja problem ze znalezieniem różnic pomiędzy Ansible i Puppetem i żeby być szczerym – ja wywodząc się z Puppeta – kiedy musiałem się przerzucić kompletnie na Ansible z powodu mojej pracy, zacząłem faworyzować Ansible ponad Puppeta. Wtedy mój kolega, dostając białej gorączki, miał jeszcze większy mętlik w głowie, bo jak mu opowiadam o Ansible, on się zastanawia po co tak naprawdę „sprzedałem” mu Puppeta, skoro już go nie faworyzuje tak bardzo… cóż nie jest to do końca prawda. Inna sprawa jest taka, że wszystkie źródła, które dostaję od w/w kolegi w języku polskim, są raczej średnie, a część z nich nie była aktualizowana od dawna, technologia zmienia się z dnia na dzień i nie ma rzetelnych materiałów na wiele tematów. Absolutnie nie sugeruję, że mój artykuł będzie się czymś wyróżniał albo po prostu będzie dużo lepszy od innych.

Różnice w skrócie.

W bardzo dużym skrócie Puppet i Ansible różnią się. Ze swojego doświadczenia, ja bym normalnie sklasyfikował oba rozwiązania bazując na ich przeznaczeniu – o tym niżej – ale zanim cokolwiek zaczniesz, musisz wiedzieć czym generalnie jest Ansible i czym jest Puppet. Żeby to wyjaśnić, zerknij proszę na angielskojęzyczna stronę Wikipedii – Ansible and Puppet. Jeśli spojrzysz na pierwsze zdania, opisujące oba rozwiązania, zauważysz że Puppet i Ansible, tak naprawdę, wykorzystywane są do trochę innych rzeczy. Na stronie Wikipedii możesz wyczytać, że Puppet jest bardziej narzędziem do zarządzania konfiguracją (Configuration Management Tool), gdzie Ansible jest bardziej orkiestratorem, zaopatrzeniowcem, narzędziem do konfiguracji oraz narzędziem do budowania infrastruktury. Ciężko to wytłumaczyć na język polski, ale posługujemy się tutaj terminami takimi jak provision, deploy, configure. Puppet oczywiście może też dostarczyć i skonfigurować infrastrukturę, ale ja osobiście bym się bardzo zastanowił zanim bym wybrał do tego celu Puppeta. Rozwijąc powyższe, wyobraź sobie sytuację, w której możesz zbudować całe swoje środowisko (dla małego przykładu) np. swój blog na WordPressie całkowicie używając kodu, czyli zbudowania albo wysłać żądanie do swojego hypervisora o dostarczenie maszyny wirtualnej, na której potem instalujesz wszystko co trzeba żeby Twój blog na WordPressie działał… i to wszystko opisane kodem, system który sam wpina się w API hypervisora albo dostawcy i buduje wszystko za Ciebie tak jak tego chcesz. To jest własnie ten etap dostarczania i jakby zaopatrzenia się w wymaganą infrastrukturę, zanim zaczniesz etap konfiguracji.

Ansible jest proste! (Tak mi mówili!)

- hosts: localhost
  tasks:
    - name: Ansible jest proste!
      debug:
        msg: "Tak mi mówili!"
class debug {
  notify { "Puppet też, prawda?": }
}

Tak na marginesie, Ansible wykorzystuje język YAML, kiedy Puppet ma swój własny język deklaratywny. W przypadku kiedy popełnisz jakiś błąd w Puppecie, Puppet lint albo po prostu agent podczas uruchomienia wskaże błąd, ponieważ kod Puppeta się nie skompiluje. W Ansible zadania są wykonywane w określonym porządku, jedno po drugim, tak jak jest napisane w playbooku, więc jeśli zrobiłeś błąd gdzieś po środku, Ansible dopiero po dotarciu do tego etapu w playbooku wyrzuci błąd. W tym przypadku informację zwrotną, która oczekujesz – tzw. feedback – dostaniesz dużo później – jednym słowem, stracisz czas czekając na ten błąd. W przypadku Puppeta, nie miałoby to raczej miejsca, ponieważ kod by się nie skompilował już na starcie. Ktoś by powiedział „OK ale Puppet też używa YAML”… oczywiście używa do hiera i wtedy błędy w hiera mogą prowadzić do takich samych efektów jak w przypadku Ansible. Żeby nieco przybliżyć to co mam na myśli spójrz na poniższy kod:

- var1:
    a: 1
    b: 2
- var2:
  a: 1
  b: 2

yaml lint nie zwróci żadnego błędu ani ostrzeżenia, ale powyższy zapis i deklaracja, psuje całą strukturę. Zobaczmy poniższy playbook:

---
- hosts: localhost
  vars:
    - var1:
        a: 1
        b: 2
    - var2:
      a: 1
      b: 2
 
  tasks:
    - name: Vars debug
      debug:
        msg: "{{ item }}"
      with_items:
        - "{{ var1 }}"
        - "{{ var2 }}"

Jeżeli go „zlintujemy” i uruchomimy:

$ yamllint yamllint.yml && echo $?
0
$ ansible-playbook yamllint.yml 
 [WARNING]: Could not match supplied host pattern, ignoring: all

 [WARNING]: provided hosts list is empty, only localhost is available


PLAY [localhost] ***************
TASK [Gathering Facts] ***************
ok: [localhost]

TASK [Vars debug] ***************
ok: [localhost] => (item={u'a': 1, u'b': 2}) => {
    "item": {
        "a": 1, 
        "b": 2
    }
}

MSG:

{u'a': 1, u'b': 2}

ok: [localhost] => (item=None) => {
    "item": null
}

PLAY RECAP ***************
localhost   : ok=2    changed=0    unreachable=0    failed=0   

Jak widać na powyższym przykładzie, pierwszy element został poprawnie znaleziony z poprawnymi wartościami klucz=>wartość, podczas gdy drugi element był pusty (null). Stało się tak z powodu błędów wcięcia w linii ( o 2 spacje dokładnie), co spowodowało kompletną zmianę w strukturze danych. Ktoś by powiedział „Tak, ale to jest przecież oczywiste i widoczne na pierwszy rzut oka”… jest i nie jest, zwłaszcza dla ludzi zaczynających z YAML i z tego co ja doświadczyłem (również na sobie), błędy polegające n a złym wyrównaniu (indentacji czy tam wcięciu linii) są najbardziej popularnymi błędami i czasami bardzo ciężkimi do wychwycenia. Co ważniejsze, w swojej karierze doświadczyłem dużo bardziej poważnych błędów w hierarchii YAML, prowadzących do kompletnie niespodziewanych efektów, ponieważ prosty błąd we wcięciu potrafi zmienić cała hierarchie danych albo jej strukturę. YAML niestety nie jest językiem bardzo przyjaznym do debuggowania.

Wniosek jest taki, że Ansible może być tak proste do nauki jak Puppet, albo tak samo skomplikowane do nauki jak Puppet.

Ansible musi wiedzieć gdzie wykonywać swoje zadania…

… tak samo jak i Puppet, aczkolwiek Ansible bardzo restrykcyjnie polega na inwentarzu (inventory), który jest zarządzany poprzez plik z listą hostów, albo przez dynamiczny lub tzw. smart inwentarz. W drugim przypadku sprawa wygląda troche inaczej, ale jeśli chcesz uruchomić zadanie na serwerze, który nie znajduje się w inwentarzu, Ansible po prostu nie wykona nic. Puppet działa trochę inaczej, ponieważ „wpina się” on do mastera i wtedy master zarządza całym inwentarzem, ale nadal musisz zadeklarować konfiguracje node’ów (serwerów), chyba że użyjesz deklaracji default (domyślnej). Puppet wymaga również podpisywania certyfikatów node’ów, które są podpisywanie ręcznie z mastera (poprzez komendę), wtedy master (ale też i Ty) ma pewność, że kod nie jest wykonywany na serwerach, które nie są częścią infrastruktury, albo które zostały zespoofowane. Oczywiście puppet posiada mechanizm automatycznego podpisywania certyfikatów.

Nie odkrywaj Ameryki na nowo…

Moduły. Oba rozwiązania dysponują świetnym zestawem modułów rozbudowujących funkcjonalność. Moduły są pisane przez różnych ludzi i są trzymane w konkretnych repozytoriach. Puppet ma swoje Puppet Forge, a Ansible Ansible Galaxy. Kiedy potrzebuje napisać coś nowego, zawsze idę i sprawdzam te zasoby, żeby oszczędzić sobie kłopotów i czasu. Jeśli znajdę moduł, który robi to co potrzebuję, to po prostu go wykorzystuję, zamiast pisać swój własny*. Z mojego doświadczenia mogę powiedzieć, że Puppet jest niezwyciężonym mistrzem jeśli chodzi o dostępne modły i ich jakość. Puppet Forge posiada ogromną bibliotekę z dostępnymi modułami i co bardzo pomaga, część modułów jest pisana i rozwijana bezpośrednio przez ludzi z PuppetLabs, ale niektóre moduły, które spełniają określone kryteria są nagradzane statusami takimi jak Supported (czyli wspierany) i Approved (czyli zatwierdzony) przez ludzi z PuppetLabs, co potwierdza jakość tych modułów.To naprawdę pomaga w podjęciu decyzji, który moduł powinno się wybrać, ponieważ każdy chce używać modułów, które są rozwijane. Dlatego tutaj moim zdaniem Ansible przegrywa bitwę. Dodatkowo próbowałem ostatnio używać modułów z Ansible Galaxy i 2 z 3 modułów, które użyłem wymagały moich modyfikacji, ponieważ nie do końca robiły to czego oczekiwałem. Nie chcę tutaj robić nikomu anty reklamy, ale napiszę tylko tyle, że używałem modułów do konfiguracji collectd i agenta Zabbix.

*odnośnie gwiazdki powyżej: potrzebował bym spędzić dość sporą ilość czasu, żeby napisać moduł, który miałby szansę na to żeby dostać status Approved albo Supported, wiec po co, skoro ktoś już to zrobił i na pewno lepiej.

Moment, ale Puppet potrzebuje dodatkowych komponentów!

Tak, to prawda. Jest to argument, który bardzo często słyszę na niekorzyść Puppeta, że wymaga dodatkowej infrastruktury. To jest fakt, jeśli chcesz w pełni korzystać z dobroci Puppeta, to potrzebujesz dodatkową infrastrukturę. Jeśli chcesz mieć dostęp do raportów, faktów o node’ach itd, to potrzebujesz puppet mastera (albo kilka) oraz puppetdb. Bardzo często ludzie sugerują, że jest to niepotrzebny koszt w infrastrukturze. Nie mogę jednoznacznie udzielić tutaj odpowiedzi na ten problem, ponieważ zaleta lub też wada tego rozwiązania, niesie za sobą pewne korzyści, które w przypadku danego zastosowania nie muszą być kompletnie przydatne.

Kolejnym argumentem przeciwko Puppetowi jest, jeśli chcesz uruchomić Puppeta, tak – potrzebujesz… puppeta (pakietu). Co prawda, że jeśli chcesz uruchomić jakikolwiek playbook w Ansible, też potrzebujesz Ansible ;-). Różnica jest taka, że ansible-playbook może być wykonane z jakiegoś centralnego miejsca, gdzie Puppet potrzebuje mieć agenta działającego na każdy z node’ów, chyba że będziesz używał Puppeta w konfiguracji bez mastera (masterless), więc wtedy będziesz potrzebował cały kod, konfiguracje oraz manifesty lokalnie na danej maszynie + dodatkowo pakiet puppeta – co dla mnie jest… raczej średnim rozwiązaniem.

Czyli w skrócie, Puppet zawsze będzie wymagał dodatkowej infrastruktury czy komponentów, niezależnie czy dziala przez mastera, czy bez mastera.

Anible może tworzyć i konfigurować infrastrukturę w tym samym czasie…

Tak, to prawda. Możesz używać wbudowanych modułów w Ansible, żeby się wpiąć w dostępne API różnych dostawców (AWS, VMWare, Azure, etc), więc tym samym możesz opisać swoją infrastrukturę jako kod i skonfigurować ja też przy użyciu jednego narzędzia. Oczywiście Puppet od jakiegoś czasu też posiada taką funkcjonalność (dla AWS  np.) ale szczerze powiedziawszy, bardzo dziwnie bym się czuł jeśli nawet nie mówiąc, że niekomfortowo żeby użyć Puppeta do tego celu… ale to jest moje osobiste odczucie. Naprawdę bardziej do tego celu bym użył terraform albo właśnie Ansible.

Dostarczanie i tworzenie skomplikowanej infrastruktury i konfiguracji…

Tak jak wspomniałem wcześniej, do pewnych zadań preferowałbym użycie Ansible (albo terraform) zamiast Puppeta. Jeśi masz skomplikowane środowisko, które posiada wiele zależności między sobą – np. podczas budowania całego stacku, jakaś maszyna czy komponent musi czekać aż drugi zostanie skonfigurowany i dostarczony to ja osobiście nie używałbym tutaj Puppeta. Nie wiem nawet – szczerze mówiąc – czy taki schemat byłby wykonalny w przypadku Puppeta (patrząc na sama naturę tego softu). Osobiście też nigdy czegoś takiego w Puppecie nie robiłem.

Tak czy inaczej uważam, że skomplikowane konfiguracje mogą być równie ciężkie albo równie łatwe w Ansible tak samo i w Puppecie. Jeśli jesteś w sytuacji, w której musisz zarządzać taką skomplikowaną infrastrukturą, to w wielu wypadkach i tak będziesz musiał pisać swój kod, swoje moduły, swoje szablony itd, wiec wybór narzędzia może być tylko i wyłącznie własną preferencją – np. to w czym czujesz się pewniej czy lepiej.

Cykliczne wykonywanie zadań…

Czasami zachodzi potrzeba wykonania pewnych zadań co jakiś czas, albo w jakiś określonych odstępach czasowych. Agent Puppeta uruchamia się domyślnie co 30 minut upewniając się, że stan konfiguracji na danej maszynie jest zgodny z jej opisem w katalogu i jeśli nie jest, agent dokonuje zmian. Ansible nie ma takiego mechanizmu w ogóle, więc jeśli wymagasz tego, że Twoje playbooki będą wykonywane w jakiś tam odstępach czasu (czy o określonym czasie) potrzebujesz Ansible Tower, który został wypuszczony jako OpenSource jakiś czas temu pod nazwa AWX. Tu się pojawia pewien problem opisywany powyżej. Pamiętasz jak mówiłem o tym, ze Puppet jest „be”, bo wymaga dodatkowej infrastruktury? No właśnie, jeśli potrzebujesz żeby Ansible miało dodatkowe funkcje lub – jak ja to mówię – fajerwerki, potrzebujesz właśnie tą dodatkową infrastrukturę, niezależnie od rozwiązania. Chyba, że będziesz uruchamiał playbooki z crona… jednak jakość tego rozwiązania według mnie pozostawia wiele do życzenia.

„Puppet to narzędzie diabła, bo…

…nie pozwala mi wykonywać zmian ręcznych na serwerach… takich o … ad-hoc”

– tak, kiedy agent Puppeta się uruchomi, to wszystkie te zmiany ręcznie (o których wie Puppet i które może porównać) zostaną przywrócone tego jak to zostało opisane w katalogu. Ale czy nie jest to coś czego tak naprawdę oczekujesz?

– „Nie do końca, chcę wprowadzać zmiany ręcznie, więc chyba zatrzymam albo wyłączę agenta Puppeta”

– tak możesz, ale zdecydowanie tego nie chcesz.

Dla niektórych ten dialog jest bardzo znany. Głównym zadaniem wykonywania cyklicznych działań, czyli posiadanie jakiegoś „schedulera” jest to, żeby kod który opisuje dane środowisko czy system, miał przełożenie na rzeczywistość. Po to zadajemy sobie trud, żeby opisywać usługi, serwery, sieci kodem, żeby mieć spójność i żeby kontrolować te zmiany. Dzięki temu mamy pewność, że jeden system w danym środowisku, ma dokładnie (lub bardzo zbliżoną) konfigurację w innym środowisku (np. dev i test). Każdy z nas wprowadzał zmiany ręcznie, każdy z nas (wliczając mnie) wyłączał agenta puppeta, żeby sprawdzić czy coś będzie działać. Dzieje się to w sytuacji, kiedy nie mamy dostępu do środowiska testowego, głównie dlatego, że ono nie istnieje. Życie nie jest idealne niestety i w niektórych wypadkach niektóre reguły trzeba ponaginać, ale powinniśmy wszyscy starać się (i to bardzo), żeby robić wszystko tak jak to powinno być zrobione (bez fuszery czy szybkich fixów). Po to zadajemy sobie trud pisania kodu, żeby przestrzegać pewnych zasad.

Podział kodu ze względu na środowisko…

Puppet posiada jedno z najlepszych narzędzi jakie kiedykolwiek widziałem i nazywa się ono r10k. Jestem absolutnie zakochany w nim od momentu kiedy rozgryzłem jak się tego używa. W dużym skrócie, r10k to jest narzędzie do zarządzania modułów i kodu Puppeta w zależności od tego, w jakim środowisku znajduje sie dany system. r10k buduje cały kod wymagany przez dane środowisko od podstaw, na podstawie tego co jest zawarte w pliku Puppetfile (proszę wygoogluj sobie). Oznacza to, że nie bedzie jakiś przypadkowych (ręcznie ściągniętych) modułów i każdy moduł jest trzymany w osobnym repozytorium git. Ułatwia to w dużym stopniu zarządzanie modułami, ich rozwijanie jest dużo łatwiejsze. Testowanie zmian w środowiskach jest prostsze, ponieważ możemy się odnieść do różnych wersji, commit pointów, gałęzi kodu itd w danym środowisku.

Dla przykładu powiedzmy, że to jest mój plik Puppetfile w środowisku produkcyjnym:

forge 'http://forge.puppetlabs.com'

mod 'camptocamp/systemd', '0.4.0'
mod 'puppet/selinux', '1.1.0'
mod 'puppet/staging', '2.2.0'
mod 'puppet/zabbix', '4.1.1'
mod 'puppetlabs/apache', '1.11.0'
mod 'puppetlabs/apt', '2.4.0'
mod 'puppetlabs/concat', '2.2.1'
mod 'puppetlabs/firewall', '1.9.0'
mod 'puppetlabs/mysql', '3.11.0'
mod 'puppetlabs/pe_gem', '0.2.0'
mod 'puppetlabs/postgresql', '4.9.0'
mod 'puppetlabs/ruby', '0.6.0'
mod 'puppetlabs/stdlib', '4.17.0'
mod 'garethr-docker', '5.3.0'
mod 'stahnma-epel', '1.2.2'
mod 'profiles', 
  :commit => '78c65fd9fe0434db11e5f14ed12385052ce627e9', 
  :git    => 'git@YOUR_GITLAB:puppet4/profiles.git'

kiedy uruchomisz r10k deploy environment production -p zakładając, że powyższy plik Puppetfile znajduje się w gałęzi production w git, r10k pobierze wszystkie moduły zawarte w Puppefile oraz moduł profili z mojego prywatnego repozytorium git z określonym commit pointem (78c65…). Teraz załóżmy, że dokonałem jakiś zmian, które chcę przetestować w środowisku QA. Mam więc gałąź QA w swoim repozytorium git, więc zmieniam plik Puppetfile jak poniżej:

forge 'http://forge.puppetlabs.com'

mod 'camptocamp/systemd', '0.4.0'
mod 'puppet/selinux', '1.1.0'
mod 'puppet/staging', '2.2.0'
mod 'puppet/zabbix', '4.1.1'
mod 'puppetlabs/apache', '1.11.0'
mod 'puppetlabs/apt', '2.4.0'
mod 'puppetlabs/concat', '2.2.1'
mod 'puppetlabs/firewall', '1.9.0'
mod 'puppetlabs/mysql', '3.11.0'
mod 'puppetlabs/pe_gem', '0.2.0'
mod 'puppetlabs/postgresql', '4.9.0'
mod 'puppetlabs/ruby', '0.6.0'
mod 'puppetlabs/stdlib', '4.17.0'
mod 'garethr-docker', '5.3.0'
mod 'stahnma-epel', '1.2.2'
mod 'profiles', 
  :branch => 'qa', 
  :git    => 'git@YOUR_GITLAB:puppet4/profiles.git'

po wykonaniu r10k wskazując na środowisko QA, wszystkie moduły i zmiany zostaną do tego specyficznego środowiska wgrane, wiec potem mogę uruchomić agenta na jakieś maszynie wskazując na przynależność do środowiska QA, dzięki czemu kod dla tego środowiska zostanie wykonany. r10k jest naprawdę świetne i zdecydowanie wymaga osobnego artykułu.

Nie znam i nie umiem sobie wyobrazić czegoś podobnego dla Ansible.

Podsumowanie

Oba rozwiązania są na pewno warte rozpatrzenia. Jeśli jesteś inżynierem sieci albo storage, wydaje mi się że nawet nie masz co patrzeć na Puppeta. Ansible wygrywa na pewno na polu dostarczania i konfiguracji całych środowisk oraz gdzie konkretna kolejność wykonywania zadań lub zależności między nimi jest bardzo ważna. Ansible jest również dobre jeśli chodzi o środowiska, które są przebudowywane bardzo często (np. codziennie, albo co commit), ponieważ zdejmuje z barków cały problem z zarządzaniem i czyszczeniem certyfikatów w Puppecie. Nie jest to jakieś bardzo duże wyzwanie. Puppet w takich środowiskach może sprawdzić się równie dobrze. Puppet jest na pewno rozwiązaniem bardziej dojrzałym i ma moim zdaniem dużo więcej do zaoferowania. Ansible może być łatwiejsze na starcie, ale może się potem okazać bardzo skomplikowane (przy kompleksowych zadaniach). Ansible bez Ansible Tower nie jest 'odporne na idiotów’ (ani też bezpieczne), ponieważ dając komuś dostęp do playbooków i inwentarza, dajemy danej osobie dostęp do calej infrastruktury i wszystkich playbooków nie tylko selektywnie wybranych. Wyobraź sobie sytuacje, że musisz zmienić hasło na roota (zgodnie z wymogami polityki bezpieczeństwa firmy), które jest zapisane w Ansible Vault, uruchamiasz swojego playbooka, patrzysz, czekasz, nagle w połowie wykonywania zadania okazuje się, że hash SHA256 hasła się nie zgadza… jeśli używasz autoryzacji przez hasło na konto roota (co jest raczej niepolecane – ładnie mówiąc), to właśnie straciłeś dostęp do polowy swojej infrastruktury bez odwrotnie. Ta sama sytuacja ma się w przypadku zmiany klucza SSH… jeśli coś zrobisz źle, nie ma odwrotu (w łatwy sposób). Z Puppetem sytuacja jest trochę inna. Z racji tego, że agent niezależnie działa na maszynie, nawet w przypadku takich błędów, można je poprawić i za pół godziny, następne wykonanie agenta poprawi taką wpadkę. W moim przekonaniu Puppet ma dużo więcej modułów, które to też są dużo lepszej jakości. W mojej opinii Puppet jest bardziej „enterprise ready”. Ansible bez Ansible Tower jest tak naprawdę bardzo ograniczone.