Это старая версия документа!
Управление хостами, по сети, через ssh подключение, нужен беспарольный доступ с хоста ансибла на целевой хост
При запуске плейбука из консоли, используется авторизованный пользователь, в файле ansible.cfg можно указать другого в параметре «remote_user»
В командах мы описываем желаемое состояние системы, при выполнении софт проверяет так ли это, если нет, то выполняет необходимые действия
Команды можно выполнять одиночно из cli, либо объединять в файлы конфигурации т.н. плейбуки «playbooks», описание в формате yaml
Описано две задачи, сначала установка nginx, затем копирование файла
- hosts: web-servers tasks: - name: Installs nginx web server apt: pkg=nginx state=installed update_cache=true - name: Push future default virtual host configuration copy: src=files/site.conf dest=/etc/nginx/sites-enabled/ mode=0640
Inventory файл в котором хранится перечень целевых хостов, с которыми ведется работа. Так же здесь могут хранится переменные.
По умолчанию /etc/ansible/inventory/hosts, можно задавать другое расположение через конфиг. Расширение добавлять не нужно
Хосты могут объединяться в группы, с-но при выполнении можно указывать эти группы
./inventory/hosts.yaml
all: children: myGroup: vars: myVariable1: value myVariable2: value myVariable3: value hosts: alias1: ansible_host: 0.0.0.0 alias2: ansible_host: 1.1.1.1 alias3: ansible_host: 2.2.2.2
Непонятки с организацией этого инвентаря, полезная команда «ansible-inventory -i hosts.yaml –list» для отладки
Далее в коде доступны эти хосты и набор переменных к каждому ./test.yaml
... with_items: "{{ groups['myGroup'] }}" # Применить таску ко всем хостам из группы "{{ hostvars[item] }}" # Элемент группы, хост "{{ hostvars[item][myVariable1-4] }}" # Во вложенном массиве доступна вся инфа о хосте, в т.ч. набор переменных из инвентори, персонализированный получается
Roles, роли используются для разделения плейбука на части, и организации файлов. Для роли создается каталог, технически структура файлов может быть любой, но есть соглашение об конкретной организации файлов, что добавляет универсальности и порядка.
Создание происходит автоматически, командой $ ansible-galaxy init role-name
Особенность в том что роли могут быть вложенными т.е. составлять цепочку выполнения
Все переменные в ансибл глобальные, но есть замороченная система уровней, которая как то работает, и есть куча нюансов, так что с именованием нужно быть аккуратнее
Хороший путь ансибл это максимально простое и линейное описание последовательного процесса, по возможности стоит максимально исключить условия, циклы и прочую логику из конфигов, лучше написать 10 тривиальных строк вместо одной «кучерявой»
Шаблоны нечто иное как использование переменных типа { { inventory_hostname } } (двойные скобки вместе), это собсна и есть «jinja2»- система шаблонов для python
notify обработчик, который будет вызван в случае успешного выполнения задачи
handlers описание обработчика, который может быть вызван по имени
ansible.cfg основной конфиг
Тут есть более полное описание
[defaults] # Используемый инвентори inventory = ./myhosts # Чтобы убрать предупреждение об автопоиске питона interpreter_python = auto_silent # Подключаемый пользователь remote_user = anuser # Отключение подтверждения нового ssh ключа host_key_checking = False # Отключение сбора фактов gathering = explicit
Созданный на хосте пользователь должен быть судошным без ввода пароля, либо нужно вводить пароль при выполнении заданий
Yaml файл начинается с директивы «—« (три тире), обозначает начало очередного файла
Установка
# apt install ansible # yum install epel-release && yum install ansible
Сука тварь блядь, пидарастический ансибл, тупое хуйло, этому пидару надо точно писать «import_tasks» SSSSSuka, или include_tasks(sssss), если не дай бох этому гандону написать без `s`, эта хуила выйбет весь мозг, будет сыпать кучей невнятных ошибок в которых хуй че разберешь, а проблема в одной пидарской букве, спасибо пидары разработчики за такое хуйло
По умолчанию ансибл выполняет все команды на хостах, указанных в параметре «hosts», со всеми вытекающими, ресурсы/зависимости и т.д.
Выполнение можно делегировать на другой хост в т.ч. на локальный, («delegate_to: localhost» и «local_action» одно и тоже, но синтаксис разный)
При делегировании возможно переменные указывают на делегированный хост, а не на инвентори, хотя тут неясно. Есть еще вариант указания «connection: local», отличий вроде нет
Во всех случаях таска выполнится столько раз сколько хостов указано, можно указать «hosts: 127.0.0.1» тогда выполнится один раз
И кстати нормально, при таком варианте инвентори и все хосты из него доступны, ничего не подменивается
Так же есть директива «run_once» для однократного выполнения на первом хосте из доступных
--- - hosts: webservers #connection: local tasks: - name: Take out of load balancer pool ansible.builtin.command: /usr/bin/take_out_of_pool {{ inventory_hostname }} delegate_to: 127.0.0.1 - name: Take out of load balancer pool local_action: ansible.builtin.command /usr/bin/take_out_of_pool {{ inventory_hostname }} - name: Send summary mail local_action: module: community.general.mail body: "{{ mail_body }}" run_once: True
ansible_facts - содержит множество данных о целевом хосте
Занимает время при каждом выполнении, можно отключить в таске либо в конфиге, «gather_facts: False»
ansible 10.11.193.43 -m setup ansible 10.11.193.43 -m setup -a 'filter=facter_*' ansible 10.11.193.43 -m setup -a 'gather_subset=!all,!any,facter' ansible all -m debug -a "var=hostvars[inventory_hostname]"
- name: Collect only facts returned by facter ansible.builtin.setup: gather_subset: !all,!facter,!ohai,network {{ ansible_facts['devices']['xvda']['model'] }} {{ hostvars['myHost']['ansible_facts']['os_family'] }}
Инвентори или group_vars:
mylist: - cl_first - cl_second - cl_three - cl_four
Простой вывод списка, заменяя префикс
--- - name: Test play hosts: 127.0.0.1 tasks: - name: check debug: msg: - "{{ mylist | replace('cl_', 'GSC ') }}" - "{{ list1 | difference(list2) }}" - "{{ list | select('match', '^prefix.*') | reject('match', '^bad1$|^bad2$') | list }}"
# Перечень групп (развернут с хостами) {{ groups }} # Содержимое конкретной группы {{ groups['<имя-группы>'] }} # Перечень без развертывания {{ groups.keys() }} # Тоже без развертывания {{ groups | list }} # Группы в которые входит хост выполнения {{ group.names }}
При использовании пароля в файле, используется только вывод файла т.е. запуск, можно указывать скрипт
# Зашифровать файл ansible-vault encrypt 'path_file' # Расшифровать файл ansible-vault decrypt 'path_file' # Зашифровать строку ansible-vault encrypt_string 'string' Переменная среды: ANSIBLE_VAULT_PASSWORD_FILE=~/.vault_pass.txt\\ Ключ для запроса в cmd: --ask-vault-pass Ключ для указания в cmd: --vault-password-file
Указывая хост (или группу) можно ограничить те места, откуда будет выполняться задача, в блоке «hosts» при этом должна быть указана родительская группа, «all» например, иначе ошибка аля «not found target»
ansible-playbook mybook.yml --limit <group[host]_name> ansible <group[host]_name> -m ping
Как минимум три модуля:
lineinfile
Дока
Поиск/замена одной строки
- name: Ensure SELinux is set to enforcing mode ansible.builtin.lineinfile: path: /etc/selinux/config regexp: '^SELINUX=' line: SELINUX=enforcing - name: Make sure group wheel is not in the sudoers configuration ansible.builtin.lineinfile: path: /etc/sudoers state: absent regexp: '^%wheel'
replace
Дока
Поинтереснее, но все же заточен на построчную поиск/замену
Указав «любой символ» работает с каждым найденным словом как с отдельным элементом, причем даже пробелы не щадит(
Для замены собсна тоже принимает строку
- name: "Запись в файл" ansible.builtin.replace: path: /tmp/hosts-test after: "# Start gameserver hosts" before: "# End gameserver hosts" #regexp: '^1.1.(.+)$' #replace: '11.11.11.11' regexp: '.*' replace: '11.11.11.11'
blockinfile
Дока
Вставляет блок текста, соблюдая строки
Можно указать регулярками (либо конец файла) после чего (до чего) вставлять блок, что то одно !!!
Работает прежде всего с блоками текста, сам текст указан в «block», обрамление в «marker» (директива »{mark}« подставляет слова BEGIN/END в метке)
С блоками работает нормально, проверяет наличие, если изменений нет, то пропускается, если есть, то обновляет текст, если указать в block пустую строку, то блок удалится вместе с метками
- name: "Запись в файл hosts" ansible.builtin.blockinfile: #become: yes #become_method: sudo #become_user: root path: /tmp/hosts-test block: | 2.2.2.2 two 3.3.3.3 three 4.4.4.4 four marker: "{mark} Gameservers from ansible inventory"
selectattr rejectattr
Есть нюанс тогда когда указанный для фильтрации ключ есть не во всех элементах. Указанный по ссылке пример в итоге возвратит два списка, один с установленным аттрибутом, второй без аттрибута.
Условия для поиска могут быть разными «equalto match search» и т.д.
# Так возвращает элемент (весь элемент словаря) у которого установлен аргумент в указанное значение # "('origin1', 'none')" - для поиска пустого значения "{{ fruit | selectattr('origin1', 'equalto', '21') }}" # "selectattr('origin1', 'undefined')" - Для поиска именно неустановленного аттрибута "{{ fruit | selectattr('origin1', 'undefined') }}"
map
# Из общего списка словарей вернет только элементы по регулярке, далее покажем только значение только одного аттрибута, всех этих элементов "{{ fruit | selectattr('origin1', 'match', '^..$') | map(attribute='origin1') }}"
Дока
dict2items,items2dict,combine,zip и т.д.
Для работы нужен «zabbix-api», плюс ансибл работает со своей версией питона
/usr/bin/python3.8 -m pip install zabbix-api ansible-galaxy collection install community.zabbix
hosts, можно короче
all: hosts: 192.168.0.101
test.yaml
--- - hosts: all tasks: - name: Test ping delegate_to: 127.0.0.1 ping:
+ в конфиге еще путь к инвентори
Инкремент переменной в цикле
--- - hosts: all gather_facts: no run_once: True tasks: - name: Get list group vars: - llist: '' - foo: '' set_fact: foo: "{{ item +' '+ hostvars[item]['ansible_host'] | default('-') }}" llist: "{{ llist + foo }}\n\r " loop: "{{ groups['remote'] }}" - name: show debug: msg: "{{ llist }}" (...) msg: "{{ hostvars[groups['gameservers_all'][0]]['ansible_winrm_host'] }}" (...) (...) vars: - zabbix_gameservers_list: '' set_fact: zabbix_gameservers_list: "{{ zabbix_gameservers_list + hostvars[item]['openvpn_ip'] | default(hostvars[item]['ansible_winrm_host']) | default('# ip not found :(') +' ' + hostvars[item]['inventory_hostname'] }}\n" loop: "{{ groups['gameservers_all'] }}" (...)
--- - hosts: all gather_facts: no run_once: True tasks: - name: Get list group shell: cmd: "ss -ntlua | grep 8086 | wc" register: resultt become: yes - debug: var=resultt.stdout_lines
Есть несколько вариантов/утилит, в зависимости от задачи
Например вывод списка из инвентори команды:
ansible-inventory, ansible-console - по функционалу почти одинаковые, и почти бесполезные, тупые, конечные (или даже конченные)
Можно просто запросом в консоли через debug
ansible localhost -m debug -a 'var=groups["my_group"]' ansible localhost -m debug -a 'var=hostvars["myHost"].myVariable' ansible-inventory --graph "my_group" --vars --export # Объявление переменной в консоли ansible-playbook runrole.yml -e "zabbix_status_host='enabled'"
--- - name: "Конфигурация systemd для новой службы" systemd: daemon_reload: yes listen: "reload_systemd" - name: "Запуск службы zookeeper" systemd: name: zookeeper state: restarted enabled: true listen: "start_restart_zookeeper"
# рекоммендуют запускать при любых изменениях чтобы убедится в применении пар-ов, в случае yes, запустит reload даже если ничего не запускал/останавливал daemon_reload: yes # Ожидание завершения no_block: false # start/stopped — это идемпотентные действия, которые не будут запускать команды без необходимости. После restarted устройство всегда будет подпрыгивать. reloaded всегда перезагружается. state: reloaded # запускает службу в любом случае state: restarted # перезапускает службу state: [stopped, started]
Проверка существования и остановка сервиса, несколько попыток с задержкой, пока не остановится, таска упадет если сервис так и не остановится
--- - name: "Получаем информацию о сервисах в системе" ansible.builtin.service_facts: - name: "Останавливаем fprofilesac.service если он есть в ОС" systemd: name: 'fprofilesac.service' state: stopped register: fprofilesacInfo until: fprofilesacInfo.state == "stopped" retries: 5 delay: 10 when: "'fprofilesac.service' in services"
handlers: запуск/перезапуск/начальная активация службы
- name: "Запуск службы FProfilesAutoAcceleration" systemd: name: fprofilesac state: restarted enabled: true daemon_reload: yes listen: "start_restart_fprofilesac"
Пример шаблона для unit файла
[Unit] Description=FProfilesAutoAcceleration service After=tank.mount After=mnt-update_server_200.mount After=network-online.target #Requires=network-online.target tank.mount mnt-update_server_200.mount [Service] ExecStart=java -Dcom.sun.management.jmxremote \ -Dcom.sun.management.jmxremote.port=10045 \ -Djava.rmi.server.hostname={{ openvpn_ip | default(ansible_host) }} \ -Dcom.sun.management.jmxremote.authenticate=false \ -Dcom.sun.management.jmxremote.ssl=false \ -jar {{ fac_appname }}.jar \ -Xmx7G WorkingDirectory={{ fac_path }} Restart=always TimeoutSec=15 User={{ fac_user }} [Install] Alias=fac.service
- name: "Создание пользователя" user: name: fprofilesac create_home: false system: true shell: /bin/false - name: "Создание директорий" file: path: "{{ item }}" state: directory owner: fprofilesac group: fprofilesac mode: '0755' loop: - "{{ fprofilesac_path }}" - "{{ fprofilesac_path }}/logs" - "{{ fprofilesac_path }}/backups" - name: "Создание ссылки симлинка" file: src: "/src/directory" dest: "dest/symlink-name" state: link