Как стать автором
Обновить
4
@identwread⁠-⁠only

Пользователь

Отправить сообщение
Или не использовать purge как вы делаете это в паппете? Впрочем это уже все усложнение и придумывание новых условий.

Так изначальный тред про purge и декларативность. О каких новых условиях идет речь? Напомню о постановке задачи edo1h:


я не могу, например, сказать «на системе должны быть user1, user2 и всё», я могу сказать «добавь пользователя user1» и «удали пользователя user4». если в системе был кем-то заведён user5, то ansible никак об этом не узнает.
и так во всём — от списка репозиториев в sources.list до конфигов нжинкса в conf.d.
я не воспринимаю конфиги ансибла как декларативные, скорее они императивные с идемпотентностью.

Поэтому ваше высказывание про придумывание новых условий — неправда.


Зачем вам переписывать все модули? Не нужно использовать только один таск, зачем вы придумываете?

Тогда лучше объясните что вы предлагаете для реализации задачи, а лучше с примером — я вас к сожалению не понял.


Нет, никакие из них не архитектурные

Архитектурные различия между puppet и ansible значительные. ansible выполняет список tasks по порядку, puppet совсем действует по другому, я уже не однократно описывал как. Именно поэтому в ansible хуже с декларативностью и есть большие проблемы с нормальной реализацией purge.


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

Нет, это неправда к сожалению.

Нет, будущее ансибл не предсказывает и это не нужно.

Текущее выполнение task'ов, которых не было в предыдущем запуске, это не будущее, а настоящее.


Использование purge одним таском, а не модуля.

Как при выполнении task purge, узнать о всех task'ах которые применятся к хосту, чтобы удалить только то, что не описано в плейбуках?


Нет, я такого не предлагал. Для чего вы это делаете? Какая ваша конечная цель?

Тогда я не понял вас, каким образом вы предлагаете обеспечить декларативность? Конечная цель сделать описание декларативным, и сделать так, чтобы все что не подконтрольно ansible удалялось для указанного типа модуля

Не правы. Есть контекст плейбука и данных с прошлых запусков если они кешируются.

Контекст текущего плейбука, а других плейбуков, которые тоже будут применены к этому хосту? Прошлые запуски не помогут узнать, что у нас появились новые task'и для модуля в другом playbook'e. Хочется посмотреть примеры на ansible, которые это используют для того чтобы удалять с хоста ресурсы, которые не под управлением ansible.


я, честно говоря, не вижу от чего тут может болеть голова

Ну это довольно не тривиальная вещь на мой взгляд. И требует большой работы. Например выше вы предложили ограничить использование модуля одним task'ом — что накладывает очень много ограничений на удобство использования такого модуля.


хочешь пиши модуль чтобы был декларативным

Не совсем понял что значит декларативность модуля. Так как модули/провайдеры выполняют как раз императивные действия, как в ansible так и в puppet. От модулей ansible/провайдеров puppet мы требуем идемпотентности как правило.


Ну вот конструкция:


tasks:
  - file:
      path: /tmp/test
      state: absent
  - file:
      path: /tmp/test
      state: directory

Не декларативна, поскольку тут один и тот же объект имеет два разных состояния. Как я понял вы предлагаете переписать модуль file на свой custom_file и делать что-то вроде:


tasks:
  - custom_file:
      - path: /tmp/test
        state: absent
      - path: /tmp/test
        state: directory
      - path: /etc/nginx
        state: directory

И уже в модуле custom_file проверять path и таким образом гарантировать декларативность и выдавать ошибку при одинаковых path. Но на мой вкус это сделает ansible просто не юзабельным, придется вытаскивать все task с использованием модуля file из всех ролей, и описать это все отдельно одним task'ом — что ужасно и не юзабельно плюс сильно ухудшит возможность переиспользования.


В придачу с другими фишками ансибла у тебя свобода

Аналогичная свобода есть в любой другой реализации SCM.

но добиться этого вполне возможно при желании

Переписать все модули ansible и использовать для таких модулей всего один task — это нерабочее решение, и его невозможно использовать адекватно. И по юзабельности и функционалу оно все равно уступает тому, что есть в puppet из коробки.


Нет принципиальных архитектурных блокеров.

Есть. Я уже выше достаточно подробно рассказал какие.


Это будет точной копией юзер модуля паппета и таким же юзабельным как он.

Нет не будет. В puppet я могу использовать модуль user в куче классов одновременно в любых вариациях. В ansible я могу такое делать со стандартным модулем user тоже, но с ограничением о котором мы говорим, а в предложенной вами реализации модуля я смогу использовать только один раз task в одном месте. Это далеко не тоже самое, что в puppet.


Зачем вам вызывать модуль, создающих юзеров, несколько раз с разными параметерами?

Чтобы создать разных пользователей. Я могу в puppet иметь разные классы, один для настройки ssh сервер который создает пользователя, другой для mysql, который тоже создает пользователя, третий для nginx с созданием пользователя. И их переиспользовать как угодно. Тоже самое я могу делать и со стандартным модулем user в ansible, правда с ограничением о котором мы как раз говорим. А предложенный вами способ заставит вынести создание пользователей из всех этих ролей в отдельный task, и каждый раз когда я хочу переиспользовать одну из таких ролей мне нужно будет добавлять такой task для модуля user отдельно и согласовывать его с другими ролями.
А если учесть что мы перепишем все модули, как вы предлагаете, то таких единичных task'ов и параметров к ним будет настолько много, что любое изменение в ролях или удаление/добавление ролей будет адово сложным.

Насколько я знаком с ansible. Он для выполнения task'а, копирует код модуля на хост и выполняет его, и так с каждым task такого модуля. Область видимости модуля один task и его свойства. Поправьте если я не прав.


Но опять же это детали. В puppet, при разработке модуля, тебе вообще не надо думать об области видимости модуля, и решать связанные с этим задачи для реализации удаления ресурсов. puppet это делает сам, сравнивая список всех ресурсов(task в терминах ansible) описанных на мастере для этого хоста, с тем что есть в этом хосте. Поэтому у тебя не болит голова о том, как реализовать purge для ресурсов, которые не описаны — оно само.

Нет, не получим никакой проблемы. С чего вдруг?

Я возможно не понял о чём идет речь. Но вы же сами писали что нужно написать свой модуль + использовать только один task с ним:


В модуле я просто сравниваю имеющихся юзеров и юзеров определенных в таске, а там уже по логике.

Какой модуль ansible имеется в виду? Можете привести пример решения задачи: "нужно чтобы на хосте были пользователи user1, user2, при этом любые другие не системные пользователи (то есть с uid > 1000), которые не объявлены в task'ах модуля user удалялись."


Да, потому что его уже написали.

Я еще раз повторю — это не часть логики конкретного модуля puppet. Никто не пишет эту логику в модулях puppet, она заложена в агенте. У него такой принцип работы, реализовывать это разработчику модуля не требуется. Вы почему-то каждый раз упускаете это из виду. У puppet архитектура другая совсем. Никто его "уже не написал". Эту логику написал разработчики puppet, а не разработчики его модулей.


Итого вы говорите принципиальных отличий нет. Но для реализации
аналогичного в ansible предлагаете:


  • Написать свой кастомный модуль
  • Использовать task такого модуля только один раз, так как если его вызывать несколько раз с разными параметрами — это выстрел по ногам. Да и в целом такой модуль не юзабелен, и вызовет больше проблем чем пользы.

В puppet это заложено архитектурно. Для этого не надо реализовывать специальную логику в его модулях и писать кастомные модули для такой логики. Нет ограничений на один ресурс(task в ansible). Можно объявлять ресурсы модуля вообще в разных местах, в puppet агент они прилетят все вместе в одном json. Вменяемо, насколько я понимаю, в ansible это не реализовать, в виду другой архитектуры.


Это и есть принципиальные отличия.

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


А иначе мы просто будем сравнивать только текущий task с тем что есть на хосте, и так каждый такой task. Что и происходит в ansible.

факты это другой механизм, он с данным вопросом не связан. Речь о механизме работы именно с ресурсами в терминах puppet и task'ами в терминах ansible.


Стандартный модуль user в ansible делает что-то с одним пользователем в одном task'e насколько я с ним знаком:


- name: 'user johnd'
  user:
    name: johnd
    comment: John Doe
    uid: 1040
    group: admin

Наверняка можно написать модуль, которому передается массив пользователей со свойствами и пусть он будет удалять всех пользователей, что нет в этом списке. Но таким модулем невозможно нормально пользоваться. Так как сразу получим проблему при использовании нескольких task в разных плеях и плейбуках, которые используют такой модуль или же если мы тащим к себе какой-то плейбук в котором используется стандартный модуль user. Наш модуль будет просто напросто удалять всех других пользователей, которые определены стандартным модулем user или же они определены в других task'ах нашим же модулем.


В puppet же, без разницы где и сколько раз ты объявил ресурс user и в каких классах или где-то еще. Всё это в итоге вместе со всеми другими ресурсами попадет на агент в виде json, и агент всё сделает. Для этого не требуется писать какой-то свой специальный модуль user, со своей специальной логикой.

Мне кажется нет смысла. Сейчас потихоньку от этих инструментов отказываются или сильно минимизируют их использование в пользу иммутбл инфры. А всякие сложные вещи реализуют на языках программирования. Например операторы для k8s пишут на golang, также установщики самого k8s пишут часто на golang (rke,pke).
Всё меньше и меньше мест, где применяют ansible/solt/puppet/chef. Поэтому начинать переходить на какой-то из них из другого в этом списке, наверное нет смысла.

Ну вот terraform похож не много на puppet и страдает как мне кажется. Появились неплохие конкуренты: pulumi и AWS Cloud Development Kit.


А порой хочется какую-то задачу просто решить через ansible чем решать ее сложно terraform'ом. Вот например ребята из skyeng так и сделали для задачи управления github'ом: https://habr.com/ru/company/skyeng/blog/516192/

В плане реализации модуля, а точнее провайдера в нем, тоже достаточно принципиальное. В puppet ты реализуешь несколько методов создать/изменить/удалить ресурс + получить список всех ресурсов. В ansible тоже нечто похожее, но принципиально отличаются архитектура и механика как это работает.


puppet получает список описанных ресурсов с мастера и получает список ресурсов хоста. Сравнивает, видит разницу, и выполняет методы провайдера модуля, чтобы привести ресурсы на хосте к тому виду, как они описаны на мастере. В твоем модуле эту механику реализовывать не требуется, такое поведение тебе гарантирует сам puppet. То есть в описанных мной примерах, если указано:


  resources { 'Clickhouse_database':
        purge => true,
    }

То puppet просто применит метод "удалить ресурс" из твоего провайдера модуля, ко всем базам clickhouse что он видит на хосте, кроме тех что описаны в puppet коде. Тебе со стороны провайдера модуля не надо реализовывать само это сравнение ресурсов, puppet это делает сам. В провайдере модуля только методы изменить аттрибут/аттрибуты ресурса/удалить ресурс/создать ресурс/получить все ресурсы и всё.


В случае с ansible такой механики насколько я знаю нет. Для выполнения task'а он копирует код модуля на хост, и запускает его. Вроде область его видимости текущий task и его свойства, этот код ничего не знает о других task'ах описанных в текущем play или других task'ов которые описаны в play других playbook'ов. Также всю эту механику: сравнить список текущих ресурсов и их свойств на хосте, со всеми tasks и их свойствами во всех playbook'ах, что будут применены к хосту, надо будет делать самому, причем в пределах выполнения каждого task такого типа, что будет очень затратно в плане скорости выполнения как минимум. Я не знаю возможно ли в ansible в пределах выполнения task модулем достать список всех тасков, которые также будут применены к хосту для сравнения со списком ресурсов которые уже есть на хосте, чтобы не удалить лишние ресурсы, но это придется делать самому + это затратная операция, чем больше таких task'ов, тем больше код будет делать одно и тоже для каждого такого типа task.


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


Ну и разница в декларативности также остается. Puppet более декларативен, в нем ты описываешь состояние, а task'и на как в ansible.

stage и anchor использовались для этого да. Обычно это говорит о том, что надо рефакторить код из-за большого количества связей ресурсов между разными классами, или наоборот, некоторые ресурсы в текущем классе выделять в отдельный.
Рекомендуется выставлять связи между классами, а не ресурсами которые зависят от ресурсов другого класса.


Типо такого:


  Class['proxysql::prerequisites']
  -> Class['proxysql::repo']
  -> Class['proxysql::install']
  -> Class['proxysql::config']
  -> Class['proxysql::service']
  -> Class['proxysql::admin_credentials']
  -> Class['proxysql::reload_config']
  -> Class['proxysql::configure']

  Class['proxysql::install']
  ~> Class['proxysql::service']

С puppet проблема — он очень сложный. Думаю из-за этого не популярен.

Потому что вы не сказали ему это сделать.


Файлы довольно уникальный тип ресурса для puppet. Так как файлов в системе очень много, и не очень эффективно их загонять все в ресурсы и знать обо всех, поэтому puppet с ними работает не много иначе. Но тем не менее, задача с source.list решается так:


Если используется /etc/apt/sources.list.d:


file {'/etc/apt/sources.list.d':
  ensure  => directory,
  recurse => true,
  purge   => true,
}

Если используется модуль apt. То нужно классу apt об этом сказать:


class {'apt':
    purge => {
        'sources.list'   => true,
        'sources.list.d' => true,
        'preferences'    => true,
        'preferences.d'  => true,
        'apt.conf.d'     => true,
    }
}

На самом деле он внутри это делает также передавая свойство purge в ресурсе файл. С конфигами nginx решается аналогично.

Отличие принципиальное. puppet знает о всех ресурсах, независимо от того описаны они в коде или нет, ansible знает только что описано в коде, а точнее знает только о тасках которые требуется выполнить.


purge => true — это не функционал модуля user. Таким же образом можно сделать например с пользователями mysql:


    resources {'Mysql_user':
         purge => true,
    }

Или базами clickhouse


    resources { 'Clickhouse_database':
        purge => true,
    }

Тип ресурса и в каком модуле он реализован — не играет роли.
Это архитектура инструмента. Когда пишешь модуль, ты обязан реализовать некоторые методы, в том числе и метод — "достать все ресурсы для этого модуля". puppet получает с мастера все ресурсы, потом смотрит какие есть ресурсы на хосте, смотрит разницу и старается привести ресурсы на хосте к описанным на мастере — если огрублять.


Но это выглядит явно не как достоинство puppet.

Это честная декларативность. Один и тот же ресурс не может иметь два состояния одновременно. Если же может, то это не декларативность. Плохо это или хорошо — зависит от ситуации. Как по мне, именно для управления конфигурациями — это хорошо, а для всяких ad-hoc задач — неудобно, тут лучше взять более императивные инструменты, например ansible.

это не решает проблему гоночек, в случае, если этим ресурсом пытается управлять какая-то еще другая система. Пример — iptables правила в случае использования docker/любого vpn. Удачи это описать бесконфликтно и чтоб надежно работало.

Никакой проблемы нет, я это делаю через puppet. Поскольку puppet знает о всех правилах iptables, ты можешь игнорировать правила docker/vpn/k8s. В результате он накатит правила которые ты описал, не тронет правила docker/vpn/k8s и удалит правила которые кто-то добавил вручную.


еще проблема, что нужно не только состояние ("куда мы идем?"), а порядок действий ("как мы туда придем?"). И в том же паппете это делается, но через боль (например, нам обязательно нужно установить пакет А до пакета Б — вот потому что это так работает, есть альтернативы — например, пересобрать пакеты самому и всю логику реализовата в post-inst, но я бы не сказал, что они радикально лучше)

Да, это так. Поскольку puppet декларативен, то нужно стараться описать конечное состояние. А вот если ты пытаешься им делать "как мы туда придем?" то будет беда скорее всего. Поэтому это делается только если ну очень надо. Но это про более сложные истории чем установить пакет А до пакета Б, поскольку зависимости как раз он умеет без проблем:


package {'p1':}
package {'p2':
  require => Package['p1']
}

"как мы туда придем?" — а для такого обычно и используют ansible, а в экосистеме puppet обычно для такого используют bolt, но можно и ansible.

Но это беда любого SCM

Я бы так не сказал. Тот же пресловутый puppet знает о всех пользователях и других ресурсах в системе, даже если они не описаны. Это заложено в архитектуре провайдеров модулей, которые управляют этими ресурсами. Там обязательно реализуется метод — "получить все ресурсы данного типа".


Например ресурс user:


Скрытый текст
$ puppet resource user
user { '_apt':
  ensure             => 'present',
  gid                => 65534,
  home               => '/nonexistent',
  password           => '*',
  password_max_age   => 99999,
  password_min_age   => 0,
  password_warn_days => 7,
  shell              => '/usr/sbin/nologin',
  uid                => 104,
}
user { 'bin':
  ensure             => 'present',
  comment            => 'bin',
  gid                => 2,
  home               => '/bin',
  password           => '*',
  password_max_age   => 99999,
  password_min_age   => 0,
  password_warn_days => 7,
  shell              => '/usr/sbin/nologin',
  uid                => 2,
}
user { 'daemon':
  ensure             => 'present',
  comment            => 'daemon',
  gid                => 1,
  home               => '/usr/sbin',
  password           => '*',
  password_max_age   => 99999,
  password_min_age   => 0,
  password_warn_days => 7,
  shell              => '/usr/sbin/nologin',
  uid                => 1,
}
user { 'dnsmasq':
  ensure             => 'present',
  comment            => 'dnsmasq,,,',
  gid                => 65534,
  home               => '/var/lib/misc',
  password           => '*',
  password_max_age   => 99999,
  password_min_age   => 0,
  password_warn_days => 7,
  shell              => '/usr/sbin/nologin',
  uid                => 107,
}
user { 'games':
  ensure             => 'present',
  comment            => 'games',
  gid                => 60,
  home               => '/usr/games',
  password           => '*',
  password_max_age   => 99999,
  password_min_age   => 0,
  password_warn_days => 7,
  shell              => '/usr/sbin/nologin',
  uid                => 5,
}
user { 'gnats':
  ensure             => 'present',
  comment            => 'Gnats Bug-Reporting System (admin)',
  gid                => 41,
  home               => '/var/lib/gnats',
  password           => '*',
  password_max_age   => 99999,
  password_min_age   => 0,
  password_warn_days => 7,
  shell              => '/usr/sbin/nologin',
  uid                => 41,
}
user { 'irc':
  ensure             => 'present',
  comment            => 'ircd',
  gid                => 39,
  home               => '/var/run/ircd',
  password           => '*',
  password_max_age   => 99999,
  password_min_age   => 0,
  password_warn_days => 7,
  shell              => '/usr/sbin/nologin',
  uid                => 39,
}
user { 'landscape':
  ensure             => 'present',
  gid                => 112,
  home               => '/var/lib/landscape',
  password           => '*',
  password_max_age   => 99999,
  password_min_age   => 0,
  password_warn_days => 7,
  shell              => '/usr/sbin/nologin',
  uid                => 108,
}
user { 'list':
  ensure             => 'present',
  comment            => 'Mailing List Manager',
  gid                => 38,
  home               => '/var/list',
  password           => '*',
  password_max_age   => 99999,
  password_min_age   => 0,
  password_warn_days => 7,
  shell              => '/usr/sbin/nologin',
  uid                => 38,
}
user { 'lp':
  ensure             => 'present',
  comment            => 'lp',
  gid                => 7,
  home               => '/var/spool/lpd',
  password           => '*',
  password_max_age   => 99999,
  password_min_age   => 0,
  password_warn_days => 7,
  shell              => '/usr/sbin/nologin',
  uid                => 7,
}
user { 'lxd':
  ensure             => 'present',
  gid                => 65534,
  home               => '/var/lib/lxd/',
  password           => '*',
  password_max_age   => 99999,
  password_min_age   => 0,
  password_warn_days => 7,
  shell              => '/bin/false',
  uid                => 105,
}
user { 'mail':
  ensure             => 'present',
  comment            => 'mail',
  gid                => 8,
  home               => '/var/mail',
  password           => '*',
  password_max_age   => 99999,
  password_min_age   => 0,
  password_warn_days => 7,
  shell              => '/usr/sbin/nologin',
  uid                => 8,
}
user { 'man':
  ensure             => 'present',
  comment            => 'man',
  gid                => 12,
  home               => '/var/cache/man',
  password           => '*',
  password_max_age   => 99999,
  password_min_age   => 0,
  password_warn_days => 7,
  shell              => '/usr/sbin/nologin',
  uid                => 6,
}
user { 'messagebus':
  ensure             => 'present',
  gid                => 107,
  home               => '/nonexistent',
  password           => '*',
  password_max_age   => 99999,
  password_min_age   => 0,
  password_warn_days => 7,
  shell              => '/usr/sbin/nologin',
  uid                => 103,
}
user { 'news':
  ensure             => 'present',
  comment            => 'news',
  gid                => 9,
  home               => '/var/spool/news',
  password           => '*',
  password_max_age   => 99999,
  password_min_age   => 0,
  password_warn_days => 7,
  shell              => '/usr/sbin/nologin',
  uid                => 9,
}
user { 'nginx':
  ensure             => 'present',
  comment            => 'nginx user,,,',
  gid                => 119,
  home               => '/nonexistent',
  password           => '!',
  password_max_age   => 99999,
  password_min_age   => 0,
  password_warn_days => 7,
  shell              => '/bin/false',
  uid                => 115,
}
user { 'nobody':
  ensure             => 'present',
  comment            => 'nobody',
  gid                => 65534,
  home               => '/nonexistent',
  password           => '*',
  password_max_age   => 99999,
  password_min_age   => 0,
  password_warn_days => 7,
  shell              => '/usr/sbin/nologin',
  uid                => 65534,
}
user { 'ntp':
  ensure             => 'present',
  gid                => 114,
  home               => '/nonexistent',
  password           => '*',
  password_max_age   => 99999,
  password_min_age   => 0,
  password_warn_days => 7,
  shell              => '/usr/sbin/nologin',
  uid                => 111,
}
user { 'pollinate':
  ensure             => 'present',
  gid                => 1,
  home               => '/var/cache/pollinate',
  password           => '*',
  password_max_age   => 99999,
  password_min_age   => 0,
  password_warn_days => 7,
  shell              => '/bin/false',
  uid                => 110,
}
user { 'postfix':
  ensure             => 'present',
  gid                => 117,
  home               => '/var/spool/postfix',
  password           => '*',
  password_max_age   => 99999,
  password_min_age   => 0,
  password_warn_days => 7,
  shell              => '/usr/sbin/nologin',
  uid                => 113,
}
user { 'proxy':
  ensure             => 'present',
  comment            => 'proxy',
  gid                => 13,
  home               => '/bin',
  password           => '*',
  password_max_age   => 99999,
  password_min_age   => 0,
  password_warn_days => 7,
  shell              => '/usr/sbin/nologin',
  uid                => 13,
}
user { 'root':
  ensure             => 'present',
  comment            => 'root',
  gid                => 0,
  groups             => ['ssh-allow-login'],
  home               => '/root',
  password           => '!',
  password_max_age   => 99999,
  password_min_age   => 0,
  password_warn_days => 7,
  shell              => '/bin/bash',
  uid                => 0,
}
user { 'sshd':
  ensure             => 'present',
  gid                => 65534,
  home               => '/run/sshd',
  password           => '*',
  password_max_age   => 99999,
  password_min_age   => 0,
  password_warn_days => 7,
  shell              => '/usr/sbin/nologin',
  uid                => 109,
}
user { 'statd':
  ensure             => 'present',
  gid                => 65534,
  home               => '/var/lib/nfs',
  password           => '*',
  password_max_age   => 99999,
  password_min_age   => 0,
  password_warn_days => 7,
  shell              => '/usr/sbin/nologin',
  uid                => 114,
}
user { 'sync':
  ensure             => 'present',
  comment            => 'sync',
  gid                => 65534,
  home               => '/bin',
  password           => '*',
  password_max_age   => 99999,
  password_min_age   => 0,
  password_warn_days => 7,
  shell              => '/bin/sync',
  uid                => 4,
}
user { 'sys':
  ensure             => 'present',
  comment            => 'sys',
  gid                => 3,
  home               => '/dev',
  password           => '*',
  password_max_age   => 99999,
  password_min_age   => 0,
  password_warn_days => 7,
  shell              => '/usr/sbin/nologin',
  uid                => 3,
}
user { 'syslog':
  ensure             => 'present',
  gid                => 106,
  groups             => ['adm', 'projects'],
  home               => '/home/syslog',
  password           => '*',
  password_max_age   => 99999,
  password_min_age   => 0,
  password_warn_days => 7,
  shell              => '/bin/false',
  uid                => 102,
}
user { 'systemd-network':
  ensure             => 'present',
  comment            => 'systemd Network Management,,,',
  gid                => 102,
  home               => '/run/systemd/netif',
  password           => '*',
  password_max_age   => 99999,
  password_min_age   => 0,
  password_warn_days => 7,
  shell              => '/usr/sbin/nologin',
  uid                => 100,
}
user { 'systemd-resolve':
  ensure             => 'present',
  comment            => 'systemd Resolver,,,',
  gid                => 103,
  home               => '/run/systemd/resolve',
  password           => '*',
  password_max_age   => 99999,
  password_min_age   => 0,
  password_warn_days => 7,
  shell              => '/usr/sbin/nologin',
  uid                => 101,
}
user { 'uucp':
  ensure             => 'present',
  comment            => 'uucp',
  gid                => 10,
  home               => '/var/spool/uucp',
  password           => '*',
  password_max_age   => 99999,
  password_min_age   => 0,
  password_warn_days => 7,
  shell              => '/usr/sbin/nologin',
  uid                => 10,
}
user { 'uuidd':
  ensure             => 'present',
  gid                => 110,
  home               => '/run/uuidd',
  password           => '*',
  password_max_age   => 99999,
  password_min_age   => 0,
  password_warn_days => 7,
  shell              => '/usr/sbin/nologin',
  uid                => 106,
}
user { 'www-data':
  ensure             => 'present',
  comment            => 'www-data',
  gid                => 33,
  home               => '/var/www',
  password           => '*',
  password_max_age   => 99999,
  password_min_age   => 0,
  password_warn_days => 7,
  shell              => '/usr/sbin/nologin',
  uid                => 33,
}

И получается задача:


я не могу, например, сказать «на системе должны быть user1, user2 и всё», я могу сказать «добавь пользователя user1» и «удали пользователя user4». если в системе был кем-то заведён user5, то ansible никак об этом не узнает.

Решается так:


  user {'user1':
      ensure => present,
  }
  user {'user2':
      ensure => present,
  }
  resources {'user':
      purge => true,
      unless_system_user => true, # Чтобы не удалить системных пользователей
  }

То есть в puppet очень просто можно сказать — удали все ресурсы какого-то типа, которые не описаны, поскольку он знает о всех ресурсах которыми управляет в системе. Конечно с пакетами в системе я бы так не советовал делать.


в ansible декларативность там какая-то недостаточно декларативная ИМХО.

Да, я тоже так считаю. ansible императивный, поскольку просто выполняет таски как скрипт, каждую по очереди. Например:


tasks:
  - file:
      path: /tmp/test
      state: absent
  - file:
      path: /tmp/test
      state: directory

Приведет к тому, что директория /tmp/test будет создана. А если поменять местами, то она будет удалена.


В puppet такое невозможно. Такая запись приведет к ошибке дубликата ресурсов. Поскольку не может быть одного ресурса с двумя состояниями:


file {'/tmp/test':
    ensure => absent,
}
file {'/tmp/test':
    ensure => directory,
}

Это будет ошибка: Error: Evaluation Error: Error while evaluating a Resource Statement, Duplicate declaration: File[/tmp/test] is already declared at (file: /root/1.pp, line: 1); cannot redeclare (file: /root/1.pp, line: 4) (file: /root/1.pp, line: 4, column: 1) on node


И без разницы, в какой последовательности их написать. И даже если попробовать схитрить и сделать так:


file {'id2':
    path   => '/tmp/test',
    ensure => directory,
}
file {'id1':
    path   => '/tmp/test',
    ensure => absent,
}

То это все равно приведет к ошибке дубликата: Error: Evaluation Error: Error while evaluating a Resource Statement, Cannot alias File[id2] to ["/tmp/test"] at (file: /root/1.pp, line: 5); resource ["File", "/tmp/test"] already declared (file: /root/1.pp, line: 1) (file: /root/1.pp, line: 5, column: 1) on node test


То есть puppet более честен в плане декларативности чем ansible. Поэтому именно с задачей управления конфигурациями как мне кажется он лучше справляется. Но эти инструменты можно комбинировать. ansible отлично справляется с другими задачами, с которыми puppet справляется хуже.


Но в эпоху иммутбл инфры, облаков. Когда есть контейнеры, packer, pulumi, terraform, AWS Cloud Development Kit, k8s, nomad и т.д. Наверное вообще не имеет смысла сидеть и сравнивать все эти инструменты (ansible/puppet/chef/solt), так как они потихоньку уходят на задний план.

Нет. bolt хорошо дополняет экосистему puppet. Более того, модули для puppet могут содержать tasks, которые можно использовать в plans(аналог плейбука) bolt'а. bolt умеет работать с puppetdb.
Он призван закрыть задачи, которые очень трудно решить puppet'ом либо их решение с помощью puppet не рационально.

Дело в том что в экосистеме puppet четко разделены задачи. 1) декларативное описание инфры — puppet, и оркестрация/ad-hoc задачи/другая автоматизация — bolt/choria. Вы просто не знакомы с таким подходом вот и всё. Замени puppet на chef, а bolt на ansible ничего не изменится, и получили решение тех же задач только с помощью chef + ansible вместо puppet + bolt. Совершенно нормальный подход

Это не дублирование инструментария, инструменты решают разную задачу, ansible хоть и пытается решить задачу менеджмента конфигураций, но решает её хуже на мой взгляд.
В задачах деплоймента приложения на хосты, не надо ставить пакеты. С этим прекрасно справятся puppet/chef. Зато выполнять в строгом порядке какие-то действия для rolling деплоймента приложения, при этом иметь доступ ко всем переменным и фактам всех хостов куда идет деплоймент — с этим уже лучше справятся ansible/bolt/choria.
То есть инструменты вполне совместимы, когда ты ими не решаешь одни и те же задачи.

Не согласен, поскольку ansible и puppet всё таки разные инструменты, и с определенными задачами puppet лучше справляется (менеджмент конфигураций), а с другими ansible (оркестрация/ad-hoc задачи). То вполне их можно безболезненно объединять. Более того, puppet как раз и предлагает это делать, правда вместо ansible предлагает использовать bolt. Но это по сути тот же ansible, только с DSL puppet'а и лучше встраивается в его экосистему.

Информация

В рейтинге
Не участвует
Зарегистрирован
Активность