Автоматизация развертывания кластеров с помощью Ansible превращает сложный и рискованный процесс в воспроизводимую и безопасную операцию. Это руководство предоставляет готовые, проверенные на практике playbook, роли и шаблоны для типовых сценариев 2026 года. Вы получите полный набор инструментов для быстрого создания кластеров Corosync/Pacemaker, отказоустойчивых балансировщиков на Keepalived и базовых конфигураций распределенного хранилища. Акцент сделан на идемпотентность, безопасное управление секретами через Ansible Vault и организацию проекта, которая упрощает поддержку и масштабирование.
Материал основан на реальном опыте развертывания в production-средах и учитывает особенности современных дистрибутивов Linux. Все примеры кода адаптированы под актуальные версии Ansible и его модулей, включая переход на рекомендованные collections. Вы сможете не только скопировать готовые решения, но и понять архитектурные принципы для адаптации под свои задачи.
Подготовка проекта Ansible: структура и безопасность для кластерных сред
Правильная организация проекта Ansible определяет его долгосрочную поддерживаемость и безопасность. Для кластерных сред, где конфигурации сложны, а секреты критичны, структура и управление доступом становятся первостепенными.
Оптимальная структура каталогов: разделение ролей и конфигураций
Логичное разделение кода упрощает переиспользование компонентов для разных типов кластеров. Для проекта по управлению кластерами рекомендуем следующую структуру каталогов:
automated_clusters/
├── ansible.cfg
├── inventories/
│ ├── production/
│ │ ├── hosts.yaml # Инвентарь в формате YAML
│ │ └── group_vars/
│ │ ├── all.yml # Общие переменные
│ │ ├── ha_nodes.yml # Переменные для узлов кластера HA
│ │ └── lb_nodes.yml # Переменные для балансировщиков
│ └── staging/ # Аналогичная структура для staging
├── roles/
│ ├── cluster_ha/ # Роль для Corosync/Pacemaker
│ ├── lb_keepalived/ # Роль для Keepalived
│ └── common/ # Общие задачи (firewall, users, packages)
├── playbooks/
│ ├── deploy_ha_cluster.yaml
│ ├── deploy_loadbalancer.yaml
│ └── site.yaml # Главный playbook, импортирующий другие
├── templates/ # Шаблоны Jinja2 для конфигураций
├── files/ # Статические файлы для копирования
├── vault/ # Зашифрованные файлы с секретами
│ └── secrets_prod.yml
└── requirements.yml # Зависимости (collections, роли)
Инвентарь в формате YAML (hosts.yaml) наглядно определяет группы и переменные на уровне хоста. Пример для двухузлового кластера Pacemaker и балансировщика:
all:
children:
ha_cluster:
hosts:
node1.cluster.local:
node_id: 1
corosync_ip: 192.168.10.11
node2.cluster.local:
node_id: 2
corosync_ip: 192.168.10.12
loadbalancers:
hosts:
lb1.cluster.local:
keepalived_priority: 150
vip: 10.0.0.100
lb2.cluster.local:
keepalived_priority: 100
vip: 10.0.0.100
Такое разделение позволяет независимо управлять разными компонентами инфраструктуры и масштабировать проект, добавляя новые роли без переписывания существующей логики.
Безопасное управление секретами с Ansible Vault: ключи, пароли, токены
Хранение паролей, ключей аутентификации Corosync и SSL-сертификатов в открытом виде создает критическую уязвимость. Ansible Vault решает эту проблему шифрованием чувствительных данных.
Создайте зашифрованный файл для секретов:
ansible-vault create vault/secrets_prod.yml
Определите в нем переменные:
# vault/secrets_prod.yml
hacluster_password: "{{ vault_hacluster_password }}"
corosync_authkey: "{{ vault_corosync_authkey }}"
ssl_certificate_private_key: "{{ vault_ssl_key }}"
Фактические значения задаются при запуске playbook через команду ansible-vault encrypt_string или хранятся в защищенном CI/CD пайплайне. Интегрируйте зашифрованные переменные в playbook, используя vars_files с тегом vault:
# playbooks/deploy_ha_cluster.yaml
- hosts: ha_cluster
vars_files:
- "{{ playbook_dir }}/vault/secrets_prod.yml"
tasks:
- name: Set hacluster password
ansible.builtin.user:
name: hacluster
password: "{{ hacluster_password | password_hash('sha512') }}"
update_password: always
tags: vault
Для генерации и безопасной передачи ключа аутентификации Corosync между узлами кластера используйте задачу, которая создает ключ на control node и распределяет его:
- name: Generate Corosync authkey if not present
ansible.builtin.command:
cmd: corosync-keygen
chdir: /tmp
delegate_to: localhost
run_once: true
register: keygen_result
changed_when: keygen_result.rc == 0
- name: Distribute Corosync authkey
ansible.builtin.copy:
src: /tmp/authkey
dest: /etc/corosync/authkey
owner: root
group: root
mode: '0400'
when: inventory_hostname in groups['ha_cluster']
Практика ротации секретов предполагает обновление зашифрованного файла через ansible-vault rekey и повторный запуск соответствующих playbook. Для автоматизации этого процесса в CI/CD используйте переменные окружения ANSIBLE_VAULT_PASSWORD_FILE.
Готовые playbook и роли для развертывания кластера Corosync/Pacemaker
Развертывание отказоустойчивого кластера на базе Corosync и Pacemaker включает несколько этапов: установка пакетов, настройка коммуникации, конфигурация кворума и создание ресурсов. Представленная роль cluster_ha объединяет эти шаги в идемпотентный playbook.
Базовая роль `cluster_ha`: установка пакетов и настройка Corosync
Роль содержит задачи для семейств RedHat (RHEL, CentOS, AlmaLinux) и Debian (Ubuntu). Основные задачи находятся в roles/cluster_ha/tasks/main.yml:
# roles/cluster_ha/tasks/main.yml
- name: Install HA packages (RedHat)
ansible.builtin.package:
name:
- corosync
- pacemaker
- pcs
- fence-agents-all
state: present
when: ansible_os_family == "RedHat"
- name: Install HA packages (Debian)
ansible.builtin.package:
name:
- corosync
- pacemaker
- pcs
- fence-agents
state: present
when: ansible_os_family == "Debian"
- name: Start and enable pcsd service
ansible.builtin.service:
name: pcsd
state: started
enabled: true
- name: Set hacluster password
ansible.builtin.user:
name: hacluster
password: "{{ hacluster_password | password_hash('sha512') }}"
update_password: always
tags: vault
- name: Configure Corosync
ansible.builtin.template:
src: corosync.conf.j2
dest: /etc/corosync/corosync.conf
owner: root
group: root
mode: '0644'
notify: restart corosync
Шаблон corosync.conf.j2 динамически формирует конфигурацию на основе переменных инвентаря:
# roles/cluster_ha/templates/corosync.conf.j2
totem {
version: 2
cluster_name: {{ cluster_name | default('my_ha_cluster') }}
transport: udpu
crypto_cipher: aes256
crypto_hash: sha256
}
nodelist {
{% for host in groups['ha_cluster'] %}
node {
ring0_addr: {{ hostvars[host].corosync_ip | default(hostvars[host].ansible_default_ipv4.address) }}
nodeid: {{ hostvars[host].node_id }}
name: {{ host }}
}
{% endfor %}
}
quorum {
provider: corosync_votequorum
two_node: {{ (groups['ha_cluster'] | length == 2) | ternary('1', '0') }}
}
logging {
to_logfile: yes
logfile: /var/log/corosync/corosync.log
to_syslog: yes
timestamp: on
}
После применения конфигурации задачи инициализируют кластер и настраивают fencing с помощью модуля community.general.pcs_cluster из коллекции community.general, актуальной для 2026 года:
- name: Authenticate nodes in cluster
community.general.pcs_cluster:
state: present
nodes: "{{ groups['ha_cluster'] | join(',') }}"
cluster_auth: "{{ hacluster_password }}"
run_once: true
delegate_to: "{{ groups['ha_cluster'][0] }}"
- name: Configure STONITH fencing (example for virsh)
community.general.pcs_stonith:
stonith_id: fence_virsh
stonith_type: fence_virsh
stonith_options:
- "pcmk_host_list={{ groups['ha_cluster'] | join(' ') }}"
- "uri=hypervisor.local"
- "key_file=/root/.ssh/id_rsa"
state: present
run_once: true
Управление ресурсами в Pacemaker: виртуальные IP и службы
Автоматизация создания ресурсов кластера завершает развертывание. Playbook для управления ресурсами использует модуль community.general.pcs_resource:
# playbooks/manage_ha_resources.yaml
- hosts: ha_cluster[0] # Запускаем на первом узле
tasks:
- name: Create virtual IP resource
community.general.pcs_resource:
resource_id: vip_web
resource_type: IPaddr2
resource_options:
- "ip={{ web_vip }}"
- "cidr_netmask=24"
state: present
cluster_auth: "{{ hacluster_password }}"
- name: Create Apache service resource
community.general.pcs_resource:
resource_id: httpd_svc
resource_type: systemd:httpd
resource_options:
- "enable=true"
state: present
cluster_auth: "{{ hacluster_password }}"
- name: Constrain resources to run together
community.general.pcs_resource:
command: constraint
constraint_type: colocation
resource_id: httpd_svc
dependent_resource_id: vip_web
score: INFINITY
state: present
cluster_auth: "{{ hacluster_password }}"
- name: Set start order (VIP first, then Apache)
community.general.pcs_resource:
command: constraint
constraint_type: order
first_resource_id: vip_web
then_resource_id: httpd_svc
options: "kind=Mandatory"
state: present
cluster_auth: "{{ hacluster_password }}"
Best practice для именования ресурсов - использовать префиксы, указывающие на тип (vip_, svc_, fs_). Это упрощает их идентификацию в выводе команд pcs status. Для управления конфигурациями разных окружений (dev, stage, prod) определите переменные web_vip, cluster_name в соответствующих файлах group_vars.
Автоматизация развертывания балансировщика нагрузки и распределенного хранилища
Принципы автоматизации кластеров применимы к другим сценариям, таким как отказоустойчивые балансировщики и распределенные хранилища. Использование общих подходов и структуры проекта ускоряет внедрение новых типов кластеров.
Кластер балансировщиков на Keepalived: отказоустойчивый виртуальный IP
Роль lb_keepalived настраивает простой, но эффективный кластер для отказоустойчивого виртуального IP (VIP), направляющего трафик на бэкенд-серверы. Основная задача - управление конфигурацией Keepalived через шаблон Jinja2.
# roles/lb_keepalived/tasks/main.yml
- name: Install Keepalived
ansible.builtin.package:
name: keepalived
state: present
- name: Configure Keepalived
ansible.builtin.template:
src: keepalived.conf.j2
dest: /etc/keepalived/keepalived.conf
owner: root
group: root
mode: '0644'
notify: restart keepalived
- name: Enable and start Keepalived service
ansible.builtin.service:
name: keepalived
state: started
enabled: true
Шаблон keepalived.conf.j2 динамически назначает роли (MASTER/BACKUP) на основе приоритета, заданного в инвентаре:
# roles/lb_keepalived/templates/keepalived.conf.j2
global_defs {
router_id LVS_{{ inventory_hostname_short | upper }}
}
vrrp_instance VI_1 {
state {{ (keepalived_priority == 150) | ternary('MASTER', 'BACKUP') }}
interface {{ ansible_default_ipv4.alias | default(ansible_default_ipv4.device) }}
virtual_router_id 51
priority {{ keepalived_priority }}
advert_int 1
authentication {
auth_type PASS
auth_pass {{ keepalived_auth_pass | default('secret_password') }}
}
virtual_ipaddress {
{{ vip }}
}
notify_master "/usr/local/bin/notify_master.sh"
notify_backup "/usr/local/bin/notify_backup.sh"
}
Механизм уведомлений (notify scripts) интегрирует кластер Keepalived с системами мониторинга (например, Prometheus) или отправляет оповещения в Slack/Telegram при смене состояния. Для проверки здоровья реальных сервисов можно добавить секцию virtual_server с health check для бэкендов.
Адаптация playbook под разные сценарии: использование переменных и тегов
Главный playbook site.yaml импортирует другие сценарии и позволяет гибко управлять развертыванием через теги и переменные.
# playbooks/site.yaml
- name: Deploy High Availability infrastructure
hosts: all
vars:
deploy_ha_cluster: true
deploy_loadbalancer: false
deploy_storage: false
tasks:
- name: Include HA cluster deployment
ansible.builtin.include_tasks: "{{ playbook_dir }}/deploy_ha_cluster.yaml"
when: deploy_ha_cluster
tags: ha
- name: Include loadbalancer deployment
ansible.builtin.include_tasks: "{{ playbook_dir }}/deploy_loadbalancer.yaml"
when: deploy_loadbalancer
tags: lb
- name: Include distributed storage deployment
ansible.builtin.include_tasks: "{{ playbook_dir }}/deploy_drbd_storage.yaml"
when: deploy_storage
tags: storage
Запустите развертывание только балансировщиков: ansible-playbook -i inventories/production/ playbooks/site.yaml --tags lb. Для кастомизации параметров под конкретное окружение переопределите переменные в инвентаре. Например, чтобы изменить виртуальный IP для staging:
# inventories/staging/group_vars/lb_nodes.yml
vip: 10.0.1.100
keepalived_priority:
lb1.staging.local: 150
lb2.staging.local: 100
Такой подход позволяет использовать один набор ролей для разных сред и требований, минимизируя дублирование кода.
Best practices и отладка: обеспечение идемпотентности и обработка ошибок
Надежная автоматизация требует не только рабочих playbook, но и стратегий тестирования, обработки сбоев и поддержания идемпотентности. Эти практики снижают риски при развертывании в production.
Тестирование playbook: от Vagrant до staging-окружения
Поэтапное тестирование минимизирует вероятность ошибок в боевой среде. Придерживайтесь следующего плана:
- Локальная проверка синтаксиса и предпросмотр изменений:
ansible-playbook playbooks/site.yaml --check --diff --limit localhost
Режим--check(dry run) показывает, какие изменения произойдут, не применяя их. Режим--diffотображает различия в изменяемых файлах. - Развертывание на изолированных виртуальных машинах с идентичной ОС: Используйте Vagrant с тем же образом, что и в production. После выполнения playbook проверьте состояние кластера с помощью модуля
ansible.builtin.shell:
- name: Verify Pacemaker cluster status
ansible.builtin.shell:
cmd: pcs status
register: pcs_status
changed_when: false
failed_when: "'OFFLINE' in pcs_status.stdout or 'Stopped' in pcs_status.stdout"
- name: Debug output on failure
ansible.builtin.debug:
var: pcs_status.stdout
when: pcs_status.failed
- Скрипт пост-деплой проверки: Автоматизируйте проверку ключевых метрик после развертывания. Пример скрипта, который проверяет доступность виртуального IP и службы:
#!/bin/bash
# scripts/post_deploy_verify.sh
VIP="10.0.0.100"
if ping -c 2 $VIP &> /dev/null; then
echo "VIP $VIP is reachable."
if curl -s --connect-timeout 5 http://$VIP/health | grep -q "OK"; then
echo "Service on $VIP is responding correctly."
exit 0
else
echo "Service health check failed."
exit 1
fi
else
echo "VIP $VIP is not reachable."
exit 1
fi
Интегрируйте этот скрипт в playbook как финальную задачу или запускайте его отдельно из CI/CD пайплайна.
Типовые проблемы при автоматизации кластеров и их решение
Автоматизация кластеров сталкивается со специфичными проблемами. Вот список частых ошибок и способы их решения в рамках Ansible:
- Отказ в аутентификации Corosync между узлами:
- Причина: Несовпадение ключа
authkeyили блокировка firewall. - Диагностика: Проверьте наличие идентичного файла
/etc/corosync/authkeyна всех узлах. Убедитесь, что порты UDP 5404-5405 открыты. - Решение в Ansible: Добавьте задачу для проверки и открытия портов в роли
common:- name: Ensure firewall allows Corosync ports ansible.posix.firewalld: port: "5404-5405/udp" permanent: true immediate: true state: enabled when: ansible_os_family == "RedHat"
- Причина: Несовпадение ключа
- Отсутствие кворума в двухузловом кластере:
- Причина: По умолчанию двухузловый кластер требует специальной настройки
two_node: 1или использования qdevice. - Решение: Убедитесь, что в шаблоне
corosync.conf.j2параметрtwo_nodeустановлен в 1. Добавьте задачу для настройки ожидания кворума в Pacemaker:- name: Configure two-node cluster quorum community.general.pcs_quorum: options: - "wait_for_all=0" - "auto_tie_breaker=1" state: present run_once: true
- Причина: По умолчанию двухузловый кластер требует специальной настройки
- Сбои fencing (STONITH):
- Причина: Неправильная конфигурация агента fencing или отсутствие доступа к гипервизору.
- Диагностика: Выполните тестовую команду fencing вручную:
fence_virsh --list. - Решение: Используйте блок
rescueв playbook для временного отключения fencing при критическом сбое, но с обязательным предупреждением:- name: Configure STONITH community.general.pcs_stonith: # ... конфигурация ignore_errors: yes register: stonith_result - name: Warn if STONITH configuration failed ansible.builtin.debug: msg: "STONITH configuration failed. Cluster will proceed without fencing (NOT RECOMMENDED FOR PRODUCTION)." when: stonith_result.failed
Обеспечьте идемпотентность, используя модули, которые сами проверяют состояние (например, ansible.builtin.template), и избегайте команд shell или command там, где есть специализированные альтернативы. Для обработки некритичных ошибок применяйте failed_when: с точными условиями и ignore_errors: yes с последующим уведомлением.
Актуальность модулей критична. В 2026 году многие устаревшие модули из ansible.builtin заменены на более функциональные в коллекциях. Например, для управления Pacemaker используйте community.general.pcs_* модули. Укажите зависимости в requirements.yml:
# requirements.yml
collections:
- name: community.general
version: ">=7.0.0"
- name: ansible.posix
version: ">=1.5.0"
Установите их командой ansible-galaxy collection install -r requirements.yml перед запуском playbook.
Для дальнейшего изучения принципов IaC и комбинирования инструментов рекомендуем наше руководство «Ansible или Terraform: выбор инструмента для управления инфраструктурой в 2026», где подробно разбирается совместное использование этих технологий.
Если вам нужно автоматизировать не только кластеры, но и другие рутинные задачи администрирования, ознакомьтесь с практическими скриптами в статье «Автоматизация инфраструктуры на практике».