Инструменты пользователя

Инструменты сайта


ansible

Это старая версия документа!


Ansible

Концепция

Управление хостами, по сети, через 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

Общее

Исполнения

По умолчанию ансибл выполняет все команды на хостах, указанных в параметре «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 }}

Аргументы/Параметры

  • -b [become] - выполнять с повышенными привилегиями
  • [roles] - список ролей применяемые для плейбука
  • -i - каталог с инвентори
  • -a - аргументы
  • -l (–limit) - группа/хост для выполнения указанного задания

Vault

При использовании пароля в файле, используется только вывод файла т.е. запуск, можно указывать скрипт

# Зашифровать файл
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 - добавить, заменить или удалить одну строку
  • replace - добавить, заменить или удалить несколько строк
  • blockinfile - добавлять несколько строк, окруженных маркерами

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 - возвращает весь элемент по указанному условию * map - вырезает только значения указанных аттрибутов, по одному аттрибуту из всех подходящих элементов * ** -

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

Для работы нужен «zabbix-api», плюс ансибл работает со своей версией питона

/usr/bin/python3.8 -m pip install zabbix-api
ansible-galaxy collection install community.zabbix
:!:
 

OverAll

:!: Минимальный стартовый пример

hosts, можно короче

all:
  hosts:
    192.168.0.101

test.yaml

---
- hosts: all
  tasks:
    - name: Test ping
      delegate_to: 127.0.0.1
      ping:

+ в конфиге еще путь к инвентори

:!:
 
ansible.1667451850.txt.gz · Последнее изменение: 2022/11/03 05:04 — admin