Cześć wszystkim!
Pracuję jako inżynier DevOps w serwisie rezerwacji hoteli . W tym artykule chciałbym opowiedzieć o naszym doświadczeniu w testowaniu ról ansible.
W Ostrovok.ru jako menedżer konfiguracji używamy ansible. Niedawno doszliśmy do konieczności testowania ról, ale jak się okazało, narzędzi do tego nie ma zbyt wiele — najpopularniejszym jest prawdopodobnie framework Molecule, dlatego postanowiliśmy go użyć. Okazało się jednak, że jego dokumentacja pomija wiele pułapek. Nie udało nam się znaleźć wystarczająco szczegółowego przewodnika w języku rosyjskim, więc postanowiliśmy napisać ten artykuł.

Molecule
— framework do pomocy w testowaniu ról ansible.
Uproszczony opis: Molekuła tworzy instancję na wybranej przez Ciebie platformie (chmura, wirtualka, kontener; więcej informacji w rozdziale ), uruchamia na niej Twoją rolę, następnie wykonuje testy i usuwa instancję. W przypadku wystąpienia błędu na jednym z etapów, Molekuła Cię o tym poinformuje.
Teraz bardziej szczegółowo.
Trochę teorii
Rozważmy dwie kluczowe encje Molekuły: Scenario i Driver.
Scenario
Scenariusz zawiera opis tego, co, gdzie, jak i w jakiej kolejności będzie wykonane. Jedna rola może mieć kilka scenariuszy, a każdy z nich to katalog w ścieżce /molecule/, zawierający opisy niezbędnych działań do testu. Musi istnieć scenariusz default, który zostanie automatycznie utworzony, jeśli zainicjujesz rolę przy pomocy Molekuły. Nazwy następnych scenariuszy są wybierane według Twojego uznania.
Sekwencja działań testowych w scenariuszu nazywa się macierz, a domyślnie wygląda następująco:
(Kroki oznaczone ?, domyślnie są pomijane, jeśli nie są opisane przez użytkownika)
lint— uruchomienie linterów. Domyślnie używane sąyamllintiflake8,destroy— usunięcie instancji z ostatniego uruchomienia Molekuły (jeśli pozostały),dependency? — установка ansible-зависимости тестируемой роли,syntax— sprawdzenie składni roli za pomocąansible-playbook --syntax-check,create— utworzenie instancji,prepare? — подготовка инстанса; например, проверка / установка python2converge— uruchomienie testowanego playbooka,idempotence— ponowne uruchomienie playbooka w celu przetestowania idempotencji,side_effect? — действия, не относящиеся непосредственно к роли, но нужные для тестов,verify— uruchomienie testów otrzymanej konfiguracji za pomocątestinfra(domyślnie) /goss/inspec,cleanup? — (в новых версиях) — грубо говоря, «очистка» внешней инфраструктуры, задетой Молекулой,destroy— usunięcie instancji.
Ta sekwencja obejmuje większość przypadków, ale w razie potrzeby można ją zmienić.
Każdy z wymienionych kroków można uruchamiać osobno za pomocą molecule. Należy jednak pamiętać, że dla każdej takiej komendy CLI może istnieć własna sekwencja działań, której można się dowiedzieć, wykonując molecule matrix. Na przykład, przy uruchamianiu komendy converge (uruchamianie testowanego playbooka) wykonane zostaną następujące kroki:
$ molecule matrix converge
...
└── default # nazwa scenariusza
├── dependency # instalacja zależności
├── create # tworzenie instancji
├── prepare # wstępne przygotowanie instancji
└── converge # uruchomienie playbookaSekwencję tych działań można edytować. Jeśli coś z listy zostało już wykonane, zostanie pominięte. Obecny stan oraz konfiguracja instancji są przechowywane przez Molecule w katalogu $TMPDIR/molecule//.
Można dodać kroki z ? opisując pożądane działania w formacie playbooka Ansible, a nazwę pliku dostosować do kroku: prepare.yml/side_effect.yml. Molecule oczekuje tych plików w folderze scenariusza.
Driver
Sterownik to byt, w którym tworzone są instancje do testów.
Lista standardowych sterowników, dla których Molecule przygotowało szablony, jest następująca: Azure, Docker, EC2, GCE, LXC, LXD, OpenStack, Vagrant, Delegated.
W większości przypadków szablony to pliki create.yml i destroy.yml w folderze scenariusza, które opisują tworzenie i usuwanie instancji odpowiednio.
Wyjątkami są Docker i Vagrant, ponieważ interakcje z ich modułami mogą odbywać się bez wyżej wymienionych plików.
Warto wyróżnić sterownik Delegated, ponieważ w przypadku jego użycia w plikach tworzenia i usuwania instancji opisana jest tylko praca z konfiguracją instancji, resztę powinien opisać inżynier.
Domyślnym sterownikiem jest Docker.
Teraz przejdźmy do praktyki i sprawdźmy dodatkowe szczegóły tam.
Rozpoczęcie pracy
Jako "hello world" przetestujemy prostą rolę instalacji nginx. Jako sterownik wybierzemy Docker – myślę, że jest zainstalowany u większości z was (pamiętajmy, że Docker to domyślny sterownik).
Przygotujemy virtualenv i zainstalujemy w nim molecule:
> pip install virtualenv
> virtualenv -p `which python2` venv
> source venv/bin/activate
> pip install molecule docker # molecule zainstaluje ansible jako zależność; docker dla sterownikaKolejnym krokiem jest zainicjowanie nowej roli.
Inicjalizacja nowej roli, podobnie jak nowego scenariusza, odbywa się za pomocą polecenia molecule init:
> molecule init role -r nginx
--> Inicjalizowanie nowej roli nginx...
Zainicjowano rolę w /nginx pomyślnie.
> cd nginx
> tree -L 1
.
├── README.md
├── defaults
├── handlers
├── meta
├── molecule
├── tasks
└── vars
6 katalogów, 1 plikPowstała typowa rola ansible. Wszystkie dalsze interakcje z CLI Molekuły odbywają się z katalogu roli.
Zobaczmy, co znajduje się w katalogu roli:
> tree molecule/default/
molecule/default/
├── Dockerfile.j2 # Szablon Jinja dla Dockerfile
├── INSTALL.rst. # Trochę informacji o instalacji zależności scenariusza
├── molecule.yml # Plik konfiguracyjny
├── playbook.yml # Plebiscyt uruchamiający rolę
└── tests # Katalog z testami etapu weryfikacji
└── test_default.py
1 katalog, 6 plikówRozważmy konfigurację molecule/default/molecule.yml (zmienimy tylko obraz docker):
---
dependency:
name: galaxy
driver:
name: docker
lint:
name: yamllint
platforms:
- name: instance
image: centos:7
provisioner:
name: ansible
lint:
name: ansible-lint
scenario:
name: default
verifier:
name: testinfra
lint:
name: flake8dependency
Ta sekcja opisuje źródło zależności.
Możliwe opcje: , , shell.
Shell – to po prostu powłoka, która jest używana, jeśli galaxy i gilt nie spełniają Twoich potrzeb.
Nie będę tu się rozwodzić, wystarczająco opisano w .
driver
Nazwa kierowcy. W naszym przypadku to docker.
lint
Jako linter używany jest yamllint.
Przydatne opcje w tej części konfiguracji to możliwość wskazania pliku konfiguracyjnego dla yamllint, przekazanie zmiennych środowiskowych lub wyłączenie lintera:
lint:
name: yamllint
options:
config-file: foo/bar
env:
FOO: bar
enabled: Falseplatforms
Opisuje konfigurację instancji.
W przypadku użycia dockera jako kierowcy, Molekuła iteruje po tej sekcji, a każdy element listy jest dostępny w Dockerfile.j2 jako zmienna item.
W przypadku kierowcy, w którym wymagane są create.yml i destroy.yml, sekcja jest dostępna w nich jako molecule_yml.platforms, a iteracje po niej opisane są już w tych plikach.
Ponieważ Molekuła zapewnia zarządzanie instancjami modułami ansible, lista możliwych ustawień również powinna być tam poszukiwana. Dla dockera, na przykład, używany jest moduł . Jakie moduły są używane w pozostałych kierowcach, można znaleźć w .
A także przykłady użycia różnych kierowców można znaleźć .
Zamienimy tutaj centos:7 na ubuntu.
provisioner
„Dostawca” to podmiot zarządzający instancjami. W przypadku Molekuły jest to ansible, wsparcie dla innych nie jest planowane, więc tę sekcję można z pewnymi zastrzeżeniami nazwać rozszerzoną konfiguracją ansible.
Tutaj można podać wiele rzeczy, wyróżnię najważniejsze, moim zdaniem, punkty:
- playbooks: można określić, które playbooki powinny być używane na określonych etapach.
provisioner:
name: ansible
playbooks:
create: create.yml
destroy: ../default/destroy.yml
converge: playbook.yml
side_effect: side_effect.yml
cleanup: cleanup.yml- config_options:
provisioner:
name: ansible
config_options:
defaults:
fact_caching: jsonfile
ssh_connection:
scp_if_ssh: True- connection_options: parametry
provisioner:
name: ansible
connection_options:
ansible_ssh_common_args: "-o 'UserKnownHostsFile=/dev/null' -o 'ForwardAgent=yes'"- options: parametry Ansible oraz zmienne środowiskowe
provisioner:
name: ansible
options:
vvv: true
diff: true
env:
FOO: BARscenario
Nazwa i opis sekwencji scenariuszy.
Można zmienić domyślną macierz działań dowolnej komendy, dodając klucz _sequence i jako wartość dla niego określając potrzebną nam listę kroków.
Załóżmy, że chcemy zmienić kolejność działań podczas uruchamiania polecenia wykonania playbooka: molecule converge
# изначально:
# - dependency
# - create
# - prepare
# - converge
scenario:
name: default
converge_sequence:
- create
- convergeverifier
Konfiguracja frameworka do testów oraz lintera do niego. Domyślnie jako linter używany jest testinfra i flake8. Możliwe opcje są podobne do wcześniej opisanych:
verifier:
name: testinfra
additional_files_or_dirs:
- ../path/to/test_1.py
- ../path/to/test_2.py
- ../path/to/directory/*
options:
n: 1
enabled: False
env:
FOO: bar
lint:
name: flake8
options:
benchmark: True
enabled: False
env:
FOO: barWróćmy do naszej roli. Edytujmy plik tasks/main.yml aż do takiej formy:
---
- name: Zainstaluj nginx
apt:
name: nginx
state: present
- name: Uruchom nginx
service:
name: nginx
state: started
I dodamy testy do molecule/default/tests/test_default.py
def test_nginx_is_installed(host):
nginx = host.package("nginx")
assert nginx.is_installed
def test_nginx_running_and_enabled(host):
nginx = host.service("nginx")
assert nginx.is_running
assert nginx.is_enabled
def test_nginx_config(host):
host.run("nginx -t")
Gotowe, wystarczy tylko uruchomić (z katalogu roli, przypominam):
> molecule testDługi wyjście pod spoilerem:
--> Walidacja schematu /nginx/molecule/default/molecule.yml.
Walidacja zakończona pomyślnie.
--> Macierz testowa
└── default
├── lint
├── destroy
├── dependency
├── syntax
├── create
├── prepare
├── converge
├── idempotence
├── side_effect
├── verify
└── destroy
--> Scenariusz: 'default'
--> Akcja: 'lint'
--> Wykonywanie Yamllint na plikach znalezionych w /nginx/...
Lint zakończony pomyślnie.
--> Wykonywanie Flake8 na plikach znalezionych w /nginx/molecule/default/tests/...
Lint zakończony pomyślnie.
--> Wykonywanie Ansible Lint na /nginx/molecule/default/playbook.yml...
Lint zakończony pomyślnie.
--> Scenariusz: 'default'
--> Akcja: 'destroy'
PLAY [Zniszcz] *****************************************************************
TASK [Zniszczenie instancji molekularnych] ************************************
zmieniono: [localhost] => (item=None)
zmieniono: [localhost]
TASK [Czekanie na zakończenie usuwania instancji] ******************************
ok: [localhost] => (item=None)
ok: [localhost]
TASK [Usunięcie sieci dockerowych] *********************************************
PODSUMOWANIE GRY ****************************************************************
localhost : ok=2 zmieniono=1 unreachable=0 failed=0
--> Scenariusz: 'default'
--> Akcja: 'dependency'
Pominięto, brak pliku wymagań.
--> Scenariusz: 'default'
--> Akcja: 'syntax'
playbook: /nginx/molecule/default/playbook.yml
--> Scenariusz: 'default'
--> Akcja: 'create'
PLAY [Utwórz] ******************************************************************
TASK [Zaloguj się do rejestru Docker] ******************************************
pominięto: [localhost] => (item=None)
TASK [Utwórz Dockerfile z nazw obrazów] ****************************************
zmieniono: [localhost] => (item=None)
zmieniono: [localhost]
TASK [Odnalezienie lokalnych obrazów Docker] ***********************************
ok: [localhost] => (item=None)
ok: [localhost]
TASK [Zbudowanie obrazu kompatybilnego z Ansible] ******************************
zmieniono: [localhost] => (item=None)
zmieniono: [localhost]
TASK [Utworzenie sieci dockerowych] *********************************************
TASK [Utwórz instancje molekularne] ********************************************
zmieniono: [localhost] => (item=None)
zmieniono: [localhost]
TASK [Czekanie na zakończenie tworzenia instancji] *****************************
zmieniono: [localhost] => (item=None)
zmieniono: [localhost]
PODSUMOWANIE GRY ****************************************************************
localhost : ok=5 zmieniono=4 unreachable=0 failed=0
--> Scenariusz: 'default'
--> Akcja: 'prepare'
Pominięto, nie skonfigurowano playbooka przygotowawczego.
--> Scenariusz: 'default'
--> Akcja: 'converge'
PLAY [Zjednoczenie] *************************************************************
TASK [Zbieranie faktów] *********************************************************
ok: [instancja]
TASK [nginx : Zainstaluj nginx] ************************************************
zmieniono: [instancja]
TASK [nginx : Uruchom nginx] ****************************************************
zmieniono: [instancja]
PODSUMOWANIE GRY ****************************************************************
instancja : ok=3 zmieniono=2 unreachable=0 failed=0
--> Scenariusz: 'default'
--> Akcja: 'idempotence'
Idempotencja zakończona pomyślnie.
--> Scenariusz: 'default'
--> Akcja: 'side_effect'
Pominięto, nie skonfigurowano playbooka efektu ubocznego.
--> Scenariusz: 'default'
--> Akcja: 'verify'
--> Wykonywanie testów Testinfra znalezionych w /nginx/molecule/default/tests/...
============================= sesja testowa zaczyna się =====================
platform darwin -- Python 2.7.15, pytest-4.3.0, py-1.8.0, pluggy-0.9.0
rootdir: /nginx/molecule/default, inifile:
wtyczki: testinfra-1.16.0
zebrano 4 elementy
tests/test_default.py .... [100%]
========================== 4 zaliczone w 27.23 sekundy ======================
Weryfikacja zakończona pomyślnie.
--> Scenariusz: 'default'
--> Akcja: 'destroy'
PLAY [Zniszcz] *****************************************************************
TASK [Zniszczenie instancji molekularnych] ************************************
zmieniono: [localhost] => (item=None)
zmieniono: [localhost]
TASK [Czekanie na zakończenie usuwania instancji] ******************************
zmieniono: [localhost] => (item=None)
zmieniono: [localhost]
TASK [Usunięcie sieci dockerowych] *********************************************
PODSUMOWANIE GRY ****************************************************************
localhost : ok=2 zmieniono=2 unreachable=0 failed=0
Nasza prosta rola przeszła test bez problemów.
Należy pamiętać, że jeśli wystąpią problemy podczas pracy test molekuł, to jeśli nie zmieniałeś standardowej sekwencji, Molekuła usunie instancję.
Do debugowania przydatne są następujące komendy:
> molecule --debug # informacje debugowe. Przy normalnym uruchomieniu Molekuła ukrywa logi.
> molecule converge # Zostawia instancję po wykonaniu testowanej roli.
> molecule login # Zaloguj się do utworzonej instancji.
> molecule --help # Pełna lista komend.Istniejąca rola
Dodanie nowego scenariusza do istniejącej roli odbywa się z katalogu roli następującymi komendami:
# полный список доступных параметров
> molecule init scenarion --help
# создание нового сценария
> molecule init scenario -r <role_name> -s <scenario_name>W przypadku, gdy jest to pierwszy scenariusz w roli, parametr -s można pominąć, ponieważ zostanie utworzony scenariusz. default.
Podsumowanie
Jak widzisz, Molekuła nie jest zbyt skomplikowana, a przy użyciu własnych szablonów można sprowadzić wdrożenie nowego scenariusza do edycji zmiennych w playbookach tworzenia i usuwania instancji. Molekuła bez problemu integruje się z systemami CI, co pozwala zwiększyć szybkość rozwoju poprzez skrócenie czasu potrzebnego na ręczne testowanie playbooków.
Dziękuję za uwagę. Jeśli masz doświadczenie w testowaniu ról ansible i nie jest ono związane z Molekułą — opowiedz o tym w komentarzach!
Źródło: habr.com
