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

Ansible

Автоматизируем поиск секретов в git и ansible

01.02.2021 00:22:31 | Автор: admin

Знаете ли вы что хранится в вашем git репозитории? Нет ли среди сотен коммитов паролей от продуктовых серверов, попавших туда по ошибке?

А что если ansible скрипт при публикации упадет и засветит пароли в логе?

Рассказываю о том как мы попробовали автоматизировать такие проверки и что из этого получилось.

Привет, Хабр!

Меня зовут Олег, я работаю в достаточно крупном для РФ банке, в подразделении "IT для IT".

Недавно к нам обратились друзьяиз внедрения (OPS)с просьбой помочь им автоматизировать процесс приемки изменений в скрипты деплоя (ansible) и конфигурации приложений напромышленных средах.

Исторически у нас есть 2 git хранилища (на самом деле больше, но это не так важно), в одном команды хранят свой код, в другом хранятся скрипты деплоя, конфигурации приложений итд, в общем все что относится к OPS составляющей.

Так вот, исходя из принятого процесса скрипты и конфигурации для промышленных сред хранятсяв masterветке и при вливании в нее нужен апрув от OPS.

Почему именно так? Потому что кроме всего там же в gitхранятся ипароли от сред (хоть и зашифрованные) и отвечают за них OPSы. Если какой то пароль засветится в логе Jenkins или попадет в открытом виде в git - это серьезная утечка и безопасность придет (с паяльником) именно к OPS.

Пароли в git? Серьезно?

Да, к сожалению серьезно. Конечно же для таких вещей нужно использовать системы secret management, такие какHashiCorp Vault,CyberArk Conjurитд.

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

Число команд, проектов и деплоев растет постоянно и ребята уже просто не могли анализировать то количество pull request которое к ним приходило.

Значит, будем автоматизировать!

Что анализируем?

Совместно с коллегами выявили 3 основных вектора утечки пароля, которые их интересовали и встречались наиболее часто:

Пароль в открытом виде

---    dev_ssh_username1: "admin"    dev_ssh_password1: "admin123"

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

Передача параметров в shell или command таски ansible

- name: Deploy  hosts: all  tasks:    - name: Update      shell: "update.sh --user={{ update_user }} --password={{ update_password }}"

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

$ ansible-playbook deploy.yml -i env/DEV/hosts -vTASK [Update] ******************************************************************changed: [192.168.1.2] => {"changed": true, "cmd": "/home/dev/update.sh --user=secret_user --password=secret_password", "delta": "0:00:05.056532", "end": "2020-11-06 09:53:09.397355", "rc": 0, "start": "2020-11-06 09:53:04.340823", "stderr": "", "stderr_lines": [], "stdout": "Update SUCCESS", "stdout_lines": ["Update SUCCESS"]}

Или, если playbook упадет то в логе выведется строка с секретами (даже если не указывать -v)

$ ansible-playbook deploy.yml -i env/DEV/hostsTASK [Update] ******************************************************************fatal: [192.168.1.2]: FAILED! => {"changed": true, "cmd": "/home/dev/update.sh --user=secret_user --password=secret_password", "delta": "0:00:00.018710", "end": "2020-11-06 10:14:30.642419", "msg": "non-zero return code", "rc": 127, "start": "2020-11-06 10:14:30.623709", "stderr": "/bin/sh: /home/dev/update.sh: Нет такого файла или каталога", "stderr_lines": ["/bin/sh: /home/dev/update.sh: Нет такого файла или каталога"], "stdout": "", "stdout_lines": []}

Чтобы этого избежать можноиспользовать environment, например:

- name: Deploy  hosts: all  tasks:    - name: Update      shell: "/home/dev/update.sh $AUTH_DATA"      environment:        AUTH_DATA: "--user={{ update_user }} --password={{ update_password }}"

Тогда в вывод наши credentials не попадут. Еще можно указывать атрибут no_log.

Неосторожное использование withCredentials

У нас используется Jenkins, в котором для работы с credentials используется конструкцияwithCredentials. С помощью нее внутри pipeline можно получить credential, сохранить его в переменные и использовать, например для подключения к какой нибудь сторонней системе. Jenkins при этом будет маскировать в логе значение этих переменных.

Однако, если мы сделаем, например так:

node {  stage('Jenkins Credentials | Decrypt Secret File') {    withCredentials([file(credentialsId: 'credentials-id', variable: 'secretFile')]) {      sh 'cat $secretFile'    }  }}

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


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

Ищем пароли

Казалось что поиск паролей в открытом виде это какая то плевая задача. У нас в арсенале есть не толькоSonarQubeно иCheckmarx. Уж они точно должны уметь решать эти задачи.

И вроде бы вот оно - есть такое правило для SonarQubeHard-coded credentials are security-sensitive

Но при внимательном рассмотрении оказывается что правило достаточно простое, оно проверяет попадает ли название переменной под определенный паттерн. Исходник, если интереснотут.

C Checkmarx оказалась так же картина - реагирует он очень выборочно. И правила там тоже очень простые (коллеги из Application Security рассказали). Например, такой код пропускает:

import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;import org.springframework.web.bind.annotation.RestController; @RestControllerpublic class DemoController {    @RequestMapping("/show_me_creds")    public @ResponseBody String show_me_creds() {        String thisIsMyLittleSecret = "qwerty12345";        return thisIsMyLittleSecret;    }}

Изучив что есть на рынке пришли к выводу что будем смотреть в сторону opensource утилит. Запрос в google "find secrets in git" выдает нам примерно такие варианты:

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

  • Проводит анализ на основе regexp, которые можно расширять;

  • Умеет учитывать энтропию при анализе;

  • Репорты в json;

  • Легко задавать исключения;

  • В пилоте показал лучший результат.

Как он работает - в основе лежит toml файл с описанием правил, например:

[[rules]]  description = "generic secret regex"  regex = '''secret(.{0,20})([0-9a-zA-Z-._{}$\/\+=]{20,120})'''  tags = ["secret", "example"]

Кроме того в правиле можно указывать требуемую энтропию, например:

[[rules]]  description = "entropy and regex example"  regex = '''secret(.{0,20})([0-9a-zA-Z-._{}$\/\+=]{20,120})'''  [[rules.Entropies]]    Min = "4.5"    Max = "4.7"

В переводе на человеческий это означает: "Если мы встречаем строку кода, которая соответствует регулярному выражению, и эта строка попадает в пределыэнтропииот 4,5 до 4,7, то это пароль"

Я не буду заниматься переводом документации, все прекрасно описанотут.

Пример срабатывания:

  ~  gitleaks --repo=gitleaks --repo=https://github.com/gitleakstest/gronit.git --verbose --prettyINFO[2020-04-28T13:00:34-04:00] cloning... https://github.com/gitleakstest/gronit.gitEnumerating objects: 135, done.Total 135 (delta 0), reused 0 (delta 0), pack-reused 135{        "line": "const AWS_KEY = \"AKIALALEMEL33243OLIAE\"",        "offender": "AKIALALEMEL33243OLIA",        "commit": "eaeffdc65b4c73ccb67e75d96bd8743be2c85973",        "repo": "gronit.git",        "rule": "AWS Manager ID",        "commitMessage": "remove fake key",        "author": "Zachary Rice",        "email": "zricethezav@users.noreply.github.com",        "file": "main.go",        "date": "2018-02-04T19:43:28-06:00",        "tags": "key, AWS"}......WARN[2020-04-28T13:00:35-04:00] 6 leaks detected. 33 commits audited in 77 milliseconds 738 microseconds

Из вызова выше видно, что gitleaks умеет работать непосредственно с репозиторием - вы можете указать ему ссылку на репо и получить отчет не клонируя репозиторий на локаль.

Если произошло ложное срабатывание - достаточно занести строку в whitelist правила, например:

[[rules]]  description = "entropy and regex example"  regex = '''secret(.{0,20})['|"]([0-9a-zA-Z-._{}$\/\+=]{20,120})['|"]'''  [[rules.Entropies]]    Min = "4.5"    Max = "4.7"  [[rules.whitelist]]    regex = '''secret.some_value_that_match_regexp_but_not_really_a_secret'''    description = "ignore that string"

В результате мы пришли к такой схеме - при создании pull request через webhook вызывается Jenkins, который скачивает репозиторий, запускает для него gitleaks и если тот находит утечки - ставит статус NEED WORK.

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

Проверяем ansible

Двигаемся дальше, итак, мы хотим находить вот такие playbook, потому что не хотим случайно засветить в логах Jenkins наши креды.

- name: Deploy  hosts: all  tasks:    - name: Update      shell: "update.sh --user={{ update_user }} --password={{ update_password }}"

С ansible все просто - его легко распарсить, можно использовать OPA (рекомендуюстатьюот Александра Токаревапо теме), можно самим быстро написать проверки в python.

Хорошо что мы этого не сделали, а вспомнили проAnsible Lint.

Это официальный линтер для Ansible, который имеет "из коробки" кучу полезных проверок, но нас интересует не это.

Нам были нужны вот эти 2 фичи:

  • Возможность писать свои правила;

  • Автоматическое сканирование и поиск playbook. Не нужно передавать конкретные файлы на проверку, можно запустить линтер в папке и он сам найдет их.

Писать правила очень просто - правило представляет собой python код, в котором обязательно должны быть объявлены переменные id, short description, description и tags (их использует Ansible Lint для ведения списка правил) и методы match или matchtask, в которых мы пишем саму логику проверки. Более подробно предлагаю почитать воригинальной документации.

Для нашей проверки мы используем matchtask, на вход которого поступает каждый найденный task, который мы можем анализировать.

К сожалению я не могу здесь привести код именно нашего таска, но он похож на этот пример (полностьютут):

class CommandsInsteadOfModulesRule(AnsibleLintRule):    id = '303'    shortdesc = 'Using command rather than module'    description = (        'Executing a command when there is an Ansible module '        'is generally a bad idea'    )    severity = 'HIGH'    tags = ['command-shell', 'resources', 'ANSIBLE0006']     _commands = ['command', 'shell']    _modules = {        'apt-get': 'apt-get',#сокращаю список, чтобы не постить простыню        'yum': 'yum',    }     def matchtask(self, file, task):        if task['action']['__ansible_module__'] not in self._commands:            return         first_cmd_arg = get_first_cmd_arg(task)        if not first_cmd_arg:            return         executable = os.path.basename(first_cmd_arg)        if executable in self._modules and \                boolean(task['action'].get('warn', True)):            message = '{0} used in place of {1} module'            return message.format(executable, self._modules[executable])

Логика примерно такая - если task является command или shell и первый аргумент является командой из списка_modules - рекомендуем заменить команду на модуль.

Нашу проверку мы организовали похожим образом - составили список интересующих нас аргументов (password/pass/login итд) и анализируем каждый аргумент тасков command и shell на попадание в список.

Кроме того мы учитываем атрибут no_log, при его наличии аргументы не анализируем. Если в будущем разработчик уберет no_log - новый код опять попадет на проверку и наше правило это увидит.

Итак, после проверки Gitleaks запускается Ansible Lint, который в режиме автоматического сканирования прогоняет наше правило. Другие правила мы отключили, т.к. не можем навязывать командам использование линтера (хотя и рекомендует всем).

Проверяем withCredentials

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

node {  stage('Jenkins Credentials | Decrypt Secret File') {    withCredentials([file(credentialsId: 'credentials-id', variable: 'secretFile')]) {      sh 'cat $secretFile'    }  }}

А вот тут нас ждал провал.

Jenkinsfile это по сути groovy, который можно разобрать наAbstract Syntax Treeи анализировать. Мы использовалиAstBuilderчтобы получить дерево, научились находитьwithCredentials, анализировать его параметры и находить их использование внутри withCredentials. Например, код выше мы научились анализировать и реагировать на него.

Однако в жизни все сложнее,например я могу записатьsecretFile в глобальную переменную и использовать где-то в другом stage, могу записатьsecretFile в какой то временный файл итп итд.

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

Кстати, если у вас используется только декларативный pipeline то потенциально можно использоватьPipeline model definition plugin, в котором можно конвертнуть pipeline в удобный json и анализировать уже его. Нам это к сожалению не подошло - у нас вовсю используется скриптовый.

Что в результате?

Мы провели внутренний пилот, в который попало 1000 pull request. Порядка 3% были ложные срабатывания gitleaks, в некоторых случаях были найдены реальные секреты, опасные скрипты ansible.

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

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

Подробнее..

Linux Sandbox

30.03.2021 20:09:12 | Автор: admin

Для будущих студентов курса "Administrator Linux. Professional" и всех интересующихся подготовили статью, автором которой является Александр Колесников.


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

Linux особенности песочниц

Основная проблема песочниц ОС Linux для анализа приложений это ограниченная поддержка процессоров, на которых работает операционная система. Так как использовать для каждой архитектуры свою физическую машину весьма дорого. То как компромисс используют виртуализированные решения типа Hyper-V, VMWare или VBox. Эти решения достаточно хорошо справляются со своей задачей, но они позволяют проводить виртуализацию только на той архитектуре, на которой работает основной хост. Чтобы запустить код для ARM, MIPS и других архитектур, придется обращаться к другим продуктам, которые могут эмулировать необходимые команды процессора. Попробуем собрать как можно больше проектов и посмотреть какие архитектуры процессоров поддерживаются.

Cuckoo Sandbox

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

Эта песочница очень популярна для анализа вредоносного программного обеспечения для ОС Windows. Разработчики утверждают, что она так же может работать и с ОС Linux. Разница только будет заключаться в том, что вместо виртуальной машины на Windows, должна быть настроена виртуальная машина на Linux. Попробуем найти еще аналоги.

LiSA Sandbox

Opensource песочница для анализа кода под ОС Linux. Найти репозиторий песочницы можно тут. Документация гласит, что эта песочница может анализировать исполняемые файлы с платформ:

  • x86_64

  • i386

  • arm

  • mips

  • aarch64

Подобная эмуляция возможна из-за использования эмулятора Qemu. Также песочница предлагает статический и динамический анализ исполняемого файла. Статический анализ производится за счёт инструмента radare2, а динамический за счет специального расширения ядра, которое собирает события взаимодействия с ОС: файловые операции, сетевые взаимодействия, запуск команд и процессов. В документации описывается, что поддержка такого большого количества процессоров позволяет работать с прошивками устройств. То есть можно просмотреть что выполняет программное обеспечение, которое будет записано на IoT устройства. Весьма полезная фича, учитывая, что вредоносы могут быть записаны в образ прошивки устройств и работать там на постоянной основе.

Из особенностей использование контейнеров Docker. Это качественно ускоряет настройку песочницы. Интерфейс песочницы:

Limon Sandbox

Относительно простой набор скриптов, который используется для анализа приложений. Не имеет возможностей для расширения.(Кроме написания новой логики). Может анализировать приложения теоритически на любом процессоре, так как полностью функционал записан на Python. Найти репозиторий можно здесь. Песочница оперирует следующими инструментами:

  • Yara

  • ssdeep

  • ldd

  • strace

  • Inetsim

  • tcpdump

  • volatility

  • sysdig

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

drakvuf Sandbox

Найти репозиторий можно тут. Единственный набор инструментов, который не направлен специально на исследование вредоносного кода. Этот набор используется для изучения программного обеспечения в целом. Записываемые артефакты выполнения приложений настолько подробны, что можно анализировать и вредоносное программное обеспечение. Документация гласит, что этот набор инструментов может работать практически со всеми популярными ОС. Единственное условие, которое нужно выполнить на устройстве - вложенная виртуализация. Все действия песочницы реализуются за счет перехвата системных функций ОС, которая используется в качестве хостовой для исследуемого приложения. К сожалению, для просмотра данных песочницы необходимо самостоятельно парсить данные из json.

Detux

Репозиторий песочницы можно найти здесь. Песочница для анализа вредоносного кода. Умеет анализировать следующие архитектуры:

  • х86

  • х86_64

  • ARM

  • MIPS

  • MIPSEL

В качестве базового гипервизора используется проект Qemu. Песочница автоматически собирает трафик и идентификаторы компрометации. Вся информация помещается в отчет. В отличии от аналогов не предоставляет красивого интерфейса, а записывает всё в отчет в формате json.

Как видно из списка выше, выбор хоть и небольшой среди песочниц все-таки есть. Однако, набор инструментов практически везде одинаковый:

  • radare2

  • Volatility

  • Yara

  • кастомный перехватчик для системных вызовов.

Поэтому пользователю придется сделать выбор, будет ли он анализировать данные, которые производит песочница или нет.


Узнать больше о курсе "Administrator Linux. Professional".

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

Подробнее..

Шпаргалка по pip, 6 заблуждений насчет AIOps, бесплатный онлайн-курс, а еще про Windows-программы на Linux

11.03.2021 18:13:13 | Автор: admin

Собрали много инсайтов, мероприятий, книжек и шпаргалок. Оставайтесь с нами станьте частью DevNation!

Узнать новое

  • Edge-вычисления и IoT идеальная парочка
    Edge локализует обработку данных как можно ближе к устройствам IoT, что положительно влияет на задержки, производительность, затраты и безопасность корпоративных ИТ-систем

Скачать

  • Шпаргалка по pip
    Поможет эффективно использовать pip менеджер сторонних пакетов для Python

Почитать на досуге

Мероприятия:

  • Виртуальный Red Hat Summit 2021, 27-28 апреля
    Бесплатная онлайн-конференция Red Hat Summit это отличный способ узнать последние новости ИТ-индустрии, задать свои вопросы техническим экспертам, услышать истории успеха непосредственно от заказчиков Red Hat и увидеть, как открытый код генерирует инновации в корпоративном секторе

Подробнее..

Перевод Как я сломал и починил кластер Kubernetes, работающий на Raspberry Pi

21.04.2021 20:21:36 | Автор: admin

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

Мой домашний кластерёнок вырос в зрелый кластер из шести нод (всё благодаря супруге, которая знала, что мне подарить на день рождения, естественно, Raspberry Pi, и не один!), и я встал перед выбором либо ещё раз выполнить собственные инструкции из статьи об установке кластера Kubernetes на Raspberry Pi, либо, применив системную инженерию (DevOps и SRE), полностью автоматизировать процессы переделки кластера и создания системы управления кластером. Эту статью можно считать прямым дополнением к моей первой статье Как я собрал домашний кластер Kubernetes на базе Raspberry Pi.


Соображения о времени и трудозатратах

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

Вариант 1

Встать с утра пораньше и повторить все операции вручную в соответствии с инструкциями из моей собственной статьи и, возможно, кое-что в процессе улучшить.

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

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

Вариант 2

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

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

Хроника автоматизации. Начало

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

Принцип 1. Настройка кластера Kubernetes на Raspberry Pi выполняется в три этапа: это настройка карты памяти, настройка нод на системном уровне и развёртывание ресурсов Kubernetes.

Принцип 2. На моём старом Intel NUC работает NFS-сервер, подключённый к хранилищу DROBO. Было бы заманчиво использовать его в качестве постоянного общего хранилища для всех нод.

Принцип 3. Кластер Raspberry Pi работает в моей домашней сети VLAN, поэтому вопросы безопасности меня волнуют не особо. Все службы и ноды должны быть легко доступны без всяких хитростей с именами и паролями.

Итак, держа всё это в голове, я приступил к программированию моего маленького Франкенштейна. Для повторения результатов (то есть для того, чтобы система заработала) вам понадобятся:

  • Mac (для форматирования карты). Если выберу время для установки Linux VM, попытаюсь обновить скрипт обнаружения платформы.

  • Ansible (я использовал версию 2.10.6).

  • Terraform (я использовал версию 0.13.4, но 0.14.8 тоже подойдёт).

  • Make, полезный инструмент, чтобы не колдовать над параметрами.

Кластер rPi. Первый шаг

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

Что происходит на первом шаге?

  • Форматируется карта памяти.

  • Карта памяти делится на два раздела: 1 ГБ плюс то, что останется.

  • Образ Alpine Linux копируется на карту памяти.

  • Создаётся системный оверлей.

Системный оверлей настраивает eth0 на "неразборчивый" (promisc) режим, это нужно для работы MetalLB, и разрешает подключение SSH к нодам Raspberry Pi без пароля.

Важно: проверьте источник 001-prepare-card.sh и убедитесь, что /dev/disk5 это именно вставленная карта памяти, иначе можно потерять данные.

Результат: подготовка шести карт памяти займёт около одной минуты.

Кластер rPi. Второй шаг

Начинается самое интересное. Итак, вы вставили в Raspberry Pi карты памяти, подсоединили все кабели (сетевые и питания) и загрузили систему. Теперь нужно получить IP-адреса устройств. Это можно сделать, либо подключив экран к каждому из них и запустив команду ifconfig eth0, либо зайдя в маршрутизатор и проверив информацию на нём. Введите в файл pi-hosts.txt соответствующие значения.

[masters]pi0 ansible_host=192.168.50.132 # Pi0[workers]pi1 ansible_host=192.168.50.135 # Pi1pi3 ansible_host=192.168.50.60  # Pi3pi4 ansible_host=192.168.50.36  # Pi4pi2 ansible_host=192.168.50.85  # Pi2pi5 ansible_host=192.168.50.230 # Pi5

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

Добавьте в файл ~/.ssh/config следующую строку, она даст root-доступ для всех нод с именами pi*.

Host pi?  User root  Hostname %h.local

Теперь наши микровычислители (вот видите, насколько я стар!) готовы, и нам нужно подготовить их к запуску Ansible. Это можно легко сделать с помощью скрипта 001-prepare-ansible.sh, который подключиться по ssh к каждому определённому в файле pi-hosts серверу, на каждом сервере настроит chrony для NTP и установит интерпретатор Python.

Важно: возможно, потребуется открыть файл rpi.yaml и изменить раздел vars в соответствии с вашими предпочтениями. Я поступил именно так.

После этого шага запускаем на Ansible команду ansible-playbook rpi.yaml -f 10, которая выполнит следующие действия:

ОБЩИЕ:

  • Установит необходимые пакеты.

  • Разобьёт на разделы и отформатирует карту памяти RPI.

  • Настроит параметры "большого" раздела как системного диска.

  • Добавит записи в файл fstab.

  • Подтвердит изменения.

  • Перезапустит Pi, чтобы тот загрузился с "постоянного" раздела.

KUBEMASTER:

  • Настроит мастер-ноду с помощью kubeadm.

  • Сохранит токены локально (в файле static/token_file).

  • Определит на Pi пользователя с правами root с доступом к kubectl.

  • Сохранит настройки Kubernetes локально (в файле static/kubectl.conf).

KUBEWORKER:

  • Скопирует токены на рабочие ноды.

  • После этого через файл токенов рабочие ноды будут присоединены к мастер-ноде.

  • Скопирует kubectl.conf на root-пользователей рабочих нод.

БАЗОВЕ:

  • Снимет отметку с мастер-ноды, что позволит ей принимать рабочие нагрузки.

  • Установит на ноды py3-pip, PyYaml и Helm.

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

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

Результат: подготовка шести нод с базовой установкой Kubernetes занимает пару минут, в зависимости от скорости подключения к Интернету.

Кластер rPi. Третий шаг

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

Вначале посмотрим на конфигурацию.

# Variables used for barebone kubernetes setupnetwork_subnet    = "192.168.50"net_hosts = {  adguard = "240"  adguard_catchall = "249"  traefik = "234"  torrent_rpc = "245"}nfs_storage = {  general = "/media/nfs"  torrent = "/mnt/drobo-storage/docker-volumes/torrent"  adguard = "/mnt/drobo-storage/docker-volumes/adguard"}# ENV variable: TRAEFIK_API_KEY sets traefik_api_key# ENV variable: GH_USER, GH_PAT for authentication with private containers

Кластер запускается в сети по адресу 192.168.50.0/24, но по умолчанию MetalLB будет использовать "конец" пула сетевых адресов с адресами 200-250. Поскольку у меня есть домашний торрент-сервер и DNS от Adguard, мне нужно задать для них конкретные адреса. Также мне нужен обслуживающий дашборды и прочие инструменты балансировщик нагрузки Traefik.

Важные замечания:

Значения nfs_*_path должны быть совместимы с настройками, заданными на втором шаге.

Убедитесь, что в конфигурационный файл Kubernetes ~/.kube/config is добавлены данные из файла static/kubernetes.conf. В качестве контекстного имени я использую home-k8s.

Что делает Terraform?

Устанавливает flannel, а также патч конфигурационных параметров для host-gw; устанавливает metalLB и задает сетевые параметры var.network_subnet 200250.

Устанавливает прокси Traefik и открывает к нему доступ в домашней сети через балансировщик нагрузки metalLB. Доступ к самому дашборду Traefik осуществляется по traefik.local.

Дашборд Traefik запускается на кластере Pi.Дашборд Traefik запускается на кластере Pi.

Устанавливает DNS-службу Adguard с запросами к хранилищам данных (persistent volumes claims) с использованием NFS; открывает доступ к дашборду (adguard.local) через Traefik и к самой службе через выделенный в домашней сети IP-адрес.

Adguard Home запускается на кластере Pi.Adguard Home запускается на кластере Pi.

Устанавливает и развёртывает на всех нодах стек мониторинга Prometheus и Grafana. Вносит изменения в DaemonSet Prometheus, устраняя необходимость в монтировании томов. Также через Traefik определяет Grafana как grafana.local. Имя и пароль пользователя Grafana по умолчанию admin:admin. В Grafana уже есть предустановленный плагин devopsprodigy-kubegraf-app. Я считаю его лучшим для мониторинга кластеров.

На кластере Pi запускается дашборд Grafana.На кластере Pi запускается дашборд Grafana.

Устанавливает дашборд Kubernetes и через Traefik определяет его как k8s.local.

Дашборд Kubernetes запускается на кластере Pi.Дашборд Kubernetes запускается на кластере Pi.

Устанавливает и развёртывает торрент-сервер (rTorrent) с веб-интерфейсом Flood. Дашборд отображается как torrent.local. Для хранения данных (в том числе конфигурационных) дашборд использует множество точек монтирования. Причина, по которой значение репликации должно быть установлено в 1, объясняется просто. У rTorrent есть проблемы с файлами блокировки, и, поскольку эта программа использует разделяемый каталог, она просто не запустится, если будет обнаружен файл блокировки. У меня rTorrent настроен на прослушивание на порте 23340.

Поскольку Raspberry Pi запускается с карты памяти, эта карта из-за постоянных операций чтения-записи со временем может выйти из строя. Поэтому я решил регулярно делать резервные копии etcd на NFS. Программа резервного копирования запускается раз в сутки с параметрами, устанавливаемыми Terraform. Каждая резервная копия весит около 32 мегабайт.

Запуск Terraform

Чтобы несколько упростить задачу, я создал Makefile, который может оказаться полезным при настройке. Возможно, понадобится установить следующие переменные окружения:

TRAEFIK_API_KEY // Traefik API keyGH_USER // Github userGH_PAT // Github Personal Access Token

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

ADDITIONAL_ARGS=-var 'traefik_api_key=$(TRAEFIK_API_KEY)' -var "github_user=$(GH_USER)" -var "github_pat=$(GH_TOKEN)"apply:cd infrastructure; terraform apply $(ADDITIONAL_ARGS) -auto-approve -var-file ../variables.tfvarsplan:cd infrastructure; terraform plan $(ADDITIONAL_ARGS) -var-file ../variables.tfvarsdestroy:cd infrastructure; terraform destroy $(ADDITIONAL_ARGS) -var-file ../variables.tfvarsdestroy-target:cd infrastructure; terraform destroy $(ADDITIONAL_ARGS) -var-file ../variables.tfvars -target $(TARGET)refresh:cd infrastructure; terraform refresh $(ADDITIONAL_ARGS) -var-file ../variables.tfvarsinit:cd infrastructure; rm -fr .terraform; terraform initimport:cd infrastructure; terraform import $(ADDITIONAL_ARGS) -var-file ../variables.tfvars $(ARGS)lint:terraform fmt -recursive infrastructure/

Заключительные замечания

Полный код можно взять на GitHub. Им можно свободно пользоваться и менять как угодно (как обычно, ваши комментарии и замечания горячо приветствуются). Я также опубликовал переделанные образы Docker (rTorrent и Flood) с multiarch-архитектурой, поддерживающей процессоры ARM64. Я довольно часто вычищаю весь кластер и делаю сборку с нуля, используя упомянутый репозиторий, а по мере появления новых функций я буду вносить в него соответствующие изменения.

Автоматизация экономит время и нервы и надо признать! хорошо выполняет свою работу. Именно поэтому DevOps-инженеров так ценят на рынке (зарплаты с пятью нулями в месяц уже обычное дело). Если хотите так же приходите учиться, помимо современных учебных программ, у нас есть менторы из ведущих компаний Рунета, которые поделятся с вами своим практическим опытом.

Узнайте, как прокачаться и в других специальностях или освоить их с нуля:

Другие профессии и курсы
Подробнее..

Стоит ли переходить с Powershell DSC на Ansible и как это сделать

03.03.2021 14:04:54 | Автор: admin
Об IaC под Windows пишут мало, потому что DevOps/SRE ассоциируется в основном c Linux и Kubernetes. Мы решили исправить эту ситуацию и сравнить инструменты, которыми можно управлять IaC на базе Windows. Статья будет полезна разработчикам, которые работают с Windows-инфраструктурой и выбирают способы управления, и тем, кто уже внедрил Powershell DSC или Ansible, но сомневается в своем решении. Ниже поделимся опытом и расскажем:
  • как устроен Powershell DSC и чем он отличается от Ansible при управлении инфраструктурой на Windows;
  • почему мы перешли на Ansible;
  • с какими проблемами столкнулись и как их решали;
  • как соотносятся ожидания и реальность после перехода на Ansible;
  • кому стоит выбрать Powershell DSC, а кому Ansible.

Почему изначально выбрали PowerShell DSC


В Mindbox развита культура DevOps/SRE, несмотря на преимущественно Windows-инфраструктуру: Hyper-V, IIS, MS SQL Server. И хотя компания постепенно переходит на Linux и Open Source, Windows пока превалирует.

Чтобы управлять этой инфраструктурой, планировали использовать инфраструктурный код: писать его, сохранять в репозиторий, а затем с помощью какого-то инструмента превращать код в реальную инфраструктуру. В то время как Ansible самый популярный инструмент управления инфраструктурой через код, он все-таки традиционно ассоциируется с Linux-миром. Нам хотелось что-то нативное и заточенное под Windows, поэтому выбрали PowerShell DSC.

Как работает Powershell DSC


PowerShell Desired State Configuration (DSC) это сервис, который уже есть в Windows из коробки и помогает управлять инфраструктурой через конфигурационные файлы. На вход он принимает инфраструктурный код на PowerShell, а внутри преобразует его в команды, которые конфигурируют систему. Кроме тривиальных операций, например установки Windows-компонентов, изменения ключей реестра, создания файлов или конфигурирования служб, он умеет многое из того, что обычно выполняется PowerShell-скриптами. Например, полный цикл настройки DNS или высокодоступный инстанс MS SQL Server.



Полезные ссылки к схеме:
Пример простой конфигурации для документов по DSC
Как использовать датафайлы
Как использовать SQL Server-базу в качестве бэкэнда для Windows Server 2019
Как настроить DSC pull server для работы с базой данных SQL для версий раньше Windows Server 2019

Чем DSC отличается от Ansible


Критерий DSC Ansible
Архитектура Служба на каждом управляемом хосте. В случае pull-модели, отдельный управляющий хост и база данных, пререквизиты в виде .NET Framework 4.0 и WMF 5.1 Несколько исполняемых файлов, например ansible, ansible-playbook и ansible-inventory. Запускается с любого Linux-хоста, пререквизит у управляемых хостов один python
Хранение состояния хостов Можно хранить в базе данных Нет
Кроссплатформенность Да Да, включая управление сетевыми устройствами
Pull/push-режимы Pull и push Только push
Устранение дрифта конфигурации Есть в pull-режиме Нет
Декларативность Декларативно-процедурный: важна последовательность выполнения тасков, можно писать недекларативные конструкции в любом месте, а значит, больше шансов написать запутанный код Декларативно-процедурный: важна последовательность выполнения тасков, при этом невозможно написать скриптовый код, не обернув в таск
Аудитория ~1300 ресурсов в Gallery ~20000 ролей в Ansible Galaxy
Используемый язык PowerShell YAML
Инвентаризация Да, по удобству проигрывает Ansible Да
Единица распространения Ресурс (модуль) Роль

Проблемы, которые возникли с DSC


Ожидания от DSC оправдались не во всём. Кроме этого, во время работы возникли новые потребности, которые не могли удовлетворить с помощью DSC.

Разработчики не могут использовать инструмент самостоятельно без помощи SRE. Хотя почти в каждой команде есть SRE, инструмент IaC должен быть достаточно простым, чтобы разработчик мог им пользоваться и тратить на это не больше получаса. DSC позволяет использовать не только декларативный код, но и любые конструкции на Powershell. Это значит, что высок шанс сделать код, который будет сложно сопровождать или который приведет к инфраструктурной аварии. Например, развертывание приложения с некорректными параметрами не в той среде.

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

Для DSC трудно организовать синтаксические проверки и проверки стиля кода. Инструментов для проверки мало, и они не обновляются. Для Ansible мы это уже сделали.

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

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

Избыточное использование двух отличных друг от друга инструментов IaC, которые конфигурируют серверы. Ansible может делать то же, что и DSC, а ведь мы уже используем Ansible для конфигурирования Linux-хостов и сетевого оборудования.

Как планировали перейти с DSC на Ansible


Сначала задача казалась простой, приблизительно на месяц. Мы выделили три этапа работ:
  • научиться подключаться к Windows-хостам с помощью Ansible;
  • переписать конфигурации DSC с помощью Ansible-модулей;
  • удалить DSC pull server, его базу данных и прочие артефакты.

Вот какой рабочий процесс был на DSC, и как планировали организовать в Ansible:




Стандартная структура ролей в Ansible

На Ansible мы планировали отделить сложный код, который что-то конфигурирует и устанавливает, в код ролей и разнести роли по отдельным репозиториям. В главном репозитории Ansible должны были остаться только вызовы ролей, переопределения параметров ролей и списки серверов по группам. Так не только SRE, но и любой разработчик мог бы развернуть роль на нужные серверы или подправить параметр, не углубляясь в логику инфраструктурного кода. Исправить же код роли разработчик сможет только после ревью SRE.

С какими сложностями столкнулись при переходе на Ansible и как их решали


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

WinRM или SSH


Первый сюрприз состоял в выборе типа подключения. В случае Windows их два WinRM и SSH. Оказалось, что Ansible медленно работает через WinRM. При этом Ansible не рекомендует использовать OpenSSH из коробки для Windows Server 2019. И мы нашли новое решение:
  1. Форкнули и переделали под себя роль из Galaxy.
  2. Написали плейбук, в котором есть только вызов этой роли. Это единственный плейбук, при котором идет подключение к хостам по WinRM.
  3. Стандартными средствами Prometheus Blackbox Exporter сделали мониторинг порта 22/tcp и версии OpenSSH:

    - alert: SSHPortDown
    expr: probe_success{job=~".*-servers-ssh",instance!~".*domain..ru"} == 0
    for: 1d
    annotations:
    summary: "Cannot reach {{`{{ $labels.instance }}`}} with SSH"
  4. Выбрали и настроили LDAP-плагин для инвентаризации, чтобы не вписывать вручную все Windows-серверы из домена в статическую инвентаризацию:

    plugin: ldap_inventory
    domain: 'ldaps://domain:636'
    search_ou: "DC=domain,DC=ru"
    ldap_filter: "(&(objectCategory=computer)(operatingSystem=*server*)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))"
    validate_certs: False
    exclude_hosts:
    - db-
    account_age: 15
    fqdn_format: True
  5. Развернули везде OpenSSH с нужными ключами и убедились, что ни одного алерта о недоступности Windows-серверов по SSH больше нет.
  6. Чуть позже интегрировали установку OpenSSH в стандартный образ. Наши образы готовятся с помощью Packer, который также умеет вызывать Ansible:

    "type": "shell-local",
    "tempfile_extension": ".ps1",
    "execute_command": ["powershell.exe", "{{.Vars}} {{.Script}}"],
    "env_var_format": "$env:%s=\"%s\"; ",
    "environment_vars": [
    "packer_directory={{ pwd }}",
    "ldap_machine_name={{user `ldap_machine_name`}}",
    "ldap_username={{user `ldap_username`}}",
    "ldap_password={{user `ldap_password`}}",
    "ansible_playbooks={{user `ansible_playbooks`}}",
    "github_token={{user `github_token`}}"
    ],
    "script": "./scripts/run-ansiblewithdocker.ps1"


Рефакторинг


Когда мы переписывали код под Ansible, то периодически натыкались на дублирование кода. Например, почти все конфигурации DSC содержали установку windows_exporter. Единственное, что отличалось это коллекторы, которые экспортер должен был использовать:



Чтобы избавиться от дублированного кода, вынесли windows_exporter в отдельную Ansible-роль, а параметры этой установки в переменные групп хостов.

Second hop authentication


Наверное, second hop authentication самая распространенная проблема, с которой сталкиваются те, кто начал использовать Ansible под Windows:

- name: Custom modules loaded into module directory
win_copy:
src: '\\share\dsc\modules'
dest: 'C:\Program Files\WindowsPowerShell\Modules'
remote_src: yes


Такая конструкция вызывает ошибку Access Denied из-за того, что по умолчанию делегировать учетные данные для авторизации на удаленном ресурсе невозможно без дополнительных настроек. Обойти ошибку помогает, например, new_credentials. Но мы предпочли воспользоваться тем, что Ansible умеет вызывать ресурсы DSC через модуль win_dsc. Вызываем DSC-ресурс File, который по умолчанию выполняется под учетной записью компьютера. Делегация Kerberos в этом случае не нужна:

- name: Custom modules loaded into module directory
win_dsc:
resource_name: File
SourcePath: '\\share\dsc\modules'
DestinationPath: 'C:\Program Files\WindowsPowerShell\Modules'
Type: Directory
Recurse: true
Force: true
MatchSource: true


При этом нет противоречия в том, чтобы отказываться от DSC, но использовать его ресурсы, если они лучше решают задачу, чем модуль Ansible. Главная цель прекратить использовать DSC-конфигурации, потому что нас не устраивала именно экосистема DSC, а не сами ресурсы. Например, если нужно создать виртуальный свитч Hyper-V, то придется использовать ресурс DSC в Ansible пока нет средств по управлению конфигурацией Hyper-V.

Сетевой дисконнект


Некоторые задачи вызывают отключение сети (дисконнект) на конфигурируемых серверах. Например, создание виртуального свитча Hyper-V из примера выше:

- name: External switch present
win_dsc:
resource_name: xVMSwitch
Ensure: 'Present'
Name: 'Virtual Network'
Type: 'External'
NetAdapterName: 'TEAM_LAN'
AllowManagementOS: True


Проблема в том, что в DSC такой вызов работает, а в Ansible завершается с ошибкой, так как управляемый хост дисконнектнул. Это происходит потому, что Windows всегда дисконнектит при создании виртуального экстернал-свитча. Решение добавить к таску аргумент async:

async: 10

Так Ansible отправляет таск на хост, ждет заданное время и только потом запрашивает состояние.

Дрифт инфраструктуры


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

Чтобы облегчить работу с IaC, мы собрали все скрипты и документы и сделали единые однозначные инструкции. Кроме этого, организовали процесс так, чтобы никто не внес случайные изменения в Ansible. Мы храним весь инфраструктурный код в GitHub, а задачи инженерам ставим через GitHub Projects, поэтому у нас есть возможность связывать изменения инфраструктурного кода (pull requests) с задачами. Так мы можем посмотреть изменения по каждой выполненной задаче. Если у задачи не будет изменений, то её не примут и вернут на доработку.

Баги сбора фактов


В отличие от DSC, Ansible при запуске собирает факты об управляемых хостах, чтобы у разработчика была возможность определить поведение тасков в зависимости от состояния хоста. При сборе фактов с Windows-хостов Ansible может выдавать ошибку, из-за некорректного кода модуля. Чтобы её исправить, нужно подключить коллекцию ansible.windows.

[WARNING]: Error when collecting bios facts: New-Object : Exception calling ".ctor" with "0" argument(s): "Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: index" At line:2
char:21 + ... $bios = New-Object -TypeName


Пайплайн для Ansible перед запуском каждого плейбука проверяет наличие файлов requirements.yml со списком необходимых ролей и коллекций, а затем устанавливает их. Туда мы и добавили коллекцию ansible.windows.

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

Тесты


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

Для тестирования Ansible обычно используют инструмент molecule как обертку для запуска тестов. Это удобный инструмент для Linux-ролей, но в случае с Windows есть проблема. Раньше molecule умела поднимать инфраструктуру сама, но сейчас разработчики убрали такую возможность. Теперь инфраструктура поднимается либо с помощью molecule в Docker, либо вне molecule. Протестировать Windows-роли в Docker чаще всего невозможно: Hyper-V и большинство других Windows-фич в Docker-контейнере не установятся. Придется разворачивать инфраструктуру под тесты вне molecule и использовать delegated driver в molecule.

Эту задачу мы еще не решили, но нашли инструменты, которые обнаружат самые очевидные ошибки:

Проверка Функционал Комментарий
Синтаксическая проверка Проверяет синтаксис и возможность запуска кода Используем синтаксическую проверку и линтинг локально и в репозитории. Например, встраиваем в pre-commit check и настраиваем GitHub Action, который будет запускаться при каждом pull request
Линтинг Проверяет код на логические ошибки
Dry run Позволяет до запуска плейбука узнать, что он сделает Используем в пайплайне раскатки кода: запускаем ansible-playbook с флагами check и diff, затем оцениваем изменения и подтверждаем раскатку. Когда пишем роли, учитываем, что для некоторых тасков необходимо явно указывать, что именно они должны поменять. Например, win_command и win_shell

Как устроена работа с Ansible


После того как мы внедрили Ansible и преодолели все сложности, сформировался процесс действий инженеров и автоматических запусков:
  1. Инженер пишет код роли и тесты к ней, если это роль для Linux-серверов. Как только инженер решит, что роль готова, он делает pull request в отдельный бранч в GitHub-репозитории, созданном специально для роли.
  2. При создании pull request автоматически запускается воркфлоу GitHub Actions, который выполняет синтаксическую проверку и линтинг роли. Если это Linux-роль, то запускаются еще и тесты. Инженер проверяет, что всё хорошо, и при необходимости исправляет.
  3. Другой инженер делает ревью кода из pull request. После того как автор роли исправляет все замечания, код вливается в мастер-бранч, а версия роли автоматически повышается.
  4. Теперь нужно развернуть новую версию роли. Версии перечислены в специальных файлах requirements.yml, которые лежат в GitHub-репозитории с плейбуками. Для каждого плейбука отдельный такой файл. Автор роли изменяет версию в таком файле. Если нужно развернуть роль на серверы, которых нет в инвентаризации Ansible, автор дополняет инвентаризацию. Потом автор снова создает pull request, но уже в репозиторий с плейбуками.
  5. После подтверждения pull request снова запускается GitHub Actions, который создает новый релиз в Octopus Deploy. Роль готова к развертыванию.
  6. Инженер заходит в Octopus Deploy и запускает развертывание. Процесс развертывания позволяет инженеру ограничить теги и хосты, а также переопределить переменные аналогично опциям команды ansible-playbook: --tags, --limit и --extra-vars.
  7. Процесс развертывания сначала запускает режим проверки, который показывает, какие изменения будут сделаны. Инженер оценивает результат проверки и либо подтверждает развертывание кода на целевую инфраструктуру, либо сначала устраняет обнаруженные недостатки.

Организация работы с Ansible




Что выбрать: DSC или Ansible


Перейти с DSC на Ansible Если важно:
  • отслеживать состояние тасков;
  • иметь возможность пропустить конфигурацию в режиме dry run перед прокаткой;
  • модифицировать инфраструктурный код;
  • делать синтаксические и логические проверки.


Если Linux-хосты или сетевое оборудование уже управляются с помощью Ansible.

Если не боитесь работать с Linux, потому что Ansible нужно централизованно запускать на Linux, будь то агент CI/CD системы или Docker-контейнер.
Внедрить с нуля или остаться на DSC Если инфраструктура только на Windows и вы не хотите работать с Linux.

Если готовы дописывать свои ресурсы для DSC.

Нужно хранить состояние инфраструктуры, а также исправлять её дрифт.
Внедрить с нуля Ansible Если управляете смешанной Windows/Linux средой и хотите переделать существующие скрипты в инфраструктурный код и разворачивать его с помощью CI/CD систем.


Евгений Берендяев, SRE-инженер
Подробнее..

Перевод Приключения с Ansible уроки, извлеченные из практики

12.04.2021 20:16:10 | Автор: admin

В преддверии старта Экспресс-курса IaC Ansible делимся с вами переводом материала.


Ansible это мощный инструмент ИТ-автоматизации и, аналогично другим подобным инструментам, его освоение требует времени.

Я использовал Ansible для автоматизации развертывания и управления корпоративными приложениями и извлек ряд уроков, с которыми хотел бы поделиться, чтобы помочь другим. В конце концов, это open source принцип.

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

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

Стандарты

Для автоматизации важны три вещи:

  1. стандарты

  2. стандарты и

  3. ах да, стандарты!

Довольно просто написать небольшой плейбук для выполнения конкретной админской задачи. Но при разработке сотни плейбуков / ролей для автоматического развертывания целого программного стека в разных окружениях все становится довольно сложным. Кроме того, ваши плейбуки и роли будут усложняться, если вы не будете переиспользовать роли и не стандартизируете их. Начать изучение Ansible вы можете с этого туториала (англ.).

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

Далекая-далекая галактика

Ansible Galaxy, централизованный репозиторий ролей Ansible от сообщества ваш друг. Вам обязательно стоит посмотреть не решил ли уже кто-то другой вашу проблему.

Но будьте осторожны. Содержимое Galaxy использовать можно, но его следует внимательно изучить и разобраться, прежде чем размещать в своем окружении. Вы легко и быстро можете испортить себе жизнь, используя роль, которая делает что-то неожиданное.

Инвентори

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

Например, у нас было много XML-файлов, содержащих информацию о конфигурации нашего программного обеспечения. Мы создали шаблоны Jinja и перенесли конфигурацию из XML-файлов в Ansible Inventory. XML-файлы стало гораздо проще генерировать и деплоить на серверы. Кроме того, для работы с разными окружениями мы использовали специально созданный Vars Plugin, написанный на Python, загружающий только нужные данные конфигурации в инвентори.

Обработчики

Обработчики (handlers) это задачи, которые запускаются по событию. Только по событию и только один раз для плейбука. Например, вы можете настроить обработчики для такого события, как перезапуск сервиса или приложения. Использование обработчиков в роли, управляющей определенным сервисом, может быть отличным решением для создания ясных и понятных задач в файле handlers / main.yml, запускаемых при изменении сервиса.

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

Идемпотентность поймите ее, живите с ней, любите ее

Идемпотентность это образ жизни ИТ-автоматизации. Вам нужно понять ее, а затем при написании кода автоматизации жить в соответствии с ней. Так что же это такое? Процитирую глоссарий Ansible, операция "идемпотентна, если результат однократного применения операции такой же, как результат ее многократного применения".

То есть вы можете запускать свои плейбуки несколько раз, а конечный результат будет оставаться неизменным целевые серверы будут находится в "desired state". И если ваш плейбук не заработает, то вы можете устранить проблемы и запустить плейбук еще раз. Поскольку плейбук является идемпотентным, целевые серверы должны быть в "desired state", и никаких других изменений не произойдет.

Что в имени твоём?

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

  1. Не пишите в плейбуках Ansible комментарии как при разработке программного обеспечения, например, "# это комментарий". Помните, Ansible это не язык программирования, это инструмент автоматизации.

  2. Вместо этого для описания Play и Task используйте "Name" с понятным названием. Опишите свои намерения, но постарайтесь воздержаться от технических деталей.

Самая большая польза от свойства "Name" заключается в том, что оно отображается при запуске плейбука. Это помогает при диагностике. А комментарии не отображаются.

Мой опыт показывает, что плейбуки / роли / задачи пишутся разными людьми в компании (по крайней мере, командами Dev и Ops), и все должны понимать, что делает задача.

Именование и приоритет переменных

Будьте осторожны при именовании переменных в инвентори, плейбуках и ролях Ansible. Помните, что Ansible присваивает каждую переменную определенному хосту (Host Variables). Поэтому с осторожностью используйте общие имена, такие как "java_path: /my path". Есть вероятность, что другое приложение на том же хосте использует Java, но по другому пути. Часто на одном хосте могут размещаться несколько приложений, использующих разные версии Java, расположенные по разным путям.

Указывайте в переменных префикс, используя название вашей компании. Например, переменные приложения AppOne, созданного компанией ABC и использующее Java 1.8, могут выглядеть следующим образом:

abc_appone_java_path: "/opt/appone/java"abc_appone_java_version: "1.8"

Не копипастите

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

Используйте IDE

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

  • Подсветка синтаксиса YAML и поддержка линтеров.

  • Подсветка синтаксиса Python и поддержка линтеров Ansible написан на Python, и, поверьте мне, вам иногда придется смотреть исходный код.

  • Интеграция с системой контроля версий.

  • Интеграция с JIRA возможность управлять изменениями сразу в нескольких тикетах.

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

  • Поддержка Ruby если вы также собираетесь работать с Vagrant.

  • Поддержка Groovy если вы собираетесь работать с заданиями Jenkins.

Эти советы помогут вам быть на шаг впереди и избежать тех ошибок, которые совершал я при использовании Ansible для развертывания и автоматизации корпоративного программного обеспечения. У вас будут свои новые и интересные ошибки! Для дополнительной информации о лучших практиках работы с Ansible смотрите раздел лучших в практик документации Ansible и скачайте чеклист по автоматизации, а также следите за блогами Red Hat и Ansible.


Узнать подробнее про Экспресс-курс IaC Ansible.

Подробнее..

Продолжаем прокачивать Ansible

22.02.2021 06:06:33 | Автор: admin

Поводом для этой статьи послужил пост в чате @pro_ansible:

Vladislav ? Shishkov, [17.02.21 20:59]Господа, есть два вопроса, касаются кастомной долгой операции, например, бекапа: 1. Можно ли через ансибл прикрутить прогрессбар выполнения кастомного баша? (если через плагин, то пните в какой-нибудь пример или документацию плиз) 2. Вроде хочется для этого баша написать плагин, но встает вопрос, как быть и как решать моменты выполнения, которые идемпотентны?

Беглый поиск по задворкам памяти ничего подходящего не подсказал. Тем не менее, я точно вспомнил, что код Ansible легко читаемый, и искаропки поддерживает расширение как плагинами, так и обычными Python-модулями. А раз так, то ничего не мешает в очередной раз раздвинуть границы возможного. Hold my beer!...

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

Исходный вопрос можно свести к двум простейшим шагам:

  1. Захватить stdout команды на целевом хосте

  2. Передать его на управляющий хост.

Передаём данные на управляющий хост

Предлагаю начать с конца: с организации дополнительного канала передачи на управляющий хост. Решение этого вопроса выглядит достаточно очевидным: вспоминаем, что Ansible работает поверх ssh, и используем функцию обратного проброса порта:

Код на Python
# добавляем куда-нибудь сюда:# https://github.com/ansible/ansible/blob/5078a0baa26e0eb715e86c93ec32af6bc4022e45/lib/ansible/plugins/connection/ssh.py#L662self._add_args(    b_command,    (b"-R", b"127.0.0.1:33333:" + to_bytes(self._play_context.remote_addr, errors='surrogate_or_strict', nonstring='simplerepr') + b":33335"),    u"ANSIBLE_STREAMING/streaming set")

Как это работает? При сборке аргументов командной строки для установления ssh-соединения эта конструкция предоставит нам на целевом хосте порт 33333 по адресу 127.0.0.1, который будет туннелировать входящие соединения на контроллер - прямиком на порт 33335.

Для простоты используем netcat (ну правда, ну что за статья без котиков?): nc -lk 33335.

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

Перехватываем stdout

Полдела сделано - идём дальше. Мы хотим перехватить stdout какой-то команды - по логике работы Ansible нам подойдёт модуль shell. Забавной, что он оказался пустышкой - в нём ни строчки кода, кроме документации и примеров, зато находим в нём отсылку к модулю command. С ним всё оказалось хорошо, кроме того факта, что нужная функция в нём напрямую не описана, хотя и использована. Но это уже было почти попадание в яблочко, потому что в итоге она нашлась в другом файле.

Под мысленное просто добавь воды просто добавляем щепотку своего кода:

Опять код
# в начале basic.py, рядом с прочими import'ами import socket# в функции run_command - где-нибудь тут:# https://github.com/ansible/ansible/blob/5078a0baa26e0eb715e86c93ec32af6bc4022e45/lib/ansible/module_utils/basic.py#L2447clientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM);clientSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)clientSocket.connect(("127.0.0.1",33333));# в функции run_command - где-нибудь тут:# https://github.com/ansible/ansible/blob/5078a0baa26e0eb715e86c93ec32af6bc4022e45/lib/ansible/module_utils/basic.py#L2455clientSocket.send(b_chunk);# в функции run_command - где-нибудь тут# https://github.com/ansible/ansible/blob/5078a0baa26e0eb715e86c93ec32af6bc4022e45/lib/ansible/module_utils/basic.py#L2481clientSocket.close()

Собираем воедино и запускаем

Осталось сделать что? Правильно, определиться со способом подключения изменённых модулей в стоковый Ansible. На всякий случай напоминаю: мы поправили один connection plugin, и один модуль из стандартной библиотеки Ansible. Новичкам в этом деле могу рекомендовать статью хабраюзера chemtech с расшифровкой моего доклада на Стачке-2019 (там как раз в том числе объясняется, какие Python-модули куда складывать), ну а опытным бойцам эти пояснения вроде и не нужны :-)

Итак, время Ч. Результат в виде статичной картинки не очень показателен, поэтому я настроил tmux и запустил запись скринкаста.

Для внимательных зрителей скринкаста

В анимации можете увидеть два полезных побочных эффекта:

  • Теперь мы видим stdout всех не-Python процессов, которые запускаются Ansible'ом на целевом хосте - например, тех, что запускаются при сборе фактов;

  • Настройки переиспользования ssh-соединений из другой моей статьи позволяют получать этот самый stdout от удалённой команды уже после отключения Ansible от хоста.

Хотите ко мне на тренинг по Ansible?

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

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

Подробнее..

Практический курс по Ansible анонс и предзаказ

30.04.2021 08:08:53 | Автор: admin

image


К 2021 году Ansible уже стал стандартом в администрировании. В трёх из четырёх DevOps вакансий просят знание Ansible. Из них только в одной из четырёх это преимущество, в остальных требование.


Для тех, кто еще не работал c Ansible или только пользовался ролями и плейбуками, написанными коллегами, мы готовим новый курс Ansible: от первых шагов до большого проекта.


Спикер Всеволод Севостьянов из vene.io (Берлин).


Программа курса


Тема 1. Инсталляция LEMP стека на машину (ручной инсталл)
Инсталляция LEMP стека на несколько машин
Что такое автоматизация инфраструктуры
Что такое Ansible
Требования Ansible
Установка Ansible


Тема 2. Инсталляция LEMP стека с помощью Ansible (playbook, ansible.cfg, hosts, templates)
Пишем первую роль
Пишем плейбук
Пишем конфигурацию
Ansible Galaxy
Практика. Деплой Symphony приложение на PHP с помощью Ansible


Тема 3. Python stack (modules, handlers)
Raw модуль
Устанавливаем uWSGI для сервера
Самоподписанные ssl сертификаты (Lets encrypt )
Практика. Деплой Flask приложение на Python с помощью Ansible


Тема 4. Не веб приложения (roles, jinja2, реестры, группировка хостов, переменные, postgresql_db, postgresqluser, mongodb, script модули, CHANGED WHEN, FAILED_WHEN)
Установка proxy сервера с помощью Ansible
Postgres cluster
Mongo cluster
Практика. Собираем Docker-контейнеры


Тема 5. Патчинг и апдейты с помощью Ansible (pre_tasks, post_tasks, include, serial и мax_fail_percentage, блоки, выбор хостов, лимиты)
Оркестрация
Что такое rolling update и как его накатывать
Практика. Накатываем апдейт веб-приложения под нагрузкой


Тема 6. Как автоматизировать рутинные задачи и зачем (коллбеки, как ускорить Ansible)
Запуск расчетных задач по расписанию
Практика. Автоматизируем ротацию логов и оценку свободного места на
машинах


Тема 7. IaaC и деплой плейбуков
Git hooks с Ansible и автодеплой (Gilab API integration, Gitlab runners)
Практика. Организуем деплой для мультисервисной системы (микросервисное веб-приложение)


Тема 8. Мониторинг (фильтрация логов)
Что такое Prometheus
Сбор системных данных
Сбор логов
Практика. Устанавливаем и настраиваем бизнес/системный мониторинг


Тема 9. Защищенные системы и правильная настройка Ansible в них (подстановки,
фильтры, в целом работа с облаками включая подъем новых машин)
Работа с Google cloud и AWS используя Ansbile
Ansible Vault и что в нем можно хранить
Bastion и правильная настройка Ansible
Ansible Tower
Как лучше ставить воркеры в сети для enterprise окружения


Тема 10. Написание своих модулей
Тема 11. Обзор конкурентов Ansible



Релиз запланирован на 27 августа.
До 6 августа курс стоит 30 000 рублей, а еще можно стать консультантом-тестером и повлиять на итоговую программу.
С 7 августа 40 000 рублей, доступна рассрочка.


Подробнее о курсе

Подробнее..

Ansible playbook для управления WindowsLinux агентами Zabbix

02.05.2021 10:10:12 | Автор: admin

Данная статья про написание простых ansible плейбуков для автоматической установки агентов на хосты с Linux/Windows и регистрации хостов через API Zabbix, включая SNMP хосты. Будут использоваться готовые роли и модули Ansible Galaxy Zabbix.

Zabbix подготовил собственные роли и модули для конфигурации многих компонентов Zabbix посредством Ansible - полный список можно найти здесь - https://galaxy.ansible.com/community/zabbix

В этой статье поговорим только о zabbix_agent и zabbix_host.

*Эта статья не рассматривает и не показывает установку и настройку Ansible, этого в интернете полно.

zabbix_agent

Это роль, которая поддерживает установку агента на следующие операционные системы:

  • Red Hat

  • Fedora

  • Debian

  • Ubuntu

  • opensuse

  • Windows (Best effort)

  • macOS

Актуальный список на официальном github проекта (https://github.com/ansible-collections/community.zabbix/blob/main/docs/ZABBIX_AGENT_ROLE.md#operating-systems)

zabbix_host

Это модуль для добавления/удаления/изменения хостов на сервере zabbix. Полное описание модуля тут - https://docs.ansible.com/ansible/2.10/collections/community/zabbix/zabbix_host_module.html

Итак переходим к настройке

Первым шагом будет установка коллекции ansible ( работает только в ansible версии 2.9+):

ansible-galaxy collection install community.zabbix

После установки коллекции, создаем файл с плейбуком. Данный плейбук делает две вещи - устанавливает роль community.zabbix.zabbix_agent на указанные хосты и вторым шагом регистрирует хост в zabbix через модуль community.zabbix.zabbix_host.

Создаем файл zabbix-agent-all.yaml со следующим содержимым:

- hosts: all   tasks:    - name: Install agent      include_role:        name:  community.zabbix.zabbix_agent # устанавливает готовую роль на хостах      tags:        - install # для удобства можно использовать тэги    - name: Create a new host or update an existing host's info # задача для регистрации агентов в zabbix сервере      local_action:        module: community.zabbix.zabbix_host # используем готовый модуль zabbix_host для регистрации агентов        server_url: "{{ zabbix_url }}" # переменная из инвентаря - url сервера для регистрации API        login_user: "{{ zabbix_api_user }}" # переменная из инвентаря - имя пользователя обычно Admin        login_password: "{{ zabbix_api_pass }}" # переменная из инвентаря - пароль         host_name: "{{ item }}" - # имя хоста из инвентаря        visible_name: "{{ hostvars[item].zabbix_visible_name | default(item) }}" # можно задать отображаемое имя отдельно для каждого хоста        description: "{{   hostvars[item].zabbix_host_description | default('') }} OS: {{  hostvars[item].ansible_distribution | default('') }} {{  hostvars[item].ansible_distribution_version | default('') }}" # берет описание из инвентаря и добавляет название операционной системы и версию из служебных переменных ansible        host_groups: "{{ hostvars[item].zabbix_host_groups }}" # в какую хост группу добавить хост        link_templates: "{{ hostvars[item].zabbix_link_templates }}" # какие template применить к хосту        status: "{{  hostvars[item].zabbix_host_status }}" # статус - Enabled или Disabled        state: present # указание ansible чтобы добавить хост, изменив на absent можно сделать обратную операцию удаления        inventory_mode: disabled # не включаем inventory mode        interfaces: # можно указать несколько интерфейсов          - type: "{{ hostvars[item].zabbix_interface_type }}" # переменная из инвентаря - может быть SNMP, Agent, JMX, IPMI            main: 1             useip: "{{ hostvars[item].zabbix_interface_use_ip }}" # можно использовать добавление по ip или по dns            ip: "{{ hostvars[item].zabbix_interface_ip }}" # если выбрано по ip - берется переменная с ip адресом            dns: "{{ item }}" # если выбрано useip: 0 то нужно указать FQDN хоста в переменных в инвентаре            port: "{{ hostvars[item].zabbix_interface_port }}" # указание порта      loop: "{{ groups['all'] }}" # цикл чтобы пройтись по всех хостам в инвентаре      run_once: true # цикл будет проходиться по всем хостам, чтобы он не прошёл по всем хостам на каждом хосте указываем чтобы данный модуль отработал один раз      tags:        - add-host #  добавляем тэги для удобств

На странице проекта вы сможете найти множество других настроек, например подключение к zabbix сервер через http, изменить версию агента и т.д., все настройки тут:

https://github.com/ansible-collections/community.zabbix/blob/main/docs/ZABBIX_AGENT_ROLE.md#role-variables

Файлы инвентаря

Далее нам нужно создать три файла linux-inventory, windows-inventory и snmp-inventory, можно сделать конечно в одном файле, но мне показалось удобнее разделить агенты по типу.

Linux хосты

Содержимое файла linux-inventory:

[all]host1.local #тут вы указываете ваши хосты по FQDN или IP-адресуhost2.local  zabbix_host_description=The host2 description host3.local zabbix_host_status=disabled # можно указать чтобы отключать хосты после добавленияhost4.local zabbix_host_groups=["Custom group"] #можно передать группы отдельно для каждого хоста[all:vars]zabbix_agent_server=my.zabbix.server # адрес сервераzabbix_url=https://my.zabbix.server # URL сервераzabbix_api_use=true # говорим что будем добавлять через APIzabbix_api_user=Adminzabbix_api_pass=StrongPa$$w0rdzabbix_interface_port="10050" # номер порта где живет агентzabbix_host_groups=["Linux servers"] # в какую группу добавлять хост по умолчаниюzabbix_link_templates=["Linux by Zabbix agent"] # список template для применения по умолчаниюzabbix_interface_type=agent # тип хоста - с использованием агентаzabbix_interface_use_ip="0" # говорим что будем добавлять по dns fqdnzabbix_interface_ip=""zabbix_host_description="My linux server" # описание по умолчаниюzabbix_host_status=enabled # состояние хоста по умолчанию

Windows

Для возможности установки агента на хосты с ОС Windows необходимо сначала настроить доступ для ansible - это вне темы этой статьи, но есть подробная инструкция на сайте ansible - https://docs.ansible.com/ansible/latest/user_guide/windows_setup.html

Содержимое файла windows-inventory:

[all]winhost01.localwinhost02.local  zabbix_host_groups=["Custom group"] #можно передать группы отдельно для каждого хостаwinhost03.local zabbix_host_status=disabled # можно указать чтобы отключать хосты после добавления[all:vars]ansible_user=user@DOMAIN.LOCAL # если хосты в домене необходимо указывать доменный путь, и также настроить kerberos в linuxansible_password=StrongPa$$w0rdansible_connection=winrm # управление windows происходит через службу WinRMansible_winrm_server_cert_validation=ignore # если у вашего домена самоподписанные сертификаты, лучше поставить ignorezabbix_agent_server=my.zabbix.server # адрес сервераzabbix_url=https://my.zabbix.server # URL сервераzabbix_api_use=true # говорим что будем добавлять через APIzabbix_api_user=Adminzabbix_api_pass=StrongPa$$w0rdzabbix_interface_port="10050" # номер порта где живет агентzabbix_host_groups=["Windows servers"]  # в какую группу добавлять хост по умолчаниюzabbix_link_templates=["Windows by Zabbix agent"] # список template для применения по умолчаниюzabbix_interface_type=agent # тип хоста - с использованием агентаzabbix_interface_use_ip="0" # говорим что будем добавлять по dns fqdnzabbix_interface_ip=""zabbix_host_description="My windows server" # описание по умолчаниюzabbix_host_status=enabled # состояние хоста по умолчанию

SNMP

Для добавления SNMP хостов создаем файл snmp-inventory:

[all]snmphost1.local zabbix_link_templates='["Cisco IOS SNMP","Network Generic Device SNMP"]' zabbix_host_groups='["Network devices"]'snmp2.local zabbix_link_templates='["Template SNMP OS ESXi"]' zabbix_host_groups='["Hypervisors"]' zabbix_snmp_community=Community[all:vars]zabbix_url=https://my.zabbix.server # URL сервераzabbix_api_use=true # говорим что будем добавлять через APIzabbix_api_user=Adminzabbix_api_pass=StrongPa$$w0rdzabbix_interface_port="161" # номер порта устройства, по умолчанию 161zabbix_interface_type=snmp # тип snmpzabbix_interface_use_ip="0" # указываем что добавляем по dnszabbix_interface_ip="" # пустой ipzabbix_host_description="My SNMP host" # описание по умолчаниюzabbix_host_status=enabled # состояние хоста по умолчаниюzabbix_snmp_community="MyCommunity" # указываем SNMP community

В итоге, файлы должны иметь следующую структуру файлов:

. linux-inventory snmp-inventory windows-inventory zabbix-agent-all.yaml

Запуск playbook

Для запуска плейбуков, необходимо запустить следующие команды:

Для установки на Linux хостах:

ansible-playbook -i linux-inventory zabbix-agent-all.yaml

Windows:

ansible-playbook -i linux-inventory zabbix-agent-all.yaml

SNMP:

ansible-playbook -i snmp-inventory zabbix-agent-all.yaml

Если нужна только установка, то можно запустить команды выше с тэгом install:

ansible-playbook -i linux-inventory zabbix-agent-all.yaml -t install

Если у вас есть особые настройки zabbix или вы хотите кастомизировать, полный список переменных роли и модуля можно найти на странице проекта: zabbix_agent и zabbix_host

Все исходники данной статьи есть на github: https://github.com/piccadil/zabbix-agent

Подробнее..

Acme.sh Ansible Alias mode Автоматизируем получение и распространение TLS сертификатов

09.06.2021 20:16:20 | Автор: admin

Acme.sh - скрипт, позволяющий без особых проблем получать let's encrypt сертификаты очень разными способами. В данной статье я разберу как получать сертификаты через DNS api, но этим уже никого не удивишь, поэтому расскажу про метод DNS alias, он свежий (всего 3 года) и интересный. А так же про автоматизацию на Ansible и немного про мониторинг сертификатов.

Видеоверсия

Режимы acme.sh получения сертификатов прямо на целевом сервере

  • Webroot

  • Nginx\Apache

  • Stanalone

Режимы хорошие и удобные, когда у вас один - два сервера и можно просто на каждый установить acme.sh. Когда количество серверов, которым нужно TLS, переваливает за десяток, удобнее начать использовать wilcard сертификаты и выделить отдельный сервер для получения и распространения сертификата\ов. Получить wildcard сертификат можно только через подтверждение владения DNS зоной. DNS режимов несколько:

  • DNS manual

  • DNS API

  • DNS alias

Все примеры буду показывать на моём личном домене и установленном локально acme.sh.

DNS manual mode

Manual режим работает супер просто. Запускаем acme.sh с флагом --dns

acme.sh --issue --dns -d *.itdog.info --yes-I-know-dns-manual-mode-enough-go-ahead-please

koala@x220:~$ acme.sh --issue --dns -d *.itdog.info --yes-I-know-dns-manual-mode-enough-go-ahead-please[Ср мая  5 14:52:29 MSK 2021] Using CA: https://acme-v02.api.letsencrypt.org/directory[Ср мая  5 14:52:29 MSK 2021] Creating domain key[Ср мая  5 14:52:29 MSK 2021] The domain key is here: /home/koala/.acme.sh/*.itdog.info/*.itdog.info.key[Ср мая  5 14:52:29 MSK 2021] Single domain='*.itdog.info'[Ср мая  5 14:52:29 MSK 2021] Getting domain auth token for each domain[Ср мая  5 14:52:32 MSK 2021] Getting webroot for domain='*.itdog.info'[Ср мая  5 14:52:32 MSK 2021] Add the following TXT record:[Ср мая  5 14:52:32 MSK 2021] Domain: '_acme-challenge.itdog.info'[Ср мая  5 14:52:32 MSK 2021] TXT value: 'QXRgFOfVOZGOBC1qxAToMNOf7Xsv9gjM8hYG6akRoJ8'[Ср мая  5 14:52:32 MSK 2021] Please be aware that you prepend _acme-challenge. before your domain[Ср мая  5 14:52:32 MSK 2021] so the resulting subdomain will be: _acme-challenge.itdog.info[Ср мая  5 14:52:32 MSK 2021] Please add the TXT records to the domains, and re-run with --renew.[Ср мая  5 14:52:32 MSK 2021] Please add '--debug' or '--log' to check more details.[Ср мая  5 14:52:32 MSK 2021] See: https://github.com/acmesh-official/acme.sh/wiki/How-to-debug-acme.sh

--issue - запрос на получение

--dns без аргумента - режим ручного DNS

--yes-I-know-dns-manual-mode-enough-go-ahead-please - интересное решение проблемы, когда люди не понимают что такое ручной режим

Он выдаёт TXT запись, которую нам необходимо добавить в наш dns хостинг, в данном случае это _acme-challenge.itdog.info TXT QXRgFOfVOZGOBC1qxAToMNOf7Xsv9gjM8hYG6akRoJ8

Ручной он потому что, мы вручную добавляем эту запись.

Анимация manual modeАнимация manual mode

После этого добавления нужно подождать какое-то время, что бы запись зарезолвилась и выполнить такую же команду, только с --renew

После добавления записи проверяем начала ли она резолвится на гугловом dns

koala@x220:~$ dig -t txt _acme-challenge.itdog.info @8.8.8.8; <<>> DiG 9.11.3-1ubuntu1.15-Ubuntu <<>> -t txt _acme-challenge.itdog.info @8.8.8.8;; ANSWER SECTION:_acme-challenge.itdog.info. 1798 IN    TXT    "QXRgFOfVOZGOBC1qxAToMNOf7Xsv9gjM8hYG6akRoJ8"

Она резолвится, а значит можно получать сертификат

koala@x220:~$ acme.sh --renew --dns -d *.itdog.info --yes-I-know-dns-manual-mode-enough-go-ahead-please[Ср мая  5 14:58:08 MSK 2021] Renew: '*.itdog.info'[Ср мая  5 14:58:09 MSK 2021] Using CA: https://acme-v02.api.letsencrypt.org/directory[Ср мая  5 14:58:09 MSK 2021] Single domain='*.itdog.info'[Ср мая  5 14:58:09 MSK 2021] Getting domain auth token for each domain[Ср мая  5 14:58:09 MSK 2021] Verifying: *.itdog.info[Ср мая  5 14:58:13 MSK 2021] Success[Ср мая  5 14:58:13 MSK 2021] Verify finished, start to sign.[Ср мая  5 14:58:13 MSK 2021] Lets finalize the order.[Ср мая  5 14:58:13 MSK 2021] Le_OrderFinalize='https://acme-v02.api.letsencrypt.org/acme/finalize/121...'[Ср мая  5 14:58:15 MSK 2021] Downloading cert.[Ср мая  5 14:58:15 MSK 2021] Le_LinkCert='https://acme-v02.api.letsencrypt.org/acme/cert/042...'[Ср мая  5 14:58:16 MSK 2021] Cert success.-----BEGIN CERTIFICATE-----certificate-----END CERTIFICATE-----[Ср мая  5 14:58:16 MSK 2021] Your cert is in  /home/koala/.acme.sh/*.itdog.info/*.itdog.info.cer [Ср мая  5 14:58:16 MSK 2021] Your cert key is in  /home/koala/.acme.sh/*.itdog.info/*.itdog.info.key [Ср мая  5 14:58:16 MSK 2021] The intermediate CA cert is in  /home/koala/.acme.sh/*.itdog.info/ca.cer [Ср мая  5 14:58:16 MSK 2021] And the full chain certs is there:  /home/koala/.acme.sh/*.itdog.info/fullchain.cer 

После этого TXT запись можно удалить.

Теперь есть ключ и сертификат, который будет действителен 3 месяца. Обновить сертификат можно будет через 2 месяца, let's enctypt даёт запас времени и если у вас вдруг что-то сломается, будет целый месяц чтобы починить и обновить сертификат.

И да, обновляется только сертификат, ключ остаётся таким, какой был выдан в первый раз. Обратите внимание на даты создания файлов, особенно *.example.com.key

# ls -l --time-style=+%Y-%m-%d \*.example.com/total 28-rw-r--r-- 1 root root 1587 2021-04-15 ca.cer-rw-r--r-- 1 root root 3433 2021-04-15 fullchain.cer-rw-r--r-- 1 root root 1846 2021-04-15 *.example.com.cer-rw-r--r-- 1 root root  719 2021-04-15 *.example.com.conf-rw-r--r-- 1 root root  980 2021-04-15 *.example.com.csr-rw-r--r-- 1 root root  211 2021-04-15 *.example.com.csr.conf-rw-r--r-- 1 root root 1675 2019-04-10 *.example.com.key

Хороший режим для одного раза или для понимания как работает подтверждение, но на постоянке каждые 2 месяца вручную обновлять TXT записи не серьёзно, поэтому рассматриваем следующий режим.

DNS API mode

Как это работает? Принцип действия тот же самый что у manual, только acme.sh сам вносит и удаляет TXT запись с помощью API вашего dns провайдера.

Анимация API modeАнимация API mode

Под DNS хостингом и DNS провайдером я буду иметь в виду сервис, в который вносятся DNS записи. Это может быть и DNS хостинг, который есть почти у каждой компании, торгующей доменами (namecheap, beget итд) или как отдельный сервис за деньги (Amazon Route 53, ClouDNS итд), или же ваш собственный сервис развернутый с помощью BIND, PowerDNS итд.

У каждого DNS провайдера свой не стандартизированный API и их поддержка в acme.sh реализована отдельными скриптами. Список всех поддерживаемых провайдеров находится тут https://github.com/acmesh-official/acme.sh/wiki/dnsapi

Для каждого написано как пользоваться и пример. Если вашего провайдера или сервиса нет в списке, можно написать свой скрипт, но не спешите открывать vim, дождитесь третьего способа.

Работу этого режима покажу на примере хостинга DNS у namecheap.

Для каждого DNS провайдера свои настройки, где-то нужно просто включить API и сгенерировать токен, где-то бывает посложнее, например для namecheap нужно ещё внести IP в allow list. Включаем API и сразу генерируется token, добавляем IP в список.

Теперь на локальной машине нужно настроить доступ к API

export NAMECHEAP_USERNAME="USERNAME"export NAMECHEAP_API_KEY="TOKEN"export NAMECHEAP_SOURCEIP="MY-IP"

Отступление про дополнительные флаги force и test. Будем использовать флаг -f (--force), что бы наши сертификаты генерировались заново, т.к. acme.sh видит уже сгенерированные сертификаты при их наличии не будет заново получать. Можно конечно просто сделать rm -rf ~/.acme.sh/domain/ вместо этого. Так же будем использовать флаг --test, что бы лишний раз не нагружать продакшн сервера let's encrypt. Вот такое сообщение мы получим, если после подтверждения в manual режиме попробуем другой режим.

[Ср мая 5 16:39:31 MSK 2021] *.itdog.info is already verified, skip dns-01.

Команда получения через API выглядит таким образом

acme.sh --issue --dns dns_namecheap -d *.itdog.info --test

Здесь после --dns мы добавляем имя провайдера.

Запускаем acme.sh

Раскрыть
koala@x220:~$ acme.sh --issue --dns dns_namecheap -d *.itdog.info --test[Ср мая  5 16:48:05 MSK 2021] Using ACME_DIRECTORY: https://acme-staging-v02.api.letsencrypt.org/directory[Ср мая  5 16:48:06 MSK 2021] Using CA: https://acme-staging-v02.api.letsencrypt.org/directory[Ср мая  5 16:48:06 MSK 2021] Creating domain key[Ср мая  5 16:48:07 MSK 2021] The domain key is here: /home/koala/.acme.sh/*.itdog.info/*.itdog.info.key[Ср мая  5 16:48:07 MSK 2021] Single domain='*.itdog.info'[Ср мая  5 16:48:07 MSK 2021] Getting domain auth token for each domain[Ср мая  5 16:48:09 MSK 2021] Getting webroot for domain='*.itdog.info'[Ср мая  5 16:48:10 MSK 2021] Adding txt value: nCH4tBWCkSVn76301f2SdJqCAzmtXvzQAB_Ag8hURLo for domain:  _acme-challenge.itdog.info[Ср мая  5 16:48:15 MSK 2021] The txt record is added: Success.[Ср мая  5 16:48:15 MSK 2021] Let's check each DNS record now. Sleep 20 seconds first.[Ср мая  5 16:48:36 MSK 2021] You can use '--dnssleep' to disable public dns checks.[Ср мая  5 16:48:36 MSK 2021] See: https://github.com/acmesh-official/acme.sh/wiki/dnscheck[Ср мая  5 16:48:36 MSK 2021] Checking itdog.info for _acme-challenge.itdog.info[Ср мая  5 16:48:37 MSK 2021] Domain itdog.info '_acme-challenge.itdog.info' success.[Ср мая  5 16:48:37 MSK 2021] All success, let's return[Ср мая  5 16:48:37 MSK 2021] Verifying: *.itdog.info[Ср мая  5 16:48:41 MSK 2021] Success[Ср мая  5 16:48:41 MSK 2021] Removing DNS records.[Ср мая  5 16:48:41 MSK 2021] Removing txt: nCH4tBWCkSVn76301f2SdJqCAzmtXvzQAB_Ag8hURLo for domain: _acme-challenge.itdog.info[Ср мая  5 16:48:46 MSK 2021] Removed: Success[Ср мая  5 16:48:46 MSK 2021] Verify finished, start to sign.[Ср мая  5 16:48:46 MSK 2021] Lets finalize the order.[Ср мая  5 16:48:46 MSK 2021] Le_OrderFinalize='https://acme-staging-v02.api.letsencrypt.org/acme/finalize/193...'[Ср мая  5 16:48:48 MSK 2021] Downloading cert.[Ср мая  5 16:48:48 MSK 2021] Le_LinkCert='https://acme-staging-v02.api.letsencrypt.org/acme/cert/fa62...'[Ср мая  5 16:48:49 MSK 2021] Cert success.-----BEGIN CERTIFICATE-----certificate-----END CERTIFICATE-----[Ср мая  5 16:48:49 MSK 2021] Your cert is in  /home/koala/.acme.sh/*.itdog.info/*.itdog.info.cer [Ср мая  5 16:48:49 MSK 2021] Your cert key is in  /home/koala/.acme.sh/*.itdog.info/*.itdog.info.key [Ср мая  5 16:48:49 MSK 2021] The intermediate CA cert is in  /home/koala/.acme.sh/*.itdog.info/ca.cer [Ср мая  5 16:48:49 MSK 2021] And the full chain certs is there:  /home/koala/.acme.sh/*.itdog.info/fullchain.cer

В логе прям видно, что acme.sh добавляет TXT запись, ждёт немного, проверяет запись через доверенные DNS сервера, удаляет запись и скачивает сертификаты с ключом.

После первого запуска через API, acme.sh заносит env переменные c доступами к API себе в файл ~/.acme.sh/account.conf и вам не нужно каждый раз их экспортировать.

Отлично, получение автоматизировали, всё вроде классно. Но у этого метода есть свои недостатки:

  • Если никто не написал скрипта для вашего провайдера\сервиса, то нужно либо писать, либо переезжать на другой провайдер

  • А есть ли у вашего провайдера API?

  • Поразмышляем немного об безопасности. Вот у меня в "открытую" лежит полный доступ к редактированию моего домена, если он каким-то образом попадёт в чужие руки, эти руки могут сделать что угодно. Эту проблему можно решить ограничим доступа в API, например по токену можно только добавлять\удалять txt записи _acme-challenge. Есть ли такая возможность у вашего провайдера? Я не встречал такого, наверное есть у какого-нибудь AWS конечно. Обычно уже хорошо если есть API, а токен один и даёт полный доступ

  • У вас несколько доменов на разных провайдерах (сочувствую). Тут конечно можно настроить каждое API и сделать для каждого провайдера отдельный запуск acme.sh со своими переменными, но мне кажется это не очень удобным. Тем более если у одного из них отсутствует API или скрипт

  • Кто-то просто не любит, что бы в DNS постоянно лазил какой-то скрипт и что-то добавлял\удалял

DNS aliase mode

Это модернизированный режим DNS API.

Идея такая: Есть технический домен, через добавления TXT записей на котором мы подтверждаем владение основным доменом. т.е. acme.sh смотрит CNAME запись у основного домена, видит "перенаправление" на технический домен и идёт к нему проверять TXT запись. А дальше всё как в режиме DNS API.

Анимация alias modeАнимация alias mode

Разберём последовательно. Для демонстрации я купил домен tech-domain.club, он выступает в качестве технического домена. В моём примере основной домен itdog.info располагается на namecheap, а техничский tech-domain.club я делегирую на Hetzner DNS, таким образом операции с записями будут производиться через API Hetzner'a.

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

_acme-challenge CNAME _acme-challenge.tech-domain.club

Для провайдера с техническим доменом мы настраиваем доступ к API.

Экспортируем токен Hetzner export HETZNER_Token="TOKEN"

Команда выглядит так (-f и --test опять же для примера)

acme.sh --issue -d *.itdog.info --challenge-alias tech-domain.club --dns dns_hetzner -f --test
Раскрыть
koala@x220:~$ acme.sh --issue -d *.itdog.info -d itdog.info --challenge-alias tech-domain.club --dns dns_hetzner -f --test[Пт мая  7 13:40:11 MSK 2021] Domains have changed.[Пт мая  7 13:40:11 MSK 2021] Using CA: https://acme-v02.api.letsencrypt.org/directory[Пт мая  7 13:40:11 MSK 2021] Multi domain='DNS:*.itdog.info,DNS:itdog.info'[Пт мая  7 13:40:11 MSK 2021] Getting domain auth token for each domain[Пт мая  7 13:40:15 MSK 2021] Getting webroot for domain='*.itdog.info'[Пт мая  7 13:40:15 MSK 2021] Getting webroot for domain='itdog.info'[Пт мая  7 13:40:15 MSK 2021] Adding txt value: Zlrij9n4y5QXfH6yx_PBn45bgmIcT70-JuW2rIUa6lc for domain:  _acme-challenge.tech-domain.club[Пт мая  7 13:40:16 MSK 2021] Adding record[Пт мая  7 13:40:17 MSK 2021] Record added, OK[Пт мая  7 13:40:20 MSK 2021] The txt record is added: Success.[Пт мая  7 13:40:20 MSK 2021] Let's check each DNS record now. Sleep 20 seconds first.[Пт мая  7 13:40:41 MSK 2021] You can use '--dnssleep' to disable public dns checks.[Пт мая  7 13:40:41 MSK 2021] See: https://github.com/acmesh-official/acme.sh/wiki/dnscheck[Пт мая  7 13:40:41 MSK 2021] Checking itdog.info for _acme-challenge.tech-domain.club[Пт мая  7 13:40:42 MSK 2021] Domain itdog.info '_acme-challenge.tech-domain.club' success.[Пт мая  7 13:40:42 MSK 2021] All success, let's return[Пт мая  7 13:40:42 MSK 2021] *.itdog.info is already verified, skip dns-01.[Пт мая  7 13:40:42 MSK 2021] Verifying: itdog.info[Пт мая  7 13:40:46 MSK 2021] Success[Пт мая  7 13:40:46 MSK 2021] Removing DNS records.[Пт мая  7 13:40:46 MSK 2021] Removing txt: Zlrij9n4y5QXfH6yx_PBn45bgmIcT70-JuW2rIUa6lc for domain: _acme-challenge.tech-domain.club[Пт мая  7 13:40:50 MSK 2021] Record deleted[Пт мая  7 13:40:50 MSK 2021] Removed: Success[Пт мая  7 13:40:50 MSK 2021] Verify finished, start to sign.[Пт мая  7 13:40:50 MSK 2021] Lets finalize the order.[Пт мая  7 13:40:50 MSK 2021] Le_OrderFinalize='https://acme-v02.api.letsencrypt.org/acme/finalize/121...'[Пт мая  7 13:40:52 MSK 2021] Downloading cert.[Пт мая  7 13:40:52 MSK 2021] Le_LinkCert='https://acme-v02.api.letsencrypt.org/acme/cert/04e...'[Пт мая  7 13:40:53 MSK 2021] Cert success.-----BEGIN CERTIFICATE-----certificate-----END CERTIFICATE-----[Пт мая  7 13:40:53 MSK 2021] Your cert is in  /home/koala/.acme.sh/*.itdog.info/*.itdog.info.cer [Пт мая  7 13:40:53 MSK 2021] Your cert key is in  /home/koala/.acme.sh/*.itdog.info/*.itdog.info.key [Пт мая  7 13:40:53 MSK 2021] The intermediate CA cert is in  /home/koala/.acme.sh/*.itdog.info/ca.cer [Пт мая  7 13:40:53 MSK 2021] And the full chain certs is there:  /home/koala/.acme.sh/*.itdog.info/fullchain.cer 

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

Кстати, если вам нужно в одном файле иметь несколько сертификатов, например и itdog.info и wildcard *.itdog.info, то просто перечислите их с -d, например

acme.sh --issue --challenge-alias tech-domain.club --dns hetzner -d *.itdog.info -d itdog.info

Это правило действует и для других методов.

И так, что даёт нам этот режим:

  • Если у вашего провайдера нет API или лень писать скрипт, возьмите технический домен, делегируйте его на сервис, который поддерживает acme.sh

  • Если у вашего провайдера нет настройки прав для доступа через API, то технический домен тоже выручает. В случае, если наш token утечёт, у злоумышленника будет доступ только к вашему техническому домену, и если вы его используете только для acme.sh, то максимум что сможет сделать злоумышленник - получить ключ и сертификат для вашего домена. Это тоже неприятно и можно использовать, но это совершенно другой уровень угрозы, по сравнению с полным доступом к доменной зоне

  • В ситуации с кучей доменов на одном или нескольких провайдерах, жизнь так же становится проще, когда все они просто имеют CNAME запись

Есть так же режим domain-alias, он даёт возможность использовать не _acme-challenge запись, а кастомную, подробности можно прочитать в документации

Автоматизация получения и распространения сертификатов

Мы получили сертификаты, лежат они у нас красиво в ~/.acme.sh и никак не используются. Надо каким-то образом их распространять на сервера. Далее расскажу, как я это делаю с помощью ansible. Ansible используется и для получения\обновления и для распространения. Сразу предупреждаю, мои плейбуки простые как три копейки и заточены под определенную инфраструктуру. Playbooks, hosts на github.

Мой сервер с ansible, уже имеет доступ ко всем необходимым серверам, на нём установлен acme.sh и реализовано два плейбука, на получение и распространение. Кстати, не забудьте закомментировать acme.sh в crontab, что бы не было лишних запросов и путаницы.

Playbook для получения сертификатов

В vars указывается только технический домен, эта переменная используется несколько раз. Токен от API вынесен в отдельный vars файл, что бы хранить его в зашифрованном виде в git. Task "Date and time" нужен для логирования, что бы понимать когда именно что-то пошло не так. Следующие два плейбука это простой shell, отличаются друг от друга количеством доменов в одном файле сертификата. Всем доменам, которым не нужно сочетать в себе обычный и wildcard домен, идут списком в loop.

Домены, которые должны подходить как для обычного, так и для wildcard идут по втором taks, тоже с помощью loop. Если вам нужно например wilcard вида *.*.itdog.info, то просто добавьте ещё один -d и ещё один subkey в item. Опция ignore_errors необходима, потому что exit code 0 будет только 6 раз за год при обновлении сертификата, в остальное время будут сообщения о том, что сертификат не нужно обновлять, для ansible это ошибка на которой он будет останавливаться.

Для чего плейбук на получение? Ведь в acme.sh и так уже всё настроено!

В одном плейбуке мы собираем всю нашу конфигурацию, доступы и все домены, которым необходим TLS, как минимум, это удобно - не надо копаться конфигах acme.sh. В случае изменения, например, токена, мы просто редактируем его в vars_files, а если нужно добавить ещё один домен\подомен, мы просто добавляем его в loop. Ну и в случае переноса сервера, не нужно переносить ~/.acme.sh, только плейбуки с vars_files взять из git.

Playbook для распространения сертификатов

Здесь нужно писать конечно под вашу инфраструктуру, поэтому повторюсь, показываю это для примера.

Три типа серверов из моей инфраструктуры:

  • tls-hosts - Обычный nginx установленный как пакет из стандартного репозитория

  • tls-hosts-docker - Веб проект с тем же nginx, но уже в docker

  • tls-hosts-docker-rename - Сторонний продукт, в который надо подкладывать сертификат и ключ с определённым именем в определённую директорию (например Harbor, Zabbix)

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

Во втором случае всё плюс-минус так же, но уже нужно сделать docker exec project-nginx -s reolad, т.е. уже другой handler.

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

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

nginx.itdog.info tls_path=/etc/letsencrypt/*.itdog.info/ DOMAIN=*.itdog.info

Для случаев, когда доменов на сервере несколько, делается два хоста с разными именами и одинаковым ansible_host (Совет, как сделать лучше, приветствуется).

nginx.example.com-1 ansible_host=nginx.example.com tls_path=/etc/letsencrypt/*.example.com/ DOMAIN=example.comnginx.example.com-2 ansible_host=nginx.example.com tls_path=/etc/letsencrypt/*.example.org/ DOMAIN=example.org

Для каждого типа серверов создана своя группа в hosts. Для каждой группы свои немного отличающиеся друг от друга tasks. Для tls-hosts-docker так же добавлена переменная с именем контейнера nginx. А для tls-hosts-docker-rename добавлена переменная, в которой задаётся конечное имя сертификата и ключа.

docker-zabbix.itdog.info tls_path=/root/docker-zabbix/zbx_env/etc/ssl/nginx/ DOMAIN=*.itdog.info CONTAINER=docker-zabbix_zabbix-web-nginx-pgsql_1 cert_name=ssl.crt key_name=ssl.key

Для nginx нужен fullchain и domain.key - копируются только они. Если файлы различаются, происходит копирование и срабатывает handler nginx -s reload. Так же есть проверка, перед тем как зарелоудить nginx, что это файл. У меня один раз был случай, в самом начале пользования acme.sh, скрипт вместо файла с сертификатом создал директорию. Прямо как traefik 1.7 создаёт acme.json директорию, вместо файла. Поэтому я сделал простую проверку. В идеале нужно делать проверку, что сертификат валидный и не просроченный, но для этого требуется иметь на каждом хосте python-pyOpenSSL.

Crontab

23 3 * * * /usr/bin/ansible-playbook /etc/ansible/playbook-get-tls.yml -v >> /var/log/get-tls.log23 4 * * * /usr/bin/ansible-playbook /etc/ansible/playbook-copy-tls.yml -v >> /var/log/copy-tls.log

Можно без проблем вызывать их каждый день, let's encrypt будет вежливо говорить, что пока не нужно обновляться. А когда придёт срок, сертификаты будут обновлены.

Мониторинг сертификатов

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

Я использую zabbix и скрипт от @selivanov_pavel

Проверим с его помощью мой домен локально

koala@x220 ~/t/acme.sh-test> ./ssl_cert_check.sh expire itdog.info 44341

41 день сертификат на itdog.info будет актуален. Сертификат в let's encrypt обновляется за 30 дней до протухания. А значит, например, если ему осталось жить 10 дней, значит что-то пошло не так и надо идти смотреть.

Темплейт состоит из одного item и одного trigger. Теплейт есть так же на github

ssl_cert_check.sh["expire","{HOST.NAME}","{$TLS_PORT}"]

В item две переменных, первая HOST.NAME берёт имя хоста, предполагается что у нас хост назван по доменному имени. Переменная $TLS_PORT по дефолту 443, но если нужно проверять на нестандартном порту, то записываем значение порта в macros.

Триггер тоже супер простой

{Check tls expire:ssl_cert_check.sh["expire","{HOST.NAME}","{$TLS_PORT}"].last()}<=10

Если полученное значение меньше 10ти - аллерт. Таким образом, мы узнаем если у нас начнут протухать сертификаты и будет 10 дней на починку.

Нормально работает?

Да, acme.sh + DNS API + ansible у меня крутится два года. acme.sh + DNS Alias + ansible крутится пол года. Проблемы возникали только, когда при тестировании доменов забыл отключить crontab и он принёс staging сертификат на прод. Такая проблема решается проверкой на валидность.

Да, в идеале, ansible должен проверять перед копированием, что сертификат валидный и не просроченный. А система мониторинга проверять, помимо expire, валидность сертификатов.

Подробнее..

Шпаргалка по Linux grep, домашний термостат на Raspberry Pi, эл. книга Ansible for DevOps и PostgreSQL на Linux

25.03.2021 12:22:32 | Автор: admin

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

Узнать новое:

  • 4 инструмента для Linux
    С Linux практически любая железка превращается в универсальный сервер общего доступа для файлов, аудио-, видео- и прочих ресурсов, с которыми легко и удобно работать с других платформ и устройств.

Скачать:

  • Шпаргалка по Raspberry Pi
    Как загрузить, как поставить ОС, как включить SSH и подключиться по WiFi, как установить ПО и обновить систему, а также где найти дополнительную справку и ответы на вопросы.

  • Шпаргалка по Linux grep
    Постигаем секреты поиска строк в файлах с помощью регулярных выражений.

Почитать на досуге:

Мероприятия:

  • Виртуальный Red Hat Summit 2021, 27-28 апреля
    Бесплатная онлайн-конференция Red Hat Summit это отличный способ узнать последние новости ИТ-индустрии, задать свои вопросы техническим экспертам, услышать истории успеха непосредственно от заказчиков Red Hat и увидеть, как открытый код генерирует инновации в корпоративном секторе

  • BCC и Red Hat: Освобождение от рутины. Как Ansible automation platform помогает решить задачи IT инфраструктуры любого уровня, 30 марта
    В ходе практической online-сессии проекта ВСС WEBINAR детально изучаем инструменты платформы Red Hat Ansible Automation, необходимые для реализации автоматизации предприятия. Также вас ждет 2 демо-сессии по оптимизации IT-инфраструктуры с помощью Ansible Automation Platform на примере настройки межсетевого экрана в среде Windows и в среде Linux

  • Вебинар про решения Red Hat для SAP на IBM Power Systems, 8 апреля
    Ваш бизнес зависит от среды SAP? Она поддерживает критически важные процессы и предоставляет данные? Неэффективная среда может вызвать задержки, снизить гибкость и повысить расходы в ваших ИТ-подразделениях. Более чем 20-летний опыт инноваций позволяет компаниям Red Hat и SAP разрабатывать решения, отвечающие требованиям критически важных приложений. Red Hat Enterprise Linux это не только стабильная платформа, она дает конкретные преимущества при работе с инсталляциями SAP

Смотри в записи:

  • AnsibleFest
    Два дня интереснейших докладов, демонстраций и практических занятий. Отличная возможность узнать, как разработчики, администраторы и ЛПР в сфере ИТ отвечают на вызовы перемен с помощью гибких технологий автоматизации с открытым кодом, которые позволяют перейти от того, что есть, к тому, что нужно

  • J4K Conference
    Новая виртуальная конференция по Kubernetes, Java и облаку: 17 сессий с сотрудниками Red Hat, включая доклад Марка Литтла (Mark Little), главного человека в Red Hat по связующему ПО.

Подробнее..

Управление инфраструктурой Open Telekom Cloud с помощью Ansible

06.05.2021 10:07:53 | Автор: admin

Open Telekom Cloud

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

Open Telekom Cloud международная публичная облачная платформа, основанная на OpenStack. Платформа идеально подходит для компаний или стартапов, которые работают с европейскими пользователями, чьи персональные данные должны храниться в пределах Евросоюза: сервис разработан Deutsche Telekom и соответствует стандартам защиты данных GDPR (Генеральный регламент о защите персональных данных) EC.

С чего все начиналось

Почти два года назад в поисках специалистов в Россию пришел проект Open Telekom Cloud. Требовалось много людей на автоматизированное тестирование и несколько человек в спецотряд под названием Ecosystems, требования были очень расплывчатые: Ну, надо знать Python и понимать, как работать с облачными сервисами

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

Команда Ecosystems занималась API мониторингом, мониторингом сервисов, созданием модулей для Ansible, разработкой и поддержкой инструментов управления инфраструктурой Open Telekom Cloud. На данный момент она участвует еще и в разработке Terraform Provider, OpenStack SDK, OpenStack Ansible Collections. Во всех наших инструментах (OTC Extensions, Terraform Provider, Ansible Collections) мы стараемся максимально соответствовать OpenStack и переиспользовать существующие решения для него.

С самого начала с Open Telekom Cloud все оказалось довольно интересно. Разработка находится на стороне Huawei, декларировалось, что облако основано полностью на технологии OpenStack. Но Huawei внесли множество своих решений в сервисы. Многие из них были полностью написаны китайскими коллегами, были заметные отличия нашего API от OpenStack API.

Но тогда это нас не сильно волновало. Первой нашей задачей в Ecosystems было создание мониторингов, которые помогут определить качество работы тех или иных сервисов, в сложных сценариях. Например, использовать балансировщик нагрузки для хостов в разных AZ (availability zone), наблюдать за распределением запросов по каждому из хостов. Или следить за отказоустойчивостью того же балансировщика нагрузки в сценарии, когда один или несколько хостов за ним выключаются по тем или иным причинам.

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

Что ж, раз Terraform стал для нас критичным инструментом, мы отправились в репозиторий с провайдером и начали исправлять баги. В какой-то момент поддержка провайдера со стороны Huawei прекратилась, да и через некоторое время HashiCorp решили всех сторонних провайдеров убрать из своего репозитория.

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

С начала работы на проекте прошло примерно полгода, из-за провайдера объем работы вырос и стало понятно, что два человека со всем не справятся. Мы набрали в команду толковых ребят, и часть из них стала заниматься развитием и поддержкой Terraform провайдера, часть осталась на мониторингах.

Ansible и коллекции

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

Точкой входа был AWX, он вызывал плейбуки с ролью Terraform, результат выполнения Terraform снова передавался в Ansible, и далее выполнялись какие-то сценарии.

Кажется, что все отлично, есть базовый .tf модуль, который создает сетевую часть, и отдельные модули на каждый сценарий, .state всех модулей хранится в s3. Все удобно, работает как часы.

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

Был только Python и Ansible

Учитывая, что Open Telekom Cloud не является решением, на 100% совместимым с OpenStack, в нем присутствуют сервисы собственной разработки, например, RDS (Relational Database Service). С помощью Ansible мы не можем построить все необходимые нам ресурсы используя OpenStack SDK и ansible-collections-openstack, в таком виде, чтобы это было легко поддерживать.

Что ж, надо расширять возможности OpenStack SDK, адаптировать под наш проект и писать собственные коллекции. Для коллекций необходимо описание ресурсов, которых нет в OpenStack SDK, для таких ресурсов был создан проект OTC Extensions.

OTC Extensions

Этот проект дополняет и расширяет функции OpenStack SDK для работы с Open Telekom Cloud, так же если он установлен в качестве python package, в OpenStack Client добавляются дополнительные плагины для работы с облаком.

Взаимодействует с:

python-openstacksdk

python-openstackclient

Структура проекта близка к OpenStack SDK:

otcextensions/    sdk/        compute/            v2/                server.py                _proxy.py    tests/        unit/            sdk/                compute/                    v2/                        test_server.py

Все дополнительные ресурсы унаследованы от базового openstack.resource.Resource, или если мы хотим изменить существующий объект то нужно наследование от него базового класса этого объекта, например, если у openstack.compute.v2.server нет поддержки тэгов или они реализованы иначе:

class Server(server.Server):    def add_tag(self, session, tag):        """Adds a single tag to the resource."""    def remove_tag(self, session, tag):        """Removes a single tag from the specified server."""

И далее патчим Connection в методе load (otcextensions/sdk/__init__.py):

openstack.compute.v2.server.Server.add_tag = server.Server.add_tagopenstack.compute.v2.server.Server.remove_tag = server.Server.remove_tag

В итоге наш connection теперь будет работать с кастомными тегами.

Для нового ресурса:

otcextensions/    sdk/        elb/            v2/                elb_certificate.py                _proxy.py

В файле elb_certificate.py создаем класс, указываем его url, какие методы поддерживает, какие параметры принимает

class Certificate(resource.Resource):resources_key = 'certificates'base_path = ('/lbaas/certificates')# capabilitiesallow_create = Trueallow_fetch = Trueallow_commit = Trueallow_delete = Trueallow_list = True_query_mapping = resource.QueryParameters(    'id', 'name', 'description',    'type', 'domain', 'content',    'private_key', 'marker', 'limit',)# Properties#: Namename = resource.Body('name')#: Idid = resource.Body('id')#: Descriptiondescription = resource.Body('description')#: Certificate type.type = resource.Body('type')#: Domain name associated with the server certificate.domain = resource.Body('domain')#: Private key of the server certificate. *Type: string*private_key = resource.Body('private_key')#: Public key of the server certificate or CA certificate. *Type: string*content = resource.Body('certificate')#: Administrative status of the certificate.admin_state_up = resource.Body('admin_state_up')#: Creation timecreate_time = resource.Body('create_time')#: Specifies the project ID.project_id = resource.Body('tenant_id')#: Time when the certificate expires.expire_time = resource.Body('expire_time')#: Time when the certificate was updated.update_time = resource.Body('update_time')

Рядом обязательно должен быть файл _proxy.py, этот класс адаптер предоставляет интерфейс для работы с инстансом Connection, в нем мы описываем методы ресурса:

class Proxy(_proxy.Proxy):    skip_discovery = True    # ======== Certificate ========    def create_certificate(self, **attrs):        return self._create(_certificate.Certificate, **attrs)    def certificates(self, **query):        return self._list(_certificate.Certificate, **query)    def delete_certificate(self, certificate, ignore_missing=True):        return self._delete(_certificate.Certificate, certificate,                            ignore_missing=ignore_missing)    def get_certificate(self, certificate):        return self._get(_certificate.Certificate, certificate)    def update_certificate(self, certificate, **attrs):        return self._update(_certificate.Certificate, certificate, **attrs)    def find_certificate(self, name_or_id, ignore_missing=False):        return self._find(_certificate.Certificate, name_or_id,                          ignore_missing=ignore_missing)

В otcextensions/sdk/__init__.py eсть структура со всеми нестандартными ресурсами - OTC_SERVICES, добавляем наш ресурс по имени папки в которой он находится:

'elb': {    'service_type': 'elb',    'replace_system': True}

OTC_SERVICES так же в методе load, добавляются в Connection:

for (service_name, service) in OTC_SERVICES.items():    if service.get('replace_system', False):        if service['service_type'] in conn._proxies:            del conn._proxies[service['service_type']]    sd = _get_descriptor(service_name)    conn.add_service(sd)

На этом добавление сервиса завершено, мы можем его использовать через OpenStack SDK.

cfg = openstack.config.get_cloud_region(cloud=TEST_CLOUD_NAME)conn = connection.Connection(config=cfg)sdk.register_otc_extensions(conn)cert = conn.elb.create_certificate(    private_key=PRIVATE_KEY,    content=CERTIFICATE,    name=NAME )

Ansible collections

Окей, ресурсы теперь есть, осталось разобраться как их использовать, есть отличный вариант, создать коллекцию своих модулей и хранить ее в ansible-galaxy, по аналогии с ansible-collections-openstack создаем коллекцию ansible-collection-cloud, которая основана на OTC extensions.

Если модуль, который мы добавляем в коллекцию существует в OpenStack коллекции, то мы стараемся максимально обеспечить обратную совместимость, создавая единый интерфейс для модулей.

Делаем все по гайду (developing-collections):

ansible-collection-cloud/    plugins/        module_utils/            otc.py        modules/            elb_certificate.py            elb_certificate_info.py

В module_utils, храним базовый для всех модулей класс:

class OTCModule:    """Openstack Module is a base class for all Openstack Module classes.    The class has `run` function that should be overriden in child classes,    the provided methods include:    """

В нем создается инстанс Connection, и патчится через OTC extensions, чтобы мы могли использовать кастомные ресурсы.

Все модули делятся на два типа с постфиксом _info возвращают информацию о существующем ресурсе, без него создают/изменяют/удаляют ресурсы.

Например, lb_certificate_info:

from ansible_collections.opentelekomcloud.cloud.plugins.module_utils.otc import OTCModuleclass LoadBalancerCertificateInfoModule(OTCModule):    argument_spec = dict(        name=dict(required=False)    )    otce_min_version = '0.10.0'    def run(self):        data = []        if self.params['name']:            raw = self.conn.elb.find_certificate(name_or_id=self.params['name'], ignore_missing=True)            if raw:                dt = raw.to_dict()                dt.pop('location')                data.append(dt)        else:            for raw in self.conn.elb.certificates():                dt = raw.to_dict()                dt.pop('location')                data.append(dt)        self.exit_json(            changed=False,            elb_certificates=data        )def main():    module = LoadBalancerCertificateInfoModule()    module()if __name__ == '__main__':    main()

аналогично выполнен и lb_certificate.

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

Установка коллекций

Для начала работы необходимо установить коллекции в окружение, для примера используем venv (venv использовать необязательно, но такой подход имеет свои плюсы):

/$ cd ~~$ python3 -m venv ansiblevenv

Активируем окружение:

~$ source ansiblevenv/bin/activate(ansiblevenv) ~$

Установим OpenStack Client, otcextensions и wheel (необязательно):

(ansiblevenv) ~$ pip install wheel(ansiblevenv) ~$ pip install openstackclient(ansiblevenv) ~$ pip install otcextensions

Для работы с коллекциями далее необходимо установить их из Ansible-Galaxy (Ansible-Galaxy содержит множество свободно распространяемых ролей и коллекций, разрабатываемых сообществом). Дополнительно ставим OpenStack коллекцию для нативных ресурсов:

(ansiblevenv) $ ansible-galaxy collection install opentelekomcloud.cloud(ansiblevenv) $ ansible-galaxy collection install openstack.cloud

В принципе все для работы с облаком готово, осталось разобраться с авторизацией. OpenStackподдерживает несколько способов авторизации.

Авторизация

clouds.yaml

OpenStack client/sdk самостоятельно ищет файл для авторизации в следующих местах:

  1. system-wide (/etc/openstack/{clouds,secure}.yaml)

  2. Home directory / user space (~/.config/openstack/{clouds,secure}.yaml)

  3. Current directory (./{clouds,secure}.yaml)

clouds:  otc:    profile: otc    auth:      username: '<USER_NAME>'      password: '<PASSWORD>'      project_name: '<eu-de_project>'      # or project_id: '<123456_PROJECT_ID>'      user_domain_name: 'OTC00000000001000000xxx'      # or user_domain_id: '<123456_DOMAIN_ID>'    account_key: '<AK_VALUE>' # AK/SK pair for access to OBS    secret_key: '<SK_VALUE>'

После того, как файл создан, указываем переменной окружения OS_CLOUD имя конфигурации, в данном случае:

~$ export OS_CLOUD=otc

Имя может быть любым, и один файл может содержать множество конфигураций.

Чтобы проверить, что все сделано правильно, можно запустить любую команду OpenStack клиента:

~$ openstack server list

Если авторизация успешна, то мы получим список серверов:

Чтобы повысить безопасность, можно вынести чувствительную информацию из clouds.yaml. Рядом с файлом clouds.yaml создаем secure.yaml и помещаем туда все, что хотим скрыть:

clouds:  otc:    auth:      password: '<PASSWORD>'

Переменные окружения

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

# .ostackrc fileexport OS_USERNAME="<USER_NAME>"export OS_USER_DOMAIN_NAME=<OTC00000000001000000XYZ>export OS_PASSWORD=<PASSWORD> # optionalexport OS_TENANT_NAME=eu-deexport OS_PROJECT_NAME=<eu-de_PROJECT_NAME>export OS_AUTH_URL=https://iam.eu-de.otc.t-systems.com:443/v3export NOVA_ENDPOINT_TYPE=publicURLexport OS_ENDPOINT_TYPE=publicURLexport CINDER_ENDPOINT_TYPE=publicURLexport OS_VOLUME_API_VERSION=2export OS_IDENTITY_API_VERSION=3export OS_IMAGE_API_VERSION=2

Создаем переменные:

~$ source .ostackrc

С авторизацией разобрались! Теперь можно полноценно использовать коллекции.

Использование коллекции

Как мы знаем в коллекции два типа модулей: с постфиксом info возвращают информацию о существующем ресурсе, без него создают/изменяют/удаляют ресурсы. Все модули вызываются по полному имени: opentelekom.cloud.*

Все info модули поддерживают поиск как по имени, так и по id ресурса, например:

- name: Get loadbalancer info  opentelekomcloud.cloud.loadbalancer_info:    name: "{{ lb_name_or_id }}"  register: result

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

Пример сценария

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

Для создания нативных ресурсов всегда используются OpenStack модули, например, сеть:

---- name: Create main network  openstack.cloud.network:    name: my_network  register: net- name: Getting info about external network  openstack.cloud.networks_info:    name: admin_external_net  register: ext_net- name: Create subnet  openstack.cloud.subnet:    name: my_subnet    network_name: "{{ net.network.name }}"    cidr: 192.168.0.0/16    dns_nameservers:          - 100.125.4.25          - 100.125.129.199  register: subnet- name: Create router  openstack.cloud.router:    name: "{{ public_router_scenario }}_router"    enable_snat: true    network: "{{ ext_net.openstack_networks[0].id }}"    interfaces:      - "{{ subnet.subnet.name }}"  register: router

Для сервера нам нужен ключ:

- name: Create key pair  openstack.cloud.keypair:    name: bastion_key_pair    public_key_file: "/tmp/keys/public.pub"  register: keypair

Создадим security group, откроем порты 80, 443 и 22 для ssh, также откроем icmp:

- name: Create security group  openstack.cloud.security_group:    name: bastion_secgroup    description: Allow external connections to ssh, http, https and icmp  register: sec_group- name: Add rules for tcp connection to the security group  openstack.cloud.security_group_rule:    security_group: "{{ sec_group.secgroup.name }}"    protocol: tcp    port_range_min: "{{ item }}"    port_range_max: "{{ item }}"    remote_ip_prefix: 0.0.0.0/0  loop:     - 22    - 80    - 443- name: Add a rule for icmp connection to the security group  openstack.cloud.security_group_rule:    security_group: "{{ secur_group.secgroup.name }}"    protocol: icmp    port_range_min: -1    port_range_max: -1    remote_ip_prefix: 0.0.0.0/0

Для подключения сервера к сети необходимо создать порт:

- name: Create a port for a bastion  openstack.cloud.port:    name: bastion_port    network: net.network.id    security_groups:      - "{{ sec_group.secgroup.name }}"     fixed_ips:       - ip_address: 192.168.200.10  register: port

Для создания сервера тоже используются нативные модули. Например, создадим bastion (это те хосты, которые принято использовать как jump для доступа в недоступные снаружи сети). Здесь также представлен пример инъекции команд при создании сервера через userdata:

- name: Getting information about a current image  openstack.cloud.image_info:    image: Standard_Debian_10_latest  register: image- name: Create a new instance  openstack.cloud.server:    state: present    name: bastion    flavor: s2.medium.2    key_name: bastion_key_pair    availability_zone: eu-de-01    security_groups:     - "{{ sec_group.secgroup.name }}"    timeout: 200    userdata: |      {%- raw -%}#!/usr/bin/env bash                 #setup ssh service config                 file=/etc/ssh/sshd_config                 cp -p $file $file.old &&                     while read key other; do                         case $key in                         GatewayPorts) other=yes ;;                         AllowTcpForwarding) other=yes ;;                         PubkeyAuthentication) other=yes ;;                         PermitTunnel) other=yes ;;                         esac                         echo "$key $other"                     done <$file.old > $file                 sudo service sshd restart                 mkdir -p /etc/sslcerts/live                 #generate Diffie-Hellman for TLS                 sudo openssl dhparam -out /etc/sslcerts/live/dhparams.pem 2048      {% endraw %}    nics:      - port-name: "{{ port.port.name }}"    boot_from_volume: true    volume_size: 5    image: "{{ image.openstack_image.id }}"    terminate_volume: true    delete_fip: true    auto_ip: true  register: bastion

Для динамической регистрации хоста используем add_host:

- name: Register nodes  add_host:    name: "{{ bastion.openstack.name }}"    groups: bastions    ansible_host: "{{ bastion.openstack.interface_ip }}"    ansible_ssh_user: linux    ansible_ssh_private_key_file: "/path/to/key"

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

- name: Wait for nodes to be up  hosts: bastions  gather_facts: no  tasks:    - name: Wait for nodes to be up      wait_for_connection:        timeout: 250

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

После того, как у нас создана сеть и есть хотя бы один сервер, мы можем создать loadbalancer:

- name: Create loadbalancer  opentelekomcloud.cloud.loadbalancer:    name: my_elastic_loadbalancer    state: present    vip_subnet: "{{ subnet.subet.id }}"    vip_address: 192.168.200.100    auto_public_ip: true  register: loadbalancer

Далее для loadbalancer создаем listener, если протокол https, то сразу можем создать сертификат:

- name: Create listener http  opentelekomcloud.cloud.lb_listener:    state: present    name: my_listener_http    protocol: http    protocol_port: 80    loadbalancer: "{{ loadbalancer.loadbalancer.id }}"  register: listener_http- name: Create Server certificate  opentelekomcloud.cloud.lb_certificate:    name: my_https_cetificate    content: "{{ some_https_certificate }}"    private_key: "{{ some_loadbalancer_https_key }}"  register: certificate- name: Create listener https  opentelekomcloud.cloud.lb_listener:    state: present    name: my_listener_https    protocol: terminated_https    protocol_port: 443    loadbalancer: "{{ loadbalancer.loadbalancer.id }}"    default_tls_container_ref: "{{certificate.elb_certificate.id }}"  register: listener_https

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

- name: Create lb pool http  opentelekomcloud.cloud.lb_pool:    state: present    name: my_pool_http    protocol: http    lb_algorithm: round_robin    listener: "{{ listener_http.listener.id }}"  register: lb_pool_http- name: Create lb pool https  opentelekomcloud.cloud.lb_pool:    state: present    name: my_pool_https    protocol: http    lb_algorithm: round_robin    listener: "{{ listener_https.listener.id }}"  register: lb_pool_https

Добавляем сервер в пул:

- name: Create members for a http pool in the load balancer  opentelekomcloud.cloud.lb_member:    state: present    name: my_member_http    pool: "{{ lb_pool_http.server_group.id }}"    address: 192.168.200.10    protocol_port: http    subnet: "{{ subnet.subet.id }}"  register: members_http- name: Create members for a https pool in the load balancer  opentelekomcloud.cloud.lb_member:    state: present    name: my_member_https    pool: "{{ lb_pool_https.server_group.id }}"    address: 192.168.200.10    protocol_port: http    subnet: "{{ subnet.subet.id }}"  register: members_https

И, наконец, добавим healthmonitor для каждого пула, чтобы наблюдать за статусом хостов:

- name: Enable health check for http members  opentelekomcloud.cloud.lb_healthmonitor:    state: present    name: http_healthcheck    pool: "{{ lb_pool_http.server_group.id }}"    delay: 1    max_retries: 2    monitor_timeout: 1    type: http- name: Enable health check for https members  opentelekomcloud.cloud.lb_healthmonitor:    state: present    name: https_healthcheck    pool: "{{ lb_pool_https.server_group.id }}"    delay: 1    max_retries: 2    monitor_timeout: 1    type: http

Если выполнять плейбук с verbosity, то в консоли мы увидим все параметры создаваемых ресурсов.

В результате на консоли можно увидеть наш сервер, балансировщик нагрузки и все остальные ресурсы:

Таким образом мы перевели инфраструктуру наших мониторингов полностью на Ansible.

Насколько мне известно, в России не одна компания пользуется услугами Huawei для создания собственных облачных сервисов, было бы интересно увидеть в комментариях, приходилось ли им решать подобные вопросы касаемо расширения ванильного OpenStack SDK и как они к этому подходили.

Весь код находится в публичном доступе и хранится на Github:

Если тема интересна, то буду рад поделиться своим опытом по работе с другими инструментами. Пишите в комментариях, готов ответить на ваши вопросы!

Подробнее..

Бесплатный онлайн-курс Основы Ansible, шпаргалка по GNU Screen, запись Red Hat Summit и многое другое

06.05.2021 14:05:14 | Автор: admin

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

Узнать новое:

Скачать:

Что еще интересного:

Мероприятия:

Вебинары:

Подробнее..

Перевод Настройка сетевого стека Linux для высоконагруженных систем

05.04.2021 18:06:45 | Автор: admin

Максимизируем количество входящих соединений

В рамках набора учащихся на курс "Administrator Linux. Professional" подготовили перевод полезного материала.

Приглашаем всех желающих посетить открытый демо-урок
Практикум по написанию Ansible роли. На этом вебинаре участники вместе с экспертом будут писать, тестировать и отлаживать ansible роли. Это важно для тех, кто хочет автоматизировать настройку инфраструктуры, поскольку это один из инструментов, который это позволяет сделать. Сетевой стек одна из самых запутанных вещей в Linux. И не только из-за сложности некоторых концепций и терминов, но и из-за изменения смысла некоторых параметров в разных версиях ядра. В этой статье приведена информация для ядра 2.2 и выше, а также, там где это возможно, указано различие между версиями вплоть до 5.5.

О том как изменять параметры ядра, описываемые здесь, можно прочитать в статье Linux Kernel Tuning for High Performance Networking: Configuring Kernel Settings.

Очередь приема и netdev_max_backlog

Каждое ядро процессора перед обработкой пакетов сетевым стеком может хранить их в кольцевом буфере. Если буфер заполняется быстрее, чем TCP-стек обрабатывает пакеты, то пакеты отбрасываются и увеличивается счетчик отброшенных пакетов (dropped packet counter). Для увеличения размера очереди следует использовать параметр net.core.netdev_max_backlog.

net.core.netdev_max_backlog параметр задается на ядро процессора.

Очередь ожидающих запросов на соединение и tcp_max_syn_backlog

Соединения создаются для SYN-пакетов из очереди приема и перемещаются в очередь ожидания (SYN Backlog Queue). Также соединение помечается как "SYN_RECV" и клиенту отправляется "SYN+ACK". Эти соединения не перемещаются в очередь установленных соединений ожидающих обработки accept() (accept queue) до тех пор, пока не будет получен и обработан соответствующий ACK. Максимальное количество соединений в этой очереди устанавливается параметром net.ipv4.tcp_max_syn_backlog.

Для просмотра очереди приема используйте команду netstat. На правильно настроенном сервере при нормальной нагрузке значение не должно быть больше 1. При большой нагрузке значение должно быть меньше размера очереди ожидания (SYN Backlog):

# netstat -an | grep SYN_RECV | wc -l

Если в состоянии "SYN_RECV" находятся много соединений, то можно также подстроить продолжительность нахождения SYN-пакета в этой очереди.

SYN Cookie

Если SYN cookie отключены, то клиент просто повторяет отправку SYN-пакета. Если включены (net.ipv4.tcp_syncookies), то соединение не создается и не помещается в SYN backlog, но клиенту отправляется SYN+ACK, так как если бы это было сделано на самом деле. SYN cookie могут быть полезны при нормальной нагрузке, но при всплесках трафика некоторая информация о соединении может быть потеряна и клиент столкнется с проблемами, когда соединение будет установлено. Подробнее о SYN cookie можно прочитать в статье Грэма Коула (Graeme Cole) SYN cookies ate my dog (SYN cookie съели мою собаку), в которой подробно объясняется, почему включение SYN cookie на высоконагруженных серверах может привести к проблемам.

Повторы SYN+ACK

Что происходит, если SYN+ACK отправлен, но ответа ACK нет? В этом случае сетевой стек сервера повторит отправку SYN+ACK. Задержка между попытками вычисляется таким образом, чтобы обеспечить восстановление сервера. Если сервер получает SYN и отправляет SYN+ACK, но не получает ACK, то тайм-аут повторной передачи вычисляется по экспоненте (Exponental Backoff) и, следовательно, зависит от количества повторных попыток. Количество повторных попыток отправки SYN+ACK задается параметром ядра net.ipv4.tcp_synack_retries (по умолчанию равно 5). Повторные попытки будут через следующие интервалы: 1с, 3с, 7с, 15с, 31с. При шести попытках последняя будет примерно через 63 секунды после первой. Это позволяет удержать SYN-пакет в очереди ожидания более 60 секунд до истечения времени ожидания пакета. Если очередь SYN backlog мала, то не требуется большого количества соединений, чтобы возникла ситуация, когда полуоткрытые соединения никогда не завершатся и тогда никакие соединения не смогут быть установлены. Установите количество повторных попыток SYN+ACK равным 0 или 1, чтобы избежать такого поведения на высоконагруженных серверах.

Повторы SYN

Несмотря на то что повторные SYN-пакеты отправляются клиентом во время ожидания SYN+ACK, они могут влиять и на высоконагруженные сервера, работающие с прокси-соединениями. Например, сервер nginx, устанавливающий несколько десятков прокси-соединений к бэкенд-серверу, из-за всплесков трафика может на некоторое время перегрузить сетевой стек, а повторные попытки создадут дополнительную нагрузку на бэкэнд как в очереди приема, так и в очереди ожидания (SYN backlog). Это, в свою очередь, может повлиять на клиентские соединения. Повторные попытки SYN контролируются параметром net.ipv4.tcp_syn_retries (по умолчанию 5 или 6 в зависимости от дистрибутива). Ограничьте количество повторных попыток SYN до 0 или 1, чтобы не было долгих повторных попыток отправки в течение 63130 с.

Более подробно о проблемах с клиентскими соединениями при обратном прокси-сервере читайте в статье Linux Kernel Tuning for High Performance Networking: Ephemeral Ports.

Очередь установленных соединений ожидающих принятия (accept queue) и somaxconn

Очередь запросов на соединение создает приложение, используя listen() и указывая размер очереди в параметре "backlog". Начиная с ядра 2.2 поведение этого параметра изменилось с максимального количества неоконченных запросов на соединение, которое может удерживать сокет, на максимальное количество полностью установленных соединений, ожидающих, пока они будут приняты. Как описано выше, максимальное количество неоконченных запросов на соединение теперь задается с помощью параметра ядра net.ipv4.tcp_max_syn_backlog.

somaxconn и параметр backlog в listen()

Хотя за размер очереди для каждого слушателя отвечает приложение, есть ограничение на количество соединений, которые могут находиться в очереди. Размером очереди управляют два параметра: 1) параметр backlog в функции listen() и 2) параметр ядра net.core.somaxconn, задающий максимальный размер очереди.

Значения по умолчанию для очереди

Значение по умолчанию для net.core.somaxconn берется из константы SOMAXCONN, которая в ядрах Linux вплоть до версии 5.3 имеет значение 128, но в 5.4 она была увеличена до 4096. Однако, на момент написания этой статьи, ядро 5.4 еще не очень распространено, поэтому в большинстве систем значение будет 128, если вы не модифицировали net.core.somaxconn.

Часто приложения для размера очереди по умолчанию используют константу SOMAXCONN, если этот размер не задается в конфигурации приложения. Хотя некоторые приложения устанавливают и свои значения по умолчанию. Например, в nginx размер очереди равен 511, который автоматически усекается до 128 в ядрах Linux до версии 5.3.

Изменение размера очереди

Многие приложения позволяют указывать размер очереди в конфигурации, указывая значение параметра backlog для listen(). Если приложение вызывает listen() со значением backlog, превышающим net.core.somaxconn, то размер очереди будет автоматически усечен до значения SOMAXCONN.

Потоки

Если очередь большая, то подумайте также об увеличении количества потоков, которые обрабатывают запросы в приложении. Например, установка для nginx очереди ожидания в 20480 для HTTP-listener без достаточного количества worker_connections для управления очередью приведет к тому, что сервер будет отказываться отвечать на запросы на установку соединения.

Соединения и файловые дескрипторы

Системные ограничения

Любое сокетное соединение использует файловый дескриптор. Максимальное количество дескрипторов, которые могут быть созданы в системе, задается параметром ядра fs.file-max. Посмотреть количество используемых дескрипторов можно следующим образом:

# cat /proc/sys/fs/file-nr1976      12       2048

Вывод показывает, что используется 1976 файловых дескрипторов. Выделено, но не используется 12 (для ядер 2.6+), а максимальное количество 2048. В высоконагруженной системе значение должно быть достаточно большим, чтобы справиться как с большим количеством соединений, так и с потребностями в файловых дескрипторах других процессов.

Пользовательские ограничения

Помимо системного ограничения количества файловых дескрипторов, у каждого пользователя есть свои лимиты. Они настраиваются в системном файле limits.conf (nofile) или, при запуске процесса под управлением systemd, в unit-файле systemd (LimitNOFILE). Чтобы увидеть значение по умолчанию запустите:

$ ulimit -n1024

Для systemd (на примере nginx):

$ systemctl show nginx | grep LimitNOFILE4096

Настройка

Для настройки системных ограничений установите параметр ядра fs.max-file в максимальное количество файловых дескрипторов, которое может быть в системе (с учетом некоторого буфера). Например:

fs.file-max = 3261780

Для настройки пользовательского лимита установите достаточно большое значение, чтобы хватило сокетам и файловым дескрипторам рабочих процессов (также с некоторым буфером). Пользовательские ограничения устанавливаются в /etc/security/limits.conf, в conf-файле в /etc/security/limits.d/ или в unit-файле systemd. Например:

# cat /etc/security/limits.d/nginx.confnginx soft nofile 64000nginx hard nofile 64000# cat /lib/systemd/system/nginx.service [Unit]Description=OpenResty Nginx - high performance web serverDocumentation=https://www.nginx.org/en/docs/After=network-online.target remote-fs.target nss-lookup.targetWants=network-online.target[Service]Type=forkingLimitNOFILE=64000PIDFile=/var/run/nginx.pidExecStart=/usr/local/openresty/nginx/sbin/nginx -c /usr/local/openresty/nginx/conf/nginx.confExecReload=/bin/kill -s HUP $MAINPIDExecStop=/bin/kill -s TERM $MAINPID[Install]WantedBy=multi-user.target

Количество worker'ов

Аналогично файловым дескрипторам, количество worker'ов или потоков, которые может создать процесс, ограничено как на уровне ядра, так и на уровне пользователя.

Системные ограничения

Процессы могут создавать рабочие потоки. Максимальное количество потоков, которые могут быть созданы, задается параметром ядра kernel.threads-max. Для просмотра максимального и текущего количества потоков, выполняющихся в системе, запустите следующее:

# cat /proc/sys/kernel/threads-max 257083# ps -eo nlwp | tail -n +2 | \    awk '{ num_threads += $1 } END { print num_threads }'576

Пользовательские ограничения

Есть свои ограничения и у каждого пользовательского процесса. Это также настраивается с помощью файла limits.conf (nproc) или unit-файла systemd (LimitNPROC). Для просмотра максимального количества потоков, которое может создать пользователь запустите:

$ ulimit -u4096

Для systemd (на примере nginx):

$ systemctl show nginx | grep LimitNPROC4096

Настройка

В большинстве случаев системные ограничения достаточно большие, чтобы справиться с высокой нагрузкой. Однако их его можно настроить через параметр ядра kernel.threads-max. Установите его значение в максимальное количество потоков, необходимых системе, плюс некоторый буфер. Например:

kernel.threads-max = 3261780

Как и в случае с nofile, ограничения для пользователей (nproc) устанавливаются в /etc/security/limits.conf, в conf-файле в /etc/security/limits.d/ или в unit-файле systemd. Пример с nproc и nofile:

# cat /etc/security/limits.d/nginx.confnginx soft nofile 64000nginx hard nofile 64000nginx soft nproc 64000nginx hard nproc 64000# cat /lib/systemd/system/nginx.service [Unit]Description=OpenResty Nginx - high performance web serverDocumentation=https://www.nginx.org/en/docs/After=network-online.target remote-fs.target nss-lookup.targetWants=network-online.target[Service]Type=forkingLimitNOFILE=64000LimitNPROC=64000PIDFile=/var/run/nginx.pidExecStart=/usr/local/openresty/nginx/sbin/nginx -c /usr/local/openresty/nginx/conf/nginx.confExecReload=/bin/kill -s HUP $MAINPIDExecStop=/bin/kill -s TERM $MAINPID[Install]WantedBy=multi-user.target

Обратный прокси и TIME_WAIT

При большом всплеске трафика прокси-соединения, застрявшие в "TIME_WAIT", суммарно могут потреблять много ресурсов при закрытии соединения. Это состояние говорит, что клиент получил последний FIN-пакет от сервера (или вышестоящего worker'а) и находится в ожидании для корректной обработки пакетов. Время нахождения соединения в состоянии "TIME_WAIT" по умолчанию составляет 2 x MSL (Maximum Segment Length максимальная длина сегмента), что составляет 2 x 60 с. В большинстве случаев случаях это нормальное и ожидаемое поведение, и значение по умолчанию в 120 с вполне приемлемо. Однако много соединений в состоянии "TIME_WAIT" может привести к тому, что приложение исчерпает эфемерные порты для соединений к клиентскому сокету. В этом случае следует уменьшить FIN тайм-аут.

Управляет этим тайм-аутом параметр net.ipv4.tcp_fin_timeout. Рекомендуемое значение для высоконагруженных систем составляет от 5 до 7 секунд.

Собираем все вместе

Очередь приема (receive queue) должна быть рассчитана на обработку всех пакетов, полученных через сетевой интерфейс, не вызывая отбрасывания пакетов. Также необходимо учесть небольшой буфер на случай, если всплески будут немного выше, чем ожидалось. Для определения правильного значения следует отслеживать файл softnet_stat на предмет отброшенных пакетов. Эмпирическое правило использовать значение tcp_max_syn_backlog, чтобы разрешить как минимум столько же SYN-пакетов, сколько может быть обработано для создания полуоткрытых соединений. Помните, что этот параметр задает количество пакетов, которое каждый процессор может иметь в своем буфере, поэтому разделите значение на количество процессоров.

Размер SYN очереди ожидания (SYN backlog queue) на высоконагруженном сервере должен быть рассчитан на большое количество полуоткрытых соединений для обработки редких всплесков трафика. Здесь эмпирическое правило заключается в том, чтобы установить это значение, по крайней мере, на максимальное количество установленных соединений, которое слушатель может иметь в очереди приема, но не выше, чем удвоенное количество установленных соединений. Также рекомендуется отключить SYN cookie, чтобы избежать потери данных при больших всплесках соединений от легитимных клиентов.

Очередь установленных соединений, ожидающих принятия (accept queue) должна быть рассчитана таким образом, чтобы в периоды сильного всплеска трафика ее можно было использовать в качестве временного буфера для установленных соединений. Эмпирическое правило устанавливать это значение в пределах 2025% от числа рабочих потоков.

Параметры

В этой статье были рассмотрены следующие параметры ядра:

# /etc/sysctl.d/00-network.conf# Receive Queue Size per CPU Core, number of packets# Example server: 8 coresnet.core.netdev_max_backlog = 4096# SYN Backlog Queue, number of half-open connectionsnet.ipv4.tcp_max_syn_backlog = 32768# Accept Queue Limit, maximum number of established# connections waiting for accept() per listener.net.core.somaxconn = 65535# Maximum number of SYN and SYN+ACK retries before# packet expires.net.ipv4.tcp_syn_retries = 1net.ipv4.tcp_synack_retries = 1# Timeout in seconds to close client connections in# TIME_WAIT after receiving FIN packet.net.ipv4.tcp_fin_timeout = 5# Disable SYN cookie flood protectionnet.ipv4.tcp_syncookies = 0# Maximum number of threads system can have, total.# Commented, may not be needed. See user limits.#kernel.threads-max = 3261780# Maximum number of file descriptors system can have, total.# Commented, may not be needed. See user limits.#fs.file-max = 3261780

И следующие пользовательские ограничения:

# /etc/security/limits.d/nginx.confnginx soft nofile 64000nginx hard nofile 64000nginx soft nproc 64000nginx hard nproc 64000

Заключение

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


Узнать подробнее о курсе "Administrator Linux. Professional".

Смотреть вебинар Практикум по написанию Ansible роли.

Подробнее..

Онбординг нового разработчика с помощью Ansible

29.01.2021 14:10:10 | Автор: admin

Ваш новый разработчик только что закончил подписывать трудовой договор и с горящими глазами готов закрывать по 15 задач в день. На его пути стоит лишь одно препятствие новый ноутбук, который пока что не настроен должным образом. Чаще всего процесс настройки окружения описывается в документе, который выдается новому разработчику. Мы не сильно далеко ушли и тоже составили такой список.

  1. Установка Xcode

  2. Настройка локального git репозитория

  3. Настройка окружения

  4. Настройка проекта

  5. Ознакомление с документацией

  6. Настройка таск-трекера (Jira/Youtrack)

Чтобы не тратить драгоценное время разработчика на ручную настройку нового ноутбука, мы автоматизировали 3 и 4 пункты, т.к. они являются самыми трудозатратными.

Итак, давайте сперва рассмотрим настройку окружения.

Настройка окружения

В нашем случае настройка окружения состоит из нескольких пунктов:

1. Установка brew зависимостей

2. Установка mint зависимостей

3. Установка и настройка ruby

4. Установка и настройка python

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

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

В конечном итоге скрипт получился вот таким:

#!/usr/bin/env bashcurl https://bootstrap.pypa.io/get-pip.py -o get-pip.py && sudo python get-pip.pysudo pip install ansiblecd $(dirname $0) && ansible-playbook main.yml

Тут происходит следующее:

  1. Установка менеджера пакетов pip

  2. Установка Ansible

  3. Запуск наших задач из main.yml

Осталось разобраться, что находится в main.yml. А там всего лишь

- hosts: localhost  roles:- common_setup

Здесь hosts отвечает за те машины, на которых мы хотим запустить наши задачи, в нашем случае это локальная машина, а roles содержит информацию о том, какие задачи нужно выполнить, переменные и метаинформацию.

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

Каждая папка (meta, tasks и vars) должна содержать файл main.yml.

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

Больше всего нас интересует папка tasks, в которой собраны задачи, выполняющие настройку. Рассмотрим структуру задачи, которая выполняет установку и настройку Ruby с помощью rvm.

---- name: Check if RVM already installedstat: path=~/.rvmregister: rvmdirchangedwhen: false- name: Install RVM for a usercommand: bash -c "\curl -sSL https://get.rvm.io | bash -s -- --ignore-dotfiles"when: rvmdir.stat.exists == False- name: Add RVM to profilecommand: bash -c "~/.rvm/bin/rvm get stable --auto-dotfiles"when: rvmdir.stat.exists == False- name: Add RVM to zshrccommand: bash -c "echo '\n[ -s ${HOME}/.rvm/scripts/rvm ] && source ${HOME}/.rvm/scripts/rvm' >> ~/.zshrc"when: rvmdir.stat.exists == False- name: Install {{ rubyversion }}command: bash -c "~/.rvm/bin/rvm install {{ rubyversion }}"when: rvmdir.stat.exists == False- name: Set Ruby Defaultcommand: bash -c "~/.rvm/bin/rvm --default use {{ rubyversion }}"when: rvmdir.stat.exists == False- name: Install Ruby Gems required for iOS app developementgem: name={{ item.name }} version={{ item.version }} state={{ if item.version is defined nil else latest }}withitems: "{{ rubygems_packages_to_install }}"when: rvmdir.stat.exists == False
  1. Проверяем, установлен ли rvm.

    name - название задачи

    stat - путь до rvm

    register сохраняет переменную для последующего использования

    changed_when переопределяет поведение смены состояния машины после исполнения

    Мы точно знаем, что после исполнения этой задачи состояние не изменится, поэтому просто ставим false. Более подробно использование этой команды описано здесь.

  2. Устанавливаем rvm.

    name - название задачи

    command - команда для выполнения

    when - условие для выполнения

    Условием для выполнения здесь является отсутствие установленного ранее rvm, мы можем узнать это из сохраненной на предыдущем шаге переменной rvmdir.

  3. Настраиваем rvm. Параметры аналогичны.

  4. Добавляем строки в .zshrc конфиг. На MacOS Catalina этот шаг обязателен, так как zsh теперь используется по умолчанию.

  5. Устанавливаем нужную нам для разработки версию Ruby. Тут параметры тоже аналогичны, единственным отличием будет использование нашей переменной из папки vars. Объявлена она в main.yml следующим образом:

    rubyversion: ruby-2.6.5

  6. Назначаем свежеустановленную версию Ruby версией по умолчанию

  7. Устанавливаем нужные гемы, в нашем случае это просто bundler. Тут также используется переменная из папки vars, объявленная таким образом:

    rubygems_packages_to_install:

    - name: bundler

    version:2.1.4

На этом настройка окружения закончена, можем переходить к настройке самого проекта.

Настройка проекта

Под настройкой проекта мы подразумеваем полную установку всех зависимостей и других необходимых вещей для успешного билда.

Скрипт для настройки проекта выглядит следующим образом:

#!/usr/bin/rubyrequire 'FileUtils'require 'colorize'RESOURCES_DIRECTORY = './Scripts/Resources'.freezeXCODE_DIRECTORY = '~/Library/Developer/Xcode'.freezedef setup_git_hooks  puts "Setting up git hooks".blue.bold  git_hooks_path = '.git/hooks'  FileUtils.mkdir_p(git_hooks_path)  Dir["#{RESOURCES_DIRECTORY}/Git hooks/*"].each do |file|FileUtils.cp_r(file, "#{git_hooks_path}/#{File.basename(file)}")  endenddef setup_file_templates  puts "\nSetting up file templates".blue.bold  file_templates_path = File.expand_path("#{XCODE_DIRECTORY}/Templates/File Templates/Tests")  FileUtils.mkdir_p(file_templates_path)  Dir["#{RESOURCES_DIRECTORY}/Templates/*.xctemplate"].each do |file|FileUtils.cp_r(file, "#{file_templates_path}/#{File.basename(file)}")  endenddef setup_xcode_snippets  puts "\nSetting up xcode snippets".blue.bold  need_to_reboot_xcode = false  code_snippets_path = File.expand_path("#{XCODE_DIRECTORY}/UserData/CodeSnippets")  FileUtils.mkdir_p(code_snippets_path)  Dir["#{RESOURCES_DIRECTORY}/Snippets/*.codesnippet"].each { |file|path = "#{code_snippets_path}/#{File.basename(file)}"next if File.file?(path)need_to_reboot_xcode = trueFileUtils.cp_r(file, path)  }  return unless need_to_reboot_xcode  puts 'Quiting Xcode'.blue  system('killall Xcode')enddef setup_gems  puts "\nSetting up gems".blue.bold  system('bundle install')enddef setup_mocks  puts "\nSetting up mocks".blue.bold  system('cd $(pwd) && swiftymocky generate')enddef setup_projects  puts "\nSetting up projects".blue.bold  system('bundle exec rake update_projects')end# StepsDir.chdir("#{File.expand_path(File.dirname(__FILE__))}/..")setup_git_hookssetup_file_templatessetup_xcode_snippetssetup_gemssetup_mockssetup_projects

Что тут вообще происходит:

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

  2. Установка темплейтов для генерации модулей.

  3. Установка полезных сниппетов.

  4. Установка гемов.

  5. Генерация моков.

  6. Настройка проекта.

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

Заключение

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

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

Подробнее..

Перевод Рекомендации по Ansible

03.02.2021 16:18:50 | Автор: admin

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

Переменные можно разделить на две категории:

  • Переменные в отдельных файлах (в таблице ниже "Filesystem").

  • Переменные в коде (в таблице ниже "Code").

Теперь, если вы посмотрите на их приоритет, то все встает на свои места.

Filesystem-переменные имеют более низкий приоритет по сравнению с Code-переменными. Обратили ли вы внимание на упомянутую выше гибкость и чрезмерную свободу? Давайте продолжим далее с учетом этих знаний.

1. Объявляйте переменные в отдельных файлах

Переменные лучше располагать в отдельных файлах (Inventory, group_vars, host_vars, role/defaults/main.yml и role/vars/main.yml). Все "постоянные" переменные должны быть явно определены. Постоянные переменные это переменные, влияющие на роль или поведение плейбука. В отличие от "временных" переменных, используемых в качестве буфера для временного хранения значений, часто с ограниченной областью видимости. Например, переменные, объявленные в vars, существуют только внутри block. Например:

- name: Variables scope  hosts: localhost  connection: local  vars:    MY_VAR: "I am global var"  tasks:    - block:      - name: Print variable inside the block.        debug:          var: MY_VAR        vars:          MY_VAR: "I am local var"- name: Print variable outside the block.  debug:    var: MY_VAR
PLAY [Variables scope] TASK [Gathering Facts] ok: [localhost] TASK [Print variable inside the block.] ok: [localhost] => { "MY_VAR": "I am local var" } TASK [Print variable outside the block.] ok: [localhost] => { "MY_VAR": "I am global var" }

Таким образом, мы должны определять переменные в файлах. И все переменные должны быть определены явно. Для роли есть файл defaults/main.yml. Значения в этом файле имеют самый низкий приоритет, поэтому здесь можно размещать в том числе пустые переменные. Это облегчит жизнь контрибьюторам, особенно тем, кто видит код впервые.

2. Используйте README

Если роль использует много различных переменных, возможно, все они даже нужные и полезные, то описывайте их в файле README. Команда ansible-galaxy init поможет вам в этом, сгенерировав шаблон README. Вероятно, вам самому, в незнакомом репозитории будет приятно увидеть README с информацией о том, что роль ожидает увидеть на входе и что будет на выходе. Плохим примером будет разделение кода и описания. Например, код в git, а описание на wiki-странице. Нет никакой гарантии, что контрибьюторы обновят и код, и wiki-страницу. Обычно после пул реквеста работа заканчивается.

3. Используйте префиксы

У всех "постоянных" переменных (упомянутых в первом совете) должны быть префиксы. В качестве префикса лучше всего использовать имя роли. Это очень полезно, когда переменные для разных ролей нужно разместить в одном месте. Например, что произойдет в плейбуке с несколькими ролями, если все роли используют переменную port? Добавляя префикс, мы гарантируем, что одни переменные не будут перезаписаны другими. Пример: роль consul. переменная url, имя переменной consul_url.

4. Называйте задачи осмысленными именами

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

Например:

# No name/description- copy: dest=/tmp/text.txt, content="bla-bla"- name: Print variable global var. debug:   var: MY_VAR
TASK [copy]changed: [localhost]TASK [Print variable global var.] *ok: [localhost] => {"MY_VAR": "I am global var"}

5. DRY (Don't Repeat Yourself)

Ansible похож на обычный язык программирования. И как и в обычном языке, в Ansible есть различные механизмы, которые помогают следовать принципу DRY (Don't Repeat Yourself). Но это требует предварительного планирования организации вашего кода. При написании кода думайте, чтобы его можно было переиспользовать.

Крупные блоки:

Блоки внутри роли: (include/import)tasks, (include/import)role. Как это может использоваться? Например, вы используете модуль uri для отправки API-запросов. Допустим, это POST-запросы. Вместо того чтобы повторять 10 раз uri со всеми настройками, можно создать что-то вроде метода и использовать его где угодно. Аналогично методам в обычных языках программирования наш метод тоже принимает входные параметры.

Например: send_post.yml

- name: .::::::::::::. [ Sent POST request ] .::::::::::::. uri:   url: "{{ URL }}"   method: POST   status_code: 200   body: "{{ BODY_VAR | to_nice_json }}"   body_format: json   validate_certs: yes   client_cert: tls.crt   client_key: tls.key   register: return_values when: BODY_VAR is defined

Этот код можно использовать повторно.

- name: Bla-bla   include_tasks: send_post.yml   vars:       URL: "{{ main_url }}/{{ item }}"       BODY_VAR: "{{ item }}"

URL и BODY_VAR это параметры метода.

6. Используйте блоки (block)

Используйте block.

Описывайте параметры задач только один раз, группируя их. Также block может использоваться аналогично блоку try / catch в традиционных языках программирования.

- block:   ...  rescue:   ...

block/rescue отличная альтернатива ignore_errors. По сути, расширенная обработка ошибок. Это может быть полезным, например, когда вам нужно выполнить какой-то код в плейбуке, даже в случае сбоя. Например, удалить некоторые файлы.

 - block:   - name: .....   - name: .....   - name: .....   always:     file:       path: /tmp/xxxx       state: absent

7. Не используйте модули command и shell

Старайтесь не использовать модули command и shell, потому что они не идемпотентные. Хотя есть ряд приемов, которые помогают смягчить эту проблему. Используйте:

  • when

  • creates (если файл существует, то этот шаг не выполняется).

  • removes (если файл существует, то этот шаг выполняется).

  • changedwhen.

Тем не менее если это возможно, держитесь подальше от command и shell.

8. Не используйте теги

Не используйте теги. Теги могут стать ночным кошмаром для людей, которые видят ваш код впервые. Комбинации тегов увеличивает количество возможных вариантов выполнения плейбука. Но если у вас нет выбора, то подробно описывайте теги и их комбинации в README. Однако не все теги плохи. Есть исключения. Например, always, never читать подробнее.

skip_ansible_lint пропустить ansible-lint для задачи.

9. Принцип минимальных привилегий

Используйте принцип минимальных привилегий. Параметр become должен быть no, пока вам это действительно не будет необходимо. Всегда используйте become явно. Например:

--- - hosts: wordpress    become: no     ...    role:      - role: wordpresstasks/main.yml---- name: Install mysql-server pkg  apt:    name: mysql-server    state: present  become: yes

10. Используйте YAML-синтаксис для параметров

Используйте YAML вместо встраиваемого синтаксиса. Сравните эти два варианта:

YAML

- name: Install apache httpd  apt:    name: apache2    state: present

Встраиваемый

- name: Install apache httpd  apt: pkg=apache2 state=pesent

11. Используйте gitignore

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

*.retry*/__pycache__*.pyc*.log### IntelliJ IDEA ###.idea*.iws*.iml*.ipr

12. Используйте советы из документации Ansible

Используйте рекомендации по организации контента с официальной страницы ansible

13. Используйте отдельный каталог для ролей сообщества

Используйте отдельный каталог для ролей сообщества, наряду с рекомендациями из предыдущего совета.

14. Тестируйте Ansible-код

Используйте фреймворки для тестирования кода Ansible. Например, molecule. Этот фреймворк позволяет тестировать ваш код с разных сторон. Помимо традиционного тестирования, он также может запускать все виды линтеров и проверять код на идемпотентность.

15. Версионирование ролей

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

requirements.yaml:

---- src: git@gitlab.company.com:mygroup/ansible.git scm: git version: "0.1"...

Допустимые атрибуты:

  • src

  • scm

  • version

  • name


Перевод статьи подготовлен в преддверии старта курсаDevOps практики и инструменты.

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

Подробнее..

Перевод Создание вашего первого модуля Ansible

23.04.2021 10:10:48 | Автор: admin

В этом блоге я расскажу, как создать свой первый модуль Ansible.



Конечно, есть документация, доступная и на Ansible.com, но разобраться в ней достаточно трудно. Запуск своего первого модуля на основе этого введения дался мне с большим трудом. Вот почему я создал это пошаговое руководство. Новые пользователи заслуживают лучшей отправной точки.


В этом блоге рассматриваются следующие темы:


  • Что такое модуль Ansible


  • Настройка нашей среды сборки


  • Серверное API


  • Разработка собственно модуля



Что такое модуль Ansible


Если вы знакомы с Ansible, то, вероятно, знаете, что каждая задача, которую вы выполняете в Ansible, является модулем Ansible. Если нет, то теперь вы это знаете.


Например, посмотрите на следующую задачу Ansible:


- name: установить последнюю версию python-requests  yum:    name: python-requests    state: latest

В данном случае используется модуль Ansible yum для установки определенного пакета.


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


Однако иногда вам нужно сделать что-то действительно конкретное, и встроенного модуля недостаточно. В этом случае вы можете искать в Интернете настраиваемые модули, например, на веб-сайте Ansible Galaxy, или создать свой собственный модуль Ansible.


В этом блоге мы создадим модуль Ansible, который может взаимодействовать с сервером API для добавления или удаления пользователей. Это простой пример, и, вероятно, вам не нужен специальный модуль, поскольку для него есть модуль по умолчанию, но он помогает объяснить, как работает концепция модуля.


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


Настройка нашей среды сборки


Я использую VSCode для разработки модуля Ansible. Если вы хотите использовать что-то другое, то вам, вероятно, нужно будет сделать некоторые шаги немного по-другому.


Сначала мы создаем репозиторий, который содержит структуру папок для модуля Ansible и сервер API, с которым можно общаться.


Этот серверное API очень простое. На нем мы можем создавать и удалять пользователей вот и все. Для целей этого блога этого достаточно.


Итак, давайте загрузим этот репозиторий и откроем его с помощью VSCode.


git clone https://gitlab.com/techforce1/ansible-module.git -b blog-setup

Убедитесь, что вы используете ветку blog-setup!


По завершении вы должны увидеть 3 папки (.devcontainer, ansible, api-server). Сначала нам нужно запустить серверное API. Для этого откройте cmd или другое терминальное приложение, перейдите в папку api-server и выполните docker build -t api-server (не забудьте точку в конце).


Теперь мы можем его запустить. Запустите docker run -it -d -p 5000: 5000 api-server. Это запускает API-сервер. Если вы теперь перейдете по адресу http://localhost:5000, вы должны увидеть простую веб-страницу.


Единственное, что осталось сделать, прежде чем мы разработаем наш модуль Ansible, это настроить VSCode. В скачанном репозитории есть папка .devcontainer. Это специальная папка для VSCode с конфигурацией в ней того, как открыть devcontainer.


С помощью этого devcontainer вы запускаете свой VSCode внутри devcontainer.


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


Чтобы открыть VSCode внутри контейнера разработчика, вам нужно щелкнуть значок в левом нижнем углу, а затем щелкнуть Reopen in container.



Теперь вы готовы приступить к самой работе.


Сервер API


У нашего сервера API есть разные конечные точки API, с которыми можно общаться. Давайте откроем браузер* и перейдем по адресу http://localhost:5000/. Вы получите веб-страницу с таблицей, в которой перечислены пользователи с правами администратора. Теперь давайте попробуем открыть конечную точку API, http://localhost:5000/API/users.


Вероятно, вы получите сообщение Несанкционированный доступ, потому что нам нужно сначала пройти аутентификацию, прежде чем мы сможем взаимодействовать с API и добавить пользователей. Мы делаем это, перейдя по адресу http://localhost:5000/API/get-token. Он запросит пароль и имя пользователя. Вы можете использовать admin и пароль initial_password.


Предоставляя учетные данные, вы получаете обратно токен, который затем можно использовать для будущих вызовов API вместо имени пользователя и пароля


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


Для доступа к API из командной строки мы можем использовать curl. Чтобы запросить токен с помощью curl, мы используем следующую команду:


$ curl -u admin:initial_password http:/172.17.0.1:5000/API/get-token{  "duration": 600,   "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwiZXhwIjoxNjA1NzE0NjI5Ljk1NzU4ODd9.8yDkOzN0umO2hN_D84KLV4Q4OuWzQoNf8puXWku9F14"}

Я использую другой URL для подключения. Это потому, что я запускаю эту команду из терминала в VSCode. Поскольку это выполняется в контейнере докера, мы используем адрес шлюза контейнера, IP-адрес хоста докера, для сервера API. В остальной части этого блога мы используем этот адрес шлюза.


Чтобы просмотреть всех пользователей, настроенных на сервере API, мы используем следующую команду curl:


$ curl -H 'Accept: application/json' -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwiZXhwIjoxNjA1NzE0NjI5Ljk1NzU4ODd9.8yDkOzN0umO2hN_D84KLV4Q4OuWzQoNf8puXWku9F14" http:/172.17.0.1:5000/API/users{  "Users": [    {      "admin": true,       "created": "Wed, 18 Nov 2020 14:41:31 GMT",       "email": "admin@api.local",       "id": 1,       "username": "admin"    }  ]}

Чтобы добавить пользователя, вы используете ту же конечную точку API, но вместо выполнения запроса GET напишите запрос POST со следующим телом:


{  "Users": [    {      "username": "test",      "email": "test@api.local",       "password": "password",      "admin": true    }  ]}

Разработка модуля Ansible


Давайте сначала создадим простую книгу. Откройте файл ansible/tasks/main.yml. И вставьте следующий код:


name: Add test user to APIour_api_module:  name: test1  state: present  email: test1@test.local  admin: False

Здесь имя модуля our_api_module, у него 4 параметра. Теперь нам нужно его создать. Я уже подготовил файл модуля с основным содержимым, необходимым для работы с Ansible.


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


Позвольте мне быстро объяснить некоторые важные части кода. Мы начинаем со строки 44, это та часть, которая вызывается, когда Ansible загружает модуль. С помощью параметра arguments_spec мы определяем, какие параметры вы можете настроить в своей playbook. Также мы установили режим проверки поддержки на false. Это означает, что этот модуль не поддерживает режим проверки Ansible.


def main():    module = AnsibleModule(        argument_spec=dict(            state=dict(type='str', default='present',                       choices=['absent', 'present']),            name=dict(type='str', required=True),            email=dict(type='str', required=True),            admin=dict(type='bool', default=False),            base_url=dict(requred=False, default=None),            username=dict(requred=False, default=None),            password=dict(requred=False, default=None, no_log=True),        ),        supports_check_mode=False,    )

Затем в строке 59 мы инициализируем класс ApiModule, это загружает класс, определенный в строке 23. При инициализации этого класса он выполняет код функции init. Здесь мы определяем аргументы модуля и запрашиваем токен у сервера API. Посмотрите, как это работает, в функции getToken в строке 37.


Благодаря встроенному модулю urls Ansible для запроса токена требуется всего 3 строки кода.


def getToken(self):    url = "{baseUrl}/API/get-token".format(baseUrl=self.baseUrl)    response = open_url(url, method="GET", url_username=self.username, url_password=self.password, validate_certs=self.verifySsl)    return json.loads(response.read())['token']

Хорошо. Модуль загружен, теперь пора написать код для создания пользователя. Мы собираемся добавить это в строку 69. Начнем с проверки того, что нам нужно сделать. Есть 2 варианта: либо добавить пользователя, либо удалить пользователя в зависимости от состояния параметра (state). Итак, давайте сделаем оператор if, добавив следующий код:


if api.state == 'absent':    if api.user_exist(api.name):       # do something to delete userelif api.state == 'present':    if not api.user_exist(api.name):       # do something to add user

Этот код довольно прост. Но он вводит новую функцию под названием user_exist. Нам нужно добавить это в класс ApiModule:


def user_exist(self, name):    url = "{baseUrl}/API/users".format(baseUrl=self.baseUrl)    headers = {        'Accept': "application/json",        "Authorization": "Bearer {}" . format(self.token),    }    response = open_url(url, method="GET", headers=headers, validate_certs=self.verifySsl)    results = json.loads(response.read())    for user in results['Users']:        if name == user['username']:            return True    return False

Как видите, мы используем заголовки, чтобы указать токен, который мы получили при инициализации модуля. После запроса всех пользователей из api endpoint /API/users мы проверяем, есть ли пользователь в ответе. В этом случае мы возвращаем True, в противном случае мы возвращаем False.


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


Добавьте следующее:


def user_add(self):        url = "{baseUrl}/API/users".format(baseUrl=self.baseUrl)        headers = {            'Accept': "application/json",            'Content-Type': "application/json",            "Authorization": "Bearer {}" . format(self.token),        }        data = {            'username': self.name,            'email': self.email,            'admin': self.admin,            'password': self.password        }        json_data = json.dumps(data, ensure_ascii=False)        try:            open_url(url, method="POST", headers=headers, data=json_data, validate_certs=self.verifySsl)            return True        except:            return False

К этому времени вы сможете самостоятельно создать функцию удаления. При необходимости просто используйте функцию user_add в качестве примера. Обратите внимание: вам нужно изменить метод HTTP с POST на DELETE, и вам не нужно указывать тело, вместо этого вы передаете имя пользователя в качестве параметра URL-адреса.
Таким образом, URL-адрес будет примерно таким:


url = "{baseUrl}/API/users/{username}".format(baseUrl=self.baseUrl, username=self.name)

Не забудьте включить функцию удаления в оператор if.


Наконец, расширьте playbook (tasks/main.yml) следующим:


- name: Add test2 user to API  our_api_module:    name: test2    state: present    email: test2@test.local    admin: False    password: "test2test2"- name: Delete test1 user to API  our_api_module:    name: test1    state: absent    email: test1@test.local    admin: False    password: "test3test3"

Запустите playbook, чтобы увидеть, что он создает 2 новых пользователя, а также удаляет 1 пользователя, которого вы только что создали.


Итоги


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


Не так уж и сложно, правда? Это не так. Простой код Python имеет большое значение.
Если вы хотите увидеть полный конечный результат, загляните в блог-результат ветки.


Подробнее..
Категории: Devops , Ansible

Почему я советую людям не учить Ansible. Андрей Девяткин

12.05.2021 10:23:58 | Автор: admin

Комментарий автора: Основная цель доклада рассказать про методы построения инфраструктуры (Configuration Synchronization/Immutable infra) и сравнить их. Ansible используется как пример одного из инструментов синхронизации конфигурации. С точки зрения автора мир движется в сторону immutable infrastructure и в докладе приводятся аргументы разъясняющие позицию автора. И так как мир движется в сторону immutable infrastructure учить инструменты использующие подход синхронизации конфигураций может быть не самым лучшим использованием времени. Повторю еще раз доклад не про инструменты, а про подходы и принятие решения от-проблемы, а не от-инструмента


Disclaimer: Этот доклад сложен тем, что готовится под российскую аудиторию, которая работает в несколько специфических условиях. Мы потрогаем все эти вещи во время презентации. В России специфичное использование инфраструктуры, потому что народ в основном живет не на Amazon. Есть компании, которые и там живут, но их мало. И это является ограничением. Это стоит учитывать во всех докладах, связанных с раскаткой инфраструктуры, чтобы не говорить: Ребята, поехали в облако, в AWS все будет отлично и тут сидит толпа людей, которые туда поехать не могут. Российская аудитория кажется очень специфичной. И те доклады, которые заходят в Европе, в России не всегда заходят. Возможно, это связано с особым восприятием данной аудитории.




Всем привет! Спасибо, что пришли послушать! Сегодня мы обсудить, почему я не рекомендую людям изучать Ansible. Давайте разбираться.



Что же все-таки не так с Ansible? Давайте разбираться.



Еще несколько дисклеймеров.


Первый дисклеймер это то, что в этой презентации нет цели сравнивать какие-то инструменты. Т. е. это не Ansible vs Terraform, Ansible против CloudFormation, Ansible против Chef и т. д.



Тогда вы сейчас, возможно, думаете: А о чем тогда это может быть?



Еще один дисклеймер:


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


И снова рассказываю не про Ansible?



Давайте поговорим про Ansible. Мы пойдем на страницу Ansible. Этот снапшот я сделал с нее 2 дня назад, так что он достаточно свежий. Мы можем попытаться разобраться, что же за инструмент такой Ansible и зачем его используют.


Мы видим из этой страницы, что его можно использовать для application deployment, менеджмента конфигурации, чтобы оркестровать таски. И это все работает либо через OpenSSH, либо WinRM.


Но это не отвечает на вопрос: Зачем нам его использовать, какую проблему он решает?.



Поэтому мы можем пойти на страницу use-cases. Те ссылки, которые я привожу, вы увидите в левом нижнем углу экрана.


Мы видим, что его рекомендуют использовать для provisioning серверов, менеджмента их конфигурации, а также для того, чтобы раскидывать там приложения. И можно что-то делать для безопасности и оркестрации.



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


А мы им говорим, что давайте пойдем от проблемы, поймем контекст.


Далее поймем методы, которые в этом контексте будут уместны.


И потом уже подберем инструмент, т. е. давайте не будем исходить из инструмента.



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


Соответственно, контекст, который они подразумевают, это либо железные сервера, либо виртуальные машины.


Метод, который предполагается использовать, это инфраструктура как код и синхронизация конфигурации.


И предлагают использовать инструмент Ansible.



Многие из вас думают, что с Ansible можно делать многое другое.


Можно еще AWS настроить.


Можно и Kubernetes.


Можно и HashiCorp Vault настроить.


Можно все сделать с помощью Ansible при желании.



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



Иначе можно оказаться в ситуации, когда мы применяем инструмент как молоток, которым мы стучим везде и все нам кажется гвоздем.



Мы не хотим быть в этой ситуации. Плюс каждый раз, когда вы немножко выходите из mainstream, нужно подумать о последствиях. Vendor, который предлагает вам инструмент, говорит, как его можно использовать. Вы можете пойти попробовать вправо-влево и сделать что-то по-другому. Но вам нужно всегда думать: Я это сделаю сейчас так, но тогда я начну что-то терять. Если я иду в другую сторону по сравнению с community, то я потеряю именно те вещи, которым community научится, т. е. какие-то инсайты я потеряю.


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


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



Именно поэтому Ansible в рамках этой конфигурации мы будем рассматривать, как инструмент менеджмента конфигурации, который используется для provisioning серверов, чтобы раскидать applications, приложения по этим серверам. Плюс можно запускать таски, он это умеет делать.



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


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



Меня зовут Андрей Девяткин.


Называю себя cloud engineering-специалистом.


Я занимаюсь в частности AWS и инструментами компании HashiCorp. Выступал на HashiConf этим летом, потому что было очень классно.


Также являюсь основателем консалтинг-бренда FivexL.


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



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


На первом этапе мы будем говорить о том, что единственная константа это изменения.


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


На третьем этапе мы поговорим про immutable-инфраструктуру и о том, что это, скорее всего, наиболее подходящая практика, которая у нас есть сегодня.


Поговорим о том, что появляются новые способы, новые контексты и новые методы.


Плюс поговорим о том, что будущее уже здесь. Оно просто неравномерно распределено.


Поехали, начнем с первого.



Мы посмотрим на большую картинку и попытаемся понять, почему происходят эти изменения в IT, которые нам надо автоматизировать.


Представьте ситуацию, что у потребителя, есть какое-то желание, которое ему нужно утолить.


И есть предприниматель. Предприниматель видит, что у потребителя есть какая-то потребность, которую он может утолить и получить за это деньги. Он тестирует какую-то бизнес-модель и предлагает продукт или услугу потребителю.


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


Но это не так. Потому у нас есть competition, т. е. есть люди, которые приходят на рынок, видят, что сделал предприниматель и делают примерно то же самое, а, может быть, даже лучше. И они пытаются занять рынок. Например, мы начали с AWS в облаках, потом пришел Google, потом пришел Amazon, Alibaba, Яндекс, Сбер и т. д. Если бы другие игроки не пришли, то у Amazon была бы полная монополия, он бы просто делал денег. Но этого не происходит.


Коментарий автора: пришел Google, потом пришел Amazon, Alibaba под Amazon имелся ввиду Azure


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


Поэтому бизнесу постоянно приходится меняться. И изменения постоянно будут продолжаться.



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



И если мы посмотрим словарь, то энтропия это мера хаоса, это процесс разрушения, процесс деградации, когда материя во Вселенной станет в ultimate state, т. е. когда все совсем уйдет в хаос.



Вы можете сейчас сидеть и думать: Как это все связано с Ansible?.



И тут нам надо задать вопрос: Как же мы позволим бизнесу меняться быстро, но при этом минимизируем энтропию?.



И тут мы можем подумать: DevOps!.


Infrastructure as code!


Ansible!, наконец.


Но что мы сейчас делаем? Мы просто выкрикиваем термины, которые всплывают в нашей голове.



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



Поэтому мы немножко обсудим конфигурацию синхронизации и энтропию.



В нашей области энтропию называют configuration drift. И первый раз я нашел это определение, когда читал книжку Keif Morris Инфраструктура как код. Он не является тем человеком, который придумал это определение. Первыми этот термин использовали PuppetLabs поэтому атрибутика идет им.


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



Потом за счет энтропии, за счет конфигурационного дрифта сервер может в какой-то момент прийти в состояние сервера-снежинки. Это что-то такое, что придумал Martin Fowler в свое время.


Сервер-снежинка это сервер, который прошел точку невозврата. Он уже настолько свой, что уже никто не знает, как его так сконфигурировали, т. к. там уже столько всего накопилось. И трогать каждый раз его страшно. Он уникальный и очень хрупкий. Я думаю, что у многих были такие моменты, когда они сталкивались с такими серверами-снежинками.



Почему это проблема? Это проблема, потому что нарушается стабильность системы, т. е. мы не можем устанавливать приложения на эти сервера и иметь 100%-ную гарантию, что все пройдет гладко, потому что всегда есть доля неизвестности.


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


Также это влияет на наш MTTR (mean time to recovery), т. е. насколько быстро мы можем восстановиться после инцидента.



Приведу вам несколько примеров. Например, вам могло прийти письмо счастья от AWS о том, что железо, на котором лежит ваш EC2 instance, деградировало, и они собираются его прибить. И если у вас там такой сервер-снежинка, то день может стать намного интересней, чем он был.


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


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


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


Например, в вашей сети находится нарушитель. И вам нужно произвести расследование и понять, что происходило на этом сервере. Эти файлы должны быть здесь или это Вася их поставил? Они должны быть в таком состоянии и в такой директории или им свойства поменяли? Должно ли быть все так?



Мы говорим сейчас в контексте именно серверов и виртуальных машин. И джентльмены, про которых мы говорили до этого, в начале 2010-ых говорили в терминологии серверов и виртуальных машин.



И что мы с этим делаем?



Обычный способ это инфраструктура как код. И есть 4 принципа, сформулированные в книге Инфраструктура как код:


  1. Все элементы в инфраструктуре должны быть воспроизводимыми, т. е. у нас должна быть возможность их воспроизвести.
  2. За счет того, что мы можем их воспроизвести, у нас должна быть возможность их выкинуть. Они должны быть disposable.
  3. И за счет того, что мы конфигурируем как код, наша инфраструктура будет консистентной.
  4. Плюс у нее нет конечного состояния. Когда мы проектируем нашу инфраструктуру, мы понимаем, что то, что мы знаем сегодня ограниченно и завтра может измениться. Может измениться бизнес-контекст, могут измениться бизнес-нужды, поэтому то, что мы выстраиваем сегодня, это не что-то такое, что вырублено на камне. Это то, что будет меняться, поэтому нужно продумать, как мы это будем менять завтра. И нужно быть готовым это изменить.

Это 4 принципа, на которых держится инфраструктура как код.



И если мы посмотрим определение из Википедии, то там написано, что инфраструктура как код это когда мы описываем всю нашу инфраструктуру в файликах. Эти файлики machine-readable, т. е. какие-то инструменты могут эти файлики прочитать и потом перевести все, что в этих файликах написано, а потом пойти и сконфигурировать системы так, как мы это описали.



Технически мы применяем методы разработки software одна конфигурация. Все те же методики, т. е. так же, как мы разрабатывали код, как мы разрабатывали продукт, тоже самое мы делаем с инфраструктурой, применяя те же методики. Можем делать code review, можем запускать CI и так далее



И наш шаблон потихоньку заполняется, т. е. мы говорим в контексте железных серверов и виртуальных машин. Применяем infra as code.



И я обещал поговорить про синхронизацию конфигурации и давайте про нее поговорим.



Было несколько поколений инструментов для работы с конфигурациями.


Я не буду называть номер этого поколения, но было конкретно выделенное поколение: Puppet, Chef. Ansible и SaltStack пришли чуть попозже. Это именно те инструменты, которые работали с синхронизацией конфигурации. Мы сейчас увидим, что это значит.


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



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


У нас есть образ сервера. Мы накатываем его на сервер. Сервер у нас поднялся. И потом мы используем инструмент синхронизации конфигурации, чтобы применять изменения. Он поэтому так и называется. У нас лежит конфигурация в нашем репозитории, и мы ее запускаем, чтобы синхронизировать на сервере. И каждый раз, когда нам нужно что-то новое сделать, мы меняем это в репозитории, запускаем инструмент. И он это синхронизирует на сервере. Если мы не уверены, как он там встал, мы запускаем еще раз и делаем еще одну синхронизацию.


Опасность здесь в том, что мы не следим за всеми файлами на сервере. У вас всегда есть вероятность, что кто-то пошел и руками поправил и это вне ответственности инструмента. Это все накапливается-накапливается.



И это может привести к спирали страха автоматизации.



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



Давайте посмотрим, как эволюционировала человеческая мысль дальше.



Потом те же ребята из ThoughtWorks пришли с идеей о Phoenix server,



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



Мы можем сделать еще один шаг.



Мы можем все изменения, все конфигурации, которые у нас есть, поместить в образ сервера и уже накатывать это сразу на сервер. И после этого не делаем никаких изменений, и просто раз в какой-то промежуток времени его удаляем. Если нам нужно сделать какие-то новые изменения, мы делаем новый образ сервера, накатываем его на сервер. Т. е. мы не делаем синхронизацию конфигурации, мы либо понимаем новую виртуалку из нового образа или вайпаем (wipe, стереть) сервер и накатываем новый образ. И поэтому требуется несколько серверов, чтобы вы могли переключать трафик. Но за счет этого вы минимизируете количество конфигурационного дрифта.


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



  • Если говорить еще о бенефитах, то, как мы уже говорили, у нас стремится к нулю конфигурационный дрифт.


  • У нас есть возможность масштабироваться быстро. Например, в рамках AWS у нас есть auto scaling группа. Вам нужно поднимать новые виртуалки. И вы не будете руками их запускать-запускать, а потом инструментом синхронизации конфигурации их настраивать, а потом только вводить в строй. Если у вас почти все заключено в ami (шаблон виртуальной машины), то тогда у вас auto scaling group может автоматически вам новые сервера легко поднимать, а также убивать. Вы ничего не теряете, потому что они всегда все одинаковые. Ничего нового здесь я не рассказываю. Это backing vs. frying, cattle vs pets. Все то что много раз рассказывалось на конференциях.


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


    Плюс, что делают те, кто нападают? Они нападают и сидят очень тихо. И они делают нумерацию всего того, что у вас есть: Ага, веб-сервер на том IP-адресе, это на том IP-адресе. А у вас все меняется постоянно. У вас постоянно происходит ротация этих серверов, ротация IP-адресов, если вы в облаке. И им намного сложнее заниматься теми делами, которыми они пришли заниматься. Например, незаметные латеральные движения в вашей сети становится намного сложнее.


  • Плюс, за счет того, что вам минимальное время нужно на provisioning сервера, потому что все уже в образе находится, вы можете воспользоваться, если вы в облаке такими опциями, как EC2 spot instance. Если даже из-под вас spot instance выдернут, то ничего страшного. В другой зоне поднимется, потому что там осталось все по-прежнему, т. е. вы ничего не теряете. У вас будет 2 минуты для того, чтобы переключить трафик, чтобы убрать этот instance из ротации.




И тут вы можете подумать: Если я использую Ansible, но при этом достаточно часто убиваю свои сервера, то, может быть, этого достаточно?.



Может быть. Как я вам сказал, эта презентация очень практичная. И мы не идеалисты, мы практики. Умные книжки вам всегда рисуют красивую картинку, но жизнь всегда сложнее. Ты приходишь иногда к заказчику и думаешь: Боже мой, что же вы наворотили! Какой ужас!.


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


Каждый для себя выбирает свою дорогу, основываясь на том фреймворке, о котором мы сейчас говорили.



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


И мы рекомендуем нашим заказчикам, что immutable-инфраструктура это более лучший подход.



Как ее имплементировать? Давайте поговорим про это.



В идеале перед тем, как вы начнете, вам надо посмотреть на ваши приложения. И будет очень хорошо, если ваши приложения будут соответствовать 12 факторам app.


Там очень полезные вещи написаны. И очень смешно, когда системные инженеры, которые занимаются инфраструктурой, выстраивают системы, приходят к девелоперам и говорят: Вы про 12 factor app слышали? Давайте так делать. И очень часто на них люди удивленно смотрят.


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


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



Давайте подумаем о тех вещах, которые стоит учесть.


У нас есть 3 большие области, про которые стоит подумать:


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


Давайте разбираться с секретами сначала.



Нам нужно решить проблему нулевого секрета. И в зависимости от той среды, которую мы используем, применяются разные вещи. Например, если это AWS, то мы можем использовать instance-профайлы, которые нам дадут credentials, которые мы потом можем в дальнейшем использовать.


Если это Bare Metal, то там много разных систем.


SPIFFE все больше набирает популярность, чтобы давать серверам identity. И потом, чтобы они могли через identity аутентифицироваться в других системах.


Другой вариант вы можете запечь зашифрованные секреты в образ с учетом того, что ключ шифрования будет доступен, когда образ будет подниматься. Например, в AWS вы можете использовать KMS ключ, который доступен только в вашем аккаунте. Если ваш AMI утек, то его нельзя будет расшифровать, потому что нет доступа к ключу. В вашем дата-центре может быть HSM (hardware security module), в котором вы можете хранить ключи шифрования, которые будете использовать для того, чтобы расшифровывать секреты. Если у вас утечет образ, то ничего страшного, потому что ключи только там.


Также сейчас есть системы, которые позволяют затаскивать секреты вовнутрь, т. е. у вас приложение может подняться и прочитать все необходимые секреты, например, с HashiCorp Vault. Если вы бежите в AWS, то мы можете Secrets Manager использовать.


Также все больше и больше возможностей генерировать динамические credentials. Это не сильно относится к immutable-инфраструктуре, но это то, что мы всегда рекомендуем.


Плюс есть возможность пулить (pull) не только секреты, но и конфигурацию, например, используя Consul.



Что делать с людьми?



Нам нужно оставить возможность попасть на сервер, либо на виртуальную машину. Но сделать это так, чтобы люди наследили, когда они это делают. Грубо говоря, не надо гасить SSH-сервер, но SSH-порт стоит закрыть на firewall. И чтобы людям туда пойти, им сначала нужно открыть firewall. И у вас получится из этого какой-то event. Это зависит от того, как ваша инфраструктура выстроена. И на этот event вы потом можете настроить автоматическое убийство машин, потому что неизвестно, что человек там наделал.


И многие компании так делают. Если там кто-то что-то наделал ручками, то автоматически эта машина через 12 часов будет убрана из ротации и полностью очищена.



Как нам поступать с debug?



  • В первую очередь нужно вытаскивать все, что мы можем вытащить. Стримить логи наружу, а также все метрики, т. е. все то, что можно вытащить, нужно вытаскивать.
  • Дальше нам нужно подумать про безопасность немножко. Наши сервера умирают достаточно часто. И может быть так, что нам понадобиться пойти назад в историю. Может быть, у нас несколько месяцев сидит злоумышленник в нашей сети, и нам нужно провести исследование, что он делал и где он был. А если вы каждую неделю вращаете свои сервера и полностью их затираете, то вы можете потерять важные улики. Поэтому системные вызовы тоже стоит стримить с серверов. И, скорее всего, ваша служба безопасности этим занимается. Либо нужно делать какие-то снапшоты перед тем, как вы сервер убиваете. И потом эти снапшоты вы храните несколько месяцев. Это полезно.
  • Если людям нужно что-то протестировать, то у вас есть образ, который такой же, как и тот. Поднимите на стороне еще одну виртуалку, например, либо еще один сервер. Пустите немножко трафика и протестируйте там. Не ходите на те машины, которые на данный момент обслуживают production.
  • А если очень нужно, то ходите, а потом прибейте.


  • Лучшие практики. Если вы делаете continuous integration, то ничего нового. Все зависимости сохраняйте под системой контроля версий.
  • Автоматизируйте построение образов ваших виртуальных машин или серверов. Постарайтесь, чтобы люди вам их не собирали, чтобы это был повторяемый процесс, который у вас происходит на сервере.
  • Автоматизируйте тестирование, т. е. пробуйте автоматически все раскатывать.
  • Старайтесь промоутить вместо того, чтобы пересобирать. Эта важно. Старайтесь, чтобы это был тот же образ, просто, чтобы он был конфигурируемый между окружениями вместе того, чтобы пересобрать в каждом окружении свой. Иначе будет вероятность, что что-то пойдет не так или у вас будет тестирование отличное от условий production.
  • Часто деплойте. Как можно меньшую дельту накапливайте перед тем, как раскатываете. В идеале раскатываете на каждый commit, если у вас есть такая возможность. Особенно, если у вас система построена так, что вы без потери трафика можете переключать виртуальные машины. Это требует определенной работы, но вполне достижимо. И сейчас есть много инструментов, которые могут с этим помочь. В идеале каждый commit. Потому что, если что-то пошло не так, у вас дельта один commit, вы знаете, что пошло не так. Проблема в этом commit. Вы его откатили, вернули зеленое состояние. И человека, который сделал этот commit, есть время, чтобы разобраться и узнать, что пошло не так. На него нет давления, что он сломал что-то в CI. Т. е. просто откатили и человек спокойно разбирается.
  • Плюс я рекомендую перекатывать по расписанию. У вас может быть так, что commitов нет. Т. е. у вас какая-то такая система, в которой не так часто происходят изменения. Поэтому я рекомендую по таймеру перекатывать, т. е. делать каждую неделю перекатку. Можно делать это в выходные, либо в понедельник с утра.


Предположим, есть задача посмотреть на практике, какие инструменты могут быть нам полезны.


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


Если вы деплоите в cloud или вам предоставлен API, то Terraform классный инструмент. Это дело вкуса, конечно. Может быть, вам vendor предоставляет какой-то более хороший инструмент, т. е. я не пытаюсь вам говорить, что Terraform самый лучший инструмент в мире. PXE очень полезен для раскатки baremetal, если вам именно baremetal нужен.


У нас сейчас тоже был проект, где мы Packerом старались собирать образа и через PXE их раскатывать. Но не было возможности, к сожалению, довести его до конца, потому что началась пандемия и потерялась возможность попасть в то место, где сервера стоят, потому что нет возможности путешествовать.


Безопасность Audibeat, WAZUH и другие инструменты. Vault очень популярный инструмент. Если вы в облаке, то используйте облачные сервисы, потому что вам их не нужно будет настраивать самому.


И с debug я тоже привожу здесь какие-то инструменты, но все очень сильно зависит от вашего контекста. Это может быть Prometheus, Logstash. Если вы в облаке, то это может быть CloudWatch, Datadog.



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


Ansible занимается менеджментом конфигурации. Если мы делаем immutable-инфраструктуру, то Ansible больше не нужно этим заниматься, потому что это часть построения образа. Теоретически вы можете через Packer Ansible запустить. Но важно то, что вы запекаете конфигурацию в образ. И теперь именно вот этот инструмент за это отвечает.


Server provisioning. Вы уже используете другие инструменты для накатки образа сервера. Вы не пытаетесь настраивать сервер после того, как он поднялся, потому что он уже настроен.


Приложение, скорее всего, вы доставляете как часть образа, т. е. его не надо раздавать.


СI task? Почему бы не нет, если вам очень нравится Ansible.



Поговорим про другие пути и о том, что происходит сейчас.



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



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



Контейнеры сами по себе immutable изначально, т. е. docker-образ, docker images он immutable. Docker container mutable. Технически вы следуете принципам 12 factors app, когда вы строите docker container. И он (образ) будет immutable изначально. Вся ваша конфигурация туда будет приходить снаружи как переменное окружение. State вы будете монтировать снаружи, базы данных доступны по сети. Здесь у нас все хорошо. Мы используем лучшие практики, которые мы в предыдущем контексте наработали.


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


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



Если вы используете оркестратор, то ваш контейнер часто будет переезжать между хостами, потому что что-то изменяется. У нас есть Kubernetes. У AWS есть ECS для тех, для кого Kubernetes слишком сложный. Если вы хотите соединять публичные clouds, prime clouds, hybrid clouds, то можно посмотреть в Nomad, хотя 2 дня тому назад на Reinvent Amazon сказал, что EKS и ECS теперь anywhere. Я не знаю, сколько это будет стоить. Но технически похоже, что EKS и ECS можно будет on-premises запускать и завязывать как-то с Amazon. Посмотрим, что будет.


Но Nomad это тоже солидное решение, которое используется большим количеством компаний (Cloudflare/CircleCI/Roblox), чтобы серьезное количество нагрузок запускать.


Плюс у нас есть целая аллея мертвецов. В 2015-ом году у нас была война оркестраторов. И победителем вышел Kubernetes, потому что CNCF вкачали в него максимальное количество маркетинговых денег. У нас есть Docker Swarm, который купил в итоге Mitrantis. И который теперь намекает всем своим заказчикам, что пора бы ехать на Kubernetes. OpenShift переделал все на Kubernetes.



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


Плюс многие из этих систем умеют самообновляться, т. е. вы ее подняли и забыли, там ничего не нужно делать после.



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


Метод у нас инфраструктура как код, чтобы взять Terraform и раскатать все, что вокруг kubernetes-кластера. Доставка в kubernetes-кластер приложений. Похоже, что community движется в сторону GitOps. У меня есть свои персональные вопросы к GitOps, но не хочу идти против течения. Как говорится, если ты не можешь остановить сумасшествие возглавь его. И сейчас у меня происходит некий нырок в GitOps.


И еще сейчас пошел новый хайповый тренд Infra as data, т. е. инфраструктура как дата. Это, по сути, тот же самый GitOps, только инфраструктура yaml написана, а не кодом.


Инструменты ArgoCD, про который на этой конференции достаточно много рассказывали. Helm, которым можно темплейтить kubernetesкие чарты и потом складывать в репозиторий. Terraform, чтобы поднимать все вокруг. Плюс есть операторы в AWS, которые позволяют Terraform запускать как часть, используя CRD в Kubernetes.


Достаточно много вопросов, как все это будет. Проблема все та же, контекст более-менее устоялся. Методология как будто бы сейчас начинает устанавливаться. Инструменты посмотрим, это зависит от методологии.



Если мы рассматриваем Ansible в контексте контейнеров, то менеджмент конфигурации происходит как часть build контейнера.


Server provisioning нам, в принципе, не нужен, потому что мы используем containers OS, которые настраивать не надо. Приложение приезжает само в контейнере и его притаскивает оркестратор. Зачастую наша операционная система будет все необходимое для оркестратора иметь. Нужно будет только сказать, где оркестратор и как к нему подключиться.



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



И новый фронтир. Хотя он не совсем новый, люди это уже делают около 8 лет, но еще пока он не прошел ту точку, когда все про это говорят. Это MicroVM.


Вы, скорее всего, слышали про AWS Firecracker, который используется для запуска лямбды.


И Unikernels это когда ваша операционная система компилится вместе с вашим приложением. И вы запускаете то, что вы построили сразу на hypervisor, если у вас есть к нему доступ.


Это что-то такое, что может быть как контекст, интересный людям, которые живут в своих дата-центрах. Потому что у вас зачастую есть доступ напрямую к hypervisor. Тем людям, которые живут в облаках, сложнее. Потому что Amazon не даст доступ напрямую к своему Nitro. Может быть, он когда-нибудь придумает свою ручку, чтобы вы смогли там свои Unikernel запускать, но до этого еще нужно дожить.


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



Чему мы можем научиться от Kubernetes? Давайте задумаемся. Kubernetes принес достаточно интересную концепцию как reconciliation loop, т. к. когда вы ему скармливаете свои yaml, он их принимает, персистит etcd и потом пытается сделать так, чтобы было так, как описано. Он постоянно идет по кругу и проверяет а оно так, а оно так? Например, pod убили, pod нет. И он поднимает новый, чтобы было так, как описано.


И, может быть, через 2-3-5 лет, провайдеры могут подумать: А почему бы нам не сделать такую же штуку? Т. е. сейчас мы постоянно запускаем Terraform, по сути вещей делаем что-то близкое к синхронизации конфигурации. И очень может быть, что cloud-провайдеры приготовят что-то такое для нас. Мы будем просто отдавать YAML, cloud будет сам это настраивать для нас так, как оно должно быть. Нам не нужно будет это постоянно запускать. Cloud сможет подстраивать свои ресурсы, сможет сам бороться с конфигурационным дрифтом. Таким образом, он автоматизирует большую часть нашей работы.



Есть еще другая идея, над которой вы можете после подумать а не идем ли мы назад? Не является ли GitOps новым Chef? Я предлагаю подумать в рамках такой метафоры: часто говорят, что Kubernetes новый Linux. Теперь Linux это такая платформа, поверх которой бежит Kubernetes. И Kubernetes это наш новый user space, где мы запускаем новые приложения. И с GitOps мы, по сути, делаем синхронизацию конфигурации. Т. е. у нас ArgoCD пуллит все то, что написано в наших репозиториях и apply на Kubernetes. По сути, делает все то же самое, что делал Chef в свое время.


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


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



История постоянно себя повторяет. И теоретически в какой-то момент мы придем к тому, что нам нужно будет придумать, как мы будем делать immutable-инфраструктуру для Kubernetes.


Может быть, пора уже убивать свои kubernetes-кластеры и полностью их пересоздавать с нуля? Хотя, в принципе, это рекомендовано. Когда Kubernetes выкатывает новую версию и если мы апгрейдим in-place, то очень часто новые security-фичи выключены, чтобы сохранить обратную совместимость. Поэтому рекомендуется свои кластера перекатывать (пересоздавать), таким образом вы убиваете весь тот мусор, который там накопился.



Мы подходим к последнему пункту о том, что будущее уже здесь и оно неравномерно распределено.



Вы можете сейчас подумать: Как же быть? Я уже потратил столько лет, работая с Ansible. Я так его хорошо знаю. Что делать?. Очень легко принести новые инструменты в организацию, но удалить их оттуда очень сложно.


Например, я вел 10 лет назад миграции с ClearCase на Git. И в больших организациях это мероприятие занимало года полтора, потому что очень много было построено вокруг него.


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


Плюс выбирать какую-то новую технологию это тяжело, это опасно. Например, в 2015-ом году все новые компании, которые стартовали, выбирали, как они будут строить свою внутреннюю платформу. Был Kubernetes, Nomad, Docker Swarm. Много всего было. И нужно было что-то выбрать. Прошло 2 года и те, кто выбрал Docker Swarm, возможно, начинают покусывать локти.


Technology bets сложные, их приходится делать. Но если вы их не сделаете, кто-то другой за вас сделает, потому что облако уже за вами идет. Даже сегодня, если есть большое желание, вы можете работать с железными серверами, вы можете продолжать с ними работать. Для вас всегда будет работа. Так что вы сами выбираете, что вам делать.



Вот, что я хочу вам показать. Я вчера сравнил Chef, Ansible и Terraform в Google Trends. Я понимаю, что когда я сравниваю Chef, Ansible и Puppet с Terraform, то я сравниваю яблоки и груши. Я не пытаюсь показать, что один инструмент лучше другого. Я хочу показать вам картинку немного в другом контексте.


Ansible лидирует. Terraform внизу. И это показывает адопцию cloud в России. Потому что в России только сейчас появляются более-менее серьезные игроки, на которые уже можно начинать полагаться.


Плюс некоторые могут пользоваться заграничными облачными решениями. И зеленую полоску поднятия Terraform я истолковываю, как показатель адопции cloud.



Я попробовал воспользоваться Yandex keyword-статистикой, что было не самым приятным опытом. И, как вы видите, Ansible 29 000.



Terraform около 6 000. Цифры примерно похожие с Google Trends.



А вот, что происходит в Штатах. Посмотрите, какой прыжок у Terraform. Ansible тоже был очень популярным, но адопция cloud в Штатах началась лет 5-6 назад. В начале 2010-ых это были только самые смелые, типа Netflix. Сейчас уже большие банки там. Если вы смотрите Reinvent, то приходит человек из большого банка и начинает рассказывать о том, как им хорошо жить в облаке. И сейчас там начинает подтягиваться длинный хвост. И именно это показывает график. Я не сравниваю инструменты. Я показываю вам график именно с точки зрения адопции cloud. Я думаю, что в ближайшие 3-5 лет это то, что мы увидим в России, даже если вы этого не хотите.



Давайте повторим о том, что мы обсудили.



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



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


И раньше мы использовали инфраструктуру как код и синхронизацию конфигурации.


Для этого хорошо подходили Ansible, Puppet, Chef и им подобные.



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



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


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



На этом у меня все. В нижнем левом углу мои контакты. И я думаю, что мы сейчас еще немножко поговорим с Максимом и обсудим ваши вопросы.


Вопросы и ответы:


Вопрос: В твоем докладе сначала было больше про инфраструктуру, а потом немного промелькнуло про установку приложения на сервер. Это достаточно два крупных блока. И вопрос от Дмитрия: Для Ansible место где-то сохраняется?. Например, в infra или для деплоя приложений. Ты про деплой приложений упоминал. Ты говорил, что деплой приложений в Kubernetes может быть через Helm. А если облаков нет, не используется Kubernetes, для деплоя приложения Ansible это нормальное решение или тоже проблемы будут?


Ответ: Мы про это говорили. Надо исходить из контекста. Если в нашем контексте фигурирует оркестратор контейнеров, то он будет отвечать за доставку. Т. е. чтобы приложение оказалось на сервере, за это будет отвечать оркестратор. Когда мы делаем immutable-инфраструктуру, то у нас есть выбор. То есть мы можем положить приложение в образ и доставлять именно готовый образ с приложением. Каждая новая версия приложения это новый образ сервера или новый образ виртуалки. И мы таким образом перекатываем. Если у вас не такой возможности, то по старинке через Ansible. Это все зависит от контекста. Я не пытаюсь быть идеалистом. Исходите из того контекста, который у вас есть.


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


Вопрос: Я правильно понимаю, что для того, чтобы пойти в immutable-инфраструктуру, нужно преодолеть достаточно высокий порог входа? Т. е. эта история примерно повторяется, как было с микросервисами. Было так, что для того, чтобы пойти в микросервисы, тебе нужно было адекватно подготовить инфраструктуру, иначе ты рехнешься все это мониторить и за всем следить. И с immutable-инфраструктурой примерно та же история, потому что желательно, чтобы были облака, а также хорошо настроенный мониторинг, чтобы не было никакой необходимости ходить на сервера вручную и что-то там смотреть, а ты мог взять и порезать всем доступ. И без этого в immutable-инфраструктуру даже соваться не стоит?


Ответ: Я бы так не сказал. Хороший мониторинг тебе нужен всегда. Хороший мониторинг нужно всегда иметь, не важно, как ты инфраструктуру раскатываешь. Я могу примерно так же ответить на другие возражения. Если хочется получить максимум бенефита с минимум усилий, то можно идти по слайдам презентации, т. е. так, как это делали раньше люди. Просто убивайте свои сервера иногда. Сейчас вы Ansible запускаете на своем сервере, но когда был тот последний раз, что вы его запускали с нуля? Это хороший вопрос. И какая гарантия, что с нуля это сработает?


Т. е. если вы просто начнете перекатывать свои сервера изредка, хотя бы раз в месяц или раз в неделю, то ваш Ansible от этого улучшится, потому что вы будете раньше находить эти проблемы. И если у вас долгобегущие сервера, на которых накапливается конфигурационный дрифт, то ваш Ansible playbook начнет потихоньку подстраиваться под то, что у вас там есть, хотите ли вы этого или нет, потому что люди всегда режут углы. Поэтому не давайте людям резать углы, перекатывайте свои сервера с нуля. И таким образом вы тестируете свои playbooks всегда. Они становятся только лучше от этого.


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


Мы всегда советуем то, что имеет смысл в бизнес-контексте. Нет смысла в вакууме добиваться какой-то победы. Если вы уже получили 95 % бенефитов и вам нет смысла дальше это делать, потому что это будет дороже, и вы ничего не выиграете, то нет смысла этого делать. Вам нужно понять, где вы находитесь и понять, как это поможет вашему бизнесу.


Вопрос: Остается ли где-нибудь место для Ansible? В некоторых ситуациях оно, действительно, где-то остается. И при этом можно immutable-инфраструктуру создать вполне себе вменяемую.


Ответ: Да. Вы можете запускать Packer, взять свой playbook и просто в Packer запустить свой playbook, чтобы построить образ. И вот у вас есть образ со всем тем, что вам нужно. Некоторые люди еще делают золотой образ, что я не советую. Они берут сервер, делают с него снапшот. И потом настраивают его руками, делают еще один снапшот. Я не советую так делать.


Вопрос: У меня хитрый вопрос остался. Как быть с серверами баз данных? Т. е. с любой инфраструктурой, которая хранит состояния? Там бывает такая ситуация, когда ты обновляешь версию базы данных, а там какое-то мажорное обновление и в этих скриптах обновлений уже заложена конвертация данных. Например, появились транзакции в MySQL базе данных и это как-то повлияло на структуру хранения, и должны эти скрипты отработать. Как в этом случае с immutable быть?


Ответ: Ты же не будешь менять базу одновременно со схемой. Я не уверен, что понимаю вопрос.


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


Ответ: Т. е. здесь ты говоришь, что у тебя нет возможности пойти обратно? Т. е. если ты уже поднял новую Mongo, то возможности пойти обратно у тебя уже нет?


Вопрос: И ты не можешь просто убить сервер, перенести данные на новую версию Mongo, где она их подхватит. Надо, чтобы они все равно конвертнулись.


Ответ: Зачастую данный volume, на котором у тебя базы данные хранятся, не хранится на том же сервере, что и база данных, т. е. есть какое-то сетевое хранилище. А сама по себе база данных immutable. И когда ты будешь делать апгрейд на какую-то мажорную версию, ты сделаешь бэкап своих данных и, скорее всего, будешь делать это очень аккуратно, потому что мало ли, что там пойдет не так. Я не вижу здесь большой разницы в процессе.


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

Подробнее..
Категории: Devops , Terraform , Ansible , Immutable infrastructure

Тестирование ansible роли для RabbitMQ кластера с помощью molecule

17.05.2021 20:19:35 | Автор: admin

Molecule это фреймворк, предназначенный для тестирования ролей в Ansible. На хабре довольно много статей про тестирование с помощью molecule и почти во всех статьях говорится о неких "сложных сценариях тестирования для ansible", и далее в примерах обычно идут какие-то простенькие роли и тесты. Мне стало интересно протестировать более сложную роль, например роль для создания RabbitMQ кластера.

Используемые версии программ на момент написания статьи. Не гарантируется корректная работа для molecule версии ниже 3.3

debian 10 Buster

ansible-3.4.0

molecule-3.3.0

docker-ce-20.10.6

yamllint-1.26.1

ansible-lint-5.0.8

Устанавливаем ansible и molecule.

pip3 install --user ansible (как именно устанавливать не столь важно, в приведенном примере установка идет в хоумдир пользователя).

pip3 install --user molecule[docker] (мы будем использовать драйвер докера)

Устанавливаем линтеры

pip3 install --user ansible-lint yamllint

Установка докера выходит за рамки этой статьи, стоит отметить только что докер вы можете установить на эту же машину, где будете запускать molecule или же установить докер на любую другую машину в сети (например если мощности локальной машины не хватает) или же использовать уже существующий докер сервер.

Во втором случае на локальную машину нужно установить только докер клиент и выставить переменную DOCKER_HOST="ssh://ansible@адрес_вашего_докер_сервера", где ansible - аккаунт, который имеет ssh доступ на сервер и под которым будут создаваться докер контейнеры. Аккаунт также должен состоять в группе docker на докер сервере.

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

Переходим в нашу условную роль

cd roles/role_rabbitmq

Создаем конфиги для линтеров в текущей директории (дефолтные конфиги будут генерить много лишних алертов) или же создаем линки на общие конфиги.

.ansible-lint

---exclude_paths:  - .cache/  - .git/  - molecule/skip_list:  - command-instead-of-module  - git-latest  - no-handler  - package-latest  - empty-string-compare  - command-instead-of-shell  - meta-no-info  - no-changed-when  - no-relative-paths  - risky-shell-pipe  - role-name  - unnamed-task

.yamllint

---extends: defaultignore: |  templates/  sites/  files/  old/  README.md  LICENSErules:  braces:    min-spaces-inside: 0    max-spaces-inside: 1  brackets:    min-spaces-inside: 0    max-spaces-inside: 1  comments:    require-starting-space: false    level: error  indentation:    spaces: 2    indent-sequences: consistent  line-length: disable  truthy: disable

Создаем директорию molecule/cluster для нашего сценария.

mkdir molecule/cluster

Открываем в редакторе файл molecule/cluster/Dockerfile.j2. Данный конфиг будет использоваться при создании докер контейнеров. Опять же ничто не ограничивает вашу фантазию - можно использовать уже готовый имидж с ансибл на борту или создать свой.

FROM registry.company.net/debian/buster:latestENV DEBIAN_FRONTEND noninteractiveENV pip_packages "ansible"ENV http_proxy "http://10.10.0.1:8888"ENV https_proxy "http://10.0.0.1:8888"ENV no_proxy "127.0.0.1,localhost,*.company.net,10.0.0.0/8,192.168.0.0/16,172.0.0.0/8"# Install dependencies.RUN apt update \  && apt-get install -y --no-install-recommends \      sudo systemd systemd-sysv \      build-essential wget libffi-dev libssl-dev \      python3-apt python3-cryptography python3-pip python3-dev python3-setuptools python3-wheel \      procps passwd curl lsof netcat gnupg ca-certificates openssh-client less vim iputils-ping iproute2 \      debian-archive-keyring dnsutils \  && rm -rf /var/lib/apt/lists/* \  && rm -Rf /usr/share/doc && rm -Rf /usr/share/man \  && apt-get clean# Create ansible userRUN groupadd --system ansible \  && useradd --system --comment "Ansible remote management" --home-dir /home/ansible --create-home --gid ansible --shell /bin/bash --password "*" ansible && echo "%ansible ALL = (ALL) NOPASSWD:ALL" > /etc/sudoers.d/ansible# Add company repoRUN curl -k "https://certs.company.net/ca.pem" > /usr/local/share/ca-certificates/ca.crt && update-ca-certificates \  && curl -k "https://company.net/repos/keys/company_repo_key.gpg" | apt-key add \  && echo "deb https://company.net/repos/buster buster-local main > /etc/apt/sources.list.d/company.list && apt-get update && pip3 install $pip_packages# Install Ansible inventory file.RUN mkdir -p /etc/ansible && echo "[local]\nlocalhost ansible_connection=local" > /etc/ansible/hosts# Exclude /usr/share/doc# Если программа использует файлы из /usr/share/doc, то следует добавить диру в игнор для dpkg, иначе файлы будут удаленыRUN sed -i 's/path-exclude \/usr\/share\/doc/#path-exclude \/usr\/share\/doc/' /etc/dpkg/dpkg.cfg.d/docker# Make sure systemd doesn't start agettys on tty[1-6].RUN rm -f /lib/systemd/system/multi-user.target.wants/getty.targetVOLUME ["/sys/fs/cgroup"]CMD ["/lib/systemd/systemd"]

Создаем файл molecule/cluster/prepare.yml. Данный файл используется молекулой также как и в ансибле - для различных pre-tasks. В данном случаем мы обновляем дебиан пакеты и устанавливаем питон модуль pika для RabbitMQ.

---- name: prepare  hosts: all  gather_facts: no  # не используем сбор фактов для ускорения выполнения  tasks:    - name: update apt cache      block:        - name: update apt cache          apt:            update_cache: yes        - name: perform upgrade of all packages to the latest version          apt:            upgrade: dist            force_apt_get: yes    - name: install python pika      pip:        name:          - pika        executable: pip3

Создаем файл molecule/cluster/converge.yml. В данном файле мы непосредственно указываем ансибл роль для тестирования. Обратите внимание на hosts, имя должно совпадать с именем группы в molecule.yml

---- name: Converge  hosts: rabbitmq_cluster  roles:    - role: role_rabbitmq

Создаем файл molecule/cluster/molecule.yml. По сути это главный файл, где мы описываем все необходимые параметры для запуска наших тестов. В данном случае мы создаем докер сеть cluster 192.168.0.0/24 и создаем три докер контейнера в этой сети - node01, node01, node03 с заранее заданными айпи адресами 192.168.0.1/2/3. Это нужно для создания RabbitMQ кластера из трех нод, где ноды должны видеть друг друга.

Инвентори мы определяем как

inventory:  links:    group_vars: ../../../../files/molecule/group_vars/

и

groups:  - rabbitmq_cluster

Поэтому создаем в структуре ансибл файл files/molecule/group_vars/rabbitmq_cluster.yml где описываем все необходимые параметры нашей ансибл роли role_rabbitmq

---rabbitmq_cluster: yescerts_dir: /etc/rabbitmq/sslrabbitmq_ssl: yesrabbitmq_ssl_certs:  - "_.company.net"rabbitmq_cookie: NJWHJPAOPYKSGTRGDLTN# обратите внимание, здесь мы указываем короткие имена нод, заданные в нашем molecule.yml# все ноды из списка должны узнавать друг друга по этим коротким именамrabbitmq_nodes:  - node01  - node02  - node03rabbitmq_master: rabbit@node01rabbitmq_master_node: node01rabbitmq_vhosts:  - name: /testrabbitmq_users:  - user: test    password: test    vhost: /testrabbitmq_exchanges:  - name: test    type: direct    durable: yes    vhost: /testrabbitmq_queues:  - name: test    durable: yes    vhost: /testrabbitmq_bindings:  - name: test    destination: test    destination_type: queue    vhost: /testrabbitmq_policies:  - name: ha-replica    vhost: /test    tags:      ha-mode: exactly      ha-params: 2      ha-sync-mode: automatic

molecule.yml

---dependency:  name: galaxy  options:    ignore-certs: Truedriver:  name: dockerplatforms:  - name: node01    image: registry.company.net/debian/buster:latest    # pre_build_image: true    privileged: True    tmpfs:      - /run      - /tmp    volumes:      - /sys/fs/cgroup:/sys/fs/cgroup:ro      - /run/dbus/system_bus_socket:/run/dbus/system_bus_socket:ro    capabilities:      - SYS_ADMIN    command: "/lib/systemd/systemd"    dns_servers:      - 10.0.0.1    groups:      - rabbitmq_cluster    docker_networks:      - name: cluster        ipam_config:          - subnet: "192.168.0.0/24"            gateway: "192.168.0.254"    networks:      - name: cluster        ipv4_address: "192.168.0.1"    network_mode: default  - name: node02    image: registry.company.net/debian/buster:latest    privileged: True    tmpfs:      - /run      - /tmp    volumes:      - /sys/fs/cgroup:/sys/fs/cgroup:ro      - /run/dbus/system_bus_socket:/run/dbus/system_bus_socket:ro    capabilities:      - SYS_ADMIN    command: "/lib/systemd/systemd"    dns_servers:      - 10.0.0.1    groups:      - rabbitmq_cluster    networks:      - name: cluster        ipv4_address: "192.168.0.2"    network_mode: default  - name: node03    image: registry.company.net/debian/buster:latest    privileged: True    tmpfs:      - /run      - /tmp    volumes:      - /sys/fs/cgroup:/sys/fs/cgroup:ro      - /run/dbus/system_bus_socket:/run/dbus/system_bus_socket:ro    capabilities:      - SYS_ADMIN    command: "/lib/systemd/systemd"    dns_servers:      - 10.0.0.1    groups:      - rabbitmq_cluster    networks:      - name: cluster        ipv4_address: "192.168.0.3"    network_mode: defaultprovisioner:  name: ansible  config_options:    defaults:        interpreter_python: auto_silent      host_key_checking: False      gathering: smart      callback_whitelist: profile_tasks, timer, yaml    ssh_connection:      pipelining: True  inventory:    links:      group_vars: ../../../../files/molecule/group_vars/  ansible_args:    - -e molecule_run=True    - -e use_proxy=False  env:    MOLECULE_NO_LOG: 0    ANSIBLE_VERBOSITY: 1verifier:  name: ansiblelint: |  set -e  ansible-lint .scenario:  name: cluster  test_sequence:    - dependency    - lint    - cleanup    - destroy    - syntax    - create    - prepare    - converge    - idempotence    - side_effect    - verify    - cleanup    - destroy

Через ansible_args можно добавлять различные переменные для ансибл роли

  ansible_args:    - -e molecule_run=True    - -e use_proxy=False

Через env можно устанавливать различные переменные environment. В данном случае мы увеличиваем дебаг для ансибла (аналогично опции -v) через ANSIBLE_VERBOSITY.

MOLECULE_NO_LOG незадокументированная опция молекулы, позволяет ставить no_log=no для удобства отладки (по дефолту no_log в молекуле всегда yes). При этом в роли можно использовать такую конструкцию no_log: "{{ molecule_no_log|d(False)|ternary(False, True) }}". Если molecule_no_log=0, то выставить no_log: no, иначе no_log: yes. Так как используются тестовые аккаунты и пароли, то запись этих данных в лог некритична.

  env:    MOLECULE_NO_LOG: 0    ANSIBLE_VERBOSITY: 1

В последних версиях ansible-lint сам вызывает yamllint, поэтому можно указать линтер только ansible-lint

lint: |  set -e  ansible-lint .

В scenario мы определяем наш сценарий cluster и все шаги, необходимые для тестирования. Просмотреть все шаги можно через команду molecule matrix test.

Обратите внимание на сценарии side_effect и verify, если их непосредственно не указать, то у меня они почему-то не вызывались, хотя показаны в выводе molecule matrix.

scenario:  name: cluster  test_sequence:    - dependency    - lint    - cleanup    - destroy    - syntax    - create    - prepare    - converge    - idempotence    - side_effect    - verify    - cleanup    - destroy

Попробуем запустить сценарий cluster, если не указать -s то молекула запустит сценарий default

molecule test -s cluster > /tmp/log 2>&1

Молекула начинает прогонять указанные в test_sequence шаги, причем делает это дважды для соблюдения idempotence. Если во втором тесте будут отличия от первого теста, то молекула завершит работу с ошибкой. Не всегда это работает как нужно (например если конфиг меняется динамически самим сервисом, как в случает с редис), хотя это всегда можно обойти директивой ансибла changed_when: no

В конце в логе /tmp/log должен появиться отчет с финальным сообщением "Idempotence completed successfully", то есть ошибок не найдено и роль можно смело использовать в продакшн ;). При возникновении ошибки на любом шаге, молекула прекращает работу и останавливает свои докер-контейнеры.

Если нужно посмотреть что же вызывает ошибку или проверить состояние конфига, то можно вызывать молекулу с опцией converge, molecule converge -s cluster. В этом случае молекула прогоняет все таски, указанные в converge.yml и не запускает destroy. Можно зайти в контейнер через "docker exec -it container_id /bin/bash" и просмотреть логи или проверить конфиги.

Самое интересное у молекулы на мой взгляд это side-effect и verify. Через side-effect таски можно задавать различные деструктивные действия (что-то вроде chaos monkey). А через verify таски можно проверять финальное состояние системы.

Например скажем молекуле сделать рестарт сервису rabbitmq на каждой ноде после создания кластера (но ничто не ограничивает вас в фантазии).

Создаем файл molecule/cluster/side_effect.yml

---- name: Side Effect  serial: 1  hosts: all  gather_facts: no  # факты нам не нужны  tasks:    - name: restart rabbitmq service      block:        - name: stop rabbitmq service          systemd:            name: rabbitmq-server            state: stopped          failed_when: no        - name: pause          pause:            seconds: 15        - name: start rabbitmq service          systemd:            name: rabbitmq-server            state: started          failed_when: no

Создаем файл molecule/cluster/verify.yml и добавим различные базовые проверки для нашего кластера (опять же ничто не ограничивает вашу фантазию).

---- name: Verify  hosts: all  gather_facts: no  tasks:  - name: cluster status    block:      - name: get cluster status        command: "rabbitmqctl cluster_status --formatter json"        register: output      - name: set facts        set_fact:          cluster_output: "{{ output.stdout|from_json }}"      - name: print nodes        debug:          var: cluster_output.disk_nodes      - name: verify fail        fail:          msg: "FAIL: number of nodes is less than 3"        when:          - cluster_output.disk_nodes | length < 3    run_once: yes  - name: check vhosts    block:      - name: get vhosts        command: "rabbitmqctl list_vhosts --formatter json"        register: output      - name: set facts        set_fact:          vhost_output: "{{ output.stdout|from_json }}.name"      - name: print vhosts        debug:          var: vhost_output      - name: verify fail        fail:          msg: "FAIL: vhost is missing"        when:          - "'/test' not in vhost_output"    run_once: yes  - name: check users    block:      - name: get users        command: "rabbitmqctl list_users --formatter json"        register: output      - name: set facts        set_fact:          user_output: "{{ output.stdout|from_json }}.user"      - name: print users        debug:          var: user_output      - name: verify fail        fail:          msg: "FAIL: user is missing"        when:          - "'test' not in user_output"    run_once: yes  - name: check queues    block:      - name: get queues        command: "rabbitmqctl -p /test list_queues --formatter json"        register: output      - name: set facts        set_fact:          queue_output: "{{ output.stdout|from_json }}.name"      - name: print queues        debug:          var: queue_output      - name: verify fail        fail:          msg: "FAIL: queue is missing"        when:          - "'test' not in queue_output"    run_once: yes  - name: check exchanges    block:      - name: get exchanges        command: "rabbitmqctl -p /test list_exchanges --formatter json"        register: output      - name: set facts        set_fact:          exchange_output: "{{ output.stdout|from_json }}.name"      - name: print exchanges        debug:          var: exchange_output      - name: verify fail        fail:          msg: "FAIL: exchange is missing"        when:          - "'test' not in exchange_output"    run_once: yes  - name: check bindings    block:      - name: get bindings        command: "rabbitmqctl -p /test list_bindings --formatter json"        register: output      - name: set facts        set_fact:          binding_output: "{{ output.stdout|from_json }}.source_name"      - name: print bindings        debug:          var: binding_output      - name: verify fail        fail:          msg: "FAIL: binding is missing"        when:          - "'test' not in binding_output"    run_once: yes  - name: check policies    block:      - name: get policies        command: "rabbitmqctl -p /test list_policies --formatter json"        register: output      - name: set facts        set_fact:          policy_output: "{{ output.stdout|from_json }}.name"      - name: print policies        debug:          var: policy_output      - name: verify fail        fail:          msg: "FAIL: policy is missing"        when:          - "'ha-replica' not in policy_output"    run_once: yes  - name: check publish    block:      - name: install consumer script        copy:          src: ../../../../files/molecule/scripts/consumer.py          dest: /usr/local/bin/consumer.py          owner: root          mode: 0755      - name: publish a message to a queue        rabbitmq_publish:          url: "amqp://test:test@localhost:5672/%2Ftest"          queue: test          body: "Test message"          content_type: "text/plain"          durable: yes      - name: receive a message from the queue        command: /usr/local/bin/consumer.py    run_once: yes

Так как ansible lookup не очень хорошо работает в докер-контейнере, создаем files/molecule/scripts/consumer.py, небольшой скрипт на питоне, который печатает сообщения из очереди test.

#!/usr/bin/python3import pika, sysurl = 'amqp://test:test@localhost/%2ftest'params = pika.URLParameters(url)params.socket_timeout = 1connection = pika.BlockingConnection(params)channel = connection.channel()channel.queue_declare(queue='test', durable=True)method_frame, header_frame, body = channel.basic_get(queue = 'test')if method_frame is None:    connection.close()    sys.exit('Queue is empty!')else:    channel.basic_ack(delivery_tag=method_frame.delivery_tag)    connection.close()    print(body)

Проверяем side-effect

molecule converge -s clustermolecule side-effect -s cluster

Проверяем verify

molecule verify -s cluster

Если все хорошо, запускаем полный тест и проверяем лог.

molecule test -s cluster >/tmp/log 2>&1tail -f /tmp/log
Подробнее..
Категории: Devops , Ansible , Molecule

Настройка Plex, Samba, Transmission на Raspberry pi 4 с помощью Ansible

24.03.2021 10:15:26 | Автор: admin

Предисловие

Не так давно я начал пользоваться Plex media server для просмотра видео на ТВ через Amazon fire stick. Plex server был настроен на десктопе, а файлы с видео были подключены через внешний USB диск и такая связка меня впринципе устраивала. Однако при выключенном ПК доступа к контенту нет и каждый раз включать ПК только для того что бы посмотреть фильм или послушать музыку уже стало напрягать. Поэтому решил настроить plex на raspberry и подключить к нему внешний USB с уже имеющимся контентом. А уже в процессе понял, что еще бы не плохо иметь доступ к файлам и качалку для торентов и заодно упростить процесс настройки с помощью автоматизации на Ansible. Playbook выложил на GitHub.

Что портебуется (железо / софт)

  • Window и Ubuntu Desktop. В принципе можно только что то одно, но мне привычнее какие то вещи делать на Window, а какие то на Ubuntu. (Ubuntu установлена через VirtualBox на Windows). Все тоже самое думаю удастся без проблем и на Mac.

  • Raspberry Pi Imager под Windows. Можно скачать с официального сайта.

  • Образ Raspberry Pi OS Lite. Тоже с официального сайта. Быстрее скачать через торрент, чем по прямой ссылке.

  • Raspberry pi 3/4. Изначально проводил опыты на третьей версии, но потом закупил четвертую.

  • SD карта 8Gb или больше. (USB кардридер)

  • Ansible под Ubuntu описание и установка здесь.

  • USB диск. Желательно с USB 3.0 если собираете на pi 4.

  • Блок питания на 3A что бы хватало мощности для подключенного USB диска.

  • Возможно потребуется подключение дисплея и клавиатуры. (настройка статического IP в первый раз)

  • Playbook для Ansible с GitHub.

  • Ansible. Установка описана здесь.

  • Git.

Подготовка raspberry pi

Для начала зальем скаченный образ с ОС на SD карту с помощью Pi Imager. В пункте Choose image выбираем Use custom и выбираем тот образ что скачали. Можно выбрать тот же образ в самой программе, но тогда он будет скачивать его из интернета, у меня процесс закачки занимал очень длительное время, поэтому снача попробовал скачать по прямой ссылке с сайта, но это тоже оказалось медленно и в итоге скачал через торрент. Указываем нашу SD карту и жмем write.

Дальше отформатируем внешний USB накопитель в NTFS и присвоим ему понятный лейбел (например usb_750g), который будем использовать в дальнейшем для монтирования диска на raspberry. У меня внеший USB накопитель был уже с данными и отформатирован в NTFS поэтому просто списал название лейбла. Можно так же отформатировать в диск в exFAT, но в таком случае на raspberry придется ставить дополнительный пакет, поэтому в данной статье рассмотрю только случай с NTFS. Возможно в дальнейшем добавлю и для других.

На данном этапе работа с Windows заканчивается и дальнейшие действия будут производиться на Ubuntu.

Подключаем нашу SD карту к компьютеру с Ubuntu или переподключаем устройство на гостевую систему в VirtualBox. Должны появиться два подматированных диска boot и rootfs. Через терминал зайдем сначала в boot cd /media/ваш_пользователь/boot/. и положим туда файл ssh который при первом запуске даст понять raspberry что нужно включить ssh соединения.

Для подключения к raspberry я буду пользоваться ssh ключем, он так же понадобится для плейбука на Ansible, поэтому создадим его командой cd ~/.ssh && ssh-gen в папке для ключей (если ее нет то создайте ее). Дайте понятное имя ключу напимер raspberry, на вопросе о секретной фразе жмем enter. На выходе получим два ключа raspberry и raspberry.pub приватный и публичный. С помощью приватного будем подключаться, а публичный положим скопируем на сам raspberry. Для этого зайдем в папку rootfs cd /media/ваш_пользователь/rootfs/ и создадим папку для ключа авторизации mkdir home/pi/.ssh и скопируме сам ключ cp ~/.ssh/raspberry.pub home/pi/.ssh/authorized_keys.

Вставляем SD карту в raspberry, подключем сетевой кабель, USB диск и питание. У меня в домашней сети IP адреса раздает роутер, если у вас такая же конфигурация и в интерфейсе роутера можно посмотреть какой IP адресс выдан устройству и его MAC адрес, то установть выдачу постоянного IP вашему raspberry по MAC адресу. MAC адресс можно посмотреть и на самом raspberry. Подключаем монитор с клавиатурой и запускаем команду cat /sys/class/net/eth0/address . Либо же выставить статический IP на самом raspberry воспользовавшись этой статьей с официального сайта.

Установка и настройка

Если на Ubuntu еще не установлен Ansible, то сделать это можно по ссылке выше. Клонируем репозиторий git clone https://github.com/notfoundsam/raspberry-plex-ansible.git и заходим в папку с плейбуками cd raspberry-plex-ansible.

Для начала установим статический IP адресс нашего raspberry в файле hosts.ini к которому будет обращаться Ansible для настройки. Можно указать и группу адресов если необходимо настроить сразу несколько устройств.

В файле group_vars/all.yml если вы не изменяли имя пользователя на raspberry, то ansible_user оставляем без изменений. Укажем путь к ssh ключу в переменной ansible_ssh_private_key_file. Если все делали по шагам как описано выше, то оставляем без изменений. Установим имя хоста в переменной host_name. В переменную usb_volume_label запишем имя лейбла подключенного USB диска. Монтирование диска будет производиться как раз по этому лейблу. Если предполагается использовать transmission для закачки торентов то установите в переменные transmission_username, transmission_password, transmission_white_list свои значения. Для transmission_white_list обязвтельно оставьте адрес 127.0.0.1.

Установка Plex и монтирование диска. Запустите плейбук plex.yml командой ansible-playbook plex.yml который установит необходимые пакеты, сам Plex изменит имя хоста и сделает перезагрузку. После завершения работы скрипта запустите другой плейбук ansible-playbook usb-volume.yml который подмонтирует наш USB диск и добавит автоматическое монтирование при старте. Зайдем на веб интерфейс плекса raspberry_ip:32400/web и пройдем шаги которые plex предлагает. На некоторых шагах бывало что plex выдавал ошибку что нет доступа, но после обновления страницы все отображалось или повторного перехода на raspberry_ip:32400/web все корректно отображалось. Я думаю это связано с тем, что при первом запуске plex сервер не успевает ответить клиенту из за большой нагрузки. Дальше в интерфейсе plex уже можно выбрать наш USB диск.

Установка Samba сервера. Заливать файлы на raspberry удобнее по сети, поэтому добавил плейбук который установит Samba сервер и расшарит USB диск в локальной сети. Для установки запускаем ansible-playbook samba.yml. Замечание, если ваш Windows хост принадлежит группе отличной от workgroup, то поправте на свою в файле /etc/samba/smb.conf на raspberry. Теперь можно зайти по IP и проверить доступ \\raspberry_ip в проводнике Windows.

Установка Transmission. Запустите плейбук который установит transmission демон и утилиту для работы с iptables, добавит папку downloads на USB диск для загрузок и сделает ее папкой по умолчанию для загружаемых торентов. Папка для временных файлов таже самая, но transmission будет добавлять .part к еще незавершенным файлам. Так же откроет порт 51413 для входящих подключений. Проверим доступ зайдя на raspberry_ip:9091/transmission, логин и пароль для входа те что прописаны в файле group_vars/all.yml. В настройках клиента на вкладке Network проверим что порт 51413 открыт, если написано closed, то нужно настроить перенаправление этого порта на вашем роутере.

Сравнение raspberry 3 с raspberry 4

Проводил проверку работы на raspberry pi 3B / 3B+/ 4B. У pi 3 сетевой интерфейс поддерживает только 100Mbs поэтому скопировать туда 100-200GB данных займет много времени. С 3B+ ситуация уже по лучше, потому что установлен сетевой интерфейс на 1Gbs. Plex сервер достаточно хорошо себя показал на нем, особенно если видео файлы в формате H.264, но на некоторых avi файлах сжатых MPEG4(XVID) кодеком, процессор еле-еле тянул, открывал видео долго, и порой не успевал отдавать видео и картинка останавливалась. Впринципе такие файлы можно заранее оптимизировать в плексе под TV и тогда эта проблема уйдет, но потребуется больше менста для перекодированных файлов.

После некоторого времени использования pi 3B+, все таки стал замечать что обновление библиотеки с несколькой сотней файлов занимае приличное время и редкие залипания, например ни как не может открыть фильм, решил заказать себе pi 4B. У pi 4B процессор уже по мощнее и это решило проблему с MPEG4 уже можно было смотреть файлы без сильных задержек и при перемотке не ждать по 10-15 секунд. USB 3.0 добавил быстроты работы с диском при обновлении библиотеки.

После прочтения этой статьи решил преобрести себе Samsung Evo+ вместо ScanDisk Ultra. И по ощущениям загрузка ОС стала быстрее.

Стоимость / Комплектущие

  • Raspberry pi 4B 4GB RAM ~ $62

  • SD карта Samsung Evo+ ~ $8

  • Металлический корпус с пассивным охлаждением ~ $17

  • USB 3.0 жесткий диск 750GB (покупал 4 года назад) ~ $50

  • Питание Anker PowerPort 6 (покупал 3 года назад) ~ $30

Если какие то комплектующие уже есть в наличии, то и список можно сократить. В моем случае получилось $87.

Выводы

За небольшую сумму удалось собрать медиа центр, который на данный момент удовлетворяет все мои потребности. При выходе из строя внешнего накопителя я согласен с полной или частичной потерей данных. Для снижения нагрузки на внешний диск медиа-сервера, собрал еще один комплект USB диск + Samba + Transmission на raspberry pi 3B, но передача данных по сети со скоростью 100Mbs начинает напрягать, планирую перенести на pi 3B+. Благодаря созданному скрипту на Ansible процесс настройки стал занимать гораздо меньше времени.
Плейбуки написаны по отдельности, что бы можно было использовать в различных комбинациях, например только для настройки Samba с подключением внешнего диска, для организации простого сетевого хранилища.

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

Дальнейшие планы

Объем данных постоянно растет, поэтому планирую докупить стойку с USB хабом и несколькими отсеками для HDD дисков. В планах приобрести что то из этого Yottamaster Hard Drive с 4мя отсеками. Есть уже со встроенным RAID контроллером, но наверное возьму без него, если потребуется RAID, то настрою его на raspberry и только для двух дисков остальные оставлю как single. А старый 750GB диск переключу на закачку торрентов.

Подробнее..

Категории

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

  • Имя: Макс
    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