Ansible – Metadane
Ostatnio zostałem zaproszony jako prelegent na spotkanie białostockiej grupy użytkowników Pythona – PyStok#22. Jakość dźwięku w nagraniu, niestety, nie zachwyca, stąd linkuję również do samej prezentacji. Napisałem także artykuł w którym omówiłem dobre praktyki korzystania z Ansible. Ostatnie przemyślenia, jednak, budzą we mnie obawę pozostawienia Czytelnika z niewiedzą i pewnymi niedopowiedzeniami. Zdecydowałem zatem, że warto unikać niedomówienia, choćby w imię swojego dobrego imienia. Chciałbym, zatem, naprawić błędy przeszłości i rozpocząć serię artykułów o Ansible. Postaram się poruszać trzy aspekty:
- dobre praktyki,
- problemy wraz z ich rozwiązaniami,
- przykłady użycia.
Zapraszam do pierwszego z serii artykułów o Ansible; dzisiaj będzie mowa o wykorzystaniu metadanych.
Wprowadzenie
Zasadniczo, zaglądając do dokumentacji Ansible znajdujemy podstawy jeśli chodzi o strukturę katalogów [4] dla pojedynczej roli.
production # inventory file for production servers staging # inventory file for staging environment group_vars/ group1 # here we assign variables to particular groups group2 # "" host_vars/ hostname1 # if systems need specific variables, put them here hostname2 # "" library/ # if any custom modules, put them here (optional) filter_plugins/ # if any custom filter plugins, put them here (optional) site.yml # master playbook webservers.yml # playbook for webserver tier dbservers.yml # playbook for dbserver tier roles/ common/ # this hierarchy represents a "role" tasks/ # main.yml # <-- tasks file can include smaller files if warranted handlers/ # main.yml # <-- handlers file templates/ # <-- files for use with the template resource ntp.conf.j2 # <------- templates end in .j2 files/ # bar.txt # <-- files for use with the copy resource foo.sh # <-- script files for use with the script resource vars/ # main.yml # <-- variables associated with this role defaults/ # main.yml # <-- default lower priority variables for this role meta/ # main.yml # <-- role dependencies library/ # roles can also include custom modules lookup_plugins/ # or other types of plugins, like lookup in this case webtier/ # same kind of structure as "common" was above, done for the webtier role monitoring/ # ""
W dzisiejszym artykule skupimy się na dobrych praktykach i przypadkach użycia jednego pliku: roles/common/meta/main.yml
.
Treść
Korzystając z narzędzia: ansible-galaxy
spróbujemy wygenerować przykładową rolę i zaczerpniemy z niej metadane.
$ ansible-galaxy init role
Odczytując teraz plik: role/meta/main.yml
, otrzymujemy taki oto szkic.
galaxy_info: author: your name description: your description company: your company (optional) # If the issue tracker for your role is not on github, uncomment the # next line and provide a value # issue_tracker_url: http://example.com/issue/tracker # Some suggested licenses: # - BSD (default) # - MIT # - GPLv2 # - GPLv3 # - Apache # - CC-BY license: license (GPLv2, CC-BY, etc) min_ansible_version: 1.2 # Optionally specify the branch Galaxy will use when accessing the GitHub # repo for this role. During role install, if no tags are available, # Galaxy will use this branch. During import Galaxy will access files on # this branch. If travis integration is cofigured, only notification for this # branch will be accepted. Otherwise, in all cases, the repo's default branch # (usually master) will be used. #github_branch: # # Below are all platforms currently available. Just uncomment # the ones that apply to your role. If you don't see your # platform on this list, let us know and we'll get it added! # #platforms: #- name: OpenBSD # versions: # - all # - 5.6 # - 5.7 # - 5.8 # - 5.9 # - 6.0 #- name: Fedora # versions: # - all # - 16 # - 17 # - 18 # - 19 # - 20 # - 21 # - 22 # - 23 # - 24 # - 25 #- name: DellOS # versions: # - all # - 10 # - 6 # - 9 #- name: MacOSX # versions: # - all # - 10.10 # - 10.11 # - 10.12 # - 10.7 # - 10.8 # - 10.9 #- name: Synology # versions: # - all # - any #- name: Junos # versions: # - all # - any #- name: GenericBSD # versions: # - all # - any #- name: Void Linux # versions: # - all # - any #- name: GenericLinux # versions: # - all # - any #- name: NXOS # versions: # - all # - any #- name: IOS # versions: # - all # - any #- name: Amazon # versions: # - all # - 2013.03 # - 2013.09 # - 2016.03 # - 2016.09 #- name: ArchLinux # versions: # - all # - any #- name: FreeBSD # versions: # - all # - 10.0 # - 10.1 # - 10.2 # - 10.3 # - 11.0 # - 8.0 # - 8.1 # - 8.2 # - 8.3 # - 8.4 # - 9.0 # - 9.1 # - 9.1 # - 9.2 # - 9.3 #- name: Ubuntu # versions: # - all # - lucid # - maverick # - natty # - oneiric # - precise # - quantal # - raring # - saucy # - trusty # - utopic # - vivid # - wily # - xenial # - yakkety #- name: Debian # versions: # - all # - etch # - jessie # - lenny # - sid # - squeeze # - stretch # - wheezy #- name: Alpine # versions: # - all # - any #- name: EL # versions: # - all # - 5 # - 6 # - 7 #- name: Windows # versions: # - all # - 2012R2 #- name: SmartOS # versions: # - all # - any #- name: opensuse # versions: # - all # - 12.1 # - 12.2 # - 12.3 # - 13.1 # - 13.2 #- name: SLES # versions: # - all # - 10SP3 # - 10SP4 # - 11 # - 11SP1 # - 11SP2 # - 11SP3 # - 11SP4 # - 12 # - 12SP1 #- name: GenericUNIX # versions: # - all # - any #- name: Solaris # versions: # - all # - 10 # - 11.0 # - 11.1 # - 11.2 # - 11.3 #- name: eos # versions: # - all # - Any galaxy_tags: [] # List tags for your role here, one per line. A tag is # a keyword that describes and categorizes the role. # Users find roles by searching for tags. Be sure to # remove the '[]' above if you add tags to this list. # # NOTE: A tag is limited to a single word comprised of # alphanumeric characters. Maximum 20 tags per role. dependencies: [] # List your role dependencies here, one per line. # Be sure to remove the '[]' above if you add dependencies # to this list.%
Widzimy, w powyższym kodzie źródłowym, że w ramach roli jesteśmy w stanie przekazać praktycznie wszystkie niezbędne informacje wydawnicze, takie jak:
- minimalną wersja oprogramowania Ansible,
- licencję, w ramach której rola jest udostępniona,
- autora, opis i firmę obsługująca rolę,
- platformy, na które została stworzona rola.
Możemy również przekazać zależności oraz tagi, o których poniżej. Oto przykład uzupełnionego pliku z metadanymi:
--- galaxy_info: author: 'Kamil Boratyński' description: 'This role configures and starts sshd service.' company: '@linsec' license: 'GPLv3' min_ansible_version: '2.2' platforms: - name: 'EL' versions: - 7 galaxy_tags: - 'sshd' - 'centos' - 'rhel' dependencies: []
Tak jak widzimy, autorem jest moja skromna osoba; rola opisuje konfigurację serwera SSH, została wydana na licencji GPL w wersji trzeciej. Jedyną platformą, z którą jest kompatybilna to Linuksy z klasy serwer, to jest: RedHat oraz CentOS w wersji siódmej. Dodałem również 3 tagi, które pomogą potomnym skorzystać z publiczne dostępnej roli. Pełną rolę można zobaczyć na profilu GitHub [5].
Kilka autorskich rad, dlaczego uważam, że warto uzupełniać metadane dla ról; nawet tych wykorzystywanych prywatnie, wewnątrz organizacji.
- Dobrze udokumentowana rola to podstawa.
- Znajomość autora, mimo, że jest prawdopodobnie znany po dzienniku narzędzia do zarządzania kodem źródłowym, rownież jest pomocna.
- Dla publicznych ról – nieoceniona jest wiadomość pod jakie systemy była pisana. Role generyczne to z zasady tak mnoga ilość zmiennych, że ich czytelność spada do zera.
- Przeczytanie informacji w polu
description
jest dużo mniej czasochłonne niż przeczytanie zestawu zadań.
Oczywiście, cytując Clean-code [6], sama nazwa roli powinna być na tyle czytelna, żeby nieobeznany Czytelnik mógł się bez problemu poznać jej przeznaczenie
Uwaga. Ominięcie metadanych jest, rzeczywiście, do zaakceptowania dla pracy w małych zespołach, gdzie każdy członek doskonale wie nad czym pracuje i jest w stanie bez problemu przyporządkować rolę do jej zastosowania. Dla publicznych ról – potrzeba tym większa, im bardziej ogólny kod.
Zależności
Jedną z rzadziej wykorzystywanych możliwości są zależności między poszczególnymi rolami. Biorąc kod źródłowy widzimy tam następującą linijkę:
dependencies: []
Przykład wykorzystania takiej dyrektywy podaję niżej:
--- galaxy_info: author: 'Kamil Boratyński' description: 'Installs and configures docker-engine-cs.' company: '@linsec' license: 'GPLv3' min_ansible_version: '2.2' platforms: - name: 'EL' versions: - 7 galaxy_tags: - 'docker' - 'docker-engine' - 'centos' - 'rhel' dependencies: - 'centsible.repository-docker-engine-cs'
Powyższe metadane to plik wzięty z autorskiej roli, centsible.docker-engine-cs
, służącej do instalacji silnika Docker.
Przed procesem instalacji, w katalogu z rolami poszukiwana rola centsible.repository-docker-engine-cs
, która odpowiada za weryfikację dostępności repozytorium pakietów na maszynie, na której ma zostać zainstalowany silnik Docker. Obie role, choć wymagają aktualizacji wersji silnika, dostępne są na GitHub [7], [8].
Role abstrakcyjne
Jednym z ciekawszych rozwiązań dla zależności są role abstrakcyjne. Powiedzmy, że mamy w infrastrukturze kilkadziesiąt aplikacji tego samego typu, ten sam stos narzędziowy, te same pakiety, te same referencje, ta sama struktura katalogów, z wyłączeniem takich szczegółów jak nazwa aplikacji. Dobrym pomysłem jest wtedy zastosowanie roli abstrakcyjnej.
roles ├── app-java-sampleapp │ └── meta │ └── main.yml └── templates └── template-java-app ├── README.md ├── defaults │ └── main.yml ├── handlers │ └── main.yml ├── meta │ └── main.yml ├── tasks │ └── main.yml └── vars └── main.yml
Wtedy metadane dla aplikacji app-java-sampleapp
mogłyby wyglądać następująco:
--- dependencies: - role: 'template-java-app' appname: 'sampleapp' appdir: '/srv/sampleapp'
Zauważmy, że cała rola aplikacyjna app-java-sampleapp
zawarta jest tylko w metadanych. Żadnych dodatkowych zadań. Czysty, prosty kod. Tutaj, jednak, ważna uwaga.
Uwaga. Każda modyfikacja roli: template-java-app
spowoduje modyfikację wszystkich z naszych aplikacji. Takie zastosowanie jest użyteczne tylko dla aplikacji opartych o ten sam schemat.
Zakończenie
W artykule podałem kilka przypadków użycia ról oraz pewne autorskie zadania na temat metadanych w Ansible.
Każdy wybór, każde rozwiązanie powinno być uwarunkowane rozsądnym podejściem do problemu.
Jedni powiedzą bowiem, że lepiej zmienne wpisać w playbook podczas uruchamiania roli; dla innych czytelność kodu będzie tym większa, im krótsze będą zapisy w pliku playbook.yml.
Każdy problem trzeba jasno zdefiniować. Odpowiedzieć sobie na przyszłościowe pytania dotyczące rozwoju. Automatyzacja to nie tylko proces pisania kodu i narzędzi ; to przede wszystkim proces rozumowego podejścia do rozwiązania problemu przy znajomości istniejącej i przyszłej infrastruktury.
Postscriptum:
Mam też kilka pomysłów na artykuły związane z Ansible; jeśli Czytelnik jest zainteresowany jakimś tematem, bardzo proszę o informację w komentarzu lub e-mailem.
- Ansible: automatyczne testowanie ról
- Ansible: pisanie własnego modułu
- Ansible: pisanie własnego filtra
- Ansible: dlaczego nie jest idealne?
Postscriptum 2:
Artykuł został udostępniony również na łamach:
What I recommend
Cloud Field Day 21: Too many clouds