Русский
Русский
English
Статистика
Реклама

Инфраструктура как код в Авито уроки, которые мы извлекли

Привет, Хабр! Меня зовут Саша Козлов, я занимаюсь разработкой инфраструктуры и системным администрированием вАвито последние три споловиной года. Я расскажу, как мы масштабировали и модернизировали современем нашу работу синфраструктурным кодом и вывели её накачественно новый уровень.


Мы отвечаем заполный цикл управления оборудованием: отзакупки и монтажа вДЦ додоставки доконечного потребителя. Речь пойдёт обуроках, которые мы вынесли запоследние несколько лет, работая стысячей единиц оборудования и несколькими тысячами единиц конфигурирования.



Речь пойдёт обинструментах первого поколения IaC, таких как Ansible, Chef, Salt, Puppet. Если вы имеете дело сon-premises инфраструктурой, и увас нет своего облака навиртуализации, то, скорее всего, вы уже используете один изэтих инструментов.


Исторически вАвито используется Puppet. Вкакой-то момент мы столкнулись снеобходимостью обновить его, поскольку использовали очень старую версию, которую Puppetlabs давно перестали поддерживать. Но мы поняли, что сделать это невозможно, поскольку кодовую базу, которая натот момент у нас была, невозможно было просто перенести напоследнюю версию. Это заставило нас критически подойти ктому, как мы разрабатываем инфраструктурный код и вообще правильный ли инструмент мы используем.


Хорошие практики важнее выбора правильного инструмента


Напервый взгляд, инструменты управления инфраструктурой очень разные. Некоторые требуют, чтобы нанодах был запущен специальный агент. Где-то используется push-модель, где-то pull, но впринципе ничего не мешает запускать ansible накаждой машине покрону и получить pull-конфигурации. Содной стороны Python, Jinja-шаблоны и YAML-программирование, сдругой DSL и Ruby. Многие инструменты включают всвою экосистему дополнительные компоненты: дашборды, хранилище конфигурации вроде PuppetDB, централизованный компонент, который хранит и распространяет навсе ноды ваш инфраструктурный код.


Слюбым инструментом подобного рода не получится работать синфраструктурным кодом ровно так же, как работает скодом разработчик сервиса. Далеко не все изменения откатываются через git revert, сама выкатка этих изменений не происходит одновременно навсей инфраструктуре, а применяется постепенно. Конфигурация влюбом случае остаётся мутабельной, а это значит, что всё равно придётся держать вголове текущее состояние машины. А А не то же самое, что 0 А. Многие такие особенности приходится учитывать.


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


  • проведении статического анализа и линтинга кода, соблюдении стиля кодирования;
  • обеспечении возможности легко тестировать код и уменьшении цикла обратной связи;
  • запуске тестов наCI-системе, чтобы код попадал вмастер только после успешных билдов;
  • уменьшении связанности кода засчёт версионирования модулей и более контролируемого применения изменений винфраструктуре;
  • разработке тулинга, который поощряет разработчика использовать практики, описанные выше;
  • упрощении наливки машины снуля дляуменьшения Configuration Drift.

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


Определите границы применимости инструмента


Любой инструмент имеет свои сильные и слабые стороны. Поэтому важно понимать границы его применимости: где его следует использовать и, что не менее важно, где не следует.


Puppet очень гибок, местами даже слишком. Он впостоянном цикле приводит состояние инфраструктуры кописанному в коде виду, т.к. использует pull-модель. Как следствие этого недостаточно хорошую обратную связь и задержку при применении изменений. По умолчанию конфигурация применяется раз в 30минут, поэтому длязадач, требующих более динамичного управления, Puppet не подходит. Кроме этого, внём нет event-driven автоматизации, как вSalt, что также ограничивает область его применения.


Обограничениях инструментов вроде мутабельности инфраструктуры и отсутствия гарантий отката инфракода я упомянул выше, сними приходится считаться. Поскольку применять их мы собирались длянастройки железных серверов и stateful-контейнеров, мы не рассчитывали, что сможем всякий раз применять конфигурацию счистого состояния. Хотя такую возможность мы себе обеспечили.


Для себя мы определили границы так: применяем Puppet дляконфигурирования железных нод и stateful-контейнеров, но не используем его длядеплоя сервисов, конфигурации рантайма инфраструктурных сервисов и других быстро меняющихся вещей. Например, мы постепенно отходим отуправления DNS-записями через Puppet, который сложился унас исторически. Мы хотим, чтобы управление было более динамическим, поэтому этим занимается инфраструктурный сервис, предоставляющий свой API.


Правильный выбор структуры кода и уменьшение связанности кода


Мы долго складывали весь инфраструктурный код водной репе, но помере увеличения команды это стало проблемой. Стало сложнее договариваться отом, как лучше писать код. Появились сабрепозитории длятех частей кода, которыми владеют инженеры издругих команд, а работать ссабмодулями вgit не очень удобно. Приэтом изменения восновных частях кода выкатывались сразу навсю огромную инфраструктуру Авито. Цена ошибки стала очень велика.


Поэтому мы выделили основные сущности: control repo и модули. Каждая находится вотдельном git-репозитории. Сами тут ничего особенно не придумали, просто обратились клучшим практикам дляPuppet-кода, которые описаны встатье "The roles and profiles method".


Control repo это репозиторий синфраструктурным кодом. Внём находится тот код, который выкатывается через CI-систему напаппет-сервер и применяется намашинах. Роль это некоторая типовая конфигурация, способ сгруппировать машины содинаковыми настройками. Это самый высокоуровневый слой абстракции условно, ответ навопрос чем занимается эта машина?. Примеры ролей: k8s-нода кластера Х, нода Kafka встейджинг окружении дляшины данных, ClickHouse-нода и т.п.


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


Роли и профили описываются вcontrol repo, а модули подключаются внеё как внешние зависимости и применяются внутри профилей или ролей.


Модуль это подключаемая библиотека, код, который занимается настройкой определённого инфраструктурного компонента, как правило переиспользуемый. Модули релизятся отдельно отcontrol repo, версионируются поsemver и подключаются как зависимости. Унас сейчас порядка 50 модулей: модуль дляработы ссекретами, настройки различных БД, конфигурирования кластеров Kubernetes и другие.


Вопрос, как структурировать код, насамом деле не очень прост в случае сPuppet. Единственно правильного ответа нанего нет. Далеко не всегда ясно, что должно считаться одной ролью, а вкаких случаях профиль нужно пилить нанесколько. Нужно ли сразу делить код намодули, или можно допоры довремени складывать всё вcontrol repo?


Здесь всё зависит отразмера инфраструктуры, её разнородности и количества инженеров, которые занимаются разработкой инфракода. Дляначала вполне подойдёт просто иметь control repo и весь код писать вней, организуя его впрофили. Унас вАвито инфраструктурой занимается несколько команд, длякаждой важны разные вещи. Одним важнее стабильность, а не скорость внесения изменений, поэтому они обложили код кучей тестов. Другим не подошла стандартная схема тестирования вDocker, и пришлось настроить свою. Поэтому унас несколько control repo, поодной натакую команду. А чтобы можно было переиспользовать решения и не дублировать код, мы пишем модули.


Вот несколько полезных статей, которые помогли нам разобраться втом, как лучше структурировать код:



А ещё на своём Гитхаб-аккаунте мы поделились шаблоном модуля и control repo, который используем усебя. Вшаблонах можно посмотреть пример того, как организовать код, а ещё там есть инструменты, которые нужны длязапуска всех видов тестов:



Используйте External Node Classifier вместо регулярных выражений


Ну хорошо, есть роли, профили и модули но как именно определяется то, какой код накакой машине выполнять?


Достаточно долго мы использовали node definitions и регулярные выражения, чтобы определять то, какие машины каким кодом настраиваем. Такой подход нормально работает нанебольшой инфраструктуре, но помере её роста вы скорее всего получите что-то похожее на это:


node /^avi-ceph(2[1-9]|3[0-9]|4[0-9]|5[0-9]|6[0-9]|7[0-9]|8[0-9]|9[0-9])/ {...}

Одна неправильно закрытая скобка тут может привести кполному краху. Поиск потакому коду не работает невозможно быстро разобраться, какой код выполняется наотдельно взятой машине. Вобщем, такой подход казался разумным вначале, но современем оказался неудачным. Дляпривязки машин кнаписанным ролям Puppetlabs предлагает использовать External Node Classifier.


External Node Classifier это компонент, который хранит всебе то, какую роль следует применить ккаждой ноде. Он даёт возможность винфракоде описывать только роли, а логику привязки ролей кконкретным машинам вынести вовнешнюю систему. Навход ENC принимает имя ноды и отдаёт некий набор параметров, которые вкоде становятся доступны как top-scope variables. Вкаждой control repo есть примерно такой код, который обеспечивает привязку ноды кроли, и это единственное место, где используется node definition:


node default {  include base # применяем базовый слой конфигурации, общий для всех control repo  if $::role != '' {    notify{ "Node ${::fqdn} has role ${::role}": loglevel => info }    include "role::${role}"  } else {    notify{ "Node ${::fqdn} has no role": loglevel => warning }  }}

Вкачестве ENC может выступать любая система, ведь логику получения и формирования ответа вы пишете сами. Такой системой внашем случае стала CMDB, внутренняя система учёта оборудования и его комплектующих. Наш CMDB основан наnetbox отDigital Ocean, сильно доработанном поднаши нужды. Он плотно интегрирован сrazor, системой провижнинга железа, и умеет собирать всевозможные данные ожелезе, которое установлено вдата-центре.


Дляподготовки сервера кработе винтерфейсе CMDB мы указываем нужный паппет-сервер и роль, отправляем сервер напереналивку и через 15-20минут получаем готовый кработе сервер, ккоторому уже применена конфигурация, описанная вроли.


Упростите процесс предварительной настройки серверов


Почему так важно иметь простой способ получить чистую конфигурацию? Длятого, чтобы уменьшить влияние Configuration Drift, который современем будет неизбежно накапливаться. Configuration Drift это неконсистентность конфигурации машины, описанной вкоде, поотношению кеё фактическому состоянию. Его источники это ручные изменения намашинах, тестирование гипотез, ресурсы, которые приизменении вышли из-подуправления Puppet.


Чем чаще код применяется снуля, тем лучше он проверен, и тем больше вы уверены втом, что сним не возникнет проблем. Это особенно актуально длятой части инфраструктуры, которая не хранит состояния и безтруда может быть переналита снуля. Например, ноды k8s-кластеров.


В идеале нужно иметь возможность получить чистое состояние, отправив машину впереналивку простым API-вызовом. Мы делаем это через netboxAPI, который расширили подсвои нужды. Подкапотом там загрузка поPXE и netboot-образ Debian, шаблонизация preseed'ов наоснове параметров, переданных вAPI, много низкоуровневой настройки и взаимодействия поIPMI черезRedfish API. И, конечно же, болей, связанных сразной его реализацией уразных вендоров.


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


Настройте инструменты тестирования инфраструктурного кода


Сначала унас не было никакого способа протестировать и проверить инфраструктурный код довыкатки. Цикл обратной связи был таким: ты вносишь изменения вкод, пушишь вмастер, и после этого узнаешь, выполняет код задачу, или ты сделал дурацкую синтаксическую ошибку.


Это приводило ктому, что проводить ревью было практически невозможно решение отлаживается напродакшене или влучшем случае натестовой машине, но код уже должен быть вмастере. Как следствие низкое качество кода и не очень хорошая коммуникация. Код безтестов невозможно рефакторить. Современем это приводит кwrite-only кодовой базе, вкоторую никому не хочется залезать.


Когда мы перешли с3.7 на6 версию Puppet, то поняли, что появилась куча крутых инструментов длятестирования. Последовательность тестирования инфраструктурного кода выглядит так:


  1. Запуск линтера.
  2. Юнит-тесты.
  3. Приёмочные тесты

Наэтих трёх шагах мы делаем следующее:


  1. Проверяем валидность синтаксиса, соответствие стайлгайдам и т.п.
  2. Проверяем, что код компилируется вкаталог, отлавливаем ошибки duplicate resource declaration. Вэтих тестах можем зафиксировать определённый контракт: например, код должен содержать ресурс сервис, который должен рестартовать приизменениях вопределённых файлах.
  3. На стадии приёмочных тестов применяем код вDocker-контейнере, проверяем, что сервис работает припомощи inspec.

Приёмочное тестирование длямодуля Kubernetes

Кластеры Kubernetes мы разворачиваем припомощи Puppet и делаем это встиле "Kubernetes The Hard Way", то есть контролируем настройку компонентов кластера досамых мелочей.


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


Приёмочные тесты, написанные наBeaker поднимают три виртуальные машины изподготовленных образов, доставляют туда секреты, применяют внутри паппет-код, который готовит кластер изтрех нод. Вконце мы ~~запускаем smoke-тест: ~~ применяем деплоймент снджинксом, сервис и проверяем его доступность черезингресс.


Код тестов, которые выполняют эту проверку, выглядят так:


  context 'application deployment' do    it 'can deploy an application into a namespace and expose it' do      shell('systemctl restart kubelet')      shell('count=0;        while [[ $(kubectl get pods -n tiller -l name=tiller -o \'jsonpath={..status.conditions[?(@.type=="Ready")].status}\') != "True" ]];          do            if [[ count -gt 180 ]]; then              break;            fi;            sleep 1 && ((count++));          done')      shell('kubectl create -f /tmp/nginx.yaml', acceptable_exit_codes: [0]) do |r|        expect(r.stdout).to match(%r{namespace/nginx created\nconfigmap/my-nginx-config created\ndeployment.apps/my-nginx created\nservice/my-nginx created\n})      end    end    it 'can access the deployed service' do      shell('count=0;        while [[ $(kubectl get pods -n nginx -l run=my-nginx -o \'jsonpath={..status.conditions[?(@.type=="Ready")].status}\') != "True" ]];          do            if [[ count -gt 180 ]]; then              break;            fi;            sleep 1 && ((count++));          done')      shell('curl --connect-timeout 1 --retry-delay 1 --retry-max-time 300 --retry 150 -s --retry-connrefused 10.100.10.5', acceptable_exit_codes: [0]) do |r|        expect(r.stdout).to match %r{Welcome to nginx!}      end    end end

Если все шаги прошли безошибок, тест считается успешным. Приёмочные тесты мы запускаем наCI накаждое изменение.


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


Вот список инструментов, которые помогут помочь настроить тестирование инфракода:



И неплохая статья про юнит-тестирование:



Development kit для инфраструктурного кода


Если хотите, чтобы разработчики инфракода использовали CI/CD или проверяли свой код допуша врепу снабдите их инструментом, который поумолчанию поддерживает необходимый workflow.


В паппет-экосистеме есть отличный инструмент PDK, который активно используется всообществе. Но его мы использовать не стали, потому что он был несовместим снашим инструментарием, и пришлось бы многое дорабатывать. ВPDK впервую очередь не хватило возможности работать сcontrol repo и тестирования сиспользованием Kitchen и Docker. Длятестирования PDK использует Beaker, укоторого довольно высокий порог входа.


Поэтому мы написали свой инструмент, который позволяет:


  • Создать изшаблона пустой проект, готовый кработе: тесты, CI пайплайны и прочее.
  • Сгенерировать заготовки дляманифестов, тестов, паппет-функций.
  • Запускать статические валидаторы кода, юнит-тесты, приёмочные тесты.
  • Разрешить зависимости вcontrol repo, показать, есть ли более новые версии зависимостей.
  • Сгенерировать документацию вмаркдауне издокстрингов.
  • Собрать модуль и запушить вовнутренний репозиторий.


Когда разработчик инфракода создаёт новый модуль припомощи development tool, он сразу получает репозиторий снастроенными сборками наCI длястатического анализа, запуска тестов и релиза модуля.


После бутстрапа проекта врепозитории уже будет настроен инструментарий дляработы скодом (puppet-rspec, puppet-linter, test-kitchen) и настроены гит хуки, которые запускают нужный инструмент накоммит или пуш. Всозданном репозитории поумолчанию применяются политики, запрещающие пуш вмастер и мердж ветки, если тесты упали или код не прошел ревью.


IDE для Puppet

Кстати, вкачестве IDE дляразработки паппет-кода неплохо подходит VSCode, внём есть замечательный плагин схорошей поддержкой DSL, автодополнением и сниппетами.


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


Об управлении зависимостями в инфраструктурном коде


Как я уже писал выше, переиспользуемый код мы выносим вмодули, которые подключаются как внешние зависимости. Напомню, что модуль это отдельный проект сосвоим релизным циклом. После релиза модуль собирается вархив и грузится вовнутренний репозиторий модулей: Forge. Впубличном Puppet Forge есть множество модулей, которые пишут всообществе.


В управлении зависимостями вPuppet есть много странных и неочевидных вещей. Например, зависимости вмодулях можно указывать двумя разными способами: metadata.json и Puppetfile. Они немного отличаются посвоим возможностям, и разные инструменты по-разному их обрабатывают. Официально поддерживаемого инструмента поразрешению зависимостей нет. Есть librarian-puppet, который, вотличие отофициального r10k, умеет резолвить зависимости второго и высших порядков. Но проект давно не развивается и не имеет нормальной документации, хотя задачу свою вцелом выполняет. За неимением лучшего инструмента взяли его он применяется везде, где требуется резолв зависимостей: вacceptance-тестах и придеплое кода напаппет-сервер.


Достаточно долго мы искали подходящий путь. Сначала подключали модули как гит репу, а версионировали git-тегами, вот так:


mod 'dba-clickhouse',  :git => 'ssh://git@github.com/iac/dba-clickhouse.git',  :ref => '1.2.2'mod 'dba-kafka',  :git => 'ssh://git@github.com/iac/dba-kafka.git',  :ref => '1.2.0'

Такой способ работает, но требует явно указывать версию модуля, и не даёт завязаться намажорную или минорную версию. Дляболее гибкого версионирования придётся поднять собственный локальный Puppet Forge, загружать модули внего, а зависимости резолвить черезlibrarian-puppet.


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


# Puppetfilemod 'arch-puppetserver', '0.20.5' # подключаем строго указанную версию модуляmod 'arch-vault', '~> 2.1' # подключаем модуль в пределах мажорной версииmod 'si-lxc' # используем самую последниюю версию

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


[22:39:43] in dba-control on  production via  ruby-2.5.1 at   unstable $ iack dep show[] Collecting modules metadataFULL NAME            | CURRENT VERSION | LATEST VERSION | OUT OF DATE?  ---------------------|-----------------|----------------|---------------si-lxc               | latest          | 0.3.2          | N/A           si-base              | latest          | 1.3.1          | N/A           petems-hiera_vault   | v0.4.1          |                | Majorarch-vault           | 2.1.0           | 2.1.0          | No   dba-postgresql       | 0.1.2           | 0.1.3          | Tiny dba-pgbouncer        | 0.4.0           | 0.5.1          | Minorsi-grub              | 0.1.0           | 0.1.0          | No   si-collectd          | 0.2.3           | 0.2.4          | Tiny si-confluent         | 0.3.0           | 0.3.0          | No   dba-redis            | 0.2.3           | 0.2.3          | No   dba-collectd_plugins | latest          | 0.2.0          | N/A           dba-mongodb          | 0.2.1           | 0.2.1          | No   dba-patroni          | 0.1.4           | 0.2.4          | Minordba-cruise_control   | 0.1.1           | 0.1.2          | Tiny dba-lxd              | 0.7.0           | 0.7.0          | No   dba-clickhouse       | 1.2.1           | 1.2.2          | Tiny dba-zookeeper        | 2.0.0           | 2.0.0          | No   si-td_agent          | 0.1.0           | 0.1.0          | No   dba-kafka            | 1.1.6           | 1.2.1          | Minorarch-puppetserver    | 0.20.1          | 0.20.2         | Tiny pcfens-filebeat      | 4.1.0           | 4.4.1          | MinorKyleAnderson-consul  | 5.0.3           | 6.0.1          | Majorpuppetlabs-apt       | 6.3.0           | 7.4.2          | Majorpuppetlabs-stdlib    | 5.2.0           | 6.3.0          | Major

Вот несколько полезных ссылок потеме:



О важности code style и документации дляинфраструктурного кода


Почему так важно иметь code style вразработке инфраструктурного кода? Всё как и слюбым другим кодом инфраструктурный код пишется один раз, а читается потом десятками людей напротяжении лет. Поэтому нужен некий стандарт, описывающий лучшие практики, который облегчит чтение кода и его поддержку, адаптацию новых инженеров. Он также упрощает и процесс review кода проще урегулировать спорные моменты, кинув ссылкой наподходящий пункт издокумента.


В Puppet сэтим всё отлично. The puppet language style guide содержит все основные рекомендации понаписанию кода. Puppet-lint, который мы запускаем наCI, проверяет соблюдение этих рекомендаций и позволяет добавлять внего собственные проверки.


Внутри Авито мы его немного расширили и дополнили его своими, более высокоуровневыми правилами и рекомендациями. Их мы выложили вместе с шаблонами модулей и control repo:



Поскольку мы используем свой development kit, важно облегчить процесс онбординга новых инженеров и адаптации старых:


  • иметь единую точку входа вовнутреннюю документацию дляинженера инфраструктуры;
  • описать процесс работы отпростого к сложному: отhow to поразработке простейшего модуля доболее справочных материалов. Часть этой документации была переработана и превратилась встатью наХабре;
  • предоставить канал поддержки, где можно задать любой вопрос потеме.

Удобное управление секретами это важно


Управление секретами дляинфракода должно быть удобным и простым, иначе секреты начинают коммитить вкод. ВPuppet управление секретами удобно делать черезинтеграцию Hiera и Vault. На Гитхабе есть hiera-backend, который позволяет получать их значения прямо изvault черезhiera_lookup.


Для работы ссекретами вАвито есть модуль-обёртка, который позволяет либо получить секрет впеременную, либо положить файл-секрет вопределённое место всистеме. Первый способ работает через функцию, которая вызывается вкоде вот так:


$token_data = vault::secret_field('tokens.csv', 'data')

В этом примере впеременную token_data получаем значение поля 'data' изсекрета tokens.csv, который находится вVault. Всамом Vault секреты хранятся иерархически, также, как вHiera:


$ vault-util ls puppet/arch/      common/nodes/roles/

Таким образом, если мы хотим иметь общее значение секрета длявсех машин, достаточно положить его вcommon. Если оно разное длякаждой роли или ноды вroles/ или nodes/.

Второй способ это дефайн и специальный формат секрета вволте, который содержит различные метаданные файла-секрета, такие как имя файла, владелец, права нафайл. Такой способ мы используем дляхранения TLS-сертификатов и ключей.


Для тех, кто хочет подробнее разобраться втом, как всё это работает, мы выложили модуль дляуправления секретами наГитхаб. Внём также можно посмотреть примеры того, как тестировать манифесты и функции наPuppet:



Canary релизы инфраструктурного кода


В Puppet есть интересный механизм, который позволяет выкатить код изветки control repo. Все ветки репозитория control repo отображаются наокружении, а каждая нода принадлежит кодному из них. Информация обэтом отдаётся изENC вместе сименем роли.


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


Это ещё один, финальный, способ протестировать свои изменения, и не уронить систему.


О достоинствах и недостатках Puppet


Если говорить одостоинствах Puppet, я бы назвал отличную документацию, развитый тулинг вокруг, очень большую гибкость и расширяемость инструмента. Если не хватает возможностей DSL его можно расширять наRuby. Hiera отличное решение дляхранения параметров, которые передаются вроли. Можно обеспечить максимальную стандартизацию инфраструктуры, приэтом сохранить возможность внести изменения сгранулярностью доконкретной машины. Данные вHiera можно выносить вразличные бэкенды, а если не хватает подходящего, можно написать свой.


Самый большой недостаток Puppet это, пожалуй, высокий порог входа и огромное количество способов решить одну и ту же задачу. Это обратная сторона гибкости. Даже спустя годы работы сэтим инструментом иногда всё равно непонятно, как лучше организовать код: как использовать профили, какие параметры выносить вHiera, а какие оставлять вкоде. Мы смогли выработать какие-то правила, но это достаточно специфичная тема, поэтому её оставим зарамками статьи.


Puppet наследовал у Ruby подход, когда узадачи имеется несколько решений. Это может быть интересно, если ты водиночку пилишь небольшой pet project. Но если ты работаешь синфраструктурой, где крайне важна стабильность и простота, вкоманде изнескольких десятков инженеров это очень вредит. Обычно получается так: если инструмент позволяет решать одну задачу несколькими способами, то будет выбран самый простой и, скорее всего, неправильный.


Ещё один недостаток плохая обратная связь привнесении изменений. Он впринципе присущ всем инструментам, работающим поpull-модели. Неизвестно, вкакой момент времени применятся изменения, которые попали вмастер. Длянекоторых задач это становится критичным, и приходится изобретать велосипеды, чтобы pull превратить вpush. Bolt, который Puppetlabs предлагает дляподобных задач, выглядит странно, сложно интегрируется сPuppetDB, и отего использования мы пока отказались.


Суммируя


Чтобы повысить эффективность и прозрачность работы синфраструктурой и получить качественный код, который будет просто переиспользовать, мы:


  1. Вместо безупречного инструмента концентрируемся наулучшении тулинга и практик, которые применяем приразработке.
  2. Определяем границы применимости каждого инструмента и фиксируем их.
  3. Стараемся уменьшать связанность инфраструктурного кода и более контролируемое применение изменений засчёт версионирования его частей.
  4. Тестируем код и обеспечиваем быструю обратную связь привнесении изменений.
  5. Обеспечиваем запуск тестов наCI и вывод их результатов приревью кода.
  6. Используем пайплайны и инструменты, которые поддерживают необходимый workflow дляинфраструктурного кода.
  7. Используем External Node Classifier вместо регулярок.
  8. Упрощаем процессы предварительной настройки серверов.
  9. Обеспечиваем безопасное и удобное получение секретов винфраструктуру.
Источник: habr.com
К списку статей
Опубликовано: 18.08.2020 14:15:47
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Блог компании авито

Системное администрирование

Серверное администрирование

Puppet

Iac

Инфраструктура как код

Hiera

Конфигурация

Ci

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru