Ansible з чого почати

В останні пару років я все частіше використовую Ansible для вирішення практично будь-яких завдань, пов'язаних з автоматизацією, будь то конфігурування, резервне копіювання або деплой проектів. Не дивлячись на те, що система дуже добре документована, я думаю зможу додати трохи корисної інформації для тих хто ще тільки починає користуватися Ansible. Для початку я хотів би розповісти про основних речах, таких як структура проекту в якому будуть міститися плейбуки, ролі, змінні, шаблони і файли необхідні для автоматизації розгортання серверів, коду і всього іншого, що можна зробити за допомогою Ansible.

Отже, Ansible це дуже гнучкий і легкий інструмент для написання сценаріїв автоматизації будь-якої складності. Ви можете описати в ньому як просте оточення розробника так складну структуру великого проекту з кількома окружениями (dev/stage/prod).

Як мені бачиться з Ansible можна вирішувати наступні завдання:
  • Установка/видалення;
  • Конфігурування ПЗ;
  • Створення/видалення користувачів;
  • Контроль користувальницьких паролів/ключів;
  • Створення/видалення контейнерів/віртуальних машин;
  • Деплой коду вашого ПЗ;
  • Запуск різних скриптів/тестів.
Пара зауважень для тих хто ще не знайомий з Ansible:
— Перш ніж приступити до написання плейбуков/ролей вам потрібно почитати документацію Ansible-playbook, Ansible-role і навчитися розуміти YAML-syntax (це дуже просто);
— Також варто відразу сказати, що краще вести розробку в git-репозиторії, тому не полінуйтеся обзавестися аккаунтом на github або заведіть локальний git-repo (mercurial, svn, etc). Якщо ви ще не навчилися користуватися git, то зараз саме час.
На мій погляд, Ansible набагато простіше і легше для «засвоєння» чим puppet і chef (мені доводилося використовувати і те, і інше) хоч це і трохи різні продукти. Після того, як ви ознайомилися з введенням і заглянули в Module Index, ви вже можете починати писати плейбуки які помітно полегшать вам життя.

Приклад плейбука для поширення вашого ssh pub-key на сервери:
# file: keys.yml
---
- hosts: app-servers
завдання:
- name: Set up authorized_keys for the backup user # Завжди вказуйте докладні імена для тасков, так вам буде простіше читати висновок і зрозуміліше для колег що ви хочете тут зробити.
authorized_key: user=backup key="{{ item }}"
with_file:
- keys/backup.pub
...

ansible-playbook playbooks/keys.yml

Це все зрозуміло, але що далі?

Розширена структура проекту

Якщо хочете описувати структуру більш або менш складного проекту з n-tier архітектурою, варто відразу визначитися зі списком груп хостів за ролями і на підставі цього прикинути які завдання повинні бути загальними для всіх хостів (наприклад, підключення репозиторіїв, створення користувачів і т. д.), а які приватні (конфігурування nginx, mysql, створення python venv і т. д.) і виходячи з цього починати писати ролі починаючи з базової загальної ролі поступово переходячи до приватних.

Розширена структура проекту може включати в себе:
  • Змінні (загальні і приватні);
  • Плейбуки (сценарії);
  • Ролі (структуровані плейбуки);
  • Списки груп хостів (inventory).
Список груп хостів (inventory)

Якщо ви хочете мати кілька оточень то доведеться завести декілька файлів inventory, за замовчуванням (deb-пакет) в конфіги ansible.cfg використовується файл hosts (/etc/ansible/hosts). Шлях до inventory не обов'язково прописувати в конфіги можна задавати різні файли з ключем -i.

Щоб описана логіка у ваших ролях або плейбуках працювала однаково на різних оточеннях заводите групи з однаковими іменами для всіх оточень, наприклад:

Production inventory
# file: production

[app-servers:children]
app-php-servers
app-python-servers

[app-php-servers]
appserv-01.example.com
appserv-03.example.com

[app-python-servers]
appserv-02.example.com
appserv-04.example.com

[db-servers]
dbserv-[01:02].example.com

[cdn]
cdn-[01:03].example.com

[app-servers:vars]
env=PROD
ansible_ssh_user=admin

Develop inventory
# file: develop

[app-servers:children]
app-php-servers
app-python-servers

[app-php-servers]
appserv-01.dev.example.com
appserv-03.dev.example.com

[app-python-servers]
appserv-02.dev.example.com
appserv-04.dev.example.com

[db-servers]
dbserv-[01:02].dev.example.com

[cdn]
cdn-[01:02].dev.example.com

[app-servers:vars]
env=DEV
ansible_ssh_user=admin

Як видно з прикладу, групи можуть включати в себе інші групи, а також можна оголошувати загальні змінні прямо в inventory.

Змінні

Структура директорій може виглядати наступним чином:
ansible/
# Групові (загальні) змінні
- group_vars/
- all/
- common
- secret 
- dev/
- common
- secret 
- stage/
- common
- secret
- prod/
- common
- secret
# Змінні окремих хостів (приватні) 
- host_vars/
- appserv-01.example.com/
- common
- secret
- dbserv-01.example.com/
- common
- secret
- cdn-01.example.com/
- common
- secret

Кожна група і кожен хост можуть мати набір змінних в різних файлах (наприклад common, secret).Назви файлів в директоріях тут не приципиальны, головне щоб ім'я директорії збігалося з іменами (групи або хоста) у відповідному inventory. Заводити директорію для групи або окремого хоста не обов'язково, можна просто створити файл та записати в нього всі необхідні змінні. Але якщо ви хочете зберігати паролі в зашифрованому вигляді (не хеши, а саме паролі), окремо від загальних змінних, то варто завести описану вище структуру, про це я розповім нижче.

Шифрування змінних з Ansible-vault
Ansible-vault утиліта для шифрування(default AES256) файлів групових або хостовых змінних і в принципі будь-яких файлів, до яких ви хочете зберігати секретні змінні (паролі, ключі тощо). Таким чином, ви зможете зберігати в репозиторії (навіть у публічному, хоча це все ж таки не бажано) будь-які дані і не переживати за їх безпеку.

Паролі має сенс зберігати в групових файлах (group_vars) по імені групи або як all якщо у вас на всіх середовищах одні і ті ж юзери.

В описаній вище структурі я вказував файли secret в яких передбачається зберігати зашифровані змінні.

Зашифрувати файл:
ansible-vault encrypt host_vars/cdn-01.example.com/secret

Відредагувати зашифрований файл:
ansible-vault edit host_vars/cdn-01.example.com/secret

Показати зашифрований файл:
ansible-vault encrypt host_vars/cdn-01.example.com/secret

Звичайно, щоб шифрувати і розшифровувати файли потрібен буде достатньо довгий ключ і зберігати його потрібно в надійному місці (наприклад KeePass). Щоб автоматично розшифровувати файли під час запуску (runtime) плейбуков потрібно буде вказувати ключ --vault-password-file або задавати шлях до файлу через конфіг ansible.cfg, в цьому випадку також потрібно буде подбати про збереження ключа і виставити йому потрібні права (0400). Ну і звичайно не варто його зберігати в репозиторії разом з зашифрованими файлами.

Плейбуки

Всі дії (tasks) які ви хочете виконати можна записувати в плейбуки (якщо їх скажемо не більше десятка, і ви не використовуєте файли та шаблони) в інших випадках варто використовувати ролі.

У плейбуке як мінімум повинні бути вказані:
Цільова група хостів (hosts) тут можна задавати виключення по масці ( — hosts: app-servers:!app-04.*), або кілька груп через двокрапку-hosts: db-servers:cdn);
— Дії (завдання) або ролі (roles.
Додатково можна вказати:
— Користувача для ssh-connect (remote_user);
— Користувача sudo (become_user);
— Підвищення використання привілеїв (become: True/False);
— Змінні (vars
— Файли змінних (vars_files
— Кількість одночасних коннектов (serial), якщо виставити 1 відбуватиметься послідовне виконання плейбука по одному вузлу за раз.

Ролі

Роль являє собою структурований плейбук містить набір (як мінімум) тасков (task), та додатково — обрабработчиков подій (handler), змінних (defaults), файлів (files), шаблонів (templates), а також опис і залежності (meta).

Структура ролі Deploy
deploy/
- defaults/
- main.yml
- files/
- maintenance.html
- handlers/
- main.yml
- meta/
- main.yml
- tasks/
- deploy.yml
- main.yml
- migrations.yml
- tests.yml
- templates/
- app_config.j2

Файл з змінними за замовчуванням:
# file: deploy/defaults/main.yml
---
branch_name: master
mysql_host: localhost
...

Тут дуже зручно задавати якісь загальні речі, наприклад за замовчуванням хост для БД localhost, а group_vars/host_vars ви можете задавати потрібні хости для відповідних середовищ.

Файл з обробниками подій
# file: deploy/handlers/main.yml
---
- name: reload uwsgi
service: name=uwsgi state=reloaded
- name: flush cache
shell: redis-cli flushall
...

В обробниках можна вказувати абсолютно будь-які дії які необхідно виконувати після тих чи інших тасков, будь то викладення нового коду або зміна конфига.

Файл з описом тасков
# file: deploy/tasks/main.yml
---
- include: deploy.yml
tags:
- common
- deploy
- include: migrations.yml
tags:
- common
- migrations
- include: pytasks.yml
when: "'app-python-servers' in group_names"
tags:
- common
- pytasks
- include: tests.yml
tags:
- tests
...

Можна ставити жорсткі умови для виконання деяких тасков. У прикладі вище я поставив умову:
when: "'app-python-servers' in group_names"

що означає, що таск буде виконуватися тільки для хостів які складаються в групі app_python-servers.

Я практично завжди використовую теги для декомпозирования великих ролей. У прикладі ролі вище я задав загальний тег «common» для перших двох тасков і окремий тег для tests для останнього таска. Якщо мені потрібно виконати тільки перші два тягаючи (тобто викотити код) я запускаю плйбук з ключем --tags common (або --skip-tags tests):
ansible-playbook -i develop playbooks/deploy.yml --tags common

потім, можна запустити тести окремо (зазвичай вони довго виконуються) просто ставлю --tags tests:
ansible-playbook -i develop playbooks/deploy.yml --tags tests

Шаблони
Як шаблонизатора Ansible використовує jinja2 з усіма його принадами. Приклад простого конфига uwsgi у вигляді ini файлу з змінними:
# {{ ansible_managed }}

[uwsgi]

chdir = {{ app_dir }}/{{ app_name }}
home = {{ app_dir }}/{{ app_name }}/env

master = True

module = {{ app_name }}.wsgi:application

chmod-socket = 660

uid = {{ web_user }}
gid = {{ web_user }}

processes = 8

listen = 256

max-requests = 100

buffer-size = 16384

vacuum = true

env DJANGO_SETTINGS_MODULE = {{ env }}.settings

{% if env is defined and env == DEV %}
# Enable logging 
req-logger = file:/{{ logs_dir }}/{{ app_name }}/reqlog
logger = file:/{{ logs_dir }}/{{ app_name }}/errlog
{% endif %}

Можна використовувати будь-які прийоми jinja2 в поєднанні зі змінними Ansible. Наприклад:
— Задавати рядка тільки хост складається в тій чи іншій групі (або задати рядка тільки для певного хоста) і при цьому використовувати loop:
# file: iptables.j2
...
{% if (groups['cdn'] is defined and inventory_hostname in groups['cdn']) %}
# Allows HTTP and HTTPS connections from anywhere
{% port for in webs_ports %}
-A INPUT -p tcp -m tcp --dport {{ port }} -j ACCEPT
{% endfor %}
{% endif %}
...

Залежно

Під залежностями тут маються на увазі інші ролі які необхідно виконати перед тим виконувати вашу роль у форматі — { role: role_name, var_name: value }, наприклад:
# file: deploy/meta/main.yml
---
dependencies:
- { role: base, apt_repo: us }

Вказавши всі необхідні залежності для кінцевої ролі (наприклад app-servers) ви може створити лише один плейбук в якому досить буде вказати цільову групу хостів і роль. І цього буде достатньо, щоб підтримувати систему в консистентном стані, запускаючи наприклад плейбук за крону. Якщо потрібно оновити якісь певні конфіги за місцем, вам прийдуть на допомогу теги.

Якщо ви хочете робити періодичні білди і контролювати їх виконання або просто дати «кнопку» розробникам, можу порекомендувати плагін Ansible для Jenkins підключивши який ви зможете задавати в тягаючи Jenkins шляху до playbook, inventory, а також tags extra-vars.

Що далі?

Описаная у статті структура не претендує на звання еталонної або універсальною, але напевно підійде для більшості випадків. Ви можете використовувати її як відправну точку. В процесі написання власних ролей ви знайдете кращий і більш зручний для вас спосіб опису інфраструктури яку ви будуєте. Головне не намагайтеся все ускладнювати, чим простіше ви пишіть і чим зрозуміліше ваші плейбуки для інших тим краще.

Тримаєте відкритою вкладку з офіційною документацією по Ansible, вона постійно доповнюється в міру розвитку проекту. Я досі знаходжу в ній нові і цікаві для себе речі.

У цій статті я розповів не все, що хотів, є думки та ідеї ще на одну-дві замітки. Якщо вас цікавлять певні питання пишіть їх у коментарях або мені на пошту, я постараюся відповісти і можливо врахувати ваші побажання при написанні наступній статті.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.