service:ansible
Различия
Показаны различия между двумя версиями страницы.
Предыдущая версия справа и слеваПредыдущая версияСледующая версия | Предыдущая версия | ||
service:ansible [19.03.2025 21:46] – [Выборка из словаря по значениям из списка] viacheslav | service:ansible [15.04.2025 12:21] (текущий) – [Выборка из словаря по значениям из списка] viacheslav | ||
---|---|---|---|
Строка 1: | Строка 1: | ||
+ | ====== Ansible ====== | ||
+ | Система управления автоматической конфигурации серверов. Контроль и отслеживание изменений в системе: | ||
+ | |||
+ | [[https:// | ||
+ | [[https:// | ||
+ | [[https:// | ||
+ | |||
+ | |||
+ | ^Task |Базовый блок. Обычно модуль с параметрами, | ||
+ | ^Role |Типа функции - объединяет задачи, | ||
+ | ^Play |Сущность, | ||
+ | ^Playbook |Файл yaml для запуска, | ||
+ | |||
+ | // | ||
+ | // | ||
+ | |||
+ | <code bash> | ||
+ | # копирование файла, используя модуль copy | ||
+ | ansible localhost -m copy -a " | ||
+ | </ | ||
+ | Важное отличие от простого копирования через консоль или скрипт - // | ||
+ | |||
+ | // | ||
+ | // | ||
+ | |||
+ | ===== ad-hoc команды ===== | ||
+ | <code bash> | ||
+ | # k3 - имя целевого хоста, -m - модуль, | ||
+ | # Создать каталог (рекурсивно, | ||
+ | ansible k3 -m file -a " | ||
+ | # проверка доступа к хосту | ||
+ | ansible k3 -m ping | ||
+ | </ | ||
+ | |||
+ | ===== Задача ===== | ||
+ | Базовый элемент. | ||
+ | <code yaml> | ||
+ | # Cписок из 5 задач | ||
+ | |||
+ | - name: Модуль maven_artifact скачивает приложение из Nexus | ||
+ | maven_artifact: | ||
+ | dest: "/ | ||
+ | repository_url: | ||
+ | group_id: " | ||
+ | artifact_id: | ||
+ | version: " | ||
+ | | ||
+ | - name: Добавление сервисного пользователя | ||
+ | user: | ||
+ | name: " | ||
+ | create_home: | ||
+ | shell: / | ||
+ | | ||
+ | - name: Шаблонизация конфигурации - управление настройками приложения с помощью переменных | ||
+ | template: | ||
+ | src: project-backend.service.j2 | ||
+ | dest: / | ||
+ | |||
+ | - name: Перечитать конфигурацию systemd | ||
+ | systemd: | ||
+ | daemon_reload: | ||
+ | |||
+ | - name: Запуск сервиса | ||
+ | service: | ||
+ | name: project-backend | ||
+ | state: running | ||
+ | </ | ||
+ | '' | ||
+ | |||
+ | ==== Управление ошибками / кодами возврата, | ||
+ | |||
+ | https:// | ||
+ | |||
+ | ==== fail / assert ==== | ||
+ | Проверка, | ||
+ | '' | ||
+ | '' | ||
+ | |||
+ | <code yaml> | ||
+ | - hosts: 127.0.0.1 | ||
+ | gather_facts: | ||
+ | connection: local | ||
+ | | ||
+ | vars: | ||
+ | fail_via_fail: | ||
+ | fail_via_assert: | ||
+ | fail_via_complex_assert: | ||
+ | | ||
+ | tasks: | ||
+ | - name: Fail if conditions warrant a failure | ||
+ | fail: | ||
+ | msg: "Task failed" | ||
+ | when: fail_via_fail | ||
+ | | ||
+ | - name: Fail if an assertion isn't validated | ||
+ | assert: | ||
+ | that: " | ||
+ | | ||
+ | - name: Assertions can have certain conditions | ||
+ | assert: | ||
+ | that: | ||
+ | - " | ||
+ | - " | ||
+ | - " | ||
+ | </ | ||
+ | ==== Debug ==== | ||
+ | Вывод информации. | ||
+ | <code yaml> | ||
+ | tasks: | ||
+ | # Вывод переменной foo, свойства rc | ||
+ | - debug: var=foo.rc | ||
+ | # Лучше использовать такой синтаксис, | ||
+ | - debug: var=foo[' | ||
+ | |||
+ | # Вывод сообщения | ||
+ | - debug: msg=" | ||
+ | </ | ||
+ | |||
+ | ===== Handlers ===== | ||
+ | Это дополнительные задачи, | ||
+ | Хэндлеры выполняются в конце плейбука после всех задач, если нет спец. настроек. | ||
+ | |||
+ | <file yaml playbook_apache.yml> | ||
+ | - name: Install Apache | ||
+ | hosts: staging_servers | ||
+ | become: yes | ||
+ | |||
+ | vars: | ||
+ | source: ~/ | ||
+ | destination: | ||
+ | |||
+ | tasks: | ||
+ | - name: Install | ||
+ | package: | ||
+ | name: apache2 | ||
+ | state: latest | ||
+ | - name: Start & enable | ||
+ | service: | ||
+ | name: apache2 | ||
+ | state: started | ||
+ | enabled: yes | ||
+ | - name: Copy index.html | ||
+ | copy: | ||
+ | src: "{{ source }}" | ||
+ | dest: "{{ destination }}" | ||
+ | mode: 0774 | ||
+ | owner: www-data | ||
+ | group: www-data | ||
+ | notify: restart apache | ||
+ | |||
+ | handlers: | ||
+ | - name: restart apache | ||
+ | service: | ||
+ | name: apache2 | ||
+ | state: restarted | ||
+ | </ | ||
+ | |||
+ | Если нужно выполнить хэндлер сразу после какой-то задачи, | ||
+ | <code yaml> | ||
+ | - name: Kick handlers immediately | ||
+ | meta: flush_handlers | ||
+ | </ | ||
+ | |||
+ | Т. к. по умолчанию обработчики выполняются в конце, есть шанс, что какая-то из задач, вызывающая хэндлер, | ||
+ | <code yaml> | ||
+ | ansible-playbook -i hosts playbook.yml --force-handlers | ||
+ | </ | ||
+ | |||
+ | Вызывать можно несколько обработчиков из одной задачи: | ||
+ | <code yaml> | ||
+ | notify: | ||
+ | - restart apache | ||
+ | - restart memcached | ||
+ | </ | ||
+ | А можно вызывать обработчик из другого обработчика, | ||
+ | ===== Роль ===== | ||
+ | Набор задач, вызываемый как единый модуль или функция одной строкой с параметрами. Это каталог с упорядоченными по назначению YAML-файлами. | ||
+ | |||
+ | Файловая структура роли: | ||
+ | <code bash> | ||
+ | defaults/ | ||
+ | files/ # каталог с файлами, | ||
+ | handlers/ | ||
+ | meta/ | ||
+ | tasks/ | ||
+ | templates/ # каталог с jinja2-шаблонами. | ||
+ | tests/ | ||
+ | vars/ | ||
+ | </ | ||
+ | Генерация файловой структуры роли: '' | ||
+ | |||
+ | ==== Include / Import ==== | ||
+ | Другая организация плейбука, | ||
+ | Здесь идёт ссылка на файл, куда вынесены соответствующие куски плейбука. | ||
+ | <code yaml> | ||
+ | handlers: | ||
+ | - import_tasks: | ||
+ | - import_tasks: | ||
+ | tasks: | ||
+ | - import_tasks: | ||
+ | - import_tasks: | ||
+ | </ | ||
+ | |||
+ | Import отличается от include тем, что import сразу формирует файл плейбука при парсинге во время запуска с подстановкой переменных, | ||
+ | |||
+ | :!: После слов '' | ||
+ | |||
+ | Импортировать/ | ||
+ | <code yaml> | ||
+ | name: playbook1 | ||
+ | hosts: all | ||
+ | become:true | ||
+ | |||
+ | tasks: | ||
+ | - import: tasks/ | ||
+ | |||
+ | - include: playbook2.yml | ||
+ | </ | ||
+ | |||
+ | ===== Плейбук ===== | ||
+ | Связь задач/ | ||
+ | <file yaml playbook.yaml> | ||
+ | --- | ||
+ | - name: Запуск backend сервиса project | ||
+ | # Шаблон целевых хостов это группа хостов с именем backend | ||
+ | hosts: backend | ||
+ | # Список ansible-ролей для backend-серверов | ||
+ | roles: | ||
+ | - project-backend | ||
+ | |||
+ | - name: Запуск frontend сервиса project | ||
+ | # Шаблон целевых хостов это группа хостов с именем frontend | ||
+ | hosts: frontend | ||
+ | # Список ansible-ролей для frontend-серверов | ||
+ | roles: | ||
+ | - project-frontend | ||
+ | </ | ||
+ | Запуск плейбука: | ||
+ | |||
+ | ==== Тэги/ | ||
+ | Задачи можно помечать определённым словом, | ||
+ | <code yaml> | ||
+ | tasks: | ||
+ | - name: ... | ||
+ | shell: ... | ||
+ | tags: | ||
+ | - api | ||
+ | - echo | ||
+ | </ | ||
+ | Запустит только задачи с тэгом api. | ||
+ | <code bash> | ||
+ | ansible-playbook playbook.yml --tags=api | ||
+ | </ | ||
+ | ===== Inventory ===== | ||
+ | Группы хостов берутся из **inventory**. Это в простом случае может быть текстовый файл со списком IP-адресов или имён серверов, | ||
+ | <code ini> | ||
+ | # Названия чувствительны к регистру - [staging_servers] и [staging_Servers] будут разными группами. | ||
+ | |||
+ | # Все сервера из всех групп входят в группу all | ||
+ | # можно писать сервера вообще без группы, | ||
+ | 192.168.1.1 | ||
+ | 192.168.1.2 | ||
+ | server.example.com | ||
+ | server2.example.com ansible_host=192.168.1.3 | ||
+ | |||
+ | [staging_db] | ||
+ | 192.168.1.51 | ||
+ | 192.168.1.52 | ||
+ | [staging_web] | ||
+ | 192.168.1.61 | ||
+ | 192.168.1.62 | ||
+ | [staging_app] | ||
+ | 192.168.1.71 | ||
+ | 192.168.1.72 | ||
+ | |||
+ | # Группа staging_all, | ||
+ | [staging_all: | ||
+ | staging_db | ||
+ | staging_db | ||
+ | staging_app | ||
+ | |||
+ | [staging_servers] | ||
+ | # чтобы не прописывать одинаковые значения ansible_user, | ||
+ | # См. [staging_servers: | ||
+ | #k2 ansible_host=192.168.1.22 | ||
+ | #k3 ansible_host=192.168.1.23 | ||
+ | k2 ansible_host=192.168.1.22 | ||
+ | k3 ansible_host=192.168.1.23 | ||
+ | |||
+ | # Vars лучше не прописывать в файле inventory, а держать отдельно, | ||
+ | [staging_servers: | ||
+ | ansible_user=user | ||
+ | ansible_ssh_private_key=/ | ||
+ | |||
+ | [windows_servers] | ||
+ | windows2012 ansible_host=192.168.1.10 | ||
+ | windows2016 ansible_host=192.168.1.11 | ||
+ | |||
+ | [windows_servers: | ||
+ | ansible_user = myadmin | ||
+ | ansible_port = 5986 | ||
+ | ansible_connection = winrm | ||
+ | ansible_winrm_server_cert_validation = ignore | ||
+ | </ | ||
+ | |||
+ | <code bash> | ||
+ | # Показать список хостов с принадлежащими им переменными | ||
+ | ansible-inventory --list | ||
+ | # Типа tree для файлов | ||
+ | ansible-inventory --graph | ||
+ | |||
+ | # проверка связи (inventory - hosts.txt, все хосты, модуль ping) | ||
+ | ansible -i hosts.txt all -m ping | ||
+ | </ | ||
+ | |||
+ | Проблема в том, что если на сервера раньше не заходили, | ||
+ | Чтобы этого избежать, | ||
+ | <file ini ansible.cfg> | ||
+ | [defaults] | ||
+ | host_key_checking = false | ||
+ | inventory = ./hosts.txt | ||
+ | </ | ||
+ | <code bash> | ||
+ | # проверка связи c учётом ansible.cfg | ||
+ | ansible all -m ping | ||
+ | </ | ||
+ | |||
+ | Формат yaml: | ||
+ | <code yaml> | ||
+ | all: # Обязательный параметр (все сервера) | ||
+ | children: | ||
+ | backend: # (группа серверов backend) | ||
+ | hosts: | ||
+ | 192.168.3.4: | ||
+ | dev-backend.example.com: | ||
+ | frontend: | ||
+ | hosts: | ||
+ | 192.168.3.5: | ||
+ | dev-frontend.example.com: | ||
+ | dev: | ||
+ | hosts: | ||
+ | dev-backend.example.com: | ||
+ | dev-frontend.example.com: | ||
+ | prod: | ||
+ | hosts: | ||
+ | 192.168.3.4: | ||
+ | 192.168.3.5: | ||
+ | </ | ||
+ | После этого можно запускать плейбук с ограничением по группе, | ||
+ | |||
+ | В файле inventory можно прописывать переменные, | ||
+ | <code yaml> | ||
+ | all: | ||
+ | children: | ||
+ | backend: | ||
+ | hosts: | ||
+ | dev-backend.example.com: | ||
+ | # Переменная только для этого хоста | ||
+ | ansible_user: | ||
+ | vars: # Переменные для группы backend | ||
+ | backend_version: | ||
+ | vars: # Переменные для всех хостов в inventory | ||
+ | ansible_connection: | ||
+ | </ | ||
+ | Лучше создавать файлы с переменными в каталоге '' | ||
+ | <code yaml> | ||
+ | ansible_connection: | ||
+ | </ | ||
+ | |||
+ | Для часто меняющейся инфраструктуры используется динамический inventory. В Ansible есть плагин динамической инвентаризации, | ||
+ | |||
+ | ===== Переменные ===== | ||
+ | Используются для управления поведением задач подобно параметрам функции в программировании. | ||
+ | |||
+ | Переменные могут быть определены в плейбуках, | ||
+ | :!: В именах переменных могут использоваться только буквы, цифры (не первый символ) и подчёркивания. | ||
+ | <code bash> | ||
+ | # Extra vars имеют наивысший приоритет | ||
+ | ansible-playbook playbook.yaml --extra-vars " | ||
+ | </ | ||
+ | Переменные, | ||
+ | |||
+ | https:// | ||
+ | https:// | ||
+ | |||
+ | Добавить переменную в '' | ||
+ | Если строки с '' | ||
+ | Если под '' | ||
+ | <code yaml> | ||
+ | - name: Var playbook | ||
+ | hosts: k3 | ||
+ | | ||
+ | tasks: | ||
+ | - name: Add environment variable to the remote user's shell | ||
+ | lineinfile: | ||
+ | dest: " | ||
+ | regexp: ' | ||
+ | line: ' | ||
+ | # Вывод переменной | ||
+ | - name: Get the ENV_VAR value | ||
+ | shell: ' | ||
+ | # Если не указать bash, то может запуститься sh с ошибкой " | ||
+ | args: | ||
+ | executable: /bin/bash | ||
+ | register: envvar | ||
+ | # Либо в составе сообщения, | ||
+ | - debug: msg=" | ||
+ | - debug: var=envvar.stdout | ||
+ | </ | ||
+ | :!: Общесистемные переменные нужно загонять в файл ''/ | ||
+ | |||
+ | ==== Местоположение переменных ==== | ||
+ | Положим, | ||
+ | <code yaml> | ||
+ | - name: Var playbook | ||
+ | hosts: k3 | ||
+ | | ||
+ | tasks: | ||
+ | - name: Download a file | ||
+ | get_url: | ||
+ | url: http:// | ||
+ | dest: /tmp | ||
+ | environment: | ||
+ | http_proxy: http:// | ||
+ | https_proxy: | ||
+ | </ | ||
+ | Можно вынести переменные прокси на верхний уровень, | ||
+ | <code yaml> | ||
+ | vars: | ||
+ | proxy_vars: | ||
+ | http_proxy: http:// | ||
+ | https_proxy: | ||
+ | | ||
+ | tasks: | ||
+ | - name: ... | ||
+ | environment: | ||
+ | </ | ||
+ | |||
+ | А можно сделать так, что переменные будут действовать на все задачи: | ||
+ | <code yaml> | ||
+ | environment: | ||
+ | http_proxy: http:// | ||
+ | https_proxy: | ||
+ | | ||
+ | tasks: | ||
+ | - name: ... | ||
+ | - name: ... | ||
+ | </ | ||
+ | |||
+ | Переменные из файла: | ||
+ | <code yaml> | ||
+ | vars: | ||
+ | key: value | ||
+ | vars_files: | ||
+ | - vars/ | ||
+ | </ | ||
+ | |||
+ | Задать переменную '' | ||
+ | <code yaml> | ||
+ | ansible_super_secret: | ||
+ | </ | ||
+ | ==== Факты ==== | ||
+ | Это переменные, | ||
+ | |||
+ | При запуске плея, при первом подключении к хостам и до запуска указанных задач, Ansible запускает модуль setup, который выполняет анализ окружения целевого хоста и формирует набор переменных в словаре ansible_facts. | ||
+ | <code bash> | ||
+ | # принудительный запуск на локальной машине | ||
+ | ansible localhost -m setup | ||
+ | </ | ||
+ | |||
+ | Так как сбор фактов - операция ресурсоёмкая, | ||
+ | - Отключение - применяется в облаках, | ||
+ | gather_facts: | ||
+ | tasks: | ||
+ | - name: waiting for connection | ||
+ | wait_for_connect</ | ||
+ | - Фильтрация - собирать только необходимую информацию. <code yaml>- hosts: development | ||
+ | gather_facts: | ||
+ | tasks: | ||
+ | - name: wait for connection | ||
+ | wait_for_connect | ||
+ | - name: selected facts | ||
+ | ansible.builtin.setup: | ||
+ | filter: | ||
+ | - ' | ||
+ | - ' | ||
+ | - ' | ||
+ | - Кэширование - настраивается в файле '' | ||
+ | cache = True | ||
+ | cache_plugin = jsonfile | ||
+ | cache_connection = ~/ | ||
+ | cache_timeout = 3600</ | ||
+ | |||
+ | ==== Шаблоны Jinja2 ==== | ||
+ | Это движок для подстановки переменных или для динамического формирования конфигурационных файлов. Ссылка на переменную идёт в любом файле yaml в двойных фигурных скобках. | ||
+ | <code yaml> | ||
+ | - name: Install package {{ package_name }} | ||
+ | package: | ||
+ | name: "{{ package_name }}" | ||
+ | </ | ||
+ | Чтобы создавать файлы на основе шаблонов, | ||
+ | <code yaml> | ||
+ | - name: Create app backend service unit | ||
+ | template: | ||
+ | src: app-backend.service.j2 | ||
+ | dest: / | ||
+ | </ | ||
+ | |||
+ | Если переменные бэкенда описаны в '' | ||
+ | <code yaml> | ||
+ | backend_maven_version: | ||
+ | backend_service_user: | ||
+ | backend_report_directory: | ||
+ | backend_lib_directory: | ||
+ | </ | ||
+ | |||
+ | То можно генерировать systemd-unit на основе шаблона '' | ||
+ | < | ||
+ | [Unit] | ||
+ | Description=app | ||
+ | |||
+ | [Service] | ||
+ | User={{ backend_service_user }} | ||
+ | Restart=always | ||
+ | Environment=REPORT_PATH={{ backend_report_directory }} | ||
+ | ExecStart=/ | ||
+ | |||
+ | [Install] | ||
+ | WantedBy=multi-user.target | ||
+ | </ | ||
+ | |||
+ | === Переменная в кавычках === | ||
+ | Чтобы после обработки шаблона переменная была окружена кавычками, | ||
+ | <code yaml> | ||
+ | datasource: | ||
+ | url: "{{ datasource_url | quote }}" | ||
+ | </ | ||
+ | Т. е., ставить кавычки сами по себе и дополнительно применять команду. Одни кавычки отбросятся, | ||
+ | |||
+ | === Копирование нескольких шаблонов одной задачей === | ||
+ | <code yaml> | ||
+ | - name: Copy templates | ||
+ | template: | ||
+ | src: "{{ item }}" | ||
+ | dest: /tmp/{{ item | basename | regex_replace(' | ||
+ | with_fileglob: | ||
+ | - ../ | ||
+ | </ | ||
+ | :!: Note '' | ||
+ | |||
+ | https:// | ||
+ | |||
+ | ==== Динамические переменные ==== | ||
+ | Используются для нескольких ОС, где разные пути к конфигам и один и тот же пакет называется по-разному, | ||
+ | <WRAP group> | ||
+ | <WRAP half column> | ||
+ | <file yaml apache_RedHat.yml> | ||
+ | apache_package: | ||
+ | apache_service: | ||
+ | apache_config_dir: | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | <WRAP half column> | ||
+ | <file yaml apache_default.yml> | ||
+ | apache_package: | ||
+ | apache_service: | ||
+ | apache_config_dir: | ||
+ | </ | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | В плейбуке рисуется раздел с предварительным заданием (на уровне tasks), который читает первый совпадающий файл с переменными: | ||
+ | <code yaml> | ||
+ | pre_tasks: | ||
+ | # - debug: var=ansible_os_family | ||
+ | - name: Load variable file | ||
+ | include_vars: | ||
+ | with_first_found: | ||
+ | - " | ||
+ | - " | ||
+ | </ | ||
+ | Вместо '' | ||
+ | ===== ansible.cfg ===== | ||
+ | Базовый способ настройки Ansible. В ansible.cfg определены директивы конфигурации для всего набора утилит (ansible, ansible-playbook, | ||
+ | < | ||
+ | [defaults] | ||
+ | roles_path = roles | ||
+ | inventory = inventory | ||
+ | |||
+ | remote_user = ansible | ||
+ | |||
+ | vault_password_file = .vault | ||
+ | host_key_checking = False | ||
+ | [privilege_escalation] | ||
+ | become = true ; повышать привилегии (sudo) | ||
+ | </ | ||
+ | |||
+ | Ansible поддерживает следующие способы передачи параметров конфигурации (по убыванию приоритета): | ||
+ | - Переменные (если значение переменной присвоено несколько раз — используется последнее) | ||
+ | - Значения из плейбуков | ||
+ | - Значения, | ||
+ | - Значения из файла конфигурации | ||
+ | |||
+ | '' | ||
+ | |||
+ | |||
+ | Ansible ищет конфигурационные файлы в следующем порядке: | ||
+ | - Путь из переменной окружения ANSIBLE_CONFIG | ||
+ | - '' | ||
+ | - '' | ||
+ | - ''/ | ||
+ | Используется только первый найденный файл, остальные игнорируются. | ||
+ | |||
+ | https:// | ||
+ | |||
+ | ===== Vault ===== | ||
+ | Шифрованное хранилище для секретных данных - паролей, | ||
+ | <code bash> | ||
+ | ansible-vault encrypt_string " | ||
+ | </ | ||
+ | Вывод команды указывается в '' | ||
+ | <code yaml> | ||
+ | secret_pass: | ||
+ | $ANSIBLE_VAULT; | ||
+ | 39836982658142836487543449583409549856387698734580384098152098347874095687623654 | ||
+ | ... | ||
+ | 8457 | ||
+ | </ | ||
+ | Передача пароля для расшифровки происходит в интерактивной сессии при запуске ansible-playbook с параметром '' | ||
+ | <code bash> | ||
+ | ansible-playbook playbook.yaml --ask-vault-pass | ||
+ | </ | ||
+ | <WRAP round important 80%> | ||
+ | При запуске Ansible в CI-системе этот способ не подойдёт и потребуется механизм передачи пароля без участия пользователя.\\ | ||
+ | Для этого нужно сформировать файл с паролем на предыдущем шаге CI и указать его расположение в '' | ||
+ | < | ||
+ | vault_password_file = .vault | ||
+ | </ | ||
+ | Файл '' | ||
+ | </ | ||
+ | |||
+ | ===== Структура проекта Ansible, коллекции ===== | ||
+ | Пример структуры. | ||
+ | < | ||
+ | . | ||
+ | ├── README.md | ||
+ | ├── ansible.cfg | ||
+ | ├── roles | ||
+ | │ | ||
+ | │ | ||
+ | ├── group_vars | ||
+ | │ | ||
+ | │ | ||
+ | │ | ||
+ | ├── inventory | ||
+ | │ | ||
+ | │ | ||
+ | └── playbook.yaml | ||
+ | </ | ||
+ | |||
+ | [[https:// | ||
+ | |||
+ | Примерный вид рабочего процесса для автоматизации управления конфигурацией: | ||
+ | - Описание зависимостей (список используемых коллекций) в особом файле, например, | ||
+ | - Установка зависимостей утилитой '' | ||
+ | - Запуск команды '' | ||
+ | |||
+ | Этот рабочий процесс может быть запущен на CI системе или в специальной системе, | ||
+ | |||
+ | ===== Ansible Galaxy ===== | ||
+ | [[https:// | ||
+ | |||
+ | По умолчанию роли оттуда ставятся в глобальный каталог типа ''/ | ||
+ | <code yaml> | ||
+ | roles_path = ./roles | ||
+ | </ | ||
+ | Рядом положить файл '' | ||
+ | <code yaml> | ||
+ | --- | ||
+ | roles: | ||
+ | - name: elliotweiser.osx-command-line-tools | ||
+ | version: 2.3.0 | ||
+ | - name: geerlingguy.mac | ||
+ | version: 4.0.0 | ||
+ | </ | ||
+ | После этого запустить установку ролей из Galaxy, указав файл зависимостей. Скачанные роли окажутся в подкаталоге roles, как указано в файле конфигурации. | ||
+ | <code bash> | ||
+ | ansible-galaxy install -r requirements.yml | ||
+ | </ | ||
+ | Плейбук: | ||
+ | <code yaml> | ||
+ | --- | ||
+ | - hosts: localhost | ||
+ | connection: local | ||
+ | | ||
+ | vars: | ||
+ | homebrew_installed_packages: | ||
+ | - pv | ||
+ | roles: | ||
+ | - elliotweiser.osx-command-line-tools | ||
+ | - role: geerlingguy.mac | ||
+ | become: true | ||
+ | </ | ||
+ | |||
+ | ===== Проверка/ | ||
+ | По степени увеличения сложности: | ||
+ | - '' | ||
+ | - '' | ||
+ | - '' | ||
+ | - molecule test (integration) | ||
+ | - '' | ||
+ | - parallel infrastructure | ||
+ | |||
+ | ==== yamllint ==== | ||
+ | Проверка YAML на корректность. Даже если плейбук запускается и работает, | ||
+ | |||
+ | <code bash> | ||
+ | # Установка | ||
+ | sudo apt install yamllint | ||
+ | # или | ||
+ | pip3 install yamllint | ||
+ | |||
+ | yamllint playbook_delegate.yml | ||
+ | playbook_delegate.yml | ||
+ | 1:1 | ||
+ | 2:1 | ||
+ | 4:11 warning | ||
+ | 11:1 warning | ||
+ | 13:81 | ||
+ | 18:13 | ||
+ | 19:81 | ||
+ | 23:1 error too many blank lines (3 > 0) (empty-lines) | ||
+ | |||
+ | </ | ||
+ | |||
+ | ==== ansible-lint ==== | ||
+ | Проверка на соответствие best practices. | ||
+ | |||
+ | <code bash> | ||
+ | # Установка | ||
+ | sudo apt install ansible-lint | ||
+ | # или | ||
+ | pip3 install ansible-lint | ||
+ | |||
+ | ansible-lint playbook2.yml | ||
+ | WARNING | ||
+ | yaml: truthy value should be one of [false, true] (truthy) | ||
+ | playbook2.yml: | ||
+ | |||
+ | package-latest: | ||
+ | playbook2.yml: | ||
+ | |||
+ | yaml: wrong indentation: | ||
+ | playbook2.yml: | ||
+ | |||
+ | yaml: truthy value should be one of [false, true] (truthy) | ||
+ | playbook2.yml: | ||
+ | |||
+ | You can skip specific rules or tags by adding them to your configuration file: | ||
+ | # .ansible-lint | ||
+ | warn_list: | ||
+ | - package-latest | ||
+ | - yaml # Violations reported by yamllint | ||
+ | |||
+ | Finished with 4 failure(s), 0 warning(s) on 1 files. | ||
+ | |||
+ | </ | ||
+ | |||
+ | ====== Конкретные задачи ====== | ||
+ | ===== Копирование с одного удалённого сервера на другой ===== | ||
+ | Существует модуль [[https:// | ||
+ | <code yaml> | ||
+ | - name: Synchronization of src on delegate host to dest on the current inventory host. | ||
+ | ansible.posix.synchronize: | ||
+ | src: / | ||
+ | dest: / | ||
+ | delegate_to: | ||
+ | </ | ||
+ | Но есть проблема аутентификации: | ||
+ | <code yaml> | ||
+ | --- | ||
+ | - name: Sync | ||
+ | hosts: k3 | ||
+ | gather_facts: | ||
+ | |||
+ | tasks: | ||
+ | - name: Copy WAR to worker from source | ||
+ | command: rsync k2:/ | ||
+ | run_once: true | ||
+ | delegate_to: | ||
+ | |||
+ | - name: Copy WAR to destination from worker | ||
+ | synchronize: | ||
+ | src: / | ||
+ | dest: / | ||
+ | </ | ||
+ | |||
+ | |||
+ | ===== Останов, | ||
+ | |||
+ | <code yaml> | ||
+ | - name: "Get service status" | ||
+ | shell: systemctl --user is-active {{ service_name }}.service | ||
+ | register: service_status | ||
+ | ignore_errors: | ||
+ | |||
+ | - name: "Get process ID" | ||
+ | shell: pgrep -f "{{ catalina_base }}" | ||
+ | register: process_id | ||
+ | ignore_errors: | ||
+ | |||
+ | - name: "Stop process by script" | ||
+ | script: "{{ deploy_dir }}/ | ||
+ | args: | ||
+ | creates: " | ||
+ | when: service_status.stdout != ' | ||
+ | |||
+ | - name: "Kill process" | ||
+ | shell: kill -9 "{{ process_id.stdout }}" | ||
+ | when: service_status.stdout != ' | ||
+ | |||
+ | - name: "Stop service" | ||
+ | systemd: | ||
+ | scope: user | ||
+ | name: "{{ service_name }}" | ||
+ | state: stopped | ||
+ | when: service_status.stdout == ' | ||
+ | |||
+ | - name: " | ||
+ | file: | ||
+ | path: "{{ item }}" | ||
+ | state: directory | ||
+ | with_items: | ||
+ | - " | ||
+ | - "{{ catalina_base }}/ | ||
+ | - "{{ catalina_base }}/ | ||
+ | - "{{ pool_settings_dir }}" | ||
+ | |||
+ | - name: " | ||
+ | shell: loginctl enable-linger $USER | ||
+ | |||
+ | - name: "Copy WAR file to worker from source ({{ war_source_server }})" | ||
+ | command: rsync "{{ deploy_user }}@{{ war_source_server }}:{{ catalina_base }}/ | ||
+ | delegate_to: | ||
+ | run_once: true | ||
+ | when: copy_war|bool == true | ||
+ | |||
+ | - name: "Copy WAR to destination" | ||
+ | syncronize: | ||
+ | src: / | ||
+ | dest: "{{ catalina_base }}/ | ||
+ | when: copy_war|bool == true | ||
+ | |||
+ | - name: "Copy conf files" | ||
+ | copy: | ||
+ | src: "{{ item }}" | ||
+ | dest: "{{ catalina_base }}/ | ||
+ | owner: "{{ deploy_user }}" | ||
+ | mode: 0660 | ||
+ | with_fileglob: | ||
+ | - " | ||
+ | - " | ||
+ | when: copy_conf|bool == true | ||
+ | |||
+ | - name: "Copy scripts" | ||
+ | copy: | ||
+ | src: "{{ item }}" | ||
+ | dest: "{{ deploy_dir }}" | ||
+ | owner: "{{ deploy_user }}" | ||
+ | mode: 0770 | ||
+ | with_fileglob: | ||
+ | - " | ||
+ | |||
+ | - name: "Copy pool-settings.xml" | ||
+ | template: | ||
+ | src: " | ||
+ | dest: "{{ pool_settings_dir }}/ | ||
+ | mode: 0660 | ||
+ | | ||
+ | - name: "Copy unit file" | ||
+ | template: | ||
+ | src: "{{ service_name }}.service.j2" | ||
+ | dest: " | ||
+ | |||
+ | - name: "Start service" | ||
+ | systemd: | ||
+ | scope: user | ||
+ | daemon_reload: | ||
+ | name: "{{ service_name }}" | ||
+ | state: started | ||
+ | enabled: true | ||
+ | |||
+ | - name: " | ||
+ | replace: | ||
+ | path: "{{ pool_settings_dir }}/ | ||
+ | regexp: ' | ||
+ | replace: ' | ||
+ | |||
+ | </ | ||
+ | |||
+ | ===== Добавить секцию в шаблон конфига по условию ===== | ||
+ | Например, | ||
+ | <code bash> | ||
+ | {% if inventory_hostname in groups[' | ||
+ | Секция, | ||
+ | {% else %} | ||
+ | Секция, | ||
+ | {% endif %} | ||
+ | </ | ||
+ | Если используются переменные из // | ||
+ | |||
+ | Дополнительно про инвентори: | ||
+ | Template Designer Documentation: | ||
+ | |||
+ | ===== Выборка из словаря по значениям из списка ===== | ||
+ | В зависимости от набора работающих служб получать набор строк для формирования API-запроса. Если есть работающие службы, | ||
+ | |||
+ | <file yaml services_status.yml> | ||
+ | - hosts: k3 | ||
+ | roles: | ||
+ | - services_status | ||
+ | vars: | ||
+ | services: | ||
+ | project-service1: | ||
+ | project-service2: | ||
+ | project-service3: | ||
+ | </ | ||
+ | |||
+ | <file yaml roles/ | ||
+ | - name: Gather running services | ||
+ | shell: | ||
+ | cmd: systemctl --user --type service --state running --plain --quiet |grep ^project- |sed -E ' | ||
+ | register: running_services | ||
+ | |||
+ | # В curl body - это --data ' | ||
+ | - name: Update API | ||
+ | uri: | ||
+ | url: https:// | ||
+ | user: admin | ||
+ | password: {{ pass }} | ||
+ | force_basic_auth: | ||
+ | method: POST | ||
+ | validate_certs: | ||
+ | body_format: | ||
+ | body: | ||
+ | dataSources: | ||
+ | - "{{ services[item] }}" | ||
+ | with_items: | ||
+ | - "{{ running_services[' | ||
+ | when: item in services | list | ||
+ | </ | ||
+ | |||
+ | Работают 1-я и 3-я службы, | ||
+ | <code bash> | ||
+ | ansible-playbook services_status.yml | ||
+ | |||
+ | TASK [services_status : debug] ********************************************************************************************************** | ||
+ | ok: [k3] => (item=project-service1) => { | ||
+ | " | ||
+ | } | ||
+ | ok: [k3] => (item=project-service3) => { | ||
+ | " | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Выбор списка значений из словаря | ||
+ | <code yaml> | ||
+ | - name: " | ||
+ | uri: | ||
+ | url: https:// | ||
+ | user: admin | ||
+ | password: admin | ||
+ | force_basic_auth: | ||
+ | method: POST | ||
+ | validate_certs: | ||
+ | body_format: | ||
+ | timeout: 600 | ||
+ | body: | ||
+ | dataSources: | ||
+ | {{ services | dict2items | selectattr(' | ||
+ | </ | ||
+ | |||
+ | ===== Учёт кода возврата в в ответе Nexus в формате json ===== | ||
+ | |||
+ | <file yaml staging.yml> | ||
+ | - hosts: 127.0.0.1 | ||
+ | connection: local | ||
+ | roles: | ||
+ | - staging | ||
+ | vars: | ||
+ | name: "" | ||
+ | version: "" | ||
+ | repo_test: " | ||
+ | repo_uat: " | ||
+ | repo_prod: " | ||
+ | nexus_user_1: | ||
+ | nexus_user_2: | ||
+ | nexus_password_1: | ||
+ | nexus_password_2: | ||
+ | timeout: 300 | ||
+ | </ | ||
+ | |||
+ | <file yaml roles/ | ||
+ | - name: " | ||
+ | uri: | ||
+ | url: https:// | ||
+ | user: "{{ nexus_user_1 }}" | ||
+ | password: "{{ nexus_password_1 }}" | ||
+ | force_basic_auth: | ||
+ | method: POST | ||
+ | validate_certs: | ||
+ | timeout: "{{ timeout }}" | ||
+ | return_content: | ||
+ | body_format: | ||
+ | body: | ||
+ | version: "{{ version }}" | ||
+ | register: response_ctag | ||
+ | |||
+ | - debug: | ||
+ | var: response_ctag.json.result | ||
+ | |||
+ | - pause: | ||
+ | seconds: 10 | ||
+ | |||
+ | - name: "Set tag" | ||
+ | uri: | ||
+ | url: https:// | ||
+ | user: "{{ nexus_user_1 }}" | ||
+ | password: "{{ nexus_password_1 }}" | ||
+ | force_basic_auth: | ||
+ | method: POST | ||
+ | validate_certs: | ||
+ | timeout: "{{ timeout }}" | ||
+ | return_content: | ||
+ | body_format: | ||
+ | body: | ||
+ | tagName: " | ||
+ | repository: "{{ repo_test }}" | ||
+ | name: "{{ name }}" | ||
+ | version: "{{ version }}" | ||
+ | register: response_stag | ||
+ | |||
+ | - debug: | ||
+ | var: response_stag.json.result | ||
+ | |||
+ | - pause: | ||
+ | seconds: 10 | ||
+ | when: (response_stag.json.result |from_json).status == 200 | ||
+ | |||
+ | - name: " | ||
+ | uri: | ||
+ | url: https:// | ||
+ | user: "{{ nexus_user_2 }}" | ||
+ | password: "{{ nexus_password_2 }}" | ||
+ | force_basic_auth: | ||
+ | method: POST | ||
+ | validate_certs: | ||
+ | timeout: "{{ timeout }}" | ||
+ | return_content: | ||
+ | body_format: | ||
+ | body: | ||
+ | repoName: "{{ repo_uat }}" | ||
+ | version: "{{ version }}" | ||
+ | register: response_uat | ||
+ | when: (response_stag.json.result |from_json).status == 200 | ||
+ | |||
+ | - debug: | ||
+ | var: response_uat.json.result | ||
+ | when: response_uat.json.result is defined | ||
+ | |||
+ | - pause: | ||
+ | seconds: 10 | ||
+ | when: response_uat.json.result |from_json).status == 200 | ||
+ | |||
+ | - name: " | ||
+ | uri: | ||
+ | url: https:// | ||
+ | user: "{{ nexus_user_2 }}" | ||
+ | password: "{{ nexus_password_2 }}" | ||
+ | force_basic_auth: | ||
+ | method: POST | ||
+ | validate_certs: | ||
+ | timeout: "{{ timeout }}" | ||
+ | return_content: | ||
+ | body_format: | ||
+ | body: | ||
+ | repoName: "{{ repo_prod }}" | ||
+ | version: "{{ version }}" | ||
+ | register: response_prod | ||
+ | when: (response_uat.json.result |from_json).status == 200 | ||
+ | |||
+ | - debug: | ||
+ | var: response_prod.json.result | ||
+ | when: response_prod.json.result is defined | ||
+ | </ | ||
+ | |||
+ | ====== Ошибки, | ||
+ | |||
+ | ===== Collection ansible.posix does not support Ansible version ===== | ||
+ | <color # | ||
+ | |||
+ | <code bash> | ||
+ | ansible-galaxy collection install ansible.posix | ||
+ | Starting galaxy collection install process | ||
+ | [WARNING]: Collection junipernetworks.junos does not support Ansible version 2.17.0 | ||
+ | [WARNING]: Collection frr.frr does not support Ansible version 2.17.0 | ||
+ | [WARNING]: Collection ibm.qradar does not support Ansible version 2.17.0 | ||
+ | [WARNING]: Collection cisco.asa does not support Ansible version 2.17.0 | ||
+ | [WARNING]: Collection ansible.posix does not support Ansible version 2.17.0 | ||
+ | Nothing to do. All requested collections are already installed. If you want to reinstall them, consider using `--force`. | ||
+ | |||
+ | ansible-galaxy collection install ansible.posix --force | ||
+ | </ | ||
+ | |||
+ | ===== The processing instruction target matching " | ||
+ | Ошибка после запуска jar, где '' | ||
+ | <code xml> | ||
+ | <?xml version=" | ||
+ | <!-- ###################### | ||
+ | # Здесь какой-то текст | ||
+ | ####################### | ||
+ | </ | ||
+ | ====== Литература ====== | ||
+ | [[https:// | ||
+ | [[https:// | ||
+ | [[https:// | ||
+ | [[https:// | ||
+ | [[https:// | ||
+ | |||