З часом кожному системному адміністратору випадає необхідність встановлювати та підтримувати дедалі більшу кількість однотипних серверів. Робити кожен раз одне й те саме далеко не найприємніша справа та і надалі це буде займати все більшу кількість часу. Перше, що приходить на думку для вирішення таких проблем - це все заскриптувати, проте цей спосіб далеко не ідеальний. По-перше, те що написано буде з часом зрозуміле лише автору скриптів, а, по-друге, скрипти навряд чи можуть запропонувати ідемпотентність, тобто кожен наступний запуск самописних скриптів над вже працюючою системою може призвести до поломок.
Щоб уникнути подібних страждань людство і придумало системи управління конфігураціями. До найбільш популярних таких систем можна віднести Puppet, Salt, Chef, CFEngine. Хронологія їх появи така:
* CFEngine. Перший реліз - 1993, написаний на С.
* Puppet - 2005, написаний на Ruby. DSL
* Chef - 2009, написаний також на Ruby, DSL
* Juju - 2010, Python.
* Salt - 2011, Python.
* Ansible. Перший реліз - 2012, Python.
Ansible один із наймолодших із існуючих популярних систем конфігурації. Проект стартував лише в 2012 році, проте вже має багато прихильників. Засновник проекту - Майкл Де Хаанн, попередньо працював над Puppet і Cobbler/Func. Назва продукту взято з науково-фантастичної літератури: в романах американської письменниці Урсули Ле Гуїн ансіблом називається пристрій для оперативного космічного зв'язку. Про Ansible далі і піде мова.
Ansible - програма з відкритим вихідним кодом для налаштування і управління серверами. Для деплоя конфігурації на новий сервер необхідно лише SSH-з’єднання та Python-інтерпритатор, який зазвичай йде по-замовчуванню в основних дистрибутивах. Сценарій конфігурації (в Ansible він називається Playbook) описується за допомогою мови розмітки YAML.
Установити останню версію Ansible можна через PIP:
# pip install ansible
Чи додавши PPA-репозиторій, якщо у якості ОС - Ubuntu:
# apt-get install software-properties-common
# apt-add-repository ppa:ansible/ansible
# apt-get update
# apt-get install ansible
Разом із основним пакетом по залежностям установляться paramiko, jinja2, PyYAML. Про установку для інших дистрибутивів можна почитати тут.
Для того щоб при кожному запуску Ansible не набирати пароль необхідно додати ssh-ключ:
# ssh-keygen -t rsa
# ssh-copy-id ubuntu@192.168.1.3
ubuntu - користувач від імені якого будуть запускатись інструкції Ansible, 192.168.1.3 - IP-адреса піддослідної віртуальної машини, на якій і будуть виконуватись всі експерименти.
Майже все готово. Cтворимо директорію для наших тестів та опишемо групу хостів.
# mkdir ~/my_ansible
hosts-file указує на те де описані хости та їх групи. Cтворимо його:
# vim ~/my_ansible/hosts
[example_hosts]
192.168.1.3
Перевіримо роботу Ansible:
# cd ~/my_ansible/
# ansible -i hosts -u ubuntu example_hosts -m ping
192.168.1.3 | success >> {
"changed": false,
"ping": "pong"
}
З виводу бачимо, що тестовий запуск пройшов успішно.
-i hosts - файл з описом груп хостів.
-u ubuntu - користувач від імені якого будуть виконуватись інструкції
-m ping - запуск ping модуля.
Ansible зручно використовувати у випадку, коли необхідно запустити одну і ту саму дію на певній групі серверів, тобто у якості таких альтернатив, як Dsh, Pdsh, Cluster SSH і т.п.
# ansible -i hosts -u ubuntu example_hosts -m command -a 'uname -a'
192.168.1.3 | success | rc=0 >>
Linux ansible1 3.13.0-49-generic #83-Ubuntu SMP Fri Apr 10 20:11:33 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux
# ansible -i hosts -u ubuntu example_hosts -m command -a 'date'
192.168.1.3 | success | rc=0 >>
Fri Apr 24 16:43:35 CEST 2015
Тобто команда буде запущена для групи хостів example_host, що описана в локальному файлі hosts.
Спершу приведу дещо просту Ansible інструкцію. Нехай перед нами стоїть задача встановити останній стабільний Nginx з ppa-репозиторію, залити власний index.html в docroot, змінити номер порту веб-сервера та перевантажити/запустити nginx. Коректний playbook матиме наступний вигляд:
# vim ~/my_ansible/simple_playbook.yml
---
- hosts: example_hosts
sudo: yes
user: ubuntu
tasks:
- name: Add PPA-repository
apt_repository: repo='ppa:nginx/stable' state=present
register: ppastable
- name: Install Nginx
apt: name=nginx update_cache=yes state=latest
when: ppastable|success
- name: Upload default index.html for host
copy: src=static_files/index.html dest=/usr/share/nginx/www/ mode=0644
- name: Move Nginx virtualhost on port 8080
lineinfile: backup=yes dest=/etc/nginx/sites-available/default regexp="listen 80 default_server;" backrefs=yes line="\tlisten 8080 default_server;" state=present
notify:
- restart nginx
handlers:
- name: restart nginx
service: name=nginx state=restarted
Необхідно чітко дотримуватись відступів, інакше виникнуть помилки. Розберемось крок за кроком з логікою роботи.
- hosts: example_hosts
sudo: yes
user: ubuntu
hosts - група хостів над котрою будуть запущені інструкції.
sudo - дозвіл запускати інструкції від sudo
user - користувач, від імені якого будуть запускатись інструкції.
Далі йде список завдань, tasks:
tasks:
- name: Add PPA-repository
apt_repository: repo='ppa:nginx/stable' state=present
register: ppastable
Перша задача відповідає за додавання нового PPA-репозиторії ppa:nginx/stable. Модуль apt_repository додає новий репозиторій. Якщо він вже присутній (state=present), то з кожним наступним запуском прейбука повторно його вже не буде додано. Ansible слідкує за станом і у разі, якщо умова дотримується, задача вважається успішно виконаною. Вкінці задачі буде зареєстрована змінна "ppastable". Вона може отримати значення success чи failed і вже до цих значень можуть бути прив’язані виконання інших задач, як от насупної в даному прикладі:
- name: Install Nginx
apt: name=nginx update_cache=yes state=latest
when: ppastable|success
У випадку якщо попередня задача буде виконана успішно (when: ppastable | success), тобто буде успішно доданий репозиторій, apt-модуль Ansible запустить оновлення пакетів (update_cache=yes) та встановить останню версію веб-серверу. Успішним виконанням та кінцевою метою задачі вважається наявність встановленої останньої версії Nginx. Знову ж, кожний наступний запуск задачі лише перевірятиме стан виконання умов і лише у разі, якщо пакет не встановлений чи версія не остання - буде знову його встановлено.
- name: Upload default index.html for host
copy: src=static_files/index.html dest=/usr/share/nginx/www/ mode=0644
Наступна задача в переліку - завантаження на піддослідний хост локального файлу static_files/index.html за допомогою модуля copy. Після копіювання будуть виставлені права 0644. src/dest - відповідно джерело файлу і його майбутня адреса на віддаленій машині.
- name: Move Nginx virtualhost on port 8080
lineinfile: backup=yes dest=/etc/nginx/sites-available/default regexp="listen 80 default_server;" backrefs=yes line="\tlisten 8080 default_server;" state=present
notify:
- restart nginx
Модуль Ansible lineinfile допоможе виконати задачу зі зміни стандартного порту Nginx 80 на 8080. Без опції "backrefs=yes" модуль lineinfile буде додавати значення змінної line в кінець конфігураційного файлу, навіть коли немає жодного співпадання з regexp. "backup=yes" дозволяє бекапити попередні конфігураційни файли перед кожною зміною.
Після цієї задачі необхідно перевантажити веб-сервер, щоб зміни вступили в силу. Для цього використовується інструкція notify. notify сигналізує шо необідно виконати по завершенню команди. У нашому випадку вона запустить handler із іменем restart nginx, котрий описаний за допомогою модуля service останнім:
handlers:
- name: restart nginx
service: name=nginx state=restarted
Лишилось залити index.html в локальну диреторію static_files:
# mkdir ~/my_ansible/static_files
# cd ~/my_ansible/static_files
# vim index.html
<h1>Hello, Ansible!<h1>
Файл hosts залишимо в незмінному стані. Запускаємо Playbook:
# ansible-playbook -i hosts simple_playbook.yml
PLAY [example_hosts] **********************************************************
GATHERING FACTS ***************************************************************
...
changed: [192.168.1.3] => {"changed": true, "name": "nginx", "state": "started"}
PLAY RECAP ********************************************************************
192.168.1.3 : ok=6 changed=5 unreachable=0 failed=0
Перевіримо результат в браузері:
Ура!
Плейбук готовий до використання. Для його запуску на більшій групі серверів необхідно додати більше хостів в ~/my_ansible/hosts.
Наступний приклад плейбука буде побудований з використанням ролей. Роль - це дія чи група дій виділена окремо задля використання в багатьох плейбуках одразу чи просто для зручності її підтримки. По факту, роль - це директорія, в котрій уже і знаходяться окремі дії плейбука.
Кожна роль може мати додаткові директорії: tasks, handlers, templates. Задач (tasks) може бути декілька, проте вони мають бути підключені в roles/tasks/main.yml в необхідному порядку. Обробники (handlers) повинні бути також описані в своїй дирекорії roles/handlers/main.yml. Роль також може мати директорію з темплейтами (templates), в котрій лежатимуть різні шаблони. На найвищому рівні (вище директорії з ролями roles) описується основний YAML-файл із включенням необхідних ролей, змінних, груп хостів. Останні, зазвичай, описуються в локальному файлі hosts.
Розберемось з цим на прикладі. Поставимо перед собою дещо складнішу задачу: проведемо повну установку Drupal на базі Nginx, PHP-FPM, MySQL. Із Ansible це справді не складно!
Кожна з ролей має описувати якийсь певний етап налаштування на ваш власний розсуд. Наприклад задачу по встановленню Drupal я поділив на такі етапи (ролі):
- оновлення пакетної бази дистрибутиву (роль update)
- установка Nginx (роль nginx):
- додавання ppa-репозиторію ppa:nginx/stable
- установка самого пакету
- видалення лінки default віртуального хосту
- копіювання на віддалений хост нового vhost для nginx
- лінкування останнього в директорію sites-enabled
- перевантаження Nginx
- установка PHP-FPM (роль php5-fpm):
- установка php5-fpm та бібліотеки php5-gd
- редагування конфігу php5-fpm
- установка MySQL (роль mysql):
- установка пакету mysql-server та бібліотеки php5-mysql
- створення нової бази
- створення нового користувача з паролем і необхідним доступом до новох бази
- копіювання коду Drupal (роль drupal):
- установка пакету git
- клонування репозиторію Drupal
- створення конфігураційного файлу settings.php
- створення конфігураційного файлу services.yml
- установка відповідних прав для конфігураційних файлів
- установка прав для директорії files
Детально про установку Drupal можна почитати наприклад тут.
Створимо нову директорію для плейбука та ролей:
# mkdir ~/drupal_setup_nginx
Як і в попередньому випадку опишемо групи хостів:
# cd ~/drupal_setup_nginx/
# vim hosts
[drupal_hosts]
192.168.1.3
Звісно, що до груп може входити більше ніж один хост.
Переходимо до опису ролей. Перша - update:
# cd ~/drupal_setup_nginx/
# mkdir roles
# cd roles
# mkdir update
# cd update
# mkdir tasks
# cd tasks
# vim main.yml
---
- name: apt-get update the server
apt: update_cache=yes
Ця роль за допомогою модуля apt оновить кеш пакетів.
Наступна роль - nginx. Вона складатиметься із двох задач - установки веб-сервера (setup.yml) та створення віртуального хосту (create_vhost.yml). Тому їх потрібно включити в загальний main.yml:
# cd ~/drupal_setup_nginx/roles
# mkdir nginx
# cd nginx
# mkdir tasks
# cd tasks
# vim main.yml
---
- include: setup.yml
- include: create_vhost.yml
Створюємо задачу для установки nginx:
# vim setup.yml
---
- name: Add Nginx Repository
apt_repository: repo='ppa:nginx/stable' state=present
register: ppastable
- name: Install Nginx
apt: name={{ item }} update_cache=yes state=latest
with_items:
- nginx
when: ppastable|success
Ця задача додасть репозиторій Nginx та встановить його в разі, якщо репозиторій було додано успішно (when: ppastable | success)
Після установки веб-сервера, необхідно створити хост для майбутньої інсталяції Drupal:
# vim create_vhost.yml
---
- name: Remove default vhost
file: path=/etc/nginx/sites-enabled/default state=absent
- name: Create Nginx vhost
template: src=nginx.conf.j2 dest=/etc/nginx/sites-available/{{ domain }}
- name: Create link in sites-enabled
file: src=/etc/nginx/sites-available/{{ domain }} dest=/etc/nginx/sites-enabled/{{ domain }} state=link
notify:
- Restart Nginx
В цій задачі буде видалено дефолтний віртуальний хост, додано новий, за допомогою модуля template, та створено посилання в /etc/nginx/sites-enabled/ для його активації. Темплейт для нового домена буде взято з директорії templates ролі:
# cd ~/drupal_setup_nginx/roles/nginx/
# mkdir templates
# cd templates
# vim nginx.conf.j2
server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
root /var/www/{{ domain }};
index index.php index.html index.htm;
server_name {{ domain }};
location / {
#try_files $uri $uri/ =404;
try_files $uri $uri/ /index.php?q=$uri&$args;
}
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /var/www/{{ domain }};
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
Для опису темплейтів використовується темплейт-мова Jinja2.
{{ domain }} - змінна, котру буде взято з основного плейбуку.
Після зміни конфігураційних файлів необхідно перевантажити Nginx. В Ansible це реалізовується через директиву notify. notify викличе handler із іменем "Restart Nginx", що має бути описаний в директорії handlers:
# cd ~/drupal_setup_nginx/roles/nginx/
# mkdir handlers
# cd handlers
# vim main.yml
---
- name: Restart Nginx
service: name=nginx state=restarted
Отже веб-сервер буде перевантажений по закінченню ролі завдяки Ansible модулю service.
Наступна роль - php5-fpm.
# cd ~/drupal_setup_nginx/roles
# mkdir php5-fpm
# cd php5-fpm
# mkdir tasks
# cd tasks
# vim main.yml
---
- include: setup.yml
- include: edit_config.yml
Як і в попередній ролі, задачі я поділив на дві частини: setup.yml та edit_config.yml.
# vim setup.yml
---
- name: Install PHP-FPM and associated packages
apt: name={{ item }} update_cache=yes state=latest
with_items:
- php5-fpm
- php5-gd
notify:
- Restart PHP-FPM
- Restart Nginx
# vim edit_config.yml
---
- name: Ensure PHP-FPM cgi.fix_pathinfo=0
lineinfile: dest=/etc/php5/fpm/php.ini regexp='^(.*)cgi.fix_pathinfo=' line=cgi.fix_pathinfo=0
notify:
- Restart PHP-FPM
По закінченню задач відбудеться перевантаження демонів Nginx та PHP-FPM:
# cd ~/drupal_setup_nginx/roles/php5-fpm/
# mkdir handlers
# cd handlers
# vim main.yml
---
- name: Restart Nginx
service: name=nginx state=restarted
- name: Restart PHP-FPM
service: name=php5-fpm state=restarted
Нічого особливо нового на цьому етапі.
Передостання роль - mysql.
# cd ~/drupal_setup_nginx/roles
# mkdir mysql
# cd mysql
# mkdir tasks
# cd tasks
# vim main.yml
---
- include: setup.yml
- include: create_db.yml
Вміст setup.yml:
# vim setup.yml
---
- name: Install MySQL server
apt: name=mysql-server state=latest
- name: Install MySQL module for PHP
apt: name=php5-mysql state=latest
Та create_db.yml:
# vim create_db.yml
---
- name: Install Python MySQLdb
apt: name=python-mysqldb state=latest
- name: Create the Drupal database
mysql_db: db={{ db_name }} state=present
- name: Create the Drupal user
mysql_user: >
name={{ db_user }}
password={{ db_password }}
priv={{ db_name }}.*:ALL
host=localhost
Python-бібліотека MySQLdb необхідна для створення користувачів, баз, та надання прав до них. Змінні {{ db_user }}, {{ db_password }}, {{ db_name }} будуть взяті із основного плейбуку, що об'єднає всі ролі.
Нарешті, остання роль - drupal.
# cd ~/drupal_setup_nginx/roles
# mkdir drupal
# cd drupal
# mkdir tasks
# cd tasks
# vim main.yml
---
- name: Install git
apt: name=git state=latest
- name: Clone Drupal
git: >
repo=http://git.drupal.org/project/drupal.git
dest=/var/www/{{ domain }}/
update=no
- name: Create settings.php
command: cp /var/www/{{ domain }}/sites/default/default.settings.php /var/www/{{ domain }}/sites/default/settings.php
args:
creates: /var/www/{{ domain }}/sites/default/settings.php
- name: Create services.yml
command: cp /var/www/{{ domain }}/sites/default/default.services.yml /var/www/{{ domain }}/sites/default/services.yml
args:
creates: /var/www/{{ domain }}/sites/default/services.yml
- name: Update permissions of settings.php
file: path=/var/www/{{ domain }}/sites/default/settings.php mode=777
- name: Update permissions of services.yml
file: path=/var/www/{{ domain }}/sites/default/services.yml mode=777
- name: Update permissions of files directory
file: >
path=/var/www/{{ domain }}/sites/default/files
mode=777
state=directory
recurse=yes
Для установки CMS Drupal скористуємось офіційним git-репозиторієм проекту http://git.drupal.org/project/drupal.git. Для успішного виконання цієї задачі необхідно спочатку встановити пакет git, клонувати репозиторій, і створити декілька конфігураційний файлів із наданих темплейтів шляхом копіювання: settings.php та services.yml. Три останні задачі ролі відповідають за встановлення необхідних прав для конфігураційних файлів та директорії files.
Між іншим, структуру директорій для ролі можна згенерувати наступним чином:
# ansible-galaxy init some_role
- some_role was created successfully
# ls -1 some_role
README.md
defaults
files
handlers
meta
tasks
templates
tests
vars
Як наслідок такої генерації, всі ролі будуть по структурі схожими. Ця можливість з'явилась в версії Ansible 1.4.2.
Основний плейбук має розміщуватись на одному рівні з директорією ролей:
# cd ~/drupal_setup_nginx/
# vim site.yml
---
- hosts: drupal_hosts
user: ubuntu
sudo: yes
vars:
- domain: drupal.com
- db_name: drupal
vars_files:
- 'databag.yml'
roles:
- update
- nginx
- php5-fpm
- mysql
- drupal
Плейбук буде виконаний для групи drupal_hosts, що описана в ~/drupal_setup_nginx/hosts. Запускати інструкції буде користувач ubuntu від sudo.
Замість змінних у подвійних фігурних дужках "{{}}" будуть підставлені відповідні значення "domain: drupal.com" та "db_name: drupal". Змінні паролей, можливо, варто винести окремо в файл (databag.yml) та за допомогою ansible-vault зашифрувати:
# vim databag.yml
---
db_user: 'drupal_user'
db_password: 'drupal_db_pass'
Не завадить, звісно, обрати у якості пароля щось оригінальніше. Шифруємо результат:
# ansible-vault encrypt databag.yml
Після шифрування, при перегляді звичайним переглядачем, він буде схожим на таке:
# cat databag.yml
$ANSIBLE_VAULT;1.1;AES256
63326230353939613938333861316631643332306233633535616239633635643765633834626130
3366663934643166663731386235383961326537616662340a653836363561633033656136663132
36396233613165363237313039663362366337313864353463306265346566626361663836623830
3562636662646639310a303138373166623135646331623931363432383638313966646134323362
36363339316364356261373839613230663937366134393332316332323762663134393135306638
66616538313135326539626562633665323565306337656261366363346561313564633239333438
353164393263343766376334383930373731
Щоб розшифрувати чи відрудагувати databag необхідно знову пропустити його через ansible-vault і указати попередній пароль:
# ansible-vault decrypt vars.yml
# ansible-vault edit vars.yml
Отже, готово! Дерево всіх створений директорій має такий вигляд:
# tree drupal_setup_nginx
drupal_setup_nginx
├── databag.yml
├── hosts
├── roles
│ ├── drupal
│ │ └── tasks
│ │ └── main.yml
│ ├── mysql
│ │ └── tasks
│ │ ├── create_db.yml
│ │ ├── main.yml
│ │ └── setup.yml
│ ├── nginx
│ │ ├── handlers
│ │ │ └── main.yml
│ │ ├── tasks
│ │ │ ├── create_vhost.yml
│ │ │ ├── main.yml
│ │ │ └── setup.yml
│ │ └── templates
│ │ └── nginx.conf.j2
│ ├── php5-fpm
│ │ ├── handlers
│ │ │ └── main.yml
│ │ └── tasks
│ │ ├── edit_config.yml
│ │ ├── main.yml
│ │ └── setup.yml
│ └── update
│ └── tasks
│ └── main.yml
└── site.yml
14 directories, 17 files
Запускаємо основний плейбук і чекаємо на результат - автоматично налаштований Drupal:
# ansible-playbook -i hosts site.yml --ask-vault-pass
Vault password:
PLAY [drupal_hosts] ***********************************************************
GATHERING FACTS ***************************************************************
ok: [192.168.1.3]
TASK: [update | apt-get update the server] ************************************
ok: [192.168.1.3]
...
NOTIFIED: [php5-fpm | Restart PHP-FPM] ****************************************
changed: [192.168.1.3]
PLAY RECAP ********************************************************************
192.168.1.3 : ok=23 changed=21 unreachable=0 failed=0
Остання стрічка інформує, що все відпрацьовано успішно. Якщо виникла помилка - варто перезапустити плейбук із ключем -v/-vv/-vvv і уважно прочитати вивід команд.
Для успішності експерименту варто drupal.com прив’язати до 192.168.1.3 в /etc/hosts.
Посилання:
https://serversforhackers.com/an-ansible-tutorial
https://www.digitalocean.com/community/tutorials/how-to-use-ansible-roles-to-abstract-your-infrastructure-environment
https://www.digitalocean.com/community/tutorials/how-to-create-ansible-playbooks-to-automate-system-configuration-on-ubuntu
https://www.digitalocean.com/community/tutorials/how-to-create-an-ansible-playbook-to-automate-drupal-installation-on-ubuntu-14-04
https://www.digitalocean.com/community/tutorials/how-to-deploy-a-basic-php-application-using-ansible-on-ubuntu-14-04
http://habrahabr.ru/company/selectel/blog/196620/
http://habrahabr.ru/post/195048/
http://habrahabr.ru/company/express42/blog/254959/
http://tomoconnor.eu/blogish/getting-started-ansible
https://adamcod.es/2014/09/23/vagrant-ansible-quickstart-tutorial.html
http://jpmens.net/2012/06/06/configuration-management-with-ansible/
https://sysadmincasts.com/episodes/43-19-minutes-with-ansible-part-1-4
https://developer.rackspace.com/blog/ansible-and-docker
http://docs.ansible.com/ansible/latest/playbooks_best_practices.html
Ansible vs Puppet
https://dantehranian.wordpress.com/2015/01/20/ansible-vs-puppet-overview/
https://dantehranian.wordpress.com/2015/01/20/ansible-vs-puppet-hands-on-with-ansible/
https://blog.ryandlane.com/2014/08/04/moving-away-from-puppet-saltstack-or-ansible/
Немає коментарів:
Дописати коментар