В тексте рассказываем про популярные заблуждения о Kubernetes и рассматриваем интересные особенности эксплуатации этой системы оркестрации.
Все вроде просто
Самое часто встречающееся заблуждение о Kubernetes — мнимая простота системы при базовом развертывании. На первый взгляд, действительно, ничего сложного нет: всего пяток бинарников. Есть понятный гайд для быстрого старта, воспользоваться которым может практически любой системный администратор. И вот вроде бы все хорошо — кластер запущен, можно выкатывать приложения. Здесь встречаем первый подводный камень: Kubernetes подойдет не для всего.
В Kubernetes рекомендуется использовать приложения, относящиеся к категории cloud-native — изначально написанные с расчетом на то, что они будут разворачиваться в облачных средах. Идеальный вариант — набор микросервисов, запускаемых в контейнерах, деплой которых выполняется в соответствии с концепцией непрерывной доставки и развертывания (пресловутый CI/CD).
Когда все выстроено по такой схеме, Kubernetes забирает на себя большую часть задач системного администратора и самостоятельно выполняет рутинные операции. Управление осуществляется не императивно, а декларативно. Это, с одной стороны, очень удобно, с другой — усложняет решение возникающих проблем.
Программисту достаточно написать конфигурационный файл (манифест), в котором нужно описать желаемое состояние инфраструктуры для своего приложения. Kubernetes же приведет текущее состояние к желаемому самостоятельно. Схема рабочая, но не без нюансов. Если попробовать развернуть в Kubernetes обычное приложение, не рассчитанное на работу в облачных средах — в большинстве случаев оно взлетит. Правда, не вверх, а вниз.
Сценарий катастрофы чаще всего выглядит следующим образом: подняли кластер, задеплоили приложение, и спустя год внезапно происходит сбой, полностью ломающий представление о надежности и контролируемости выстроенного кластера. Тогда системному администратору приходится залезать в самые недра, а там все сделано весьма хитроумно. Рассмотрим по порядку, что чаще всего ломается и как это предотвращать.
Stateful, Stateless… Что?
Когда у нас есть приложение, в абсолютном большинстве случаев его данные нужно где-то хранить. Но сама концепция контейнеров не предусматривает сохранения состояния, поэтому возникает потребность в хранении данных. Проще всего ее закрыть облачным файловым хранилищем. Но тут сразу возникает много нюансов.
Во-первых, надо четко понимать, что задача монтирования внешних хранилищ решается с помощью сторонних модулей. Те, в свою очередь, отдают абстракцию тома в Kubernetes. Это слабое место, которое может сломаться в первую очередь.
Апдейт самого Kubernetes в некоторых случаях может сломать корректную работу абстракций хранилищ данных. Если разработчик не учел каких-либо особенностей работы новой версии API Kubernetes, есть ненулевая вероятность встретиться с багами и различными «артефактами». Так что каждое обновление — большой риск.
Решение вопроса о хранении данных важнее, чем может показаться на первый взгляд. Сразу встают вопросы о надежности и безопасности. Приложениям важно иметь минимальную задержку до хранилища, поэтому наличие хорошей сетевой связности и местоположение имеет значение.
Если строить свое хранилище традиционными средствами при помощи готовых аппаратных решений, сразу возникает vendor lock-in и ситуации, при которых нет иных вариантов, кроме как приобретать дорогие и одобренные производителем системы хранения данных (СХД).
Можно воспользоваться бесплатными решениями по построению программной СХД, но сразу же встает вопрос, кто будет заниматься обслуживанием такой системы. Это отдельная специализация, требующая профильных специалистов, и при всей бесплатности ПО все равно потребует вполне конкретных вложений.
Часто встречается ситуация, когда внешнего хранилища нет, а данные размещаются непосредственно на нодах кластера. Это прямой путь к потере данных при выполнении рутинных сервисных операций — обновление пакетов ОС, перезагрузка ноды или ее конфигурирование. В таких случаях кластер хранения умирает и чаще всего уносит за собой данные в цифровую Вальхаллу.
Балансировка и отказоустойчивость
Запросы клиентов требуют распределения, и базовые сценарии использования Kubernetes отлично подходят для веб-приложений. Система открывает порт (или несколько портов) наружу, что не добавляет безопасности. Где она это делает и как балансирует — остается тайной за семью печатями. А если нужно безопасно выставить приложение во внешний мир и при этом иметь возможность отражать потенциальные атаки на отказ в обслуживании?
Конечно, существуют сторонние модули, призванные решить эту проблему. Они вроде бы «родные», но все равно созданы сторонними разработчиками. Речь о тех самых Ingress-контроллерах из нашей прошлой статьи. Их много, они все разные, каждый имеет свои особенности использования.
Сама же балансировка — почти rocket-science. Часто требуется «приземлять» клиента на одну и ту же реплику, чтобы тот не бродил по всем остальным. Обычно это решается использованием механизма «липких сессий». Но как поднять разные каналы для разных задач — таких, как загрузка, веб-сокеты и отдача лендинга сайта? Как обезопасить поды от случайных попаданий извне?
Универсального рецепта нет, и в большинстве случаев придется использовать сторонний балансировщик нагрузки или встраивать логику балансировки непосредственно в свое приложение. Первое ставит в зависимость от сторонних разработчиков, а второе требует дополнительных затрат на написание, отладку и тестирование кода.
Предположим, что мы смогли и приложение каким-то образом наружу выставили. Сразу возникают новые испытания — как не подпустить никого к мастер-нодам кластера? Как правильно сконфигурировать и распределить кластер таким образом, чтобы минимизировать шанс отказа всех нод сразу? Что если ляжет сеть провайдера и приложение потеряет связность? Но до этих вопросов админы доходят лишь после решения прочих насущных проблем, а их тоже немало.
Даже такая простая вещь, как хранение образов для запуска в кубере, превращается в квест. Хранить напрямую на нодах — сложно, да и потерять легко. Без дополнительной оплаты на наружные сервисы никто не пустит. Приходится изобретать самодельные решения, которые позволят хранить образы.
Дальше задачи становятся все более и более разнообразными. У китайцев даже есть такое проклятье: «Чтобы ты жил в эпоху перемен». Выдержит ли API-сервер увеличение количества запросов при масштабировании? Можно ли будет динамически делать ресайз кластера при резких скачках нагрузки?
Здесь важно не только иметь возможность масштабироваться в сторону расширения, но и наоборот: если нагрузка существенно уменьшится, кластер надо ужимать, чтобы не платить за простаивающие ресурсы. Еще один важный момент — убирать ноды из кластера необходимо таким образом, чтобы, во-первых, не потерять сессии клиентов, а, во вторых, случайно не вызвать проблему нехватки ресурсов. Соблюдение такого баланса — непростая задача.
Выстрел в ногу
Это увлекательное занятие программистов интересно реализуется в Kubernetes. Чаще всего «выстрел в ногу» происходит при попытках настройки сетевого взаимодействия. Достичь фееричных спецэффектов просто, достаточно использовать одну сеть на несколько проектов OpenStack и интегрировать туда Kubernetes. Когда все это упадет — придется бежать за админским бубном.
Еще на нашей практике встречались забавные опечатки, которые уставшим взглядом в три часа ночи заметить сложно. Кластер при этом ведет себя непредсказуемо, пытаясь подстроиться под противоречащие здравому смыслу настройки. Лишь найдя, в чем именно заключается логическая ошибка конфигурации, можно разорвать «порочный круг» и вернуть кластер в штатное состояние.
Семь бед, один Reset
«А вы пробовали выключить и снова включить?» — этот мем из сериала The IT Crowd актуален и в наше время, в том числе и в Kubernetes. Подавляющее большинство нестандартных ситуаций разрешается повторным развертыванием. Здесь как раз проявляется сложность декларативного подхода. Точечное воздействие почти бесполезно.
Чтобы столь сложной системе вернуться в полностью штатное состояние, необходимо ее уничтожить и развернуть заново. Приходит осознание того, что весь тот сложный путь проб и ошибок, которым кластер шел долгое время, придется каким-то образом повторить. До этого момента про GitOps и другие практики системный администратор даже не задумывался.
Это настоящая боль, которую поймет только тот, кто через нее уже проходил. Наиболее похожая метафора — настройка нового смартфона без использования механизмов клонирования: придется не только заново переустанавливать все нужные приложения, но и каждое настраивать отдельно (авторизация и прочее).
В определенный момент становится ясно, что большую часть проблем можно было избежать на этапе проектирования архитектуры и выбора всех модулей. И здесь кроется дьявольское коварство — рядовой системный администратор не эксперт по Kubernetes и не может принять правильных решений по проектированию архитектуры и инфраструктуры будущих кластеров. К тому же, сами модули каждый год видоизменяются так, что их нужно изучать практически с нуля.
Я что-то нажал и теперь не отжимается
«Но делать было нечего, и помощи ждать было неоткуда: приходилось или нести наказание, или молить об отмене его».
(Марк Твен — «Принц и нищий»)
Когда в кластере Kubernetes что-то ломается, в большинстве случаев системный администратор сражается с этим один на один. Основная проблема — понять, на каком уровне абстракции произошел сбой. И вряд ли тут помогут советы гуру или StackOverflow. Придется или скрупулезно препарировать систему, пытаясь найти и устранить возникшую ошибку, или же снова уничтожать все и деплоить по новой.
Представьте, что купили автомобиль, собранный из деталей пары десятков разных автопроизводителей, а потом обращаетесь к производителям и дилерам с вопросом: «А что она у меня то едет, то не едет?». Если думаете, что мы утрируем, расстроим — описываемая ситуация встречается сплошь и рядом.
Вместо заключения
Какие выводы можно из этого сделать? В первую очередь, подумайте — вам вообще нужен Kubernetes? Одно дело, если у вас cloud-native приложение, полностью готовое функционировать в облачной среде, и совсем другое — монолит, который внезапно решили крутить в контейнерах с помощью системы оркестрации.
Если Kubernetes все-таки нужен, уделите должное время подготовке физической инфраструктуры. Надо понимать, что может стать возможным источником проблем. Если на секунду задуматься, то как бы мы не пытались абстрагироваться от физических серверов, в конечном итоге все нагрузки исполняются на них. Все аспекты традиционной инфраструктуры могут повлиять на будущий кластер и на объем возникающих проблем.
Есть одна фраза, которая универсальна и всегда к месту: «Если вы боитесь выполнить какое-то действие, значит, у вас тут засела здоровенная проблема». Обычно системные администраторы стараются лишний раз не трогать то, что хорошо работает. Эта тактика не работает с Kubernetes: если с кластером ничего не делать, он прекратит работать и, скорее всего, это произойдет без намека на реальную причину.
Когда мы в Selectel задумались о том, как помочь клиентам с развертыванием приложений в Kubernetes, то разработали соответствующую услугу Managed Kubernetes. С помощью нее берем на себя подготовку, обслуживание физической инфраструктуры и даем клиентам готовые к развертыванию приложений кластеры. Этим снимаем часть возможных проблем с системного администратора, их решение забираем на себя.