Созданный на хосте пользователь должен быть судошным без ввода пароля, либо нужно вводить пароль при выполнении заданий
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 # ask password --ask-vault-password, --ask-vault-pass # создать, сохранится зашифрованный ansible-vault create new_file # расшифровать ansible-vault decrypt file.txt --output file.txt_open # открыть для редактирования, при закрытии снова зашифруется ansible-vault edit ansible-vault view
export chiphertext='$ANSIBLE_VAULT;1.1;AES256 63363161346637313765383762356535366637653435313436623435666166326338346163303232 6432313962643561306631303363613334303664666536350a653561613564343236313766646336 66336363396339353734643732343833333135336662613038326435633831613935326434633865 6631626366386234620a386332633965613436386337613138353762623566623461353034653832 62306266396439656533636430643261376331616436656334376430386264656532' printf "%s\n" $chiphertext | ansible-vault decrypt
Указывая хост (или группу) можно ограничить те места, откуда будет выполняться задача, в блоке «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
# become для винды become: yes become_method: runas become_user: "{{ admin_user }}"
Есть проблемы с синхронным/ассинхронным выполнением на хостах, бывает что ансиблом запускаешь процесс, все норм он запускается но завершается вместе с плейбуком т.к. плейбук по умолчанию работает в синхронном режиме, надо запускать в ассинхронном
- name: "test" win_shell: | cd {{ my_path }} ./myScript.ps1 async: 15 ignore_errors: yes register: start_app failed_when: start_app.started != 1 # Еще примеры запуска скриптов/процессов - name: "Старт приложения" win_shell: | cd {{ my_path }} ./myScript.ps1 - name: "Старт приложения" win_shell: | cd {{ my_path }} (Start-Process java -ArgumentList "-Xmx4096m -Dapplication.env=PROD Main" -passthru).ID > myScript.pid # Рестарт Stop-Process -Id $(cat myScript.pid) ./myScript.ps1
Переменная с результатом содержит примерно такой вывод, при условии что консоль продолжила работать
(и started и finished == 1)
Если консоль сразу же корректно завершилась то вывод не отличается, тобишь здесь можно отследить проблему именно при физическом запуске доп процесса, типа если pShell не сможет его запустить, а вот если приложение упадет после запуска, это уже надо другими способами проверять, хотя бы ПИД как минимум
ok: [host_mes_mds_prod] => { "msg": { "ansible_async_watchdog_pid": 1516, "ansible_job_id": "41177545019.4340", "changed": true, "cmd": "cd C:\\test_mds\\\n./startMDS2.ps1", "delta": "0:00:00.437520", "end": "2023-12-04 12:59:59.234763", "failed": false, "failed_when_result": false, "finished": 1, "rc": 0, "results_file": "C:\\Users\\ansible\\.ansible_async\\41177545019.4340", "start": "2023-12-04 12:59:58.797243", "started": 1, "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": [] } }
Смысл в том что можно в рантайме сформировать группу хостов, на основе каких то данных в нем, например факты или хостовые переменные
Динамические группы формируются на все попадаемые значения, указанной переменной, содержатся только в памяти и к сожалению похоже нельзя вывести весь список, что усложняет работу с ними
Так же использовать можно только в текущем плейбуке, «не отходя от кассы» что называется
Например:
# Выполняем на всех хостах, группируем по знаяениям в хостовой переменной - hosts: all gather_facts: no tasks: - name: Set 'appgrp_mgaimports' group_by: key: "{{ variable_in_host }}" # Далее запускаем плейбуки с определенными параметрами только для нужной группы - hosts: value_1 gather_facts: false roles: - my_role_for_1 - hosts: value_2 gather_facts: false roles: - my_role_for_2
command - работает чуть быстрее но запускается не через оболочку («bin/sh»), поэтому в нем не доступны переменные окружения и прочие плюшки шелла, типа конвейеров, перенаправления вывода и проч
- name: "Получаем имя из конфига" ansible.builtin.shell: "yq '.profile' {{ workdir }}{{ profile_config }}" register: try_name - name: "msg" debug: msg: "{{ try_name }}"
ok: [localhost] => { "msg": { "changed": true, "cmd": "yq '.profile' /home/path/my-file.yml", "delta": "0:00:00.121520", "end": "2024-11-20 12:35:49.242621", "failed": false, "failed_when_result": false, "rc": 0, "start": "2024-11-20 12:35:49.121101", "stderr": "", "stderr_lines": [], "stdout": "my-super-name", "stdout_lines": [ "my-super-name" ] } }