суббота, 20 февраля 2016 г.

Ansible: введение

Первое мое знакомство с ansible, крутая штука.
В этой заметке оставлю основные выдержки из вводной документации: http://docs.ansible.com/ansible/intro.html

В VirtualBox развернул Virtuozzo 7, а в нем окружение, в котором я буду тестировать ansible.
Выглядит это так, ansible.host.tld - мастер-сервер ансибла, остальные три виртуалки подопытые кролики:
[[email protected] ~]# prlctl list -o name,ip,ostemplate,hostname
NAME           IP_ADDR         OSTEMPLATE             HOSTNAME
ansible-c7     192.168.0.151   .centos-7-x86_64       ansible.host.tld
centos7        192.168.0.154   .centos-7-x86_64       centos7.host.tld
debian8        192.168.0.153   .debian-8.0-x86_64     debian8.host.tld
ubuntu14       192.168.0.152   .ubuntu-14.04-x86_64   ubuntu14.host.tld

Ansible удобен в первую очередь тем, что на агентах ему не нужно ничего ставить, т.к. общение между мастером и агентами происходит по SSH, никаких тебе демонов, сервисов, кучи зависимостей в виде ruby и прочих Java.

Установка ansible на мастере в CentOS 7:
[[email protected] ~]# yum install epel-release git
[[email protected] ~]# yum install ansible
[[email protected] ~]# ansible --version
ansible 1.9.4
  configured module search path = None

В EPEL для RHEL7 до сих пор версия 1.9.4, хотя уже давно 2.0.0 есть для той же Ubuntu.
Пробовал я и с помощью исходников ставить на CentOS 7, но у меня при компиляции просил какой-то пакет asciidoc, который тянет за собой половину иксов, поэтому я поставил с EPEL'а.

Для  инвентаризации хостов ansible использует так называемы инвентори-файл /etc/ansible/hosts.
Приведем его к такому виду:
[[email protected] ~]# vim /etc/ansible/hosts
centos7.host.tld
debian8.host.tld
ubuntu14.host.tld

Я внес туда список хостнеймов моих подопытных виртуалок, но т.к. у меня локальная сеть и DNS-сервер я не настраивал, то дополнительно я еще и добавлю такие записи в /etc/hosts:
[[email protected] ~]# vim /etc/hosts
192.168.0.152 ubuntu14.host.tld
192.168.0.153 debian8.host.tld
192.168.0.154 centos7.host.tld

Далее сгенерируем SSH-ключ и раскидаем его по виртуалкам, чтобы постоянно не вводить пароль:
[[email protected] ~]# ssh-keygen
[[email protected] ~]# ls /root/.ssh/
authorized_keys  id_rsa  id_rsa.pub
[[email protected] ~]# cat /etc/ansible/hosts | xargs -i ssh-copy-id {}

После того как ключи раскиданы, можно проверить коннект:
[[email protected] ~]# ansible all -m ping
ubuntu14.host.tld | success >> {
    "changed": false, 
    "ping": "pong"
}

debian8.host.tld | success >> {
    "changed": false, 
    "ping": "pong"
}

centos7.host.tld | success >> {
    "changed": false, 
    "ping": "pong"
}

Все работает.

Приведем инвентори к такому виду:
[[email protected] ~]# vim /etc/ansible/hosts 
[frontend]
centos7.host.tld:2222

[backend]
debian8.host.tld

[backend:vars]
frontend_server=centos7
database_server=ubuntu14

[database]
ubuntu14.host.tld backend_server=debian8

Здесь в  инвентори в.ini формате можно объединять хосты в группы и создавать переменные.
Например я тут создал группы frontend, backend и database, понятно, какие хосты к каким группам относятся.
Для группы backend установлены две переменные frontend_server и backend_server, которые впоследствии мы сможем использовать.
Для хоста ubuntu15.host.tld установили переменную backend_server=debian8.
Строка centos7.host.tld:2222 указывает подключение к порту 2222 по SSH.

Хранение переменных нежелательно в инвентори файле, предпочтительно хранить их отдельно. Пример:
[[email protected] ~]# mkdir /etc/ansible/{host,group}_vars
[[email protected] ~]# vim /etc/ansible/group_vars/backend
---
frontend_server: centos7
database_server: ubuntu14

[[email protected] ~]# vim /etc/ansible/host_vars/ubuntu14.host.tld
---
backend_server: debian8

[[email protected] ~]# vim group_vars/frontend
---
ansible_ssh_port: 2222
# Тут стоит обратить внимание на то, что с версии ansible 2.0
# имена переменных меняются, например на ansible_port
# http://docs.ansible.com/ansible/intro_inventory.html#list-of-behavioral-inventory-parameters

Таким образом я аккуратно разнес переменные для хостов и групп по файлам. А также указал, что для хостов группы frontend порт для подключения SSH 2222.
Теперь можно привести инвентори снова к такому виду:
[[email protected] ~]# cat /etc/ansible/hosts
[frontend]
centos7.host.tld

[backend]
debian8.host.tld

[database]
ubuntu14.host.tld

Я в самом начале устанавливал git не зря, ведь удобно хранить все конфиги ansible в репозитории:
[[email protected] ~]# cd /etc/ansible/
[[email protected] ansible]# git init
Initialized empty Git repository in /etc/ansible/.git/
[[email protected] ansible]# git config --global user.name "Amet13"
[[email protected] ansible]# git config --global user.email [email protected]
[[email protected] ansible]# git commit -m "First commited ansible configuration"

Пример использования ansible, отключаем httpd на frontend-серверах:
[[email protected] ~]# systemctl is-active httpd
active
[[email protected] ~]# ansible frontend -m service -a "name=httpd state=stopped"
centos7.host.tld | success >> {
    "changed": true, 
    "name": "httpd", 
    "state": "stopped"
}
[[email protected] ~]# systemctl is-active httpd
inactive

Вместо группы frontend можно указать all или *, тогда httpd отключится на всех хостах, можно указать только хостнейм, например debian8.host.tld.
Отправить ping только группам backend и frontend:
[[email protected] ~]# ansible backend:frontend -m ping

Для примера добавим centos7.host.tld в секцию frontend и backend:
[[email protected] ~]# vim /etc/ansible/hosts
[frontend]
centos7.host.tld

[backend]
debian8.host.tld
centos7.host.tld
...

И пропингуем все хосты, которые находятся в группе backend, но не находятся в группе frontend:
[[email protected] ~]# ansible backend:\!frontend -m ping
debian8.host.tld | success >> {
    "changed": false, 
    "ping": "pong"
}

Как видим пинг отправился только для debian8.host.tld, который находится только в группе backend.

Можно использовать подстановки для хостнеймов и IP:
[[email protected] ~]# ansible *.host.tld -m ping

С этими возможностями можно делать множество различиных подстановок.
Более подробно тут: http://docs.ansible.com/ansible/intro_patterns.html#patterns

По умолчанию модуль command не поддерживает пайпы, кавычки, переменные, например при выполнении такой команды:
[[email protected] ~]# ansible frontend -a 'cat /etc/passwd | wc -l'
centos7.host.tld | FAILED | rc=1 >>
cat: invalid option -- 'l'
Try 'cat --help' for more information.

Для этого нужно использовать модуль shell:
[[email protected] ~]# ansible frontend -m shell -a 'cat /etc/passwd | wc -l'
centos7.host.tld | success | rc=0 >>
26

Либо второй вариант, в /etc/ansible/ansible.cfg изменить:
module_name = command
на
module_name = shell

Модуль copy позволяет посредством scp копировать файлы на хосты:
[[email protected] ~]# ansible backend -m copy -a 'src=/etc/hosts dest=/tmp/hosts'

Модуль file позволяет менять владельца файла и права на него.
[email protected]:~# ls -l /tmp/hosts 
-rw-r--r-- 1 root root 353 Feb 20 13:09 /tmp/hosts
[[email protected] ~]# ansible backend -m file -a 'dest=/tmp/hosts mode=600 owner=mail group=mail'
[email protected]:~# ls -l /tmp/hosts 
-rw------- 1 mail mail 353 Feb 20 13:09 /tmp/hosts

Создать директорию:
[[email protected] ~]# ansible backend -m file -a 'dest=/tmp/dir/test state=directory'
[email protected]:~# ls /tmp/dir/ -l
total 0
drwxr-xr-x 2 root root 40 Feb 20 13:15 test

Рекурсивно удалить директорию:
[[email protected] ~]# ansible backend -m file -a 'dest=/tmp/dir/ state=absent'
[email protected]:~# ls /tmp/dir/ -l
ls: cannot access /tmp/dir/: No such file or directory

Управление пакетами.
Создадим в инвентори группы centos и debian-ubuntu для удобства управления пакетами:
[[email protected] ~]# cat /etc/ansible/hosts
...
[centos]
centos7.host.tld

[debian-ubuntu]
debian8.host.tld
ubuntu14.host.tld

Установим httpd на все хосты с CentOS:
[[email protected] ~]# ansible centos -m yum -a 'name=httpd state=present'

И удалим его сразу:
[[email protected] ~]# ansible centos -m yum -a 'name=httpd state=absent'

Аналогично с apache2 для Debian/Ubuntu:
[[email protected] ~]# ansible debian-ubuntu -m apt -a 'name=apache2 state=present'
[email protected]:~# systemctl is-active apache2
active
[[email protected] ~]# ansible debian-ubuntu -m apt -a 'name=apache2 state=absent'
[email protected]:~# systemctl is-active apache2
inactive

Создание и удаление пользователей:
[[email protected] ~]# ansible debian-ubuntu -m user -a 'name=ansible-user password=$6$IT91ljNr$vfLfnBi4ntlyqsmCa8feNLCwmruv5iZLxW6ZOwcCZ1dOFHrOdithHRbqWdLnTueLi5nf0bDiI3TSYpa51Z/QW/'
[[email protected] ~]# ssh [email protected]
[email protected]'s password: 123456
[[email protected] ~]# ansible debian-ubuntu -m user -a 'name=ansible-user state=absent'

Пример пароля можно сгенерить так:
[[email protected] ~]# mkpasswd --method=sha-512
Password: 
$6$yX3fe4eqN6C4lqFO$q86.JTDz3ShkPnErY8HdiOf9HYaGzKHw79TBD6SUnOOWpdN0.raq8ZIBAdOpDd5LYnzpV.yfnsfZlj04ZvfiI.

Модуль git используется для деплоймента приложений например:
[[email protected] ~]# ansible frontend -m yum -a 'name=git state=present'
[[email protected] ~]# ansible frontend -m git -a 'repo=https://github.com/Amet13/icinga2-plugins-extra.git dest=/srv version=HEAD'
[[email protected] ~]# cat /srv/.git/config | grep url
url = https://github.com/Amet13/icinga2-plugins-extra.git

Управление сервисами, тут все понятно:
[[email protected] ~]# ansible debian-ubuntu -m service -a 'name=postfix state=started'
[[email protected] ~]# ansible debian-ubuntu -m service -a 'name=postfix state=restarted'
[[email protected] ~]# ansible debian-ubuntu -m service -a 'name=postfix state=stopped'

Запуск задачи в фоновом режиме:
[[email protected] ~]# ansible all -B 60 -P 0 -a 'ping ya.ru'
background launch...
[[email protected] ~]# ansible ubuntu14.host.tld -m async_status -a "jid=254659849531.1643"
ubuntu14.host.tld | success >> {
    "ansible_job_id": "254659849531.1643", 
    "changed": false, 
    "finished": 0, 
    "results_file": "/root/.ansible_async/254659849531.1643", 
    "started": 1
}

Порядок чтения конфигов ansible'ом:
1. Переменная ANSIBLE_CONFIG
2. ansible.cfg в текущей директории
3. .ansible.cfg в домашней директории
4. /etc/ansible/ansible.cfg

По конфигу можно почитать тут, информация периодически меняется:
http://docs.ansible.com/ansible/intro_configuration.html

Тут я познакомился с плейбуками в nginx (рекомендую к ознакомлению): http://blog.amet13.name/2016/02/ansible-playbook-nginx.html

Ссылки пригодятся:
Книга Ansible for DevOps: https://www.dropbox.com/sh/m9z66tysri8l53f/AADa3vMrwjb8XyUXMXqxZpGga/pub/eng/administration/Ansible%20for%20DevOps.pdf
Получасовое видео о Ansible, где четко поясняют основы: https://www.ansible.com/get-started

3 комментария:

Dmitry Erohin комментирует...

Очень хороший пост. Все понятно и есть примеры для практического использования.

Хотя я для того же в основном работаю через pssh, он конечно не на столько удобный в описании нужных хостов (нельзя там убрать хосты что повторяются в другом списке), и для выполнения нужной команда нужно описать как она выглядит в shell.

Dmitry Erohin комментирует...

О вспомнил.. один вопрос возник.
Для того, чтобы всё-таки вводить пароль, если не делать проброс ключа, то как команда должна выглядеть, хотя бы того же пинга?

Amet Umerov комментирует...

http://docs.ansible.com/ansible/intro_getting_started.html

>When speaking with remote machines, Ansible by default assumes you are using SSH keys. SSH keys are encouraged but password authentication can also be used where needed by supplying the option --ask-pass. If using sudo features and when sudo requires a password, also supply --ask-become-pass (previously --ask-sudo-pass which has been deprecated).