Инструкция: как да тествате ansible роли и да откривате проблеми преди продукцията

Здравейте на всички!

Работя като DevOps инженер в услуги за резервация на хотели Ostrovok.ru. В тази статия искам да споделя нашия опит в тестването на ansible роли.

В Ostrovok.ru като мениджър на конфигурации използваме ansible. Напоследък стигнахме до необходимостта от тестване на ролите, но, както се оказа, инструментите за това не са много — най-популярният е, вероятно, фреймворкът Molecule, затова решихме да го използваме. Но се оказа, че неговата документация не покрива много подводни камъни. Не успяхме да намерим достатъчно подробно ръководство на български, затова решихме да напишем тази статия.

Инструкция: как да тествате ansible роли и да откривате проблеми преди продукцията

Molecule

Molecule — фреймворк за помощ в тестването на ansible роли.

Определение: Molecule създава инстанция на избраната от вас платформа (облако, виртуален сървър, контейнер; повече информация вижте в раздела Driver), изпълнява вашата роля на нея, след което стартира тестовете и изтрива инстанцията. В случай на неуспех на една от стъпките, Molecule ще ви уведоми за това.

Сега по-подробно.

Няколко теории

Нека разгледаме две ключови единици на Molecule: Scenario и Driver.

Scenario

Сценарият съдържа описание на това какво, къде, как и в каква последователност ще бъде изпълнено. Една роля може да има няколко сценария, като всеки един от тях е директория в пътя /molecule/, съдържаща описания на необходимите действия за теста. Задължително трябва да присъства сценарий по подразбиране, който ще бъде автоматично създаден, ако инициализирате ролята с помощта на Molecule. Имената на останалите сценарии се избират по ваше усмотрение.

Последователността на действията в сценария за тестване се нарича matrix, а по подразбиране тя е следната:

(Стъпките, обозначени ?, по подразбиране се пропускат, ако не са описани от потребителя)

  • lint — изпълнение на линтъри. По подразбиране се използват yamllint и flake8,
  • destroy — изтриване на инстанции от предишното изпълнение на Molecule (ако има останали),
  • dependency? — установка ansible-зависимости тестируемой роли,
  • syntax — проверка на синтаксиса на ролята с помощта на ansible-playbook --syntax-check,
  • create — създаване на инстанция,
  • prepare? — подготовка инстанса; например, проверка / установка python2
  • converge — стартиране на тествания плейбук,
  • idempotence — повторно изпълнение на плейбука за тест на идемпотентност,
  • side_effect? — действия, не относящиеся непосредственно к роли, но нужные для тестов,
  • verify — стартиране на тестовете на получената конфигурация с помощта на testinfra(по подразбиране) /goss/inspec,
  • cleanup? — (в новых версиях) — грубо говоря, «очистка» внешней инфраструктуры, задетой Молекулой,
  • destroy — изтриване на инстанцията.

Тази последователност покрива повечето случаи, но при необходимост може да бъде променена.

Всяка от гореописаните стъпки може да се стартира отделно с помощта на molecule. Но е важно да разберем, че за всяка такава cli-команда може да има своя последователност от действия, която може да се открие чрез изпълнение на molecule matrix. Например, при стартиране на командата converge (изпълнение на тестовия плейбук) ще се извършат следните действия:

$ molecule matrix converge
...
└── default         # име на сценария
    ├── dependency  # инсталиране на зависимости
    ├── create      # създаване на инстанс
    ├── prepare     # предварителна настройка на инстанса
    └── converge    # изпълнение на плейбука

Последователността на тези действия може да се редактира. Ако нещо от списъка вече е изпълнено, то ще бъде пропуснато. Текущото състояние, както и конфигурацията на инстансите, Молекула съхранява в директорията $TMPDIR/molecule//.

Добавянето на стъпки с ? може да се извърши, описвайки желаните действия във формат ansible плейбук, а името на файла да бъде съответно на стъпката: prepare.yml/side_effect.yml. Очаквайте тези файлове Молекула да бъдат в папката на сценария.

Driver

Драйверът е същество, където се създават инстанси за тестове.
Списъкът със стандартни драйвери, за които Молекулата има готови шаблони, е следният: Azure, Docker, EC2, GCE, LXC, LXD, OpenStack, Vagrant, Delegated.

В повечето случаи шаблоните са файлове create.yml и destroy.yml в папката на сценария, които описват създаването и изтриването на инстанс съответно.
Изключения представляват Docker и Vagrant, тъй като взаимодействията с техните модули могат да се осъществяват без гореспоменатите файлове.

Стои да се открои драйверът Delegated, тъй като в случай на неговото използване в файловете за създаване и изтриване на инстанси е описана само работа с конфигурацията на инстансите, останалото трябва да опише инженер.

По подразбиране драйверът е Docker.

Сега да преминем към практиката и по-другите особености да разгледаме там.

Започване

Като 'hello world' ще тестваме проста роля за инсталиране на nginx. Като драйвер ще изберем Docker – мисля, той е инсталиран при повечето от вас (и помнете, че Docker е драйверът по подразбиране).

Подготвяме virtualenv и инсталираме в него molecule > pip install virtualenv > virtualenv -p `which python2` venv > source venv/bin/activate > pip install molecule docker # molecule ще инсталира ansible като зависимост; docker за драйвера:

Следващата стъпка е иницииране на нова роля.

Инициирането на нова роля, както и на нов сценарий, се извършва с помощта на командата
molecule init молекула инициализиране:

> molecule init role -r nginx
--> Инициализиране на нова роля nginx...
Ролята е инициализирана успешно в /nginx.
> cd nginx
> tree -L 1
.
├── README.md
├── defaults
├── handlers
├── meta
├── molecule
├── tasks
└── vars

6 директории, 1 файл

Получена типична ansible роля. Всички взаимодействия с CLI Molecule се извършват от корена на ролята.

Нека да видим какво има в директорията на ролята:

> tree molecule/default/
molecule/default/
├── Dockerfile.j2  # Jinja шаблон за Dockerfile
├── INSTALL.rst.   # Няколко информации за инсталиране на зависимости
├── molecule.yml   # Конфигурационен файл
├── playbook.yml   # Плейбук за стартиране на ролята
└── tests          # Директория с тестове от етапа verify
    └── test_default.py

1 директория, 6 файла

Нека разгледаме конфигурацията molecule/default/molecule.yml (ще сменим само docker image):

---
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: flake8

dependency

Тази секция описва източника на зависимости.

Възможни варианти: galaxy, gilt, shell.

Shell е просто команден интерфейс, който се използва, когато galaxy и gilt не покриват вашите нужди.

Няма да се задържам дълго тук, достатъчно е написано в документацията.

driver

Име на драйвера. При нас това е docker.

lint

Като линтер се използва yamllint.

Полезни опции в тази част на конфигурацията са възможността да посочите конфигурационен файл за yamllint, да предадете променливи на средата или да деактивирате линтера:

lint:
  name: yamllint
  options:
    config-file: foo/bar
  env:
    FOO: bar
  enabled: False

platforms

Описва конфигурацията на инстансите.
При използване на docker като драйвер, Molecule итерира през тази секция, и всеки елемент от списъка е достъпен в Dockerfile.j2 като променлива item.

При драйвер, който изисква create.yml и destroy.yml, секцията е достъпна в тях като molecule_yml.platforms, а итерациите по нея са описани в тези файлове.

Тъй като Molecule предоставя управление на инстансите на ansible модулите, списъкът с възможни настройки трябва да се търси там. Например, за docker се използва модул docker_container_module. Можете да намерите кои модули се използват в останалите драйвери в документацията.

Също така примери за използване на различни драйвери могат да бъдат намерени в тестовете на самата Molecule.

Ще сменим тук centos:7 на ubuntu.

provisioner

«Provisioner» — субект, управляващ инстансите. В случая на Molecule това е ansible, поддръжката на други не е планирана, така че тази секция може да бъде наречена разширена конфигурация на ansible с уговорка.
Тук можете да посочите много неща, но ще изтъкна основните, на моето мнение, моменти:

  • playbooks: можете да посочите кои плейбуци трябва да се използват на определени етапи.

provisioner:
  name: ansible
  playbooks:
    create: create.yml
    destroy: ../default/destroy.yml
    converge: playbook.yml
    side_effect: side_effect.yml
    cleanup: cleanup.yml

provisioner:
  name: ansible
  config_options:
    defaults:
      fact_caching: jsonfile
    ssh_connection:
      scp_if_ssh: True

provisioner:
  name: ansible  
  connection_options:
    ansible_ssh_common_args: "-o 'UserKnownHostsFile=/dev/null' -o 'ForwardAgent=yes'"

  • опции: параметри Ansible и променливи на средата

provisioner:
  name: ansible  
  options:
    vvv: true
    diff: true
  env:
    FOO: BAR

scenario

Име и описание на последователностите на сценария.
Можете да промените матрицата на действия по подразбиране за определена команда, като добавите ключ <command>_sequence и зададете необходимия списък от стъпки като стойност за него.
Да предположим, че искаме да променим последователността на действията при изпълнение на командата за стартиране на плейбук: molecule converge

# изначально:
# - dependency
# - create
# - prepare
# - converge
scenario:
  name: default
  converge_sequence:
    - create
    - converge

verifier

Настройка на рамката за тестове и линтер за нея. По подразбиране се използва линтер testinfra и flake8. Възможните опции са подобни на вече споменатите:

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: bar

Нека се върнем към нашата роля. Нека редактираме файла tasks/main.yml до следния вид:

---
- name: Install nginx
  apt:
    name: nginx
    state: present

- name: Start nginx
  service:
    name: nginx
    state: started

И добавим тестове в 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")

Готово, остава само да изпълните (от корена на ролята, напомням):

> molecule test

Дълъг изход под спойлер:

--> Валидация на схемата /nginx/molecule/default/molecule.yml.
Валидацията завърши успешно.
--> Тестова матрица

└── default
    ├── lint
    ├── destroy
    ├── dependency
    ├── syntax
    ├── create
    ├── prepare
    ├── converge
    ├── idempotence
    ├── side_effect
    ├── verify
    └── destroy

--> Сценарий: 'default'
--> Действие: 'lint'
--> Изпълнение на Yamllint върху файловете в /nginx/... 
Lintът завърши успешно.
--> Изпълнение на Flake8 върху файловете в /nginx/molecule/default/tests/... 
Lintът завърши успешно.
--> Изпълнение на Ansible Lint върху /nginx/molecule/default/playbook.yml...
Lintът завърши успешно.
--> Сценарий: 'default'
--> Действие: 'destroy'

    PLAY [Унищожаване] *************************************************************

    ЗАДАЧА [Унищожаване на молекулни екземпляри] ********************************    
    променено: [localhost] => (item=None)
    променено: [localhost]

    ЗАДАЧА [Изчакване за завършване на изтриването на инстанцията(ите)] ************
    добре: [localhost] => (item=None)
    добре: [localhost]

    ЗАДАЧА [Изтриване на docker мрежи] ********************************************

    PLAY РЕЗЮМЕ ******************************************************************
    localhost                  : добре=2    променено=1    недостъпен=0    неуспешен=0

--> Сценарий: 'default'
--> Действие: 'dependency'
Пропуснато, липсва файлът с изисквания.
--> Сценарий: 'default'
--> Действие: 'syntax'

    playbook: /nginx/molecule/default/playbook.yml

--> Сценарий: 'default'
--> Действие: 'create'

    PLAY [Създаване] ***************************************************************

    ЗАДАЧА [Вход в Docker хранилище] **********************************************
    пропуснато: [localhost] => (item=None)

    ЗАДАЧА [Създаване на Dockerfiles от имена на изображения] *********************
    променено: [localhost] => (item=None)
    променено: [localhost]

    ЗАДАЧА [Откриване на локални Docker изображения] ******************************
    добре: [localhost] => (item=None)
    добре: [localhost]

    ЗАДАЧА [Създаване на образ, съвместим с Ansible] ***************************
    променено: [localhost] => (item=None)
    променено: [localhost]

    ЗАДАЧА [Създаване на docker мрежи] ********************************************

    ЗАДАЧА [Създаване на молекулни екземпляри] **********************************
    променено: [localhost] => (item=None)
    променено: [localhost]

    ЗАДАЧА [Изчакване за завършване на създаването на инстанцията(ите)] ******** 
    променено: [localhost] => (item=None)
    променено: [localhost]

    PLAY РЕЗЮМЕ ******************************************************************
    localhost                  : добре=5    променено=4    недостъпен=0    неуспешен=0

--> Сценарий: 'default'
--> Действие: 'prepare'
Пропуснато, playbook за подготовка не е конфигуриран.
--> Сценарий: 'default'
--> Действие: 'converge'

    PLAY [Конвергенция] ***********************************************************

    ЗАДАЧА [Събиране на факти] ***************************************************
    добре: [инстанция]

    ЗАДАЧА [nginx : Инсталиране на nginx] ****************************************
    променено: [инстанция]

    ЗАДАЧА [nginx : Стартиране на nginx] ******************************************
    променено: [инстанция]

    PLAY РЕЗЮМЕ ******************************************************************
    инстанция                   : добре=3    променено=2    недостъпен=0    неуспешен=0

--> Сценарий: 'default'
--> Действие: 'idempotence'
Idempotence завърши успешно.
--> Сценарий: 'default'
--> Действие: 'side_effect'
Пропуснато, playbook за странични ефекти не е конфигуриран.
--> Сценарий: 'default'
--> Действие: 'verify'
--> Изпълнение на Testinfra тестовете, намерени в /nginx/molecule/default/tests/... 
    ============================= тест сесията започва ==============================
    платформа darwin -- Python 2.7.15, pytest-4.3.0, py-1.8.0, pluggy-0.9.0
    rootdir: /nginx/molecule/default, inifile:
    плъгини: testinfra-1.16.0
събрани 4 елемента

    tests/test_default.py ....                                               [100%]

    ========================== 4 преминати за 27.23 секунди ===========================
Проверяващият завърши успешно.
--> Сценарий: 'default'
--> Действие: 'destroy'

    PLAY [Унищожаване] *************************************************************

    ЗАДАЧА [Унищожаване на молекулни екземпляри] ********************************
    променено: [localhost] => (item=None)
    променено: [localhost]

    ЗАДАЧА [Изчакване за завършване на изтриването на инстанцията(ите)] ************
    променено: [localhost] => (item=None)
    променено: [localhost]

    ЗАДАЧА [Изтриване на docker мрежи] ********************************************

    PLAY РЕЗЮМЕ ******************************************************************
    localhost                  : добре=2    променено=2    недостъпен=0    неуспешен=0

Нашата проста роля успешно премина тестовете.
Важно е да запомните, че ако се появят проблеми при работата molecule test, ако не сте променяли стандартната последователност, Молекула ще изтрие инстанса.

За дебъг полезни са следните команди:

> molecule --debug  # информация за дебъг. При нормално стартиране Молекула скрива логовете.
> molecule converge          # Оставя инстанса след изпълнението на тестуваната роля.
> molecule login             # Вход в създадения инстанс.
> molecule --help            # Пълен списък с команди.

Съществуваща роля

Добавянето на нов сценарий към съществуваща роля става от директорията на роля следните команди:

# полный список доступных параметров
> molecule init scenarion --help
# создание нового сценария
> molecule init scenario -r <role_name> -s <scenario_name>

В случай, че това е първият сценарий в ролята, параметърът -s може да бъде пропуснат, тъй като ще бъде създаден сценарий. по подразбиране.

Заключение

Както виждате, Молекула не е много сложна, а при използване на собствени шаблони може да се сведе разгръщането на нов сценарий до редактиране на променливи в плейбуковете за създаване и изтриване на инстанси. Молекула безпроблемно се интегрира с CI системи, което позволява увеличаване на скоростта на разработване, като се намалява времето за ръчно тестване на плейбуковете.

Благодаря за вашето внимание. Ако имате опит с тестване на ansible роли и той не е свързан с Молекула — споделете го в коментарите!

Източник: habr.com

Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри 🔥 Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри | ProHoster