Всем привет! Меня зовут Павел Агалецкий.
Я работаю тимлидом в команде, которая занимается разработкой системы доставки Lamoda. В 2018 году я выступал на конференции HighLoad++ и сегодня хотел бы представить расшифровку своего доклада.
Моя тема посвящена опыту нашей компании по развертыванию систем и сервисов в различных средах.
Начиная с наших доисторических времен, когда мы развертывали все системы на обычных виртуальных серверах, заканчивая постепенным переходом от Nomad к развертыванию в Kubernetes. Расскажу, почему мы это сделали и какие проблемы у нас возникли в процессе.
Развертывание приложений на виртуальной машине Начнем с того, что 3 года назад все системы и сервисы компании были развернуты на обычных виртуальных серверах.
Технически оно было организовано таким образом, что весь код наших систем хранился и собирался средствами автоматической сборки, с использованием jenkins. С помощью Ansible он был перенесен из нашей системы контроля версий на виртуальные серверы.
При этом каждая система, которая была у нашей компании, была развернута как минимум на 2-х серверах: один из них на головном, второй на хвосте.
Эти две системы были абсолютно идентичны друг другу по всем своим настройкам, мощности, конфигурации и т. д. Единственная разница между ними заключалась в том, что head получала пользовательский трафик, а хвост никогда не получал пользовательский трафик.
Почему это было сделано? Когда мы развертывали новые версии нашего приложения, мы хотели обеспечить плавное развертывание, то есть без заметных последствий для пользователей.
Этого удалось добиться за счет того, что следующий скомпилированный релиз с использованием Ansible был выкачен в хвост. Там люди, которые занимались развертыванием, могли проверить и убедиться, что все в порядке: все метрики, разделы и приложения работают; запускаются необходимые скрипты.
Только после того, как убедились, что все в порядке, движение переключили.
Он начал обращаться к серверу, который раньше был хвостовым.
А тот, что раньше был головой, остался без пользовательского трафика, при этом имея на себе предыдущую версию нашего приложения.
Так что для пользователей все прошло гладко.
Потому что переключение мгновенное, так как это просто переключение балансира.
Вы можете очень легко откатиться к предыдущей версии, просто переключив балансировщик обратно.
Мы также могли проверить работоспособность приложения еще до того, как оно получило пользовательский трафик, что было весьма удобно.
Какие преимущества мы увидели во всем этом?
- Во-первых, этого достаточно это просто работает. Всем понятно, как работает такая схема развертывания, ведь большинство людей когда-либо выполняли развертывание на обычных виртуальных серверах.
- Достаточно надежно , поскольку технология развертывания проста, проверена тысячами компаний.
Таким образом развернуты миллионы серверов.
Трудно что-то сломать.
- И наконец мы смогли получить атомные развертывания .
Развертывания, происходящие для пользователей одновременно, без заметного этапа переключения между старой версией и новой.
- Помимо производственной среды, среды разработки, существуют и другие среды.
Например, контроль качества и подготовка производства.
На тот момент у нас было много серверов и около 60 сервисов.
По этой причине было необходимо для каждой службы поддерживать для нее последнюю версию виртуальная машина.
Более того, если вы хотите обновить библиотеки или установить новые зависимости, вам нужно сделать это во всех средах.
Вам также необходимо было синхронизировать время, когда вы собираетесь развернуть следующую новую версию вашего приложения, со временем, когда devops выполнит необходимые настройки среды.
В этом случае легко попасть в ситуацию, когда наше окружение будет несколько разным во всех средах сразу.
Например, в среде QA будут одни версии библиотек, а в производственной среде — разные, что приведет к проблемам.
- Сложность обновления зависимостей.
ваше приложение.
Это зависит не от вас, а от другой команды.
А именно, от команды Devops, которая обслуживает серверы.
Вы должны дать им соответствующее задание и описание того, что вы хотите сделать.
- На тот момент мы тоже хотели разделить имевшиеся у нас большие большие монолиты на отдельные маленькие сервисы, так как понимали, что их будет все больше и больше.
На тот момент их у нас было уже более 100. Для каждого нового сервиса необходимо было создавать отдельную новую виртуальную машину, которую также нужно было обслуживать и развертывать.
К тому же вам понадобится не одна машина, а как минимум две.
Ко всему этому добавляется среда контроля качества.
Это вызывает проблемы и затрудняет создание и запуск новых систем.
сложный, дорогой и длительный процесс.
Если у вас есть докер, вам нужна система, способная запускать приложение в кластере, так как просто так поднять контейнер не получится.
Обычно требуется отслеживать количество поднятых контейнеров, чтобы они поднимались автоматически.
По этой причине нам необходимо было выбрать систему управления.
Мы долго думали, какой из них взять.
Дело в том, что на тот момент этот стек развертывания на обычных виртуальных серверах был несколько устаревшим, поскольку на них не было последних версий операционных систем.
В какой-то момент появилась даже FreeBSD, которую было не очень удобно поддерживать.
Мы поняли, что нам нужно как можно быстрее мигрировать на докер.
Наши разработчики изучили свой существующий опыт работы с различными решениями и выбрали такую систему, как Nomad.
Переключиться на Номад
Nomad — продукт HashiCorp. Они также известны своими другими решениями:
"Консул" это инструмент для обнаружения сервисов.
«Терраформ» — система управления серверами, позволяющая настраивать их посредством конфигурации, так называемая инфраструктура как код. "Бродяга" позволяет развертывать виртуальные машины локально или в облаке с помощью определенных файлов конфигурации.
Nomad на тот момент казался достаточно простым решением, на которое можно было быстро перейти, не меняя всю инфраструктуру.
Кроме того, этому довольно легко научиться.
Именно поэтому мы выбрали именно его в качестве системы фильтрации для нашего контейнера.
Что вам нужно для развертывания вашей системы в Nomad?
- Прежде всего вам нужно образ докера ваше приложение.
Вам необходимо собрать его и поместить в репозиторий образов Docker. В нашем случае это артефактарий — система, позволяющая запихивать в нее различные артефакты разных типов.
Он может хранить архивы, образы докеров, пакеты композитора PHP, пакеты NPM и т. д.
- Также требуется Файл конфигурации , который сообщит Nomad, что, где и в каком количестве вы хотите развернуть.
Это расширенный набор Yaml, позволяющий описывать ваш сервис в терминах Nomad.
Он позволяет вам сказать, сколько контейнеров вы хотите развернуть, из каких образов передать им различные параметры во время развертывания.
Таким образом, вы скармливаете этот файл Nomad, и он по нему запускает контейнеры в производство.
В нашем случае мы поняли, что просто писать абсолютно одинаковые HCL-файлы для каждого сервиса будет не очень удобно, ведь сервисов очень много и иногда хочется их обновить.
Бывает, что один сервис развернут не в одном экземпляре, а в множестве разных.
Например, одна из систем, которые у нас находятся в производстве, имеет более 100 экземпляров в производстве.
Они запускаются из одних и тех же образов, но отличаются настройками конфигурации и файлами конфигурации.
Поэтому мы решили, что нам будет удобно хранить все наши конфигурационные файлы для развертывания в одном общем репозитории.
Таким образом, они были видны: их было легко обслуживать, и мы могли видеть, какие системы у нас есть.
При необходимости также легко что-то обновить или изменить.
Добавить новую систему также не составит труда – достаточно создать файл конфигурации внутри нового каталога.
Внутри него находятся следующие файлы: service.hcl, который содержит описание нашего сервиса, и несколько env-файлов, позволяющих настроить этот самый сервис, развернутый в продакшене.
Однако некоторые наши системы развернуты в производстве не в одном экземпляре, а сразу в нескольких.
Поэтому мы решили, что нам будет удобно хранить не конфиги в чистом виде, а в шаблонном.
И мы выбрали Джинджа 2 .
В этом формате мы храним как конфиги самого сервиса, так и необходимые для него env-файлы.
Кроме того, мы поместили в репозиторий общий для всех проектов скрипт развертывания, который позволяет запустить и развернуть ваш сервис в продакшен, в нужное окружение, в нужный таргет. В случае, когда мы превратили наш конфиг HCL в шаблон, то файл HCL, который раньше был обычным конфигом Nomad, в этом случае стал выглядеть немного иначе.
То есть мы заменили некоторые переменные местоположения конфигурации вставками переменных, взятыми из файлов env или других источников.
Кроме того, у нас появилась возможность собирать HCL-файлы динамически, то есть мы можем использовать не только обычные вставки переменных.
Поскольку jinja поддерживает циклы и условия, вы также можете создавать там файлы конфигурации, которые меняются в зависимости от того, где именно вы развертываете свои приложения.
Например, вы хотите развернуть свой сервис в предварительной и рабочей версиях.
Допустим, в предварительной версии вы не хотите запускать сценарии cron, а просто хотите увидеть службу в отдельном домене, чтобы убедиться, что она работает. Для тех, кто развертывает сервис, процесс выглядит очень простым и прозрачным.
Все, что вам нужно сделать, это выполнить файл Deploy.sh, указать, какой сервис вы хотите развернуть и для какой цели.
Например, вы хотите развернуть определенную систему в России, Белоруссии или Казахстане.
Для этого просто измените один из параметров, и у вас будет правильный файл конфигурации.
Когда сервис Nomad уже развернут в вашем кластере, это выглядит так.
Во-первых, вам нужен какой-то балансировщик снаружи, который будет получать весь пользовательский трафик.
Он будет работать совместно с Consul и узнавать от него, где, на каком узле, по какому IP-адресу находится конкретный сервис, соответствующий тому или иному доменному имени.
Услуги в Consul предоставляются самим Nomad. Поскольку это продукты одной компании, они весьма родственны друг другу.
Можно сказать, что Nomad из коробки может прописать все запущенные в нем сервисы внутри Consul. Как только ваш внешний балансировщик нагрузки узнает, в какую службу отправлять трафик, он перенаправляет его в соответствующий контейнер или несколько контейнеров, соответствующих вашему приложению.
Естественно, необходимо подумать и о безопасности.
Несмотря на то, что все службы работают на одних и тех же виртуальных машинах в контейнерах, обычно для этого требуется запретить свободный доступ одной службы к любой другой.
Мы добились этого за счет сегментации.
Каждый сервис запускался в собственной виртуальной сети, в которой прописывались правила маршрутизации и правила разрешения/запрета доступа к другим системам и сервисам.
Они могли располагаться как внутри этого кластера, так и за его пределами.
Например, если вы хотите запретить службе подключаться к определенной базе данных, это можно сделать с помощью сегментации на уровне сети.
То есть даже по ошибке вы не сможете случайно подключиться из тестовой среды к своей производственной базе.
Сколько нам обошелся этот переход с точки зрения человеческих ресурсов? Переход всей компании на Nomad занял примерно 5-6 месяцев.
Мы двигались по отдельности, но довольно быстрыми темпами.
Каждой команде пришлось создавать свои контейнеры для сервисов.
Мы приняли такой подход, что каждая команда самостоятельно отвечает за докер-образы своих систем.
DevOps предоставляет общую инфраструктуру, необходимую для развертывания, то есть поддержку самого кластера, поддержку CI-системы и так далее.
А у нас на тот момент в Nomad было перевезено более 60 систем, что составило около 2 тысяч контейнеров.
Devops отвечает за общую инфраструктуру всего, что связано с развертыванием и серверами.
А каждая команда разработчиков, в свою очередь, отвечает за внедрение контейнеров для своей конкретной системы, поскольку именно команда знает, что вообще ей нужно в том или ином контейнере.
Причины отказа от Номада Какие преимущества мы получили, перейдя на развертывание с использованием Nomad и Docker, в том числе?
- Мы при условии равных условий для всех сред. При разработке, среде контроля качества, подготовке к производству и производстве используются одни и те же образы контейнеров с одинаковыми зависимостями.
Соответственно, у вас практически нет шансов, что в производство попадет не то, что вы ранее тестировали локально или в своей тестовой среде.
- Мы также обнаружили, что этого достаточно легко добавить новую услугу .
С точки зрения развертывания любые новые системы запускаются очень просто.
Просто зайдите в репозиторий, в котором хранятся конфиги, добавьте туда еще один конфиг для вашей системы, и все готово.
Вы можете развернуть свою систему в рабочей среде без каких-либо дополнительных усилий со стороны разработчиков.
- Все файлы конфигурации в одном общем репозитории оказался на рассмотрении .
В то время, когда мы разворачивали наши системы с помощью виртуальных серверов, мы использовали Ansible, в котором конфиги находились в одном репозитории.
Однако большинству разработчиков с этим было немного сложнее работать.
Здесь объем конфигов и кода, который нужно добавить для развертывания сервиса, стал намного меньше.
Кроме того, разработчикам очень легко это исправить или изменить.
В случае перехода, например, на новую версию Nomad, они могут взять и массово обновить все операционные файлы, расположенные в одном и том же месте.
При выкатке контейнеров в разных условиях он мог оказаться работающим, и Nomad воспринимал его как контейнер, готовый принимать трафик.
Это произошло еще до того, как приложение внутри него успело запуститься.
По этой причине система стала выдавать 500 ошибок на небольшой промежуток времени, потому что трафик начал идти в контейнер, который еще не был готов его принять.
Мы столкнулись с некоторыми ошибки .
Самая существенная ошибка заключается в том, что Nomad не очень хорошо справляется с большим кластером, если у вас много систем и контейнеров.
Когда вы захотите вывести на обслуживание один из серверов, входящих в кластер Nomad, существует достаточно большая вероятность того, что кластер будет себя чувствовать не очень хорошо и развалится.
Некоторые контейнеры могут, например, упасть и не подняться — это вам потом очень дорого обойдется, если все ваши производственные системы будут расположены в кластере под управлением Nomad. Поэтому мы решили подумать, куда нам двигаться дальше.
В этот момент мы стали гораздо лучше осознавать, чего хотим достичь.
А именно: мы хотим надежности, чуть большего количества функций, чем предоставляет Nomad, и более зрелой, более стабильной системы.
В связи с этим наш выбор пал на Kubernetes как самую популярную платформу для запуска кластеров.
Особенно учитывая, что размер и количество наших контейнеров были достаточно большими.
Для таких целей Kubernetes показался наиболее подходящей системой, на которую мы могли обратить внимание.
Переход на Кубернетес
Расскажу немного об основных концепциях Kubernetes и чем они отличаются от Nomad.
Прежде всего, самой базовой концепцией в Kubernetes является концепция модуля.
капсула представляет собой группу из одного или нескольких контейнеров, которые всегда работают вместе.
И работают они всегда как бы строго на одной виртуальной машине.
Они доступны друг другу по IP 127.0.0.1 на разных портах.
Предположим, у вас есть PHP-приложение, состоящее из nginx и php-fpm — классическая схема.
Скорее всего, вам захочется всегда держать контейнеры nginx и php-fpm вместе.
Kubernetes позволяет добиться этого, описывая их как один общий модуль.
Это именно то, чего нам не удалось добиться с Nomad. Вторая концепция развертывание .
Дело в том, что капсула сама по себе — вещь эфемерная; оно начинается и исчезает. Хотите ли вы сначала уничтожить все свои предыдущие контейнеры, а затем сразу запускать новые версии, или вы хотите развертывать их постепенно? Именно за этот процесс отвечает концепция развертывания.
В нем описывается, как вы развертываете свои поды, в каком количестве и как их обновлять.
Третья концепция – услуга .
Ваш сервис на самом деле является вашей системой, которая получает некоторый трафик, а затем перенаправляет его на один или несколько модулей, соответствующих вашему сервису.
То есть позволяет сказать, что весь входящий трафик на такой-то сервис с таким-то именем должен направляться на эти конкретные поды.
И в то же время обеспечивает балансировку трафика.
То есть вы можете запустить два пода своего приложения, и весь входящий трафик будет равномерно балансироваться между подами, относящимися к этому сервису.
И четвертая основная концепция Вход .
Это сервис, работающий в кластере Kubernetes. Он действует как внешний балансировщик нагрузки, который принимает на себя все запросы.
Используя API Kubernetes, Ingress может определить, куда следует отправлять эти запросы.
Причём делает он это очень гибко.
Можно сказать, что все запросы к этому хосту и такому-то URL отправляются на этот сервис.
И эти запросы, поступающие на этот хост и на другой URL, отправляются в другой сервис.
Самое крутое, с точки зрения человека, который разрабатывает приложение, — это то, что ты можешь управлять всем этим сам.
Настроив конфиг Ingress, вы можете весь трафик, поступающий на такой-то API, отправлять в отдельные контейнеры, написанные, например, на Go. Но этот трафик, приходящий на тот же домен, но на другой URL, должен отправляться в контейнеры, написанные на PHP, где много логики, но они не очень быстрые.
Если сравнить все эти понятия с Nomad, то можно сказать, что первые три понятия — это все вместе Сервис.
И последняя концепция отсутствует в самом Номаде.
В качестве него мы использовали внешний балансировщик: это может быть haproxy, nginx, nginx+ и так далее.
В случае с кубом отдельно вводить это дополнительное понятие не нужно.
Однако, если вы посмотрите на Ingress изнутри, то увидите, что это либо nginx, либо haproxy, либо traefik, но они как бы встроены в Kubernetes. Все описанные мной концепции по сути являются ресурсами, существующими внутри кластера Kubernetes. Для их описания в кубе используется формат yaml, более читабельный и привычный, чем файлы HCL в случае с Nomad. Но структурно они описывают то же самое в случае, например, pod. Они говорят — я хочу развернуть там такие-то поды, с такими-то образами, в таком-то количестве.
Кроме того, мы поняли, что не хотим вручную создавать каждый отдельный ресурс: деплой, сервисы, Ingress и т. д. Вместо этого мы хотели описать каждую нашу систему с точки зрения Kubernetes во время развертывания, чтобы нам не пришлось вручную воссоздать все необходимые зависимости ресурсов в правильном порядке.
В качестве системы, которая позволила нам это сделать, был выбран Helm. Основные понятия в Helm Хелм менеджер пакетов для Кубернетеса.
Это очень похоже на то, как работают менеджеры пакетов в языках программирования.
Они позволяют хранить сервис, состоящий, например, из развертывания nginx, развертывания php-fpm, конфига для Ingress, configmaps (это сущность, позволяющая задавать env и другие параметры для вашей системы) в виде так- называемые диаграммами.
В то же время Хелм работает поверх Kubernetes .
То есть это не какая-то стоящая в стороне система, а просто еще один сервис, запущенный внутри куба.
Вы взаимодействуете с ним через его API с помощью консольной команды.
Его удобство и красота в том, что даже если helm сломается или вы удалите его из кластера, ваши сервисы не исчезнут, так как helm по сути служит только для запуска системы.
В этом случае за производительность и состояние сервисов отвечает сам Kubernetes. Мы также поняли, что шаблонизация , что мы раньше были вынуждены делать сами, вводя в свои конфиги jinja, является одной из основных особенностей helm. Все конфиги, которые вы создаете для своих систем, хранятся в helm в виде шаблонов, немного похожих на jinja, но, по сути, использующих шаблонизацию языка Go, на котором написан helm, как и Kubernetes. Helm добавляет для нас еще несколько концепций.
Диаграмма - это описание вашего сервиса.
В других менеджерах пакетов это будет называться пакетом, пакетом или чем-то подобным.
Здесь это называется диаграммой.
Ценности — это переменные, которые вы хотите использовать для создания конфигураций из шаблонов.
Выпускать .
Каждый раз, когда служба, развернутая с помощью helm, получает дополнительную версию выпуска.
Helm запоминает конфигурацию службы в предыдущем выпуске, в предыдущем выпуске и т. д. Поэтому, если вам нужно выполнить откат, просто запустите команду обратного вызова helm, указав ей предыдущую версию релиза.
Даже если соответствующая конфигурация в вашем репозитории недоступна на момент отката, helm все равно запомнит, какая она была, и выполнит откат вашей системы к состоянию, в котором она была в предыдущем выпуске.
В случае, когда мы используем helm, обычные конфиги для Kubernetes также превращаются в шаблоны, в которых можно использовать переменные, функции и применять условные операторы.
Таким образом вы сможете собрать конфигурацию вашего сервиса в зависимости от среды.
На практике мы решили поступить немного иначе, чем с Nomad. Если в Nomad и конфиги развертывания, и n-переменные, необходимые для развертывания нашего сервиса, хранились в одном репозитории, то здесь мы решили разделить их на два отдельных репозитория.
В репозитории «deploy» хранятся только n переменных, необходимых для развертывания, а в репозитории «helm» хранятся конфиги или диаграммы.
Что это нам дало?
Несмотря на то, что в самих файлах конфигурации мы не храним особо конфиденциальных данных.
Например, пароли к базам данных.
Они хранятся как секреты в Kubernetes, но, тем не менее, там все еще есть определенные вещи, к которым мы не хотим предоставлять доступ всем желающим.
Поэтому доступ к репозиторию «deploy» более ограничен, а репозиторий «helm» просто содержит описание сервиса.
По этой причине к нему может безопасно получить доступ более широкий круг людей.
Поскольку у нас есть не только продакшн, но и другие среды, благодаря такому разделению мы можем повторно использовать наши helm-чарты для развертывания сервисов не только в продакшене, но и, например, в среде QA. Даже для их локального развертывания с помощью Миникуб — это штука для локального запуска Kubernetes. Внутри каждого репозитория мы оставили разделение на отдельные каталоги для каждого сервиса.
То есть внутри каждого каталога есть шаблоны, относящиеся к соответствующему графику и описывающие ресурсы, которые необходимо развернуть для работы нашей системы.
В репозитории «deploy» мы оставили только env. В данном случае мы не использовали шаблонизацию с помощью jinja, потому что helm сам предоставляет шаблонизацию «из коробки» — это одна из его основных функций.
Мы оставили скрипт развертывания — Deploy.sh, который упрощает и стандартизирует запуск для развертывания с помощью helm. Итак, для всех, кто хочет выполнить развертывание, интерфейс развертывания выглядит точно так же, как и при развертывании через Nomad. Тот же Deploy.sh, имя вашего сервиса и место, где вы хотите его развернуть.
Это приводит к внутреннему запуску Helm. Он, в свою очередь, собирает конфиги из шаблонов, вставляет в них файлы с нужными значениями, затем разворачивает их, запуская в Kubernetes.
Выводы
Служба Kubernetes кажется более сложной, чем Nomad.
Здесь исходящий трафик приходит на Ingress. Это всего лишь фронт-контроллер, который принимает на себя все запросы и в дальнейшем отправляет их соответствующим данным запроса сервисам.
Он определяет их на основе конфигов, которые входят в описание вашего приложения в helm и которые разработчики устанавливают самостоятельно.
Сервис отправляет запросы своим подам, то есть конкретным контейнерам, балансируя входящий трафик между всеми контейнерами, принадлежащими этому сервису.
И, конечно же, не следует забывать, что от безопасности на сетевом уровне никуда не надо уходить.
Поэтому в кластере Kubernetes работает сегментация, основанная на тегах.
Все сервисы имеют определенные теги, с которыми связаны права доступа сервисов к определенным внешним/внутренним ресурсам внутри или за пределами кластера.
Совершив переход, мы увидели, что Kubernetes обладает всеми возможностями Nomad, которые мы использовали ранее, а также добавило много нового.
Его можно расширить с помощью плагинов и, по сути, с помощью пользовательских типов ресурсов.
То есть у вас есть возможность не просто использовать что-то, что идет в комплекте с Kubernetes из коробки, а создать свой ресурс и сервис, который будет читать ваш ресурс.
Это дает вам дополнительные возможности для расширения вашей системы без необходимости переустановки Kubernetes и внесения изменений.
Примером такого использования является Prometheus, который работает внутри нашего кластера Kubernetes. Для того, чтобы он начал собирать метрики с конкретного сервиса, нам необходимо добавить в описание сервиса дополнительный тип ресурса, так называемый монитор сервиса.
Prometheus, благодаря тому, что он может читать пользовательский тип ресурса при запуске в Kubernetes, автоматически начинает собирать метрики из новой системы.
Это довольно удобно.
Первое развертывание Kubernetes мы сделали в марте 2018 года.
И за это время у нас ни разу не возникло с ним проблем.
Работает вполне стабильно, без существенных ошибок.
Кроме того, мы можем расширить его дальше.
Сегодня нам достаточно тех возможностей, которые он имеет, и нам очень нравятся темпы развития Kubernetes. На данный момент в Kubernetes находится более 3000 контейнеров.
Кластер занимает несколько узлов.
При этом он исправен, стабилен и очень управляем.
Теги: #Системное администрирование #Kubernetes #deploy #nomad
-
Как Удалить Вирусы Из Разных Версий Windows
19 Oct, 24 -
Firefox Будет Портирован На Qt
19 Oct, 24 -
Семь Маргинальных Гипотез О Природе Эмоций
19 Oct, 24 -
Пятничный Разгон
19 Oct, 24