Это старая версия документа!
Управление хостами, по сети, через 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
loop, with_item, until, первый замена второму, with_item уже выводится из использования
кстати говоря может быть with_<много вариантов> (list/dict и т.д.)
По умолчанию обход выполняется просто по хостам из инвентаря, массив хостов- «hostvars[inventory_hostname][…]«
Инвентарь
all: children: loctest: vars: zabbix_agent_server: 10.10.40.204 zabbix_url: http://10.10.40.204:800 zabbix_api_use: true zabbix_host_status: enabled hosts: one: param_name: 10.10.40.204 two: param_name: 1.1.1.1 three: param_name: 2.2.2.2
Обработка
--- - name: Test play hosts: loctest gather_facts: False tasks: - name: check delegate_to: 127.0.0.1 ansible.builtin.debug: msg: - "{{ hostvars[item]['zabbix_interface_ip'] }}" (либо {{ item }}) loop: "{{ groups['loctest'] }}"
Инвентарь
all: children: cl_autotest: hosts: S221AUTOTEST: S222AUTOTEST: cl_halftest: hosts: S201: S207: cl_sib: hosts: S202: S203:
Обработка
--- - name: Show List Inventory hosts: 127.0.0.1 gather_facts: False tasks: - name: Groups ansible.builtin.debug: msg: "{{ item }}" # или без цикла, сразу список "{{ groups | list }}" loop: "{{ groups | list }}" msg: "{{ groups | select('match', '^cl_.*') | replace('cl_', 'GSC_') }}" # Еще есть вариант фильтрации (reject работает наоборот) "..| reject('match', '^value2$|^value2$') |.." # Еще варианты перебора и отображения инвентаря, тут передан параметр --limit для фильтрации хостов - name: Print #vars: # cluster_group: "{{ group_names | select('match', '^cl_.*') }}" ansible.builtin.debug: msg: #- "{{ hostvars[inventory_hostname] }}" #- "{{ inventory_hostname }}" #- "{{ groups['cl_test-cluster'] }}" #- "{{ group_names | select('match', '^cl_.*') }}" #- "{{ groups[ansible_limit] }}" - "Host name is - {{ hostvars[item]['host_name'] }}" loop: "{{ groups[ansible_limit] }}"
Дока
«Магические» переменные типа hostvars, groups, group_names, inventory_hostname, ansible_limit
# Распечатать инфу хоста ansible myhost -m setup ansible myhost -m setup -a "filter=ansible_cmdline" # Или в плейбуке - name: "Print facts" ansible.builtin.debug: msg: - "{{ ansible_facts }}" - "{{ ansible_facts["processor"] }}"
Результат выполнения
- name: Execute against dev shell: 'echo "dev"' register: cmd_dev_out - debug: msg: "{{ cmd_dev_out }}" - name: Get host info local_action: module: community.zabbix.zabbix_host_info server_url: '{{ zabbix_server_url }}' (..) register: cmd_dev_out - name: "Use" ansible.builtin.debug: msg: "{{ cmd_dev_out }}"
when - выполнять таску если условие выполняется
Применяется очевидно только к таскам
- name: Checking that required variables are set fail: msg="{{ item }} is not defined" when: item not in vars loop: - win_zbx_path - win_repo - name: Run with items greater than 5 ansible.builtin.command: echo {{ item }} loop: [ 0, 2, 4, 6, 8, 10 ] when: item > 5* - name "dcdc" when: condition1 == "condition1" or condition2 == "condition2" - name "fvfv" when: - sshkey_result.rc == 1 - github_username is undefined or github_username |lower == 'none' (...) when: hostvars[item]['my_variable'] is defined
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-inventory --graph "my_group" --vars --export # Объявление переменной в консоли ansible-playbook runrole.yml -e "zabbix_status_host='enabled'"