salt-sshВ этом посте я хотел бы поделиться своим опытом использования системы управления конфигурациями SaltStack, и, в частности, её применением в Masterless режиме при помощи salt-ssh компонента.


По сути, salt-ssh является аналогом системы Ansible.


salt-ssh '*-ec2.mydomain.com' test.ping

Будут затронуты следующие темы:


  • Почему SaltStack, ключевые особенности
  • Базовые понятия SaltStack
  • Salt-ssh установка и использование

Почему SaltStack, ключевые особенности


Когда я несколько лет назад, вдоволь насытившись puppet (multiple environments, 100+ nodes), для нового проекта выбирал новую же систему управления конфигурациями, то Masterless режим работы был ключевым требованием. Но также хотелось сохранить возможность Master-Slave режима работы. Хотелось хорошей, обширной документации и гибкости. Хотелось уметь управлять облачными инфраструктурами.


А еще хотелось построить систему, в которой будут легко уживаться самые разные окружения. Все это удалось сделать при помощи salt-ssh.


Salt-ssh это компонент SaltStack, который, как и Ansible, использует ssh для соединения с удаленными машинами и не требует никаких предварительных настроек со стороны удаленных машин. Никаких агентов. Pure ssh!


Конечно, при выборе системы я рассматривал и Ansible. Но чаша весов тогда склонилась к SaltStack.


В отличие от Ansible, SaltStack использует Jinja2 как для обработки шаблонов, так и для построения логики.


Причем эту логику можно построить практически любыми способом. Что с одной стороны и хорошо и плохо. Хорошо т.к. даёт гибкость. Но плохо, т.к. нет одного стандартного способа и подхода к реализации. Мне даже кажется, что SaltStack больше представляет из себя конструктор в этом плане.


Также, рендеринг шаблонов и логики происходит на этапе запуска. Полученный пакет шаблонов, настроек и инструкций копируется на удаленный сервер и выполняется. По завершению выполнения salt-ssh выдает в консоль отчет, что было выполнено, а с чем произошли ошибки, если они возникли. Здесь отличие с ansible очень разительно. Последний выполняет задачи\плейбуки поочередно, в режиме shell-скрипта. Не скрою, что наблюдать за ходом выполнения ansible скриптов приятнее, но постепенно все это отходит на второй план, когда число хостов переваливает за несколько десятков. Также, в сравнении с ansible, SaltStack имеет более высокий уровень абстракции.
Как бы то ни было, и ansible и salt-ssh это два очень интересных инструмента, каждый из которых имеет свои плюсы и минусы.


Базовые понятия SaltStack


SaltStack — это система управления конфигурациями и инфраструктурой. Как на уровне отдельных серверов, так и в различных облачных платформах (SaltCloud). Также это система удаленного выполнения команд. Написана на python. Очень бурно развивается. Имеет множество самых разных модулей и возможностей, в том числе даже такие как Salt-Api и Salt-Syndic (master of masters или система, позволяющая строить иерархию мастер серверов, тобишь синдикат).


По умолчанию SaltStack подразумевает Master-Slave режим работы. Обмен сообщениями между нодами происходит через ZeroMQ протокол. Умеет горизонтально масштабироваться при помощи MultiMaster настроек.


Но самое приятное, Salt умеет еще работать и в Agentless режиме. Что может быть реализовано при помощи локального запуска стейтов либо при помощи salt-ssh, героя данного топика.


Salt master — процесс, работающий на машине, с которой происходит управление подключенными агентами. В случае с salt-ssh можно назвать мастером ту ноду, где лежат наши state и pillar данные
Salt minion — процесс, работающий на управляемых машинах, т.е. slave. В случае с salt-ssh minion это любой удаленный сервер
State — декларативное представление состояния системы (аналог playbooks в ansible)
Grains — статическая информация об удаленном minion (RAM,CPUs,OS,etc)
Pillar — переменые для одного или более minions
top.sls — центральные файлы, реализующие логику какие state и pillar данные какому minion назначить
highstate — все определенные state данные для minion
SLS — так называются все конфигурационные файлы для pillar\states в SaltStack, используется YAML


Одним из недостатков системы SaltStack является более высокий порог вхождения. Далее я покажу примеры, чтобы начать работать с этой замечательной системой было проще.


Salt-ssh установка и использование


Установка salt-ssh происходит тривиально.


На сайте https://repo.saltstack.com/ есть все необходимые репозитории и инструкции для подключения их в разнообразных системах.


Для установки нужен только сам salt-ssh.


sudo apt-get install salt-ssh (На примере Deb систем)


Подготовка тестового окружения и Vagrant


Чтобы начать использовать salt-ssh нам достаточно установить его. Как минимум можно управлять своей локальной машиной, либо любым удаленным сервером, что гораздо наглядней.


В данном же примере, для тестов я буду использовать две виртуальные машины, созданные при помощи Vagrant. На одной из них будет установлен собственно сам salt-ssh, другая будет чистая, не считая подключенного публичного ключа с первой машины.


Сам Vagrantfile и необходимые salt states выложены в репозиторий https://github.com/skandyla/saltssh-intro.


Vagrantfile
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.configure(2) do |config|

  # VM with salt-ssh
  config.vm.define :"saltsshbox" do |config|
    config.vm.box = "ubuntu/trusty64"
    config.vm.hostname = "saltsshbox"
    config.vm.network "private_network", ip: "192.168.33.70"
    config.vm.provider "virtualbox" do |vb|
      vb.memory = "512"
      vb.cpus = 2
    end

    config.vm.synced_folder ".", "/srv"

    # Deploy vagrant insecure private key inside the VM
    config.vm.provision "file", source: "~/.vagrant.d/insecure_private_key", destination: "~/.ssh/id_rsa"

    # Install salt-ssh
    config.vm.provision "shell", inline: <<-SHELL
      wget -O - https://repo.saltstack.com/apt/ubuntu/14.04/amd64/latest/SALTSTACK-GPG-KEY.pub | sudo apt-key add -
      sudo echo 'deb http://repo.saltstack.com/apt/ubuntu/14.04/amd64/latest trusty main' > /etc/apt/sources.list.d/saltstack.list
      sudo apt-get update
      sudo apt-get install -y salt-ssh
    SHELL
  end

  # VM for testing
  config.vm.define :"testserver" do |config|
    config.vm.box = "ubuntu/trusty64"
    config.vm.hostname = "testserver"
    config.vm.network "private_network", ip: "192.168.33.75"
    config.vm.provider "virtualbox" do |vb|
      vb.memory = "512"
    end

    # Deploy vagrant public key
    config.vm.provision "shell", inline: <<-SHELL
      curl https://raw.githubusercontent.com/mitchellh/vagrant/master/keys/vagrant.pub >> ~/.ssh/authorized_keys2 2>/dev/null
      curl https://raw.githubusercontent.com/mitchellh/vagrant/master/keys/vagrant.pub >> /home/vagrant/.ssh/authorized_keys2 2>/dev/null
    SHELL
  end

end

Я предполагаю что аудитория знакома с Vagrant, но на всякий случай: Vagrant — это своеобразный фреймворк для систем виртуализации, предназначенный для упрощения процесса разработки и создания его воспроизводимым. Для запуска виртуальных машин нам понадобятся установленными сам Vagrant и Virtualbox.


Дальше клонируем репозиторий:


git clone https://github.com/skandyla/saltssh-intro

и в нем инициализируем Vagrant виртуальные машины:


vagrant up

После запуска последних, заходим в saltsshbox:


vagrant ssh saltsshbox

Вся дальнейшая работа будет вестись из данной виртуальной машины. По умолчанию SaltStack предполагает что мы будет действовать от имени root, поэтому сразу делаем:


vagrant@saltsshbox:~$ sudo -i

Понимание salt roster


Целевые хосты прописываются в файл /etc/salt/roster, однако, можно указать любой сторонний roster file. В некотором смысле можно провести аналогии с inventory файлами ansible. Roster файл, представляет собой YAML, с множеством разных опций. Ниже показаны несколько способов записи одного и того же хоста.


/srv/saltstack/saltetc/roster_test
testserver:
  host: 192.168.33.75
  priv: /home/vagrant/.ssh/id_rsa

thesametestserver:
  host: 192.168.33.75
  user: vagrant
  sudo: True

thesametestserver2:
  host: 192.168.33.75
  user: vagrant
  passwd: vagrant
  sudo: True

Теперь попробуем выполнить команду test.ping применительно ко всем хостам, указанным в нашем ростере.


root@saltsshbox:~# salt-ssh -i --roster-file=/srv/saltstack/saltetc/roster_test '*' test.ping
Permission denied for host thesametestserver, do you want to deploy the salt-ssh key? (password required):
[Y/n] n
thesametestserver:
    ----------
    retcode:
        255
    stderr:
        Permission denied (publickey,password).
    stdout:
testserver:
    True
thesametestserver2:
    True

Как видите, salt-ssh слегка ругнулся, что не может зайти на удаленный сервер и предложил туда развернуть ключ, но я отменил это. Остальные два сервера(по факту один под разными именами) ответили положительно. Произошло это потому что мы работает из-под root, для которого никаких ssh-ключей не определено. Поэтому можно просто добавить ключ через ssh-agent и снова повторить команду.


повтор:
root@saltsshbox:~# eval `ssh-agent`; ssh-add /home/vagrant/.ssh/id_rsa
Agent pid 2846
Identity added: /home/vagrant/.ssh/id_rsa (/home/vagrant/.ssh/id_rsa)
root@saltsshbox:~# salt-ssh -i --roster-file=/srv/saltstack/saltetc/roster_test '*' test.ping
testserver:
    True
thesametestserver:
    True
thesametestserver2:
    True

Теперь все хорошо! Мало того, можно запросто добавить ключ с паролем через ssh-agent. Но если же вы решите деплоить ключ, который предлагает сам salt, то брать его по умолчанию он будет вот по такому пути: /etc/salt/pki/master/ssh/salt-ssh.rsa


Здесь для теста я намеренно работал с отдельным roster файлом, чтобы показать интересные нюансы. Для дальнейшей же работы указывать roster нам будет не нужно, потому что он уже и так через symlink указан в требуемое место(/etc/salt/roster). Ключ -i необходим когда мы начинаем работать с новыми хостами, он просто запрещает StrictHostKeyChecking, давая возможность принять новый host key. Для дальнейшей работы он нам тоже будет не нужен.


root@saltsshbox:~# salt-ssh '*' test.ping
testserver:
    True

Напомню, что по умолчанию salt смотрит на roster здесь: /etc/salt/roster в котором у нас сейчас определен только один хост.


Удаленное выполнение команд


Теперь когда мы убедились что наша машина с salt-ssh прекрасно видит указанный в roster тестовый сервер, поработаем с ним в стиле ad-hoc.


root@saltsshbox:~# salt-ssh testserver cmd.run "uname -a"
testserver:
    Linux testserver 3.13.0-87-generic #133-Ubuntu SMP Tue May 24 18:32:09 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux

cmd.run это по сути аналог ключа -a в ansible.


Также можно использовать встроенные модуля saltstack, например:


salt-ssh testserver service.get_enabled
salt-ssh testserver pkg.install git
salt-ssh testserver network.interfaces
salt-ssh testserver disk.usage
salt-ssh testserver sys.doc 

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


Salt Grains или факты о системе


Grains это мощный механизм, представляющий набор фактов об удаленной системе. В дальнейшем на основе Grains можно также строить различную логику.


Но для начала посмотрим как с ними начать работать:


root@saltsshbox:~# salt-ssh testserver grains.items
testserver:
    ----------
    SSDs:
    biosreleasedate:
        12/01/2006
    biosversion:
        VirtualBox
    cpu_flags:
        - fpu
        - vme
        - de
        - pse
        - tsc
...
    cpu_model:
        Intel(R) Core(TM) i7-2620M CPU @ 2.70GHz
    cpuarch:
        x86_64
    disks:
        - sda
...

Вывод команды урезан.


К нужной ветви Grains можно обратиться, непосредственно указав её:


root@saltsshbox:~# salt-ssh testserver grains.get 'ip4_interfaces'
testserver:
    ----------
    eth0:
        - 10.0.2.15
    eth1:
        - 192.168.33.75
    lo:
        - 127.0.0.1

Либо даже более конкретно:


root@saltsshbox:~# salt-ssh testserver grains.get 'ip4_interfaces:eth1'
testserver:
    - 192.168.33.75

Salt master файл и top.sls


Теперь пришла пора рассказать про еще один важный файл /etc/salt/master. Вообще он идет в комплекте с пакетом salt-master и определяет некоторые важные опции логирования и директории в которых salt будет искать наши states и pillar данные. По умолчанию для states определена директория /srv/salt. Но на практике часто более рационально использовать другую структуру, в том числе и для этих примеров.


/etc/salt/master:


state_verbose: False
state_output: mixed
file_roots:
  base:
    - /srv/saltstack/salt
pillar_roots:
  base:
    - /srv/saltstack/pillar

state_verbose и state_output это переменные, которые отвечают за отображение статуса выполнения на экране. На мой взгляд такая комбинация наиболее практична, но рекомендую поэкспериментировать.


file_roots и pillar_roots указывают пути к нашим state и pillar данным соотвественно.


Важно! этих путей может быть несколько. По принципу разных окружений, разных данных и т.д. и т.п., но это тема для отдельной статьи по настройке multi-environment среды, для начала нам же просто необходимо знать, куда нам положить наши state файлы, чтобы salt их нашел.


Далее, в каждой из этой директорий (file_roots и pillar_roots) сальт будет искать файлы top.sls, которые определяют уже дальнейшую логику обработки salt файлов.


В нашем случае:


/srv/saltstack/salt/top.sls:


base:                                                                                
  '*':                                                                               
    - common
    - timezone

  'testserver':
    - chrony

Что означает, для всех хостов применить state common и timezone, а для testserver применить еще и chrony (сервис синхронизации времени).


Для pillar также необходим top.sls файл. Который будет определять в каком порядке и как будут назначаться переменные.
/srv/saltstack/pillar/top.sls:


base:
  '*':
    - timezone

  'testserver':
    - hosts/testserver

В нашем случае файл этот предельно простой, указано лишь подключить все переменные из файла timezone.sls и еще подключить переменые из файла hosts/testserver для нашего testserver, однако, за этой простотой скрывается мощная концепция, т.к. переменные можно назначить как угодно и на какое угодно окружение. Правда, перекрытие и слияние переменных (Variables Overriding and Merging) это отдельная тема, пока скажу что приоритетность задается сверху вниз. Т.е. если бы у нас здесь в файле hosts/testserver.sls содержались переменные с timezone, то они бы имели преимущество.


В файлах top.sls все указывается без расширения .sls.


Работа с salt states


Приступим к простенькому state:


/srv/saltstack/salt/packages.sls:


# Install some basic packages for Debian systems
{% if grains['os_family'] == 'Debian' %}
basepackages:
  pkg.installed:
    - pkgs:
      - lsof
      - sysstat
      - telnet
{% endif %}

Как видно, здесь мы применили и jinja и grains и собственно сам pkg модуль.


Попробуем применить этот state в тестовом режиме:


root@saltsshbox:/srv/saltstack# salt-ssh testserver state.sls packages test=true
[INFO    ] Fetching file from saltenv 'base', ** done ** 'packages.sls'
testserver:
  Name: basepackages - Function: pkg.installed - Result: Differs

Summary for testserver
------------
Succeeded: 1 (unchanged=1)
Failed:    0
------------
Total states run:     1

И далее уже в реальном:


root@saltsshbox:/srv/saltstack# salt-ssh testserver state.sls packages
[INFO    ] Fetching file from saltenv 'base', ** skipped ** latest already in cache 'salt://packages.sls'
testserver:
  Name: basepackages - Function: pkg.installed - Result: Changed

Summary for testserver
------------
Succeeded: 1 (changed=1)
Failed:    0
------------
Total states run:     1

Salt Pillar или переменные


Следующее важное звено — это Pillar. Так в SaltStack называется переменные, или всё что задается со стороны мастера для удаленных систем. Частично они уже вам знакомы из описанного выше, поэтому сразу к делу.


Получить все pillar переменные, определенные для хоста:


root@saltsshbox:~# salt-ssh testserver pillar.items
testserver:
    ----------
    chrony:
        ----------
        lookup:
            ----------
            custom:
                # some custom addons
                # if you need it
    timezone:
        ----------
        name:
            Europe/Moscow

Также как и с Grains можно запросить отдельно взятую переменную:


salt-ssh testserver pillar.get 'timezone:name'

Использование state вместе с pillar


Рассмотрим следующий state:


/srv/saltstack/salt/timezone.sls:


{%- set timezone = salt['pillar.get']('timezone:name', 'Europe/Dublin') %}
{%- set utc = salt['pillar.get']('timezone:utc', True) %}
timezone_settings:
  timezone.system:
    - name: {{ timezone }}
    - utc: {{ utc }}

Здесь мы задали переменную на основе данных из pillar. Причем в этой конструкции:


{%- set timezone = salt['pillar.get']('timezone:name', 'Europe/Dublin') %}

Europe/Dublin — это значение по умолчанию, если по каким-то причинам salt не сможет получить значение из Pillar.


Выполним:
root@saltsshbox:/srv/saltstack# salt-ssh testserver state.sls timezone
[INFO    ] Fetching file from saltenv 'base', ** skipped ** latest already in cache 'salt://timezone.sls'
testserver:
  Name: Europe/Moscow - Function: timezone.system - Result: Changed

Summary for testserver
------------
Succeeded: 1 (changed=1)
Failed:    0
------------
Total states run:     1

Real Life пример


И вот, наконец-то, мы добрались уже до реального жизненного примера. Рассмотрим state синхронизации времени — chrony. Находится он у нас здесь:


/srv/saltstack/salt/chrony/init.sls


Причем init.sls это индекс по умолчанию, salt ищет его автоматически, но можно использовать и любой другой файл.


Здесь мы введем еще одну типичную конструкцию для salt — это map.jinja.


/srv/saltstack/salt/chrony/map.jinja:


{% set chrony = salt['grains.filter_by']({
  'RedHat': {
    'pkg': 'chrony',
    'conf': '/etc/chrony.conf', 
    'service': 'chronyd',
  },
  'Debian': {
    'pkg': 'chrony',
    'conf': '/etc/chrony/chrony.conf', 
    'service': 'chrony',
  },
}, merge=salt['pillar.get']('chrony:lookup')) %}

Её назначение — это создать необходимый набор статических переменных под нашу систему, но с возможностью слияния с переменными из pillar, если таковые вдруг понадобится указать.


Дальше сам /srv/saltstack/salt/chrony/init.sls:


{% from "chrony/map.jinja" import chrony with context %}

chrony:
  pkg.installed:
    - name: {{ chrony.pkg }}
  service:
    - name: {{ chrony.service }}
    - enable: True
    - running
    - require:
      - pkg: {{ chrony.pkg }}
      - file: {{ chrony.conf }}

{{ chrony.conf }}:
  file.managed:
    - name: {{ chrony.conf }}
    - source: salt://chrony/files/chrony.conf.jinja
    - template: jinja
    - user: root
    - group: root
    - mode: 644
    - watch_in:
      - service: {{ chrony.service }}
    - require:
      - pkg: {{ chrony.pkg }}

Здесь отдельного внимания заслуживает шаблон salt://chrony/files/chrony.conf.jinja формата jinja.


/srv/saltstack/salt/chrony/files/chrony.conf.jinja:


# managed by SaltStack
{%- set config = salt['pillar.get']('chrony:lookup', {}) -%}
{%- set vals = {
  'bindcmdaddress': config.get('bindcmdaddress','127.0.0.1'),
  'custom': config.get('custom', ''),
}%}

### chrony conf
server 0.centos.pool.ntp.org iburst                                              
server 1.centos.pool.ntp.org iburst                                              
server 2.centos.pool.ntp.org iburst                                              
server 3.centos.pool.ntp.org iburst                                              
stratumweight 0                                                                  
driftfile /var/lib/chrony/drift                                                  
rtcsync                                                                          
makestep 10 3                                                                    
bindcmdaddress {{ vals.bindcmdaddress }}
bindcmdaddress ::1                                                               
keyfile /etc/chrony.keys                                                         
commandkey 1                                                                     
generatecommandkey                                                               
noclientlog                                                                      
logchange 0.5                                                                    
logdir /var/log/chrony 

{% if vals.custom -%}                                                                                                                                          
{{ vals.custom }}                                               
{%- endif %}

В этом шаблоне мы также запрашиваем переменные из Pillar и обрабатываем их. Просмотреть как этот state воспринялся salt можно при помощи state.show_sls:


Вывод salt-ssh testserver state.show_sls chrony
root@saltsshbox:/srv/saltstack# salt-ssh testserver state.show_sls chrony
[INFO    ] Fetching file from saltenv 'base', ** done ** 'chrony/init.sls'
[INFO    ] Fetching file from saltenv 'base', ** done ** 'chrony/map.jinja'
testserver:
    ----------
    /etc/chrony/chrony.conf:
        ----------
        __env__:
            base
        __sls__:
            chrony
        file:
            |_
              ----------
              name:
                  /etc/chrony/chrony.conf
            |_
              ----------
              source:
                  salt://chrony/files/chrony.conf.jinja
            |_
              ----------
              template:
                  jinja
            |_
              ----------
              user:
                  root
            |_
              ----------
              group:
                  root
            |_
              ----------
              mode:
                  644
            |_
              ----------
              watch_in:
                  |_
                    ----------
                    service:
                        chrony
            |_
              ----------
              require:
                  |_
                    ----------
                    pkg:
                        chrony
            - managed
            |_
              ----------
              order:
                  10002
    chrony:
        ----------
        __env__:
            base
        __sls__:
            chrony
        pkg:
            |_
              ----------
              name:
                  chrony
            - installed
            |_
              ----------
              order:
                  10001
        service:
            |_
              ----------
              name:
                  chrony
            |_
              ----------
              enable:
                  True
            - running
            |_
              ----------
              require:
                  |_
                    ----------
                    pkg:
                        chrony
                  |_
                    ----------
                    file:
                        /etc/chrony/chrony.conf
            |_
              ----------
              order:
                  10000
            |_
              ----------
              watch:
                  |_
                    ----------
                    file:
                        /etc/chrony/chrony.conf

Дальше уже просто выполним его:


root@saltsshbox:/srv/saltstack# salt-ssh testserver state.sls chrony
testserver:
  Name: chrony - Function: pkg.installed - Result: Changed
  Name: /etc/chrony/chrony.conf - Function: file.managed - Result: Changed
  Name: chrony - Function: service.running - Result: Changed

Summary for testserver
------------
Succeeded: 3 (changed=3)
Failed:    0
------------
Total states run:     3

Здесь salt докладывает о 3х выполненных state по суммарному колличеству задействованных модулей. Если выполнить повторно, то видно что никаких изменений не было произведено:


root@saltsshbox:/srv/saltstack# salt-ssh testserver state.sls chrony
testserver:

Summary for testserver
------------
Succeeded: 3
Failed:    0
------------
Total states run:     3

Сразу же можно и посмотреть как сформировался конфигурационный файл для chrony:


salt-ssh testserver cmd.run 'cat /etc/chrony/chrony.conf'

Финально стоит упомянуть еще команду state.highstate.


salt-ssh testserver state.highstate

Она применяет все прописанные state для нашего тестового сервера.


Заключение


Итак, мы узнали чтоже из себя представляет salt-ssh из комплекта SaltStack и как его использовать. Узнали ключевые особенности построения окружения, необходимого для работы salt-ssh. Настроили тестовую среду при помощи Vagrant. И планомерно провели эксперименты с фундаментальными концепциями SaltStack, такими как: Grains, States, Pillar. Также мы узнали как писать state от простого к сложному, дойдя до реальных примеров, позволяющих на своей базе уже строить дальнейшую автоматизацию.


На этом пока все. За бортом осталось еще множество интересных тем, но я надеюсь что эта информация поможет начать работать с этой замечательной системой управления конфигурациями.


Полезная информация:


best_practices
walkthrough
starting_states
pillar
formulas
tutorials

Поделиться с друзьями
-->

Комментарии (10)


  1. methodx
    16.06.2016 12:41

    Хороший пост. В работе на production используется Сhef (давний выбор, пока на нем и остаемся). Для личных целей рассматривал Ansible и Salt-ssh, остановился на первом варианте.
    Интересно почитать мнения остальных, использует ли кто-нибудь salt-ssh или salt в masterless-mode на production. Будет круто, если расскажете про проблемы, с которыми сталкиваетесь.


  1. nightvich
    16.06.2016 15:26

    Давно была мысль ознакомиться с Salt. Спасибо за статью!
    Использую Ansible, как в личных целях, так и в production. Если есть опыт в других системах контроля конфигураций, будет интересно увидить сравнение сильных и слабых сторон.
    P.S. На данный момент Ansible покрывает все мои задачи.


    1. skandyla
      16.06.2016 17:54

      Здесь довольно тяжело сделать обьективное сравнение. Т.к. каждый инструмент предназначен для решения своих задачь и сравнение "в лоб" неизбежно обречено на провал. Также, тема весьма обширная, и для обьективного сравнения очень неплохо бы одинаково хорошо знать все используемые инструменты, что в жизни почти недостижимо.
      Считаю, что в первую очередь нужно определиться с целями, что и для чего мы делаем, а дальше выбирать инструменты, наиболее эффективно отвечающие достижению поставленных целей.


      Если рассматривать configuration management с методом push over ssh, то здесь на текущий момент дефакто два лидера — Ansible и Saltstack.
      Причем первенство с большим отрывом держит Ansible. У него вся документация, примеры и сценарии применения заточены под Push метод.


      Фундаментальное отличие на мой взгляд одно. SaltStack использует Jinja и для шаблонов и для логики стейтов. Это значит что Jinja выполняется перед самим state. (Saltstack изначально ориентирован на Master-Slave режим работы, поэтому такое поведение рационально).
      Эта особенность является одновременно и сильной и слабой стороной SaltStack.
      Сильной, т.к. позволяет строить продвинутую логику.
      Например, в цикле обработать несколько state и на их основе сгенерировать данные для другого state.
      Допустим, у каждой роли есть свои настройки firewall. Jinja при запуске видит все эти роли и собирает итоговые переменные для роли фаервола. В результате получается довольно красиво и элегантно. Но, плата за сиё — избыточная сложность и, зачастую, меньшая прозрачность.
      В ansible концептуально другой подход. "Только одни переменные, только в одном месте" ( variable-precedence-where-should-i-put-a-variable ).
      Есть, конечно, хаки в духе hash_behaviour=merge, но они не рекомендуются к использованию.
      С ansible проще начать. Больше сообщество и, как следствие, больше написанных ролей, которые можно повторно использовать.
      Он лучше подходит для небольших энвайроментов. Мне очень нравится последовательное выполнение таска в сочетании с register.
      Благодаря этому довольно просто реализуются развертывания в различных Cloud providers а также выкатавания апдейтов ПО: guide_rolling_upgrade.
      Да и вообще это как старый добрый shell на стероидах.


      Субьективно, нишевая область применения SaltStack это, всё таки, Master-Slave режим работы. А Salt-ssh — просто удобное дополнение к последнему, которое можно использовать и само по себе. Возможность же быстро переключаться между режимами работы — это тоже, однозначно, сильная сторона SaltStack.


      Интересная, хоть и слегка устаревшая статья на эту тему: Moving away from Puppet: SaltStack or Ansible?


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


  1. SirEdvin
    16.06.2016 18:28

    В отличие от Ansible, SaltStack использует Jinja2 как для обработки шаблонов, так и для построения логики.


    Стоп-стоп.
    Ansible в yml разметках тоже использует Jinja2. Я пишу все роли и шаблоны через него и работает же.

    Он сначала собирает инфу о системе, потом генерит роли (или таски) и выполняет их. Или тут говорилось об этом в другом ключе?


    1. skandyla
      16.06.2016 22:37

      Ansible в плейбуках использует свой собственный DSL (with\when\register\etc), а Jinja2 используется уже для templates.
      Также, обработка циклов и условий осуществляется на уровне индивидуальных тасков.
      Не сочтите за рекламу salt (в нем полно своих косяков), но эти различия довольно хорошо показаны по уже упомянутой ссылке.


      SaltStack использует Jinja2 и для логики.
      Причем, при желании там такого можно наворотить, что грустно становится....


  1. kt97679
    16.06.2016 19:36

    Я использую salt достаточно давно на большом парке серверов (сейчас больше 6000 машин). Сперва использовался режим master-minion. Работало очень не надежно, подвисали и мастера и миньоны. Я перевел все на salt-ssh, стало работать лучше. Тем не менее у меня к salt много претензий:

    1. У них явно есть проблемы с тестированием и сохранением обратной совместимости. Каждое обновление версии это боль. Сейчас мы используем 2015.8.7.
    2. У нас salt в частности используется для конфигурирования pam. Недавно столкнулись с ситуацией, когда некоторые конфигурационные файлы pam на отдельных машинах оказались пустыми. Залогиниться невозможно, крон не работает. Повторяемо воспроизвести не получается. Начал диалог с ребятами из saltstack-а, но они ожидаемо помочь не смогли (потому что не смогли воспроизвести). Мое доверие к salt сильно подорвано.
    3. Для работы salt на клиенте должен стоять python, у которого есть свои проблемы. В результате пришлось собирать python-2.7.10, ставить его в /opt вне всех путей, чтобы никому не мешал и хачить salt, чтобы использовал этот python.
    4. Общее мнение людей, которые используют у нас salt — не удобно.

    Все свои проекты и деплою через пакеты. Это позволяет и откатить быстро если что не так (в salt откат мягко говоря не прост, версионирования стейтов нет, chef в этом смысле много удобнее), и сделать dpkg-divert чтобы обновление другого пакета не испортило кастомный конфиг.

    Когда будет время буду смотреть на ansible.


    1. SirEdvin
      16.06.2016 22:11

      Немного могу сказать про ansible

      3. Для его работы в большинстве случаев должен тоже стоять python. Без него работает только несколько команд, которые можно использовать для его установки.

      Так же, можно попробовать pypy. Его не обязательно собирать, оно почти везде из под коробки работает.

      Пример роли для ansible для установки pypy можно найти тут.


    1. kt97679
      16.06.2016 22:20

      Еще к недостаткам salt-ssh можно отнести сложность отладки. Для того, чтобы понять, что пошло не так на клиенте надо прогнать salt-ssh с высоким уровнем логирования, вычленить команду для запуска salt на клиенте, залогиниться на клиента, выполнить команду предварительно отредактировав ее и добавив логирования.


      1. skandyla
        16.06.2016 22:55

        У нас на порядок меньше хостов, но я с вами солидарен. Есть впечатление, что ребята из saltstack занимаются созданием фич, при этом обратная совместимость прихрамывает. Баги касательно salt-ssh у них также в очень низком приоритете.
        Трудно сказать как дела обстоят с ansible, но есть предположение что получше. т.к. последний изначально проектировался под push метод работы и находится под крылом RedHat.
        С python у меня лично не было проблем, кроме одного случая с багом в file.rescue, когда требовался python-msgpack. Но это довольно быстро починили.


    1. methodx
      17.06.2016 15:09

      Насчет chef — здесь тоже не все хорошо. У нас он используется в виде solo, запускается по получению события после сборки пакета с приложением. То есть имитируется push (через serf). Беда в том, что исторически было принято использовать только один run_list для ноды — один конечный state, который настраивается путем переписи атрибутов в нескольких ролях. Все издержки атомарного деплоя приходится учитывать в конечном cookbook. Кстати в плане отката версии пакета мы сталкиваемся с той же проблемой что и вы. Я это обходил так: в data_bag указываю конкретную версию комита (в описании пакета хранится commit), chef поставит этот пакет и создаст правильные symlink'и. Ту же логику я мог бы реализовать на Ansible намного быстрее.

      В конечном итоге поддерживать это становится также неудобно, и я все больше оглядываюсь в сторону Ansible. Того же мнения и некоторые коллеги.