Ansible — система управления конфигурациями: знакомство
Приветствую!

В этой заметке мы с вами познакомимся с популярной open source системой управления конфигурациями с говорящим названием — Ansible 🎻. Выполним установку и настройку🛠, составим инвентаризацию📋, научимся запускать плейбуки🚀, поговорим про Ansible facts🗂, узнаем, что такое Ansible console🖥 и напишем playbook📝 который правит конфиг sshd и копирует файлы на сервер. Будет познавательно😉!

Предисловие

Чтобы чуть лучше понимать суть происходящего, рекомендую прочитать мою теоретическую заметку 📝 про системы управления конфигурациями (Cofiguration Management, CM). В ней дано описание термина, какие виды существуют, а также коротко рассказано про популярные реализации подобных систем и их отличия.

Возвращаясь к Ansible, с его помощью можно автоматизировать конфигурацию серверов путем описания желаемого состояния в удобном формате yaml. Стоит также отметить, что операции Ansible имеют свойство идемпотентности 🙄. Это означает, что, если вы запускаете плейбук или задачу несколько раз, состояние системы не изменится после первого успешного выполнения.

Ну и прежде, чем мы продолжим углубляться перечислю основные сущности данной CM📒:

Установка Ansible в Debian / Ubuntu / Linux mint

Пример установки я буду показывать в среде дистрибутива Debian 12😎:

Т.к. Ansible работает по push принципу (т.е. все управление происходит с административного сервера), то и устанавливать его необходимо только на одной машине. На управляемых серверах понадобится установленный Python🐍 и доступ по SSH.

Подробнее про использование и настройку SSH в Linux, в т.ч. и работу с ключами, читайте в отдельной инструкции: SSH – Безопасное подключение к удалённым хостам: введение.

Все необходимые пакеты Ansible есть в стандартных репозиториях 🗄. Поэтому просто открываем терминал и вводим команду:

BASH
sudo apt update && sudo apt install -y ansible sshpass
Нажмите, чтобы развернуть и увидеть больше

Также мы устанавливаем утилиту sshpass, которая понадобится для передачи пароля при соответствующей SSH аутентификации, а также получение полномочий через sudo 💪.

Зависимостей много, поэтому установка займет некоторое время⏳.

Настройка Ansible — файл ansible.cfg

Переходим к начальной настройке. Для этого создаем необходимые директории и файл конфигурации. Открываем его любым текстовым редактором (я предпочитаю vim/neovim)🧑💻:

BASH
mkdir -p ~/ansible/{playbooks,roles,tmp}

nvim ~/ansible/ansible.cfg
Нажмите, чтобы развернуть и увидеть больше

И наполняем его:

PLAINTEXT
[defaults]
home = ~/ansible/                    ; основная директория Ansible
local_tmp = ~/ansible/tmp/           ; локальная временная директория для выполнения задач
inventory = ~/ansible/inventory.yml  ; путь к файлу инвентаря с перечнем хостов
playbook_dir = ~/ansible/playbooks/  ; директория, где хранятся плейбуки
roles_path = ~/ansible/roles/        ; директория, где Ansible ищет роли
remote_user = ivan                   ; пользователь, под которым Ansible подключается к хостам
become_method = sudo                 ; метод повышения прав (sudo, su)
become_user = root                   ; пользователь, от имени которого выполняются sudo команды
gather_facts = True                  ; собирать факты о системе перед выполнением задач
private_key_file = ~/ansible/id_ed25519  ; путь к приватному ключу для подключения по SSH
host_key_checking = False            ; отключает проверку ключей хоста SSH
fact_caching = jsonfile              ; кэширование фактов с использованием формата json
fact_caching_connection = ~/ansible/tmp/  ; путь для хранения кэшированных фактов
Нажмите, чтобы развернуть и увидеть больше

Такой конфигурации достаточно, чтобы начать использовать Ansible👍. Этот файл определяет поведение Ansible по умолчанию, которое может быть переопределено в других местах: файлах инвентаризации, плейбуках, файлах переменных, командах или в других конфигах.

Если есть потребность изучить все доступные параметры файла конфигурации🤓, то его можно сгенерировать такой командой:

BASH
ansible-config init --disabled > ~/ansible/ansible.cfg.example
Нажмите, чтобы развернуть и увидеть больше

Файл хорошо прокомментирован🧐.

Подробнее про процесс конфигурации смотрите на оф. сайте: Ansible Configuration Settings.

Также для удобства, рекомендую сразу добавить переменную окружения, указывающую на наш дефолтный файл конфига:

BASH
export ANSIBLE_CONFIG="$HOME/ansible/ansible.cfg"

# для создания переменной при входе в систему
echo 'export ANSIBLE_CONFIG="$HOME/ansible/ansible.cfg"' >> ~/.profile
Нажмите, чтобы развернуть и увидеть больше

Замените ~/.profile на файл загрузки окружения вашей оболочки. В моем случае используется интерактивная оболочка ZSH.

В таком случае можно будет пользоваться командами Ansible из любой директории без явного указания пути до файла с настройками😌.

Полный список доступных переменных окружения также смотрите в оф. доке: Environment Variables.

Инвентаризация — файл inventory.yml

Переходим к инвентаризации хозяйства, которым будем управлять. Для этого создаем файл инвентаря:

BASH
nvim ~/ansible/inventory.yml
Нажмите, чтобы развернуть и увидеть больше

И добавляем в него список наших серверов📋, которыми будем управлять. В моем примере 5 Linux серверов (3 — Debian, 2 — Ubuntu):

YAML
## HOSTS
all:
  hosts:
    debian12-vpn:
      ansible_host: 192.168.122.93
      ansible_port: 22
      ansible_user: ivan
      ansible_ssh_private_key_file: ~/.ssh/id_ed25519
      project: vpn
    debian12-dns:
      ansible_host: 192.168.122.201
      ansible_port: 22
      ansible_user: ivan
      ansible_ssh_private_key_file: ~/.ssh/id_ed25519
      project: vpn
    debian12-monitoring:
      ansible_host: 192.168.122.30
      ansible_port: 22
      ansible_user: ivan
      ansible_ssh_private_key_file: ~/.ssh/id_ed25519
      project: monitor
    ubuntu22-storage:
      ansible_host: 192.168.122.5
      ansible_port: 22
      ansible_user: ivan
      ansible_ssh_private_key_file: ~/.ssh/id_ed25519
      # ansible_password: "Pa$$w0rD"
      project: storage
    ubuntu22-backup:
      ansible_host: 192.168.122.249
      ansible_port: 22
      ansible_user: ivan
      ansible_ssh_private_key_file: ~/.ssh/id_ed25519
      # ansible_become_password: "Pa$$w0rD"
      project: storage

## GROUPS
debian:
  hosts:
    debian12-vpn:
    debian12-dns:
    debian12-monitoring:
ubuntu:
  hosts:
    ubuntu22-storage:
    ubuntu22-backup:
Нажмите, чтобы развернуть и увидеть больше

Не забудьте заменить параметры на свои 😉

Сохраняем, закрываем.

Имейте в виду, что формат YAML отступозависимый. Это значит, что отступы являются частью синтаксиса, и даже один неправильный пробел приведет к ошибке.

Коротко опишу содержимое на примере сервера debian12-vpn:

Также обратите внимание, что после списка хостов идет список групп🖥🖥:

которые включают в себя хосты, разделенные по ОС (в моем случае). Группы также формируются произвольно, главное соблюдайте синтаксис.

Весь список доступных параметров подключения для файла inventory смотрите тут: Connecting to hosts: behavioral inventory parameters.

Наш файл инвентаря составлен, давайте посмотрим на его структуру такой командой:

BASH
ansible-inventory --graph
Нажмите, чтобы развернуть и увидеть больше

Эта визуализация особенно полезна, если у вас большая инфраструктура из сотен серверов, разбитых на группы📊.

Пишем первый плейбук: проверка доступности серверов с помощью модуля ping

В контексте Ansible модуль ping не выполняет ping сервера в классическом понимании🤷♂️. Его задача произвести подключение к серверу по SSH, выполнить некоторые проверки (например наличие интерпретатора Python) и вернуть сообщение об успехе/ошибке👀.

Создаем файл плейбука в директории, которую мы обозначили в файле конфигурации:

BASH
nvim ~/ansible/playbooks/ping.yml
Нажмите, чтобы развернуть и увидеть больше

В самом простом виде плейбук выглядит так:

YAML
---

- name: Check connection

  hosts: all
  gather_facts: false
  tasks:
    - name: Ping
      ping:
Нажмите, чтобы развернуть и увидеть больше

Где:

Сохраняем файл и запускаем плейбук:

BASH
ansible-playbook ./ansible/playbooks/ping.yml
Нажмите, чтобы развернуть и увидеть больше

При использовании команды ansible-playbook путь до плейбуков нужно указывать явно. Чтобы не делать этого каждый раз, для удобства можно создать короткую функцию и вызывать ее в консоли (смотрите далее в статье).

В моем примере доступ до серверов настроен по ключам SSH. Поэтому проверка доступности выполнена успешно😎.

Если вы используете пароль (что не рекомендуется)😳 можете задать его в файле ansible.cfg (параметр ansible_password) или передать интерактивно с помощью ключа --ask-pass (для этого мы устанавливали пакет sshpass):

BASH
ansible-playbook ./ansible/playbooks/ping.yml --ask-pass
Нажмите, чтобы развернуть и увидеть больше

В случае каких-либо ошибок, Ansible выведет страшного вида красные сообщения⛔️. Например, при недоступности хоста по SSH такое:

Чтобы запустить плейбук на указанной группе серверов, можно их явно указать в команде через параметр -l (limit):

BASH
ansible-playbook ./ansible/playbooks/ping.yml -l ubuntu
Нажмите, чтобы развернуть и увидеть больше

Для запуска плейбука от имени привилегированного пользователя💪 добавьте ключ -b (become). В моем примере модуль ping запуститься от имени root:

BASH
ansible-playbook ./ansible/playbooks/ping.yml -b
Нажмите, чтобы развернуть и увидеть больше

В моей конфигурации серверов настроен беспарольный доступ к root через sudo (т.к. у меня тестовый стенд🛠), поэтому все отработало корректно. Но если у вас доступ по паролю, его можно передать в файле ansible.cfg (параметр ansible_become_password, не рекомендуется) или ввести интерактивно, с помощью ключа --ask-become-pass:

BASH
ansible-playbook ./ansible/playbooks/ping.yml -b --ask-become-pass
Нажмите, чтобы развернуть и увидеть больше

Ну а для безопасной автоматизации передачи паролей используется шифрование с помощью ansible-vault, но это тема для отдельной статьи😉.

Ansible facts — модули setup и debug

Как уже ранее говорилось, факты Ansible (facts) — это информация об управляемом сервере в виде системных переменных, таких как тип и версия ОС, IP-адрес, версии пакетов и пр., автоматически собираемая Ansible во время выполнения плейбуков (если активирован флаг gather_facts) и команд🤯.

Давайте теперь соберем эти факты, чтобы понять, как они выглядят👨🏭. Для этого используется стандартный модуль setup. Выполняем:

BASH
ansible all -m setup
Нажмите, чтобы развернуть и увидеть больше

all — тут логично представляет всех хостов из файла инвентаря. Тут также можно указать любой хост отдельно, список хостов через запятую или группу.

В зависимости от количества хостов, сбор фактов может занять какое-то время. В выводе команды вы увидите все собранные факты в формате json:

Факты можно фильтровать, например так:

BASH
ansible debian12-vpn -m setup -a 'filter=os_family,distribution_version'
Нажмите, чтобы развернуть и увидеть больше

Ранее в файле ansible.cfg мы настроили кэширование фактов с помощью fact_caching ☝️.

Это значит, что собранные факты будут сохранятся в виде json файлов во временной директории ~/ansible/tmp, которую мы также обозначили в файле конфига:

BASH
ls -l ./ansible/tmp/
Нажмите, чтобы развернуть и увидеть больше

Часто необходимо получить значения только конкретных фактов или переменных. Это можно сделать с помощью модуля debug 🔨 в плейбуках или прямо в терминале.

Вот пример получения информации об OS, объеме диска /dev/sda1, переменной project и выводе shell команды на сервере debian12-vpn в playbook:

BASH
nvim ./ansible/playbooks/debug.yml
Нажмите, чтобы развернуть и увидеть больше
YAML
---

- name: Debug

  hosts: debian12-vpn
  gather_facts: true
  become: true

  tasks:

    - name: Debug 1
      debug:
        msg: "Example to use var: {{ ansible_facts.os_family }}"

    - name: Debug 2
      debug:
        var: ansible_facts.distribution_version

    - name: Debug 3
      debug:
        var: project

    - name: Shell 1
      shell:
        cmd: whoami
      register: user_name

    - name: Debug 4
      debug:
        var: user_name.stdout
Нажмите, чтобы развернуть и увидеть больше

И запускаем:

BASH
ansible-playbook ./ansible/playbooks/debug.yml
Нажмите, чтобы развернуть и увидеть больше

Просмотр фактов с помощью команд в терминале:

В этом случае факты будут взяты из кэша.

BASH
ansible debian12-vpn -m debug -a 'var=ansible_facts.os_family,ansible_facts.distribution_version'

ansible debian12-vpn -m debug -a 'var=ansible_facts.devices.keys()'

ansible debian12-vpn -m debug -a 'var=ansible_facts.devices.sda.partitions.sda1.size'
Нажмите, чтобы развернуть и увидеть больше

Можно было заметить, что обращение к фактам происходит с помощью синтаксиса точки, как в Python (ansible написан на этом языке👌).

Подборную инфу по сбору фактов также смотрите на оф сайте: Discovering variables: facts and magic variables. Инфа по модулю debug тут.

Выполнение shell команд в терминале на нескольких серверах одновременно

С помощью Ansible и стандартного модуля shell можно выполнять удаленные команды сразу на множестве серверов🧑💻💻💻💻. Это очень удобно для дебага или выполнения простых операций.

Удаленная команда передается в кавычках с помощью ключа -a. Например, вывести список сетевых интерфейсов их IP адресов:

BASH
ansible all -m shell -a 'ip -br a'
Нажмите, чтобы развернуть и увидеть больше

Или выполнить команду из нашей Linux викторины 🧐, которая выводит uptime сервера в формате Unix time:

BASH
ansible debian12-vpn,ubuntu22-storage -m shell -a 'date -d "$(uptime -s)" +%s'
Нажмите, чтобы развернуть и увидеть больше

Для запуска команд от имени привилегированного пользователя также используется -b:

BASH
ansible all -m shell -a 'whoami' -b
Нажмите, чтобы развернуть и увидеть больше

Что такое Ansible console

В комплекте с Ansible идет такой полезные инструмент, как ansible-console 😏. Это интерактивная оболочка для выполнения команд на нескольких серверах. Похоже на команды модуля shell, которые мы выполняли ранее, но способ взаимодействия немного другой:

BASH
ansible-console

whoami
Нажмите, чтобы развернуть и увидеть больше

Для выхода введите exit или нажмите сочетание клавиш Ctrl+d.

Пример использования модуля ping группы debian:

BASH
ansible-console debian

ping
Нажмите, чтобы развернуть и увидеть больше

Просмотр фактов Ansible:

BASH
setup filter=ansible_hostname
Нажмите, чтобы развернуть и увидеть больше

Для выполнения привилегированных команды, добавьте ключ -b (но будьте осторожны!):

BASH
ansible-console debian -b

whoami
Нажмите, чтобы развернуть и увидеть больше

При выполнении удаленных команд ansible-console по умолчанию используется модуль command ❗️. Он вызывает команды напрямую, поэтому некоторые shell механизмы тут работать не будут. Для задействования shell укажите его явно:

BASH
shell echo $HOME
Нажмите, чтобы развернуть и увидеть больше

Документация Ansible прямо в терминале

У Ansible очень хорошая документация (правда на английском🙄) и она доступна даже из консоли с помощью утилиты ansible-doc. Например, чтобы посмотреть описание и примеры использования модуля systemd выполните:

BASH
ansible-doc systemd
Нажмите, чтобы развернуть и увидеть больше

Вы попадете в режим просмотра less. Обычно примеры использования модулей в плейбуках находятся внизу документации:

Как по мне, это очень удобно!

Пример ansible playbook: правка конфига sshd и копирование SSH ключа

В качестве практики напишем плейбук📝, который:

Генерируем новый ключ:

BASH
ssh-keygen -t ed25519 -f ~/ansible/id_ed25519_new
Нажмите, чтобы развернуть и увидеть больше

Создаем плейбук:

BASH
nvim ./ansible/playbooks/sshd_config.yml
Нажмите, чтобы развернуть и увидеть больше

Наполняем его:

YAML
---

- name: Config sshd service and copy private key   # имя/описание плейбука
  hosts: all                            # применять ко всем хостам
  gather_facts: false                   # не собирать факты (ускоряет выполнение)
  become: true                          # выполнять с повышением привилегий (sudo)
  force_handlers: true                  # выполнять обработчики даже в случае ошибки

  vars:
    - new_ssh_port: 4444                # новый номер порта SSH
    - user_name: "ivan"                 # имя пользователя, для которого добавляем SSH ключ
    - user_ssh_privkey: "/home/ivan/ansible/id_ed25519_new" # путь до приватного SSH ключа для пользователя
    - user_ssh_pubkey: "/home/ivan/ansible/id_ed25519_new.pub" # путь до публичного SSH ключа для пользователя

  tasks:
    - name: Change sshd port number
      lineinfile:
        path: /etc/ssh/sshd_config      # путь к файлу конфигурации SSH
        regexp: "^Port"                 # регулярное выражение для поиска строки с портом
        line: "Port {{ new_ssh_port }}" # изменить порт на значение из переменной
        backup: true                    # создать бэкап файла sshd_config перед изменением
      notify:
        - Restart sshd                  # обработчик для перезапуска SSH при изменении
      tags:
        - ssh_server                      # тег для возможности выборочного запуска задачи
    
    - name: Disable sshd password auth
      lineinfile:
        path: /etc/ssh/sshd_config      # путь к файлу конфигурации SSH
        regexp: '^PasswordAuthentication yes'  # регулярное выражение для поиска строки
        line: 'PasswordAuthentication no'      # отключаем аутентификацию по паролю
      notify:
        - Restart sshd                  # обработчик для перезапуска SSH
      tags:
        - ssh_server                      # тег для задачи SSH сервера

    - name: Create ssh directory if it does not exist
      file:
        path: "/home/{{ user_name }}/.ssh/"  # путь к директории .ssh для пользователя
        state: directory               # создать директорию, если не существует
        owner: "{{ user_name }}"       # задать владельца
        group: "{{ user_name }}"       # задать группу
        mode: 0700                     # права доступа к директории
      tags:
        - ssh_client                   # тег для задач SSH клиента
    
    - name: Add user ssh public key
      lineinfile:
        path: "/home/{{ user_name }}/.ssh/authorized_keys"  # путь к файлу authorized_keys
        line: "{{ lookup('file', '{{ user_ssh_pubkey }}') }}"  # в качестве добавляемой строки использовать содержимое файла публичного ключа
        owner: "{{ user_name }}"       # задать владельца
        group: "{{ user_name }}"       # задать группу
        mode: 0600                     # права доступа к файлу
        backup: true                   # создать бэкап файла перед изменением
        create: true                   # создать новый файл, если он не существует
      tags:
        - ssh_client                   # тег для задач SSH клиента

    - name: Copy user ssh private key
      copy:
        src: "{{ user_ssh_privkey }}"  # путь к файлу на локальной машине
        dest: "/home/{{ user_name }}/.ssh/"  # путь к директории на удаленном сервере, слэш обязателен
        owner: "{{ user_name }}"       # задать владельца
        group: "{{ user_name }}"       # задать группу
        mode: 0600                     # права доступа к файлу
        backup: true                   # создать бэкап файла перед изменением
      when: project == "vpn"           # выполнять только если у хоста переменная project = "vpn"
      tags:
        - ssh_client                   # тег для задач SSH клиента

  handlers:
    - name: Restart sshd
      systemd:
        name: sshd                     # название systemd сервиса SSH
        state: restarted               # перезапуск сервиса
Нажмите, чтобы развернуть и увидеть больше

И запускаем:

ВНИМАНИЕ ❗️ Данный playbook меняет номер порта демона sshd и отключает парольный доступ к серверу по SSH. Крайне рекомендуется запускать необкатанные плейбуки в тестовых средах. Все действия вы выполняете на свой страх и ответственность. Я вас предупредил)

BASH
ansible-playbook ./ansible/playbooks/sshd_config.yml
Нажмите, чтобы развернуть и увидеть больше

Если все отработало корректно, то порт доступа SSH на удаленных серверах будет изменен. Не забудьте скорректировать номер порта в файле инвентаризации. Или верните старый номер порта подобной командой:

BASH
ansible-playbook ./ansible/playbooks/sshd_config.yml -e 'ansible_port=4444' -e 'new_ssh_port=22'
Нажмите, чтобы развернуть и увидеть больше

В ней переопределены переменная ansible_port (из конфига ansible.cfg), используемая для подключения к хостам, и кастомная переменная new_ssh_port (из плейбука sshd_config.yml), определяющая новый номер порта в плейбуке😶🌫️.

На всякий случай повторю, что плейбук также добавляет наш новый публичный (~/ansible/id_ed25519_new.pub) ключ на сервера.

Возможно вы обратили внимание🧐, что в плейбуке используются теги🔖. С их помощью можно осуществлять выборочный запуск. Например, выполнить только те задачи, которых есть тег ssh_client (ключ -t или --tags):

BASH
ansible-playbook ./ansible/playbooks/sshd_config.yml -t ssh_client
Нажмите, чтобы развернуть и увидеть больше

Или наоборот, игнорировать задачи (--skip-tags):

BASH
ansible-playbook ./ansible/playbooks/sshd_config.yml --skip-tags ssh_server
Нажмите, чтобы развернуть и увидеть больше

Коротко про Ansible roles

Как говорилось в начале статьи, Ansible role — это структура файлов и директорий для удобной организации задач, шаблонов, файлов переменных для Playbook🚜. Создать шаблон роли можно такой командой:

BASH
ansible-galaxy init ./ansible/roles/base-config
Нажмите, чтобы развернуть и увидеть больше

Краткое описание структуры:

BASH
./ansible/roles/base-config/
├── README.md           # документация роли
├── defaults/           # значения переменных по умолчанию (main.yml)
├── files/              # файлы, которые будут копироваться на целевые хосты
├── handlers/           # обработчики для уведомлений (main.yml)
├── meta/               # метаданные роли, включая зависимости (main.yml)
├── tasks/              # основные задачи роли (main.yml)
├── templates/          # шаблоны Jinja2, которые будут рендериться
├── tests/              # тесты для роли (inventory, test.yml)
└── vars/               # переменные роли с более высоким приоритетом (main.yml)
Нажмите, чтобы развернуть и увидеть больше

В будущем, мы обязательно будем писать роли Ansible для различных задач!

Но и это уже совсем другая история)

Создаем функцию для удобного запуска плейбуков

Ранее я обещал функцию для удобного выполнения плейбуков. Она довольно проста, но полезна. Выполняем:

BASH
echo 'ap() { ansible-playbook ~/ansible/playbooks/"$@"; }' >> ~/.profile

source ~/.profile

which ap
Нажмите, чтобы развернуть и увидеть больше

Проверяем работу:

BASH
ap ping.yml
Нажмите, чтобы развернуть и увидеть больше

Отлично!

Пару слов про переменные

Как вы поняли, переменные в Ansible это отдельная тема, про которую лучше меня расскажет оф. документация. Я лишь укажи на самое проблемное место — переопределение переменных. В Ansible для них существует вот такая иерархия (нижние переопределяют верхние):

Послесловие

Вот мы с вами и познакомились с таким крутым и полезным инструментом управления конфигурациями под названием Ansible😎. Базовые знания у нас есть, теперь осталось применить их на практике для решения рутинных задач😌. В планах написать плейбук по базовой настройке сервера Linux, установке и запуску различных сервисов, установке Arch linux на шифрованные разделы и много чего другого🤯.

Все файлы с примерами доступны в моем репозитории на GitHub.

Спасибо, что уделили время 😊. Если появились вопросы – приглашаю в наш дружелюбный Вороний чат 🚶♀️🐧🚶🐧🚶♂️🐧 в телеге 😅. Также обязательно подписывайтесь на основной телеграм канал: @r4ven_me, уведомления о новых материалах на сайте приходят туда в день публикации.

Удачи!

Полезные источники

Авторские права

Автор: Иван Чёрный

Ссылка: https://r4ven.me/automation/ansible-sistema-upravleniya-konfiguraciyami-znakomstvo/

Лицензия: CC BY-NC-SA 4.0

Использование материалов блога разрешается при условии: указания авторства/источника, некоммерческого использования и сохранения лицензии.

Начать поиск

Введите ключевые слова для поиска статей

↑↓
ESC
⌘K Горячая клавиша