Обновление OpenSSL на 300 серверах за 2 недели: внедряем SaltStack

Задача клиента: 300 серверов и ручное управление

В январе 2026 года к нам в АйТи Фреш обратился хостинг-провайдер «НордХост» из Санкт-Петербурга. Ребята обслуживали 1 200 клиентов на VPS и dedicated — и всё это хозяйство крутилось на 300 физических и виртуальных серверах в двух дата-центрах.

Поводом стала CVE-2024-9143 — критическая дыра в OpenSSL. Четыре сисадмина два недели ходили по серверам через SSH и обновляли библиотеку вручную. Итог предсказуемый: 18 машин вообще пропустили, на 7 nginx упал из-за конфликта версий. Классика жанра, если честно.

«Мы тратили 60% рабочего времени на рутину — обновление пакетов, создание пользователей, раскатку конфигов. При этом у нас не было единого источника правды: что стоит на каждом сервере, какие конфиги, какие версии. Каждый администратор настраивал серверы по-своему» — руководитель IT-отдела «НордХост».

Аудит инфраструктуры и выбор инструмента

Мы провели аудит. Картина вышла живописная:

  • 300 серверов: 180 Ubuntu 22.04, 80 Debian 12, 40 CentOS Stream 9
  • 4 администратора — у каждого своя голова и свой подход к настройке
  • Bash-скрипты — за «автоматизацию» отвечал набор скриптов с захардкоженными IP прямо в теле
  • Нет инвентаря — актуальность Google Sheets с серверами проверялась раз в квартал, если не забывали
  • SSH-ключи — у каждого админа свой ключ на каждой машине, последняя ротация была 3 года назад

Мы сравнили три системы управления конфигурациями — и выбор был не очевидным с первого взгляда:

КритерийAnsiblePuppetSaltStack
Скорость на 300 узлахМедленно (SSH последовательно)Средне (agent pull)Быстро (ZeroMQ push)
Порог входаНизкийВысокий (свой DSL)Средний (YAML+Jinja)
Real-time executionНетИнтервал 30 минДа (мгновенный push)
Event-driven реакцииНет (без AWX)ОграниченоReactors + Beacons
Масштабируемость~500 узловТысячиДесятки тысяч

Хостинг-провайдеру нужны две вещи: скорость при экстренных патчах и event-driven реакции, когда что-то пошло не так. По обоим пунктам SaltStack оказался впереди. Выбор был сделан.

Установка Salt Master и Minion

Архитектура Salt прямолинейная: Master — центральный сервер, который командует, Minion — агент на каждой управляемой машине, который слушается. Общаются через ZeroMQ на портах 4505/4506, трафик шифруется AES-256.

Установка и конфигурация Salt Master

Под Salt Master выделили отдельную машину — Ubuntu 22.04, 8 CPU, 16 GB RAM, SSD. Никаких соседей, никакой экономии на ресурсах:

# Установка Salt 3006 LTS (Sulfur)
curl -o /tmp/bootstrap-salt.sh -L https://bootstrap.saltproject.io
sudo sh /tmp/bootstrap-salt.sh -M -x python3 stable 3006

# Проверяем версию
salt --version
# salt 3006.8 (Sulfur)

# Конфигурация Master
sudo vim /etc/salt/master

Конфигурация /etc/salt/master:

# /etc/salt/master

# Интерфейс — слушаем на внутреннем IP
interface: 10.0.0.1

# Worker threads — по 1 на каждые 50-100 minion
worker_threads: 8

# Таймауты
timeout: 30

# File server
file_roots:
  base:
    - /srv/salt
    - /srv/salt/formulas

# Pillar data
pillar_roots:
  base:
    - /srv/pillar

# Auto-accept minions по определённому паттерну (для автоматизации)
# В продакшене лучше ручной accept
autosign_grains_dir: /etc/salt/autosign_grains

# Nodegroups для удобного targeting
nodegroups:
  webservers: 'G@role:webserver'
  databases: 'G@role:database'
  ubuntu: 'G@os:Ubuntu'
  debian: 'G@os:Debian'
  centos: 'G@os:CentOS Stream'
  dc1: 'G@datacenter:dc1'
  dc2: 'G@datacenter:dc2'

# Reactor — автоматические реакции на события
reactor:
  - 'salt/auth':  # Новый minion подключается
    - /srv/reactor/new_minion.sls
  - 'salt/beacon/*/inotify/*':  # Изменение файла
    - /srv/reactor/file_changed.sls
  - 'salt/beacon/*/diskusage/*':  # Диск заполнен
    - /srv/reactor/disk_alert.sls

# Включаем git fileserver для GitOps
fileserver_backend:
  - roots
  - gitfs

gitfs_remotes:
  - https://git.nordhost.local/infra/salt-states.git:
    - mountpoint: salt://git

# Логирование
log_level: warning
log_file: /var/log/salt/master
# Создаём структуру каталогов
sudo mkdir -p /srv/{salt,pillar,reactor}
sudo mkdir -p /srv/salt/{users,packages,services,monitoring,security}
sudo mkdir -p /srv/pillar/{users,servers}
sudo mkdir -p /etc/salt/autosign_grains

# Запускаем Salt Master
sudo systemctl enable salt-master
sudo systemctl start salt-master

# Открываем порты
sudo ufw allow from 10.0.0.0/16 to any port 4505 comment "Salt publish"
sudo ufw allow from 10.0.0.0/16 to any port 4506 comment "Salt return"

Массовая установка Minion через salt-ssh

Тут возникает классическая проблема курицы и яйца: чтобы поставить Salt Minion на 300 серверов, нужен инструмент автоматизации — которого ещё нет. Мы решили это через salt-ssh. Он не требует агента и работает напрямую по SSH:

# Устанавливаем salt-ssh
sudo apt install -y salt-ssh

# Создаём roster — инвентарь серверов для SSH
# /etc/salt/roster
sudo bash -c 'cat > /etc/salt/roster << EOF
# Формат: minion_id : host, user, port, sudo
web-01:
  host: 10.0.1.11
  user: deploy
  sudo: True
  minion_opts:
    grains:
      role: webserver
      datacenter: dc1

web-02:
  host: 10.0.1.12
  user: deploy
  sudo: True
  minion_opts:
    grains:
      role: webserver
      datacenter: dc1

db-01:
  host: 10.0.1.21
  user: deploy
  sudo: True
  minion_opts:
    grains:
      role: database
      datacenter: dc1
EOF'

# Для 300 серверов генерируем roster из CSV
#!/bin/bash
# generate_roster.sh
while IFS=, read -r hostname ip role dc; do
  cat >> /etc/salt/roster << EOF
${hostname}:
  host: ${ip}
  user: deploy
  sudo: True
  minion_opts:
    grains:
      role: ${role}
      datacenter: ${dc}
EOF
done < servers.csv

# Устанавливаем minion на все серверы через salt-ssh
salt-ssh '*' state.apply install_minion

# State для установки minion:
# /srv/salt/install_minion.sls
install-salt-minion:
  cmd.run:
    - name: |
        curl -o /tmp/bootstrap-salt.sh -L https://bootstrap.saltproject.io
        sh /tmp/bootstrap-salt.sh -x python3 stable 3006
    - unless: salt-minion --version | grep 3006

configure-minion:
  file.managed:
    - name: /etc/salt/minion
    - contents: |
        master: 10.0.0.1
        grains:
          role: {{ grains['role'] }}
          datacenter: {{ grains['datacenter'] }}
    - require:
      - cmd: install-salt-minion

start-minion:
  service.running:
    - name: salt-minion
    - enable: True
    - require:
      - file: configure-minion

Когда minion поднялся на всех серверах, принимаем ключи на master:

# Смотрим ожидающие ключи
sudo salt-key -L
# Unaccepted Keys:
# web-01
# web-02
# ...(298 keys)

# Принимаем все
sudo salt-key -A -y

# Проверяем связь
salt '*' test.ping
# web-01: True
# web-02: True
# ...
# 300 minions responded

Grains и Pillars: данные об инфраструктуре

Grains — это то, что minion знает о себе: ОС, IP-адрес, сколько RAM, какая роль. Pillars — конфиденциальные данные, которые master отдаёт только тем, кому положено. Вместе они дают полную и актуальную картину инфраструктуры без единого Google Sheets.

Настройка Grains для классификации серверов

Для каждого сервера мы прописали кастомные grains — роль, окружение, принадлежность к дата-центру:

# /etc/salt/grains на каждом minion (или через конфигурацию)
# Пример для web-сервера:
role: webserver
datacenter: dc1
rack: A3
customer_tier: premium
services:
  - nginx
  - php-fpm
  - node-exporter

# Используем grains для сбора информации
salt '*' grains.item os osrelease mem_total num_cpus
# web-01:
#   mem_total: 32768
#   num_cpus: 8
#   os: Ubuntu
#   osrelease: 22.04

# Фильтрация по grains
salt -G 'os:Ubuntu' test.ping       # Все Ubuntu
salt -G 'role:webserver' test.ping   # Все веб-серверы
salt -G 'datacenter:dc1' test.ping   # Все в DC1

# Compound matching — комбинация условий
salt -C 'G@role:webserver and G@datacenter:dc1' test.ping
salt -C 'G@os:Ubuntu and not G@role:database' test.ping

Pillars для секретов и per-server конфигурации

В Pillars уходит всё, что должно отличаться от сервера к серверу — или от группы к группе:

# /srv/pillar/top.sls — маршрутизация pillars к minions
base:
  '*':
    - common
    - users.admins
  'G@role:webserver':
    - match: compound
    - nginx
    - certificates
  'G@role:database':
    - match: compound
    - postgresql
  'web-01':
    - servers.web-01

# /srv/pillar/common.sls
ntp_servers:
  - 10.0.0.5
  - 10.0.0.6
dns_servers:
  - 10.0.0.3
  - 10.0.0.4
monitoring:
  prometheus_server: 10.0.0.50
  node_exporter_port: 9100

# /srv/pillar/users/admins.sls
admins:
  ivanov:
    uid: 1001
    ssh_key: "ssh-ed25519 AAAAC3Nza... ivanov@nordhost"
    sudo: True
  petrov:
    uid: 1002
    ssh_key: "ssh-ed25519 AAAAC3Nza... petrov@nordhost"
    sudo: True
  sidorov:
    uid: 1003
    ssh_key: "ssh-ed25519 AAAAC3Nza... sidorov@nordhost"
    sudo: False

# /srv/pillar/nginx.sls
nginx:
  worker_processes: auto
  worker_connections: 4096
  ssl_protocols: "TLSv1.2 TLSv1.3"
  ssl_ciphers: "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256"

# Применяем и проверяем
salt 'web-01' pillar.items
salt 'web-01' pillar.get admins:ivanov:ssh_key

States: инфраструктура как код

States — это SLS-файлы, которые описывают, каким должен быть сервер. Salt берёт машину и приводит её к этому состоянию. Идемпотентно: запусти хоть десять раз подряд — результат тот же, лишних действий не будет.

top.sls и структура states

Файл top.sls — это карта: кому какие states применять:

# /srv/salt/top.sls
base:
  '*':                          # Все серверы
    - common.packages
    - common.ntp
    - common.dns
    - common.sysctl
    - users
    - security.ssh
    - security.firewall
    - monitoring.node-exporter

  'G@role:webserver':           # Веб-серверы
    - match: compound
    - nginx
    - nginx.vhosts
    - certbot

  'G@role:database':            # Серверы БД
    - match: compound
    - postgresql
    - postgresql.backup

  'G@role:mailserver':          # Почтовые серверы
    - match: compound
    - postfix
    - dovecot

Практические states: пользователи, пакеты, сервисы

Вот реальные states, которые мы написали под задачи «НордХост» — не шаблонные примеры, а то, что работает в production:

# /srv/salt/users/init.sls — управление пользователями
{% for username, user in pillar.get('admins', {}).items() %}
user_{{ username }}:
  user.present:
    - name: {{ username }}
    - uid: {{ user.uid }}
    - shell: /bin/bash
    - home: /home/{{ username }}
    - groups:
      - sudo
      {% if user.get('sudo', False) %}
      - adm
      {% endif %}

ssh_key_{{ username }}:
  ssh_auth.present:
    - user: {{ username }}
    - name: {{ user.ssh_key }}
    - require:
      - user: user_{{ username }}

{% if user.get('sudo', False) %}
sudoers_{{ username }}:
  file.managed:
    - name: /etc/sudoers.d/{{ username }}
    - contents: "{{ username }} ALL=(ALL) NOPASSWD: ALL"
    - mode: 440
    - require:
      - user: user_{{ username }}
{% endif %}
{% endfor %}

# Удаляем бывших сотрудников
{% for username in pillar.get('removed_users', []) %}
remove_{{ username }}:
  user.absent:
    - name: {{ username }}
    - purge: True
{% endfor %}

# /srv/salt/common/packages.sls — базовые пакеты
base_packages:
  pkg.installed:
    - pkgs:
      - htop
      - iotop
      - tmux
      - curl
      - wget
      - vim
      - net-tools
      - dnsutils
      - unattended-upgrades
      - fail2ban
      {% if grains['os_family'] == 'Debian' %}
      - apt-transport-https
      {% elif grains['os_family'] == 'RedHat' %}
      - epel-release
      {% endif %}

# /srv/salt/security/ssh.sls — хардеринг SSH
sshd_config:
  file.managed:
    - name: /etc/ssh/sshd_config
    - source: salt://security/files/sshd_config.jinja
    - template: jinja
    - mode: 644
    - check_cmd: sshd -t -f

sshd_service:
  service.running:
    - name: sshd
    - enable: True
    - watch:
      - file: sshd_config

# /srv/salt/security/files/sshd_config.jinja
Port 22
ListenAddress {{ grains['ip_interfaces']['eth0'][0] }}
Protocol 2
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3
ClientAliveInterval 300
ClientAliveCountMax 2
X11Forwarding no
AllowTcpForwarding no
{% for username in pillar.get('admins', {}).keys() %}
AllowUsers {{ username }}
{% endfor %}

State для обновления OpenSSL — та самая задача

Теперь тот самый state, ради которого всё и затевалось. Обновление OpenSSL на 300 серверах — то, что вручную заняло две недели и всё равно прошло криво:

# /srv/salt/security/openssl_update.sls

# Обновляем OpenSSL
openssl_update:
  pkg.latest:
    - name: openssl
    - refresh: True

libssl_update:
  pkg.latest:
    - pkgs:
      {% if grains['os_family'] == 'Debian' %}
      - libssl3
      {% elif grains['os_family'] == 'RedHat' %}
      - openssl-libs
      {% endif %}
    - refresh: True

# Перезапускаем сервисы, зависящие от OpenSSL
{% set ssl_services = salt['service.get_all']() %}
{% for svc in ['nginx', 'apache2', 'httpd', 'postfix', 'dovecot', 'sshd'] %}
{% if svc in ssl_services %}
restart_{{ svc }}:
  service.running:
    - name: {{ svc }}
    - watch:
      - pkg: openssl_update
      - pkg: libssl_update
{% endif %}
{% endfor %}

# Проверяем версию после обновления
verify_openssl:
  cmd.run:
    - name: openssl version
    - require:
      - pkg: openssl_update

Запускаем обновление сразу на всех 300 серверах:

# Сначала dry-run (test=True)
salt '*' state.apply security.openssl_update test=True

# Результат: 300 серверов, 300 OK, 0 failed
# Все 300 серверов покажут, что будет обновлено

# Поэтапная раскатка — сначала DC1, потом DC2
salt -C 'G@datacenter:dc1' state.apply security.openssl_update
# 152 servers responded. All succeeded.
# Duration: 3 min 47 sec

salt -C 'G@datacenter:dc2' state.apply security.openssl_update
# 148 servers responded. All succeeded.
# Duration: 3 min 22 sec

# Проверяем версию на всех
salt '*' cmd.run 'openssl version'
# web-01: OpenSSL 3.0.13 30 Jan 2024
# web-02: OpenSSL 3.0.13 30 Jan 2024
# ...(все 300 одинаковые)

# Итого: 7 минут 9 секунд вместо 2 недель

Reactors и Beacons: event-driven автоматизация

Salt Reactor — штука, которую я считаю главным козырем SaltStack. Заполнился диск — сам чистит логи. Изменился критичный файл — перезапускает сервис. Появился новый сервер — раскатывает базовую конфигурацию. Без участия человека.

Beacons для мониторинга событий на minions

Beacons живут на minion и непрерывно мониторят состояние системы, генерируя события. Master ловит эти события через reactor и реагирует:

# /etc/salt/minion.d/beacons.conf (на всех серверах)
beacons:
  diskusage:
    - /:
        percent: 90%
    - /var/log:
        percent: 85%
    - interval: 60  # Проверка каждые 60 секунд

  inotify:
    - files:
        /etc/ssh/sshd_config:
          mask:
            - modify
            - delete
        /etc/passwd:
          mask:
            - modify
    - interval: 5

  service:
    - services:
        nginx:
          onchangeonly: True
        sshd:
          onchangeonly: True
    - interval: 30

Reactor: автоматические реакции

На master прописываем, что делать при каждом типе событий от beacons:

# /srv/reactor/disk_alert.sls
# Автоматическая очистка при заполнении диска
{% set data = data.get('data', {}) %}
{% if data.get('percent', 0) >= 90 %}
auto_cleanup:
  local.state.apply:
    - tgt: {{ data['id'] }}
    - arg:
      - maintenance.disk_cleanup

notify_admins_disk:
  runner.http.query:
    - url: "https://api.telegram.org/bot/sendMessage"
    - method: POST
    - data:
        chat_id: "-100123456789"
        text: "Disk alert: {{ data['id'] }} — {{ data.get('mount', '/') }} at {{ data.get('percent', '?') }}%"
{% endif %}

# /srv/reactor/file_changed.sls
# Кто-то изменил sshd_config — откатываем к нашей версии
revert_ssh_config:
  local.state.apply:
    - tgt: {{ data['id'] }}
    - arg:
      - security.ssh

# /srv/salt/maintenance/disk_cleanup.sls
clean_old_logs:
  cmd.run:
    - name: |
        find /var/log -name '*.gz' -mtime +30 -delete
        find /var/log -name '*.1' -mtime +14 -delete
        journalctl --vacuum-time=7d
        apt clean 2>/dev/null || yum clean all 2>/dev/null
    - shell: /bin/bash

Оркестрация: сложные сценарии развёртывания

Оркестрация нужна, когда операция затрагивает несколько серверов и порядок важен. Классический пример — rolling update nginx без единой секунды простоя: сначала выводим сервер из балансировки, обновляем, возвращаем обратно.

Rolling update nginx на всех веб-серверах

Обновляем nginx поэтапно — каждый сервер аккуратно выходит из балансировки и возвращается обратно:

# /srv/salt/orch/nginx_rolling_update.sls

# Шаг 1: Обновляем nginx в DC1 (по одному серверу)
{% set dc1_servers = salt.saltutil.runner('manage.up', tgt='G@datacenter:dc1 and G@role:webserver', tgt_type='compound') %}

{% for server in dc1_servers %}
remove_{{ server }}_from_lb:
  salt.function:
    - name: cmd.run
    - tgt: 'lb-01'
    - arg:
      - "echo 'disable server backends/{{ server }}' | socat stdio /var/run/haproxy/admin.sock"

update_nginx_{{ server }}:
  salt.state:
    - tgt: '{{ server }}'
    - sls:
      - nginx
    - require:
      - salt: remove_{{ server }}_from_lb

verify_{{ server }}:
  salt.function:
    - name: cmd.run
    - tgt: '{{ server }}'
    - arg:
      - 'curl -sf http://localhost/health || exit 1'
    - require:
      - salt: update_nginx_{{ server }}

add_{{ server }}_to_lb:
  salt.function:
    - name: cmd.run
    - tgt: 'lb-01'
    - arg:
      - "echo 'enable server backends/{{ server }}' | socat stdio /var/run/haproxy/admin.sock"
    - require:
      - salt: verify_{{ server }}
{% endfor %}

# Запуск оркестрации
# salt-run state.orchestrate orch.nginx_rolling_update

Автоматическое развёртывание нового сервера

Когда «НордХост» вводит новый сервер, Salt поднимает его с нуля до production-ready без единого ручного действия:

# /srv/reactor/new_minion.sls
# Реагирует на событие salt/auth (новый minion подключился)
{% if data.get('act') == 'accept' %}
bootstrap_new_server:
  local.state.apply:
    - tgt: {{ data['id'] }}
    - arg:
      - common.packages
      - common.ntp
      - users
      - security.ssh
      - security.firewall
      - monitoring.node-exporter

notify_new_server:
  runner.http.query:
    - url: "https://api.telegram.org/bot/sendMessage"
    - method: POST
    - data:
        chat_id: "-100123456789"
        text: "New server bootstrapped: {{ data['id'] }}"
{% endif %}

# Результат: новый сервер полностью настроен за 4-5 минут
# автоматически, без участия администратора

Salt Cloud: автоматическое создание VM

Отдельно мы настроили Salt Cloud — модуль для автоматического создания виртуальных машин. Схема простая: клиент заказал VPS — Salt Cloud создаёт машину, ставит minion, запускает bootstrap. Одна команда, и через несколько минут сервер готов к работе:

# /etc/salt/cloud.profiles.d/nordhost.conf
vps-basic:
  provider: proxmox-dc1
  technology: qemu
  clone: template-ubuntu-2204
  vmid: auto
  cores: 2
  memory: 4096
  disk_size: 40
  net:
    net0:
      bridge: vmbr0
  minion:
    master: 10.0.0.1
    grains:
      role: customer-vps
      tier: basic

# Создание VPS одной командой
salt-cloud -p vps-basic client-web-042 -l info

# Результат через 90 секунд:
# [INFO] Salt installed on client-web-042
# [INFO] client-web-042 provisioned successfully
# [INFO] Bootstrap state applied: users, ssh, firewall, monitoring

# Вся цепочка: создание VM → установка ОС → Salt Minion → bootstrap states
# Полная готовность сервера: 2 минуты вместо 4 часов вручную

Salt Cloud работает с Proxmox, VMware, KVM/libvirt и облачными провайдерами — AWS, GCP, Azure, Yandex Cloud, Hetzner. Для «НордХост» это значит один и тот же процесс создания серверов вне зависимости от того, на каком гипервизоре они крутятся.

Результаты внедрения: от хаоса к порядку

Внедрение SaltStack заняло 18 рабочих дней. Три дня — аудит и проектирование. Пять — установка и настройка Salt. Восемь — написание states и прогон тестов. Ещё два — обучение команды. Без героических ночных смен, строго в рамках плана.

Измеримые результаты

МетрикаДо SaltStackПосле SaltStack
Обновление OpenSSL на 300 серверах2 недели7 минут
Развёртывание нового сервера4 часа (ручная настройка)5 минут (автоматически)
Создание нового пользователя на всех серверах1 день30 секунд
Дрифт конфигурации47% серверов различаются0% (Salt enforcment)
Рутинные задачи (% времени команды)60%15%
Инцидентов из-за конфигурации12 в квартал1 в квартал

После завершения проекта мы передали команде «НордХост» полную документацию и провели двухдневный тренинг для администраторов. Сейчас они самостоятельно пишут новые states и планомерно расширяют автоматизацию — без нашего участия.

Долгосрочные преимущества

Но быстрые результаты — это только половина истории. Внедрение SaltStack дало «НордХост» кое-что поважнее: стратегические преимущества, которые работают вдолгую.

  • Infrastructure as Code — вся конфигурация живёт в Git-репозитории. Новый администратор разбирается в инфраструктуре по SLS-файлам, а не звонит коллегам с вопросом «а как у нас настроен nginx?»
  • Аудируемость — каждое изменение проходит через Git: code review, история коммитов, всё прозрачно. Хотите узнать, кто и когда поменял настройку SSH на веб-серверах? Один запрос в git log — и ответ перед вами
  • Единообразие — все 300 серверов настроены одинаково, без исключений. Конфигурационный дрифт обнаруживается за секунды одной командой salt '*' state.apply test=True
  • Масштабируемость — добавить 100 новых серверов можно без роста нагрузки на команду. Salt Cloud поднимает VM, Salt states сами их настраивают. Люди в процессе почти не участвуют
  • Compliance — при аудите безопасности не нужно вручную проверять каждый сервер. Показываете SLS-файлы и результаты их применения — и у аудитора практически не остаётся вопросов

В ближайших планах «НордХост» — распространить Salt на управление конфигурациями клиентских серверов и упаковать это в отдельную услугу managed hosting. Честно говоря, идея логичная: инструмент уже работает, экспертиза есть.

Часто задаваемые вопросы

Нас часто спрашивают: а почему не Ansible? Отвечу прямо. Ansible — хороший инструмент, и для инфраструктуры до 100 серверов или разовых операций он отлично справляется. Но когда серверов 300+ и нужен мгновенный push и event-driven автоматизация — SaltStack выигрывает без вариантов. Salt гоняет команды параллельно через ZeroMQ и укладывается в секунды, тогда как Ansible последовательно ходит на каждый сервер по SSH. Для хостинг-провайдера, где экстренный патч безопасности нужно раскатить прямо сейчас — эта разница в скорости решает всё.

States пишутся на YAML с Jinja-шаблонами. Если вы работали с Ansible или Docker Compose — синтаксис покажется знакомым с первых минут. Главное, что нужно понять, — это модель master-minion и система targeting: glob, grain, compound. На нашем тренинге администраторы начинали писать собственные states уже к концу первого дня. Продвинутые вещи — reactors, orchestration, custom modules — осваиваются за 2–3 недели реальной практики. Не месяцами, но и не за один вечер.

Теперь про безопасность — и здесь я бы не стал экономить на внимании. Salt Master — критически важный компонент. Компрометация мастера означает полный контроль над всей инфраструктурой. На практике мы всегда рекомендуем шесть вещей: 1) выделенный сервер только под мастер, без посторонних задач; 2) SSH-доступ исключительно с jump-хоста; 3) firewall — порты 4505/4506 открыты только для подсети minion; 4) регулярная ротация master-ключей; 5) audit log всех команд Salt; 6) разграничение прав через Salt ACL (External Auth) — разным администраторам разные уровни доступа. Пропустить хотя бы один пункт — значит оставить дыру.

Да, Salt Minion работает и на Windows. Управлять можно пакетами через chocolatey, службами, реестром, файлами, пользователями. Другой вопрос, что экосистема states для Windows пока заметно беднее, чем для Linux. Тем не менее для смешанных инфраструктур SaltStack — один из лучших вариантов: один master спокойно рулит и Linux-, и Windows-серверами с единым набором pillars и targeting. В нашей практике такие связки работают стабильно.

Нужна помощь с настройкой?

Если хотите внедрить SaltStack без лишних экспериментов — специалисты АйТи Фреш помогут с настройкой с нуля. Больше 15 лет в инфраструктурных проектах, обслуживание от 15 000 ₽/мес.

📞 Связаться с нами
#SaltStack настройка#Salt master minion#управление конфигурациями#SaltStack states SLS#SaltStack pillars grains#Salt оркестрация#Salt reactor beacons#автоматизация серверов