Недавно мы создали распределенную систему планирования cron-заданий на основе Kubernetes – захватывающей новой платформы для управления кластером контейнеров. Сейчас Kubernetes занимает лидирующие позиции и предлагает множество интересных решений. Одно из основных его достоинств – то, что инженерам не нужно знать, на каких машинах работают их приложения.
Распределенные системы по-настоящему сложны, и управление их службами – одна из самых больших проблем, с которыми сталкиваются операционные группы. Внедрить новое программное обеспечение в производство и научиться надежно управлять им – задача, к которой стоит относиться серьезно. Чтобы понять, почему обучение работе с Kubernetes важно (и почему это сложно!), мы предлагаем ознакомиться с фантастическим одночасовым переключением, вызванным ошибкой в Kubernetes.
В этой статье объясняется, почему мы решили построить архитектуру на Kubernetes. Мы расскажем, как проходила интеграция Kubernetes в существующую инфраструктуру, приведем подход к построению (и улучшению) доверия надежности кластера Kubernetes, а также рассмотрим абстракции, которые мы реализовали над Kubernetes.
Что такое Kubernetes?
Kubernetes – распределенная система для планирования работы программ в кластере. Вы можете приказать Kubernetes запустить пять копий программы, и он динамически запланирует их развертывание на рабочих узлах. Развертывание контейнеров происходит автоматически, что улучшает утилизацию ресурсов и экономит деньги. Мощные примитивы развертывания (Deployment Primitives) позволяют постепенно выкатывать новый код, а контексты безопасности (Security Contexts) и сетевые политики (Network Policies) – безопасно запускать разные проекты в одном кластере.
В Kubernetes существует множество встроенных возможностей планирования. Планирование длительных (long-running) служб HTTP, демоны (daemonsets), которые запускаются на каждом узле кластера, cron-задачах, которые запускаются каждый час, и т.д. Если вы хотите узнать больше, Kelsey Hightower дал несколько отличных разъяснений: Kubernetes for sysadmins и healthz: Stop reverse engineering applications and start monitoring from the inside. Также существует отличное сообщество в Slack.
Почему Kubernetes?
Каждый инфраструктурный проект (надеюсь!) начинается с потребностей бизнеса, и наша цель заключалась в повышении надежности и безопасности существующей распределенной системы cron-задач. Наши требования:
- Построить инфраструктуру и управлять ей с относительно небольшой командой (только 2 человека работали на проекте полный рабочий день).
- Запланировать около 500 различных cron-задач по всем 20 узлам.
Вот несколько причин, по которым мы решили использовать для этого Kubernetes: - Мы хотели построить инфраструктуру поверх существующего проекта с открытым исходным кодом.
- Kubernetes включает в себя распределенный планировщик cron-задач, поэтому нам не пришлось бы писать его самостоятельно.
- Kubernetes – очень активный проект, который регулярно принимает вклады.
- Kubernetes написан на Go, который достаточно просто изучить. Почти все исправления в Kubernetes были сделаны малоопытными программистами нашей команды.
Раньше мы использовали Chronos в качестве системы планирования cron-задач, но она больше не отвечала нашим требованиям надежности. На текущий момент Chronos практически не поддерживается (1 коммит за последние 9 месяцев; последний раз запрос на слияние был одобрен вмарте 2016 года). Поскольку Chronos не поддерживается, мы решили, что не стоит продолжать инвестировать в улучшение существующего кластера.
Если вы рассматриваете Kubernetes, имейте в виду: не используйте Kubernetes только потому, что другие компании используют его. Для создания надежного кластера требуется огромное количество времени, и бизнес-пример его использования не всегда очевиден. Инвестируйте свое время в разумный путь.
Что означает надежный?
Когда дело доходит до операционных услуг, слово «надежный» не имеет смысла само по себе. Чтобы говорить о надежности, сначала необходимо установить SLO (цель уровня обслуживания).
У нас было три цели:
- 99,99% cron-задач должны быть запланированы и начать работу в течение 20 минут после запланированного времени. 20 минут – довольно широкое окно, но мы опросили внутренних клиентов, и никто из них не попросил более высокую точность.
- Задачи должны выполняться до 99,99% времени (без завершения).
- Наша миграция в Kubernetes не должна вызывать инцидентов, связанных с клиентами.
Это означало несколько вещей: - Короткие периоды простоя в API Kubernetes приемлемы (если API упал на 10 минут– это нормально, пока мы можем восстановить работоспособность в течение 5 минут).
- Ошибки планирования (когда запущенная cron-задача не выполняется) неприемлемы. Мы очень серьезно относились к сообщениям об ошибках в планировании.
- Нужно быть осторожными в работе с контейнерами, чтобы задания не прерывались слишком часто.
- Нужен хороший план миграции.
Построение кластера Kubernetes
Наш базовый подход к построению первого кластера Kubernetes состоял в том, чтобы создать кластер с нуля, без применения таких инструментов, как kubeadm или kops, используя в качестве справочника Kubernetes The Hard Way. Мы развернули кластер с помощью Puppet. Построение кластера с нуля было хорошим решением по двум причинам: мы смогли глубоко интегрировать Kubernetes в нашу архитектуру и приобрели глубокое понимание работы его внутренних компонентов.
Построение с нуля позволит интегрировать Kubernetes в существующую инфраструктуру.
Мы хотели бесшовную интеграцию с существующими системами для ведения журнала логов, управления сертификатами, секретов, сетевой безопасности, мониторинга, управления инстансами AWS, развертывания, прокси-серверов баз данных, внутренних DNS-серверов, управления конфигурацией и т.д. Интеграция всех этих систем иногда требовала небольшого творчества, но в целом интегрировать было легче, чем заставить kubeadm/kops сделать то, что мы хотели.
Мы уже доверяем существующим системам и знаем, как ими управлять,, поэтому хотели бы продолжать использовать их в новом кластере Kubernetes. Например, безопасное управление сертификатами является очень сложной проблемой, и у нас уже есть способ выпуска сертификатов и управления ими. Мы смогли избежать создания нового CA только для Kubernetes с надлежащей интеграцией.
Мы должны были понять, как установленные параметры повлияли на настройку Kubernetes. Например, существует более дюжины параметров настройки сертификатов/центров сертификации, используемых для аутентификации. Понимание работы этих параметров облегчило отладку установки, когда мы столкнулись с проблемами аутентификации.
Укрепление доверия к Kubernetes
В начале работы с Kubernetes ни один из членов команды раньше не работал с ним (за исключением некоторых случаев для игрушечных проектов). Как вы относитесь к таким утверждениям:, «никто из нас никогда не использовал Kubernetes», «мы уверены в том, что Kubernetes готов работать в производстве»?
Стратегия 0: поговорите с другими компаниями
Мы спросили об опыте работы с Kubernetes нескольких сотрудников других компаний. Все они использовали Kubernetes по-разному или в разных средах (для запуска HTTP-сервисов, на голом железе, в Google Kubernetes Engine и т.д.).
Когда речь идет большой и сложной системе, такой как Kubernetes, важно серьезно подумать о вариантах использования. Нужно проводить эксперименты, укреплять уверенность в своей среде и принимать собственные решения. Например, вы не должны прочитать эту статью и решить: «Хорошо, Stripe успешно использует Kubernetes, поэтому и мы будем его использовать!»
Вот что мы узнали от сотрудников компаний, работающих с кластерами Kubernetes:
- Приоритезируйте работы над надежностью кластера (etcd, где хранится состояние вашего кластера Kubernetes.)
- Некоторые функции Kubernetes более стабильны, чем другие, поэтому будьте осторожны с альфа-функциями. Некоторые компании используют стабильные функции только после того, как они помечаются как стабильные для нескольких выпусков (например, если функция стала стабильной в 1.8, перед ее использованием они дождались 1.9 или 1.10).
- Рассмотрите возможность использования hosted-системы Kubernetes, такой как GKE/AKS/EKS. Правильная установка и настройка Kubernetes с нуля – это огромная работа. Во время старта наших работ AWS еще не управляла службой Kubernetes, поэтому для нас этот путь был недоступным.
- Будьте осторожны с дополнительной задержкой в сети, созданной overlay и software defined-сетями.
Опыт других компаний, конечно, не дал нам четкого представления о том, будет ли Kubernetes хорошим выбором для нас. Но информация позволила задать себе правильные вопросы и понять, на что стоит обращать внимание при работе с Kubernetes.
Стратегия 1: прочитайте код
В наших планах была очень сильная зависимость от одного из компонентов Kubernetes – контроллера cronjob. Этот компонент был в то время в состоянии альфа, что вызвало у нас беспокойство. Мы проверили его работу в тестовом кластере, но не могли сказать, будет ли это работать в производстве?
К счастью, все основные функции контроллера cronjob составляют всего 400 строк на Go. Чтение исходного кода быстро показало, что:
- Контроллер сronjob – это stateless служба (как и любой другой компонент Kubernetes, кроме etcd).
- Каждые десять секунд этот контроллер вызывает функцию syncAll:
go wait.Until(jm.syncAll, 10 * time.Second, stopCh)
. - Функция
syncAll
извлекает все задания cron из API Kubernetes, выполняет итерацию через этот список, определяет, какие задания должны выполняться в следующий раз, а затем запускает эти задания.
Основная логика выглядела относительно простой для понимания. Что еще более важно, мы чувствовали, что, если в этом контроллере будет ошибка, мы сможем ее исправить самостоятельно.
Стратегия 2: выполняйте нагрузочное тестирование
Перед созданием кластера мы провели небольшое нагрузочное тестирование. Мы не беспокоились о том, каким количеством узлов сможет управлять Kubernetes (в наших планах речь шла о ~20 узлов), но действительно хотели, чтобы некоторые узлы могли обрабатывать как можно больше заданий cron (около 50 в минуту).
Мы провели тест на кластере из 3 узлов, где создали 1000 заданий cron, каждое из которых выполнялось раз в минуту. Каждое из этих заданий просто запускало bash -c 'echo hello world'
. Мы выбрали простые задания, потому что хотели протестировать возможности планирования и оркестровки кластера, а его не общую вычислительную емкость.
Наш тестовый кластер не мог обрабатывать 1000 заданий cron в минуту. Мы заметили, что каждый узел будет запускать не более одного элемента в секунду, и кластер мог бы запускать 200 заданий cron в минуту без проблем. Поскольку мы хотели запустить только примерно 50 заданий cron в минуту, то решили, что эти ограничения не являются блокирующими и что мы могли бы справиться с ними позже, если потребуется. Вперед!
Стратегия 3: приоритет на построение и тестирование кластера etcd высокой доступности
Одна из самых важных задач, которые нужно выполнить при настройке Kubernetes, – запуск etcd. Etcd – это сердце вашего кластера Kubernetes. Здесь хранятся данные обо всех событиях в вашем кластере. Все компоненты Kubernetes, кроме etcd, являются stateless. Если etcd не запущен, вы не сможете вносить изменения в свой кластер (хотя существующие службы будут продолжать работать!).
На этой диаграмме показано, как именно etcd выполняет роль «сердца» вашего кластера Kubernetes. Сервер API – конечная stateless-точка перед etcd, каждый компонент кластера разговаривает с etcd через сервер API.
При работе необходимо учитывать два важных момента:
- Настройте репликацию, чтобы кластер не умер, если вы потеряли узел. Сейчас у нас есть три реплики.
- Убедитесь, что у вас достаточно пропускной способности ввода/вывода. У нашей версии etcd была проблема: один узел с высокой задержкой fsync мог инициировать непрерывные выборы лидера. Это привело к недоступности кластера. Мы исправили это, убедившись, что все узлы имеют бОльшую пропускную способность ввода/вывода, чем количество операций записи etcd.
Настройка репликации не является операцией «настроил-и-забыл». Мы тщательно протестировали потерю узла etcd и убедились, что кластер сможет спокойно восстановиться в случае подобных проблем.
Вот некоторые из работ, которые мы сделали для настройки кластера etcd: - Настройка репликации.
- Мониторинг доступности etcd (если etcd не работает, мы хотим знать об этом сразу).
- Создание нескольких простых инструментов чтобы легко развернуть новые узлы etcd и объединить их в кластер.
- Проверка восстановления из резервной копии etcd.
- Проверка и подтверждение того, что мы можем пересобрать весь кластер без простоев.
Мы были счастливы, что мы провели тестирование на раннем этапе, и вот почему. В пятницу утром в нашем производственном кластере один из узлов etcd перестал отвечать на пинг. Мы получили предупреждение об этом, остановили узел, подняли новый, присоединили его к кластеру, а тем временем кластер Kubernetes продолжал работать без инцидентов. Фантастика.
Стратегия 4: постепенная миграция заданий cron в Kubernetes
Мы ставили цель перенести наши cron-задачи в Kubernetes без перебоев в работе. Секрет успеха успешных производственных миграций заключается не в том, чтобы избежать ошибок (это невозможно), а в том, чтобы спроектировать миграцию так, чтобы уменьшить последствия ошибок.
К счастью, у нас есть множество разных cron-задач для миграции в новый кластер. Были и некоторые задачи с низким уровнем приоритета, которые мы могли бы перенести с небольшим простоем.
Перед началом миграции мы создали простой в использовании инструмент, который при необходимости позволил бы перемещать задания вперед и назад между старыми и новыми системами менее чем за 5 минут. Этот простой инструмент значительно уменьшил влияние ошибок. Если во время переезда всплыла бы, например, зависимость, которую мы не планировали, ничего страшного! Мы могли бы просто переместить задачу обратно, исправить проблему и повторить попытку позже.
Вот общая стратегия миграции, которую мы использовали:
- Установите строгий порядок задач по важности.,
- Выполняйте повторные действия по переезду над каждой cron-задачей. Если обнаруживаем новую проблему, быстро откатываемся назад, исправляем и повторяем попытку.
Стратегия 5: исследуйте ошибки Kubernetes (и исправьте их)
В начале проекта ым установили правило: если Kubernetes делает что-то странное или неожиданное, мы должны провести расследование, выяснить причины и внести исправления.
Расследование каждого вопроса занимает много времени, но это очень важно. Если мы не будем обращать внимания на ошибки, то при работе в производственной среде обязательно столкнемся с проблемами.
После принятия данного подхода мы обнаружили (и смогли исправить!) несколько ошибок в Kubernetes.
Вот несколько обнаруженных во время исследования проблем:
- Cronjobs с именами длиной более 52 символов молча не выполняют запланированные задания (исправлено здесь).
- Иногда Pods зависали в состоянии ожидания (исправлено здесь и здесь).
- Планировщик падает каждые 3 часа (фиксируется здесь).
- Бэкенд hostgw Flannel не заменил устаревшие записи таблицы маршрутов (исправлено здесь).
Исправление этих ошибок позволило нам почувствовать себя намного увереннее в использовании Kubernetes.
У Kubernetes определенно есть ошибки, как и в любом другом программном обеспечении. В частности, мы много и часто используем планировщик (потому что наши задания cron постоянно создают новые модули), а использование кэширования планировщика иногда приводит к ошибкам, регрессиям и сбоям. Кэширование – это сложно! Но кодовая база доступна, и мы смогли обработать ошибки, с которыми столкнулись.
Если вы работаете с большим кластером Kubernetes, внимательно прочитайте документацию по контроллеру узла, тщательно продумайте и протестируйте настройки. Каждый раз, когда мы тестировали изменение конфигурации этих параметров (например,--pod-eviction-timeout
), создавая сетевые разделы, происходило удивительное. Всегда лучше обнаруживать эти сюрпризы при тестировании, а не в 3 часа ночи в производстве.
Стратегия 6: преднамеренно вызывайте проблемы кластера Kubernetes
Раньше мы обсуждали упражнения на игровом дне в Stripe, и мы все еще делаем их. Идея состоит в том, чтобы придумать ситуации, которые, как вы ожидаете, произойдут в производстве (например, падение сервера API Kubernetes). Затем нужно преднамеренно воспроизвести эти ситуации в производстве (в течение рабочего дня с предупреждением), чтобы вы могли их обработать.
Выполняемые упражнения к кластере часто выявляли проблемы в мониторинге или конфигурационных файлах. Мы были рады обнаружить (и контролировать!) эти проблемы на ранней стадии, а не увидеть неожиданно через шесть месяцев.
Вот несколько упражнений с дня игры, которые мы использовали:
- Выключите один сервер API Kubernetes.
- Выключите все серверы API Kubernetes и запустите их обратно (к нашему удивлению, это сработало очень хорошо).
- Выключите узел etcd.
- Сократите количество рабочих узлов в кластере Kubernetes. Это должно привести к тому, что все pods мигрируют на другие узлы.
Нам было очень приятно видеть, насколько хорошо Kubernetes обработал множество сбоев, которые мы ему устроили. Kubernetes спроектирован так, чтобы быть устойчивым к ошибкам. Он имеет один etcd-кластер, хранящий все состояния, сервер API, который является простым интерфейсом REST для этой базы данных и набором stateless-контроллеров, которые координируют управление кластерами.
Если какой-либо из основных компонентов Kubernetes (сервер API, менджер контроллера или планировщик) падает или перезапускается, параметры считывают соответствующее состояние из etcd и продолжают работать без проблем. Это была та функция, на которую мы надеялись и верили, что она будет работать хорошо. Она на самом деле показала отличные практические результаты.
Вот несколько проблем, которые мы обнаружили во время тестов: - «Странно, я не получил информации о том, что действительно произошло. Давайте исправим наш мониторинг».
- «Когда мы уничтожили экземпляры сервера API и вернули их, они потребовали вмешательства человека. Мы лучше это исправим».
- «Иногда, когда мы настраиваем etcd failover, сервер API запускает запросы тайминга до тех пор, пока мы не перезапустим его».
После запуска этих тестов мы разработали исправления для обнаруженных проблем: улучшили мониторинг, выявили проблемы с фиксированной конфигурацией и ошибки с Kubernetes.
Создание простых в использовании cron-задач
Давайте кратко рассмотрим, как мы сделали систему на основе Kubernetes простой в использовании.
Наша первоначальная цель заключалась в разработке системы для работы cron, которую наша команда смогла бы легко обслуживать. Мы стали уверены в выборе Kubernetes, далее нужно было упростить для коллег-инженеров настройку и добавление заданий в cron. Мы разработали простой формат конфигурации YAML, чтобы для использования системы пользователям не нужно разбираться во внутренних функциях Kubernetes. Формат, который мы разработали:
name: job-name-here
kubernetes:
schedule: '15 */2 * * *'
command:
- ruby
- "/path/to/script.rb"
resources:
requests:
cpu: 0.1
memory: 128M
limits:
memory: 1024M
Мы не сделали здесь ничего интересного, только написали простую программу, чтобы взять за основу этот формат и на выходе получать конфигурацию в cronjob в Формате Kubernetes, которые мы применяем с kubectl
.
Мы также написали тесты, чтобы гарантировать, что имена заданий не слишком длинные (имена cron-задач не могут превышать 52 символа) и что все имена уникальны. В настоящее время мы не используем cgroups для ограничения объема памяти на большинстве cron-задач, но планируем заняться этим вопросом в будущем.
Простой формат конфигурационного файла был прост в использовании, и поскольку мы автоматически генерировали определения заданий для Chronos и Kubernetes из одного и того же исходного описания, перемещение задания между любой системой было очень простым. Это было ключевой частью надежной работы постепенной миграции.
Мониторинг Kubernetes
Мониторинг внутреннего состояния кластера Kubernetes оказался проще, чем предполагалось. Мы используем пакет показателей kube-state-metrics для мониторинга и небольшую программу Go veneur-prometheus, чтобы собрать метрики в Prometheus. Показатели kube-state-metrics публикуют их в качестве показателей statsd для нашей системы мониторинга.
Например, вот диаграмма количества ожидающих контейнеров в нашем кластере за последний час. Ожидание означает, что они ждут назначения рабочего узла для запуска. Вы можете видеть, что в 11 утра был всплеск. Это потому, что многие наши задания cron работают на 0-й минуте часа.
У нас также есть монитор, который проверяет, чтобы в состоянии ожидания не осталось ни одного контейнера. Мы убеждаемся, что каждый блок начинает работать на узле в течение 5 минут, иначе получим предупреждение.
Будущие планы для Kubernetes
Процесс настройки кластера Kubernetes и переноса наших cron-задач в новый кластер занял у нас пять месяцев при участии трех инженеров, работающих полный рабочий день.
Мы инвестировали в изучение Kubernetes, поскольку ожидаем, что сможем более широко использовать Kubernetes на Stripe.
Вот некоторые принципы, которые применяются при работе с Kubernetes (или любой другой сложной распределенной системы):
- Определите четкую бизнес-причину для проектов Kubernetes (и всех инфраструктурных проектов!). Понимание бизнес-кейса и потребностей наших пользователей значительно упростили проект.
- Агрессивно сокращайте объем. Мы решили не использовать многие основные функции Kubernetes для упрощения кластера. Это позволяет поставлять изменения быстрее. Например, поскольку для проекта не требовалось сетевое взаимодействие, мы могли бы использовать брандмауэр для всех сетевых соединений между узлами и отложить размышления о сетевой безопасности в Kubernetes для будущего проекта.
- Вложите значительное количество времени в изучение правильной работы кластера Kubernetes. Тщательно тестируйте все решения и изменения. Распределенные системы чрезвычайно сложны, и есть большая вероятность отклонения от плана. Возьмем пример, который мы описали ранее: контроллер узла может убить все контейнеры в кластере, если они потеряют контакт с серверами API.Изучение поведения Kubernetes после каждого изменения конфигурации требует времени и тщательной фокусировки.
Не останавливаясь на этих принципах, мы с уверенностью можем использовать Kubernetes, продолжать расти и развиваться. Например, мы с интересом наблюдаем за выпуском AWS EKS. Мы завершаем работу над другой системой, чтобы работать с моделью машинного обучения, а также изучаем перемещение некоторых HTTP-сервисов в Kubernetes. По мере использования Kubernetes в производстве мы планируем внести свой вклад в проект с открытым исходным кодом.
Оригинал: Learning to operate Kubernetes reliably.
KIVagant
Похоже никто не спешит комментировать. Спасибо за перевод, мой опыт использования Kubernetes подтверждает, что лучше много и заранее проводить тесты, чем потом искать способы поднять кластер посреди ночи, когда он уже очевидно и безнадёжно мёртв. Впрочем, однажды мне довелось полностью удалить все мастер-ноды и затем все системные поды, чтобы оживить кластер, так что в целом он может вернуться в работоспособное состояние даже после критических проблем. Но я бы точно избегал запускать что-либо stateful. И рекомендовал бы держать минимум 2 полностью идентично настроенных кластера, чтобы мгновенно переключаться между ними в случае проблем с одним из них, не тратя время на исследование. И, кстати, так безопаснее обновлять версию K8s и его внутренностей. По моему убеждению, Kubernetes — это (пока ещё) — игрушка не для маленьких команд. И если возможно использовать SaaS, то лучше так и сделать.