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

Ansible: введение

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

В VirtualBox развернул Virtuozzo 7, а в нем окружение, в котором я буду тестировать ansible.
Выглядит это так, ansible.host.tld - мастер-сервер ансибла, остальные три виртуалки подопытые кролики:
[root@virtuozzo ~]# 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:
[root@ansible ~]# yum install epel-release git
[root@ansible ~]# yum install ansible
[root@ansible ~]# 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.
Приведем его к такому виду:
[root@ansible ~]# vim /etc/ansible/hosts
centos7.host.tld
debian8.host.tld
ubuntu14.host.tld

Я внес туда список хостнеймов моих подопытных виртуалок, но т.к. у меня локальная сеть и DNS-сервер я не настраивал, то дополнительно я еще и добавлю такие записи в /etc/hosts:
[root@ansible ~]# 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-ключ и раскидаем его по виртуалкам, чтобы постоянно не вводить пароль:
[root@ansible ~]# ssh-keygen
[root@ansible ~]# ls /root/.ssh/
authorized_keys  id_rsa  id_rsa.pub
[root@ansible ~]# cat /etc/ansible/hosts | xargs -i ssh-copy-id {}

После того как ключи раскиданы, можно проверить коннект:
[root@ansible ~]# 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"
}

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

Приведем инвентори к такому виду:
[root@ansible ~]# 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.

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

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

[root@ansible ~]# 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.
Теперь можно привести инвентори снова к такому виду:
[root@ansible ~]# cat /etc/ansible/hosts
[frontend]
centos7.host.tld

[backend]
debian8.host.tld

[database]
ubuntu14.host.tld

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

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

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

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

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

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

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

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

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

По умолчанию модуль command не поддерживает пайпы, кавычки, переменные, например при выполнении такой команды:
[root@ansible ~]# 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:
[root@ansible ~]# 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 копировать файлы на хосты:
[root@ansible ~]# ansible backend -m copy -a 'src=/etc/hosts dest=/tmp/hosts'

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

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

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

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

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

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

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

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

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

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

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

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

Запуск задачи в фоновом режиме:
[root@ansible ~]# ansible all -B 60 -P 0 -a 'ping ya.ru'
background launch...
[root@ansible ~]# 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 комментария:

Unknown комментирует...

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

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

Unknown комментирует...

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

Amet13 комментирует...

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).