[Перевод] Руководство по Kubernetes для хейтеров Kubernetes

llxypyhfgpibrvl-ix2xbbzraei.png

Есть среди программистов такая фракция, в которой Kubernetes пользуется дурной репутацией как чрезмерно сложный пожиратель времени и технология, которой следует сторониться стартапам. Использовать Kubernetes в рамках небольшой команды — явный признак инженерного переусложнения.

Я и сам замешан в перемывании косточек на эту тему.

Конечно, могу иногда поворчать по поводу Kubernetes, но, справедливости ради, это технологический шедевр. Настоятельно рекомендую всем моим конкурентам им пользоваться.
— Paul Butler (@paulgb) September 9, 2022


Несмотря на такое ёрничанье, я искренне считаю Kubernetes «технологическим шедевром». В сентябре 2022 года я также написал о том, насколько в самом деле обоснована сложность Kubernetes с учётом тех задач, которые он решает.

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

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

Зачем вообще нужен Kubernetes?


На мой взгляд, Kubernetes — это столбовая дорога к цели, если вы стремитесь добиться сразу трёх следующих вещей:

  1. Одновременно выполнять множество процессов/серверов/назначенных заданий.
  2. Выполнять их с избыточностью и при этом балансировать нагрузку между ними.
  3. Конфигурировать их в коде и выражать отношения между ними в виде кода.


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

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

Как мы используем Kubernetes


Для контекста:  Jamsocket— это сервис для динамического поднятия процессов, с которыми может «общаться» веб-приложение. Чем-то похоже на AWS Lambda, но время жизни процесса увязывается с соединением WebSocket, а не с отдельно взятым запросом/откликом.

При помощи Kubernetes мы выполняем долгоиграющие процессы, необходимые для поддержки таких операций. Сервер API, реестр контейнеров, контроллер, сборщик логов, некоторые сервисы DNS, сбор метрик и т.д.

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

  • Эфемерные процессы как таковые. В самом начале работы мы успели активно ими попользоваться, но вскоре обнаружили, что они скорее нас сковывают (подробнее об этом ниже)
  • Статические/маркетинговые сайты. Для этой цели мы пользуемся Vercel. Он дороже, но, что ж, час времени разработчика на маленьком стартапе тоже стоит дорого, и в нашем случае Vercel вполне себя окупает.
  • В любых контекстах, где непосредственно сохраняются такие данные, о потере которых мы бы очень горевали. Мы используем персистентные тома для кэширования или производных данных, но, как правило, мы предпочитаем работать с управляемой базой данных Postgres DB вне кластера и хранилища блоб-данных.


При этом немаловажно, что сами мы не администрируем Kubernetes. Его главное преимущество заключается в возможности аутсорсить его эксплуатацию на уровне инфраструктуры! Нас вполне устраивает Google Kubernetes Engine, и, пусть фиаско Google Domains поколебало мою веру в Google Cloud, я, как минимум, сплю спокойно, полностью отдавая себе отчёт в том, что при необходимости нам не составит труда мигрировать на Amazon EKS.

Вещи, которыми мы активно пользуемся


В k8s есть несколько типов ресурсов, которыми мы пользуемся без колебаний. Здесь я перечислю только те ресурсы, которые мы явно создаём. Большинство из них сами неявно создают другие ресурсы (например, поды), которые я не стану упоминать, но которыми мы, конечно же (косвенно) пользуемся.

  • Развёрнутые инстансы: большинство наших подов мы специально развёртываем. Каждый развёрнутый инстанс, критичный для работы нашего сервиса, существует в нескольких репликах, и к нему применяются скользящие обновления.
  • Сервисы, а именно:  ClusterIP для внутренних сервисов и LoadBalancer для внешних. Мы избегаем пользоваться сервисами NodePort и ExternalName, предпочитаем, чтобы наша конфигурация DNS находилась вне Kubernetes.
  • CronJobs для скриптов очистки и тому подобных вещей.
  • ConfigMaps и Secrets: для передачи данных вышеупомянутым ресурсам.


Вещи, которыми мы пользуемся с осторожностью


  • StatefulSet и PersistentVolumeClaim: да, нам доводилось время от времени пользоваться StatefulSet. Эта конфигурация посложнее, чем при развёртывании обычных инстансов, зато позволяет сохранять персистентный том от перезапуска к перезапуску. Важные данные мы предпочитаем долговременно хранить в управляемых сервисах вне k8s. У нас нет табу на использование томов, так как иногда бывает удобно сохранить, скажем, кэш между перезапусками. Но я предпочитаю обходиться без томов, так как при скользящем подходе к развёртыванию между ними возможны патологические взаимодействия (взаимные блокировки).
  • RBAC (управление доступом на основе ролей): таким подходом мы пару раз пользовались, например, чтобы дать сервису право обновить секрет. Но такой подход слишком усложняет наш маленький кластер, поэтому я его в основном избегаю.


Вещи, которых мы активно избегаем


  • Писать YAML вручную. В YAML достаточно вариантов, как выстрелить себе в ногу, поэтому я предпочитаю не иметь с ним дел. Напротив, определения наших ресурсов Kubernetes мы создаём из TypeScript при помощи Pulumi.
  • Невстроенные ресурсы и операторы. Ранее я писал о том, почему паттерн цикла управления — это палка о двух концах. Да, это ключевой фактор, обеспечивающий надёжность K8s, но он же — источник лишней опосредованности и сложности. При помощи паттерна оператор и собственных ресурсов можно позволить сторонним программам использовать надёжную инфраструктуру Kubernetes в их собственных управляющих циклах, и в теории эта идея отличная, а на практике, как оказалось — довольно неудобная. Мы не работаем с cert-manager, а автоматизируем работу с сертификатами при помощи Caddy.
  • Helm. Helm для нас не вариант, так как он требует работать с операторами и иметь дело с YAML без правил, но я к тому же полагаю, что, используя шаблонизацию неструктурированных строк для генерации данных, которые затем попадали бы под машинный парсинг, мы сами повышаем хрупкость нашей системы, ничего не выигрывая. Мне кажется, что nindent — это как «железом по стеклу», извините.
  • Что угодно с «mesh» в названии. Полагаю, кому-то они полезны, но точно не мне и, кстати, ему тоже не нравятся.
  • Ingress-ресурсы. Лично у меня рубцов от них нет, и я даже знаю людей, умеющих продуктивно пользоваться такими ресурсами, но наш успешный опыт работы с Kubernetes подсказывает, что нужно избегать лишних уровней косвенности. Нам вполне хватает сконфигурировать Caddy, так и действуем.
  • Попытки реплицировать целый стек k8s на локальной машине. Мы не пользуемся такими вещами как k3s или ему подобными для точного воспроизведения продакшен-среды, а обходимся Docker Compose или нашими собственными скриптами для запуска того подмножества системы, которое нас в настоящий момент интересует.


Человек не должен дожидаться пода


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

Признаю, что здесь я пиарю мою книгу, но она хотя бы опенсорсная: в ней рассказывается о лицензированном в MIT оркестраторе на Rust, который называется Plane. Этот оркестратор предназначен для быстрого назначения и выполнения процессов, обслуживающих интерактивные рабочие нагрузки (в частности, такие, которых приходится дожидаться человеку).

Более высокоуровневые абстракции


Для полноты картины также должен упомянуть, что уже появившиеся альтернативы достаточно хороши. В частности, если для вас не актуально требование #3 из вышеприведённого списка (возможность расписывать инфраструктуру в коде). В одном из наших продуктов мы решили воспользоваться Railway вместо кластера k8s, в основном для обслуживания превью-сред. Некоторые коллеги, которых я очень уважаю, топят за Render (я им тоже баловался, но мне кажется, что предлагаемая в Railway модель среды всё-таки чище). Кроме того, я выступаю за принятый в Flight Control подход «приходи со своим облаком».

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


Возможно, захочется почитать и это:

Новости, обзоры продуктов и конкурсы от команды Timeweb.Cloud — в нашем Telegram-канале


b5pjofdoxth14ro-rjsrn7sbmiy.png

Habrahabr.ru прочитано 10153 раза