Написать пост

Kubernetes: шпаргалка для собеседования

Гайд для собеседований по Kubernetes: главные (и не только) вопросы и как на них ответить

Всем привет! Меня зовут Олег, я работаю исполнительным директором по разработке в Газпромбанке. На разных этапах карьеры я участвовал во многих собеседованиях, а в настоящее время сам собеседую кандидатов на должность инженеров DevOps и системных администраторов в Газпромбанке, вследствие чего у меня сформировалось некоторое представление о вопросах, которые могут быть заданы соискателям. И сейчас я хочу остановиться на разделе вопросов про Kubernetes.

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

Как проходят собеседования по Kubernetes

Вопросы по Kubernetes достаточно часты на собеседованиях на инженерные вакансии, связанные с администрированием и эксплуатацией. Вопросы могут варьироваться от базовых, рассчитанных на механическую проверку теоретических знаний («объясните, что такое service»), до более сложных и комплексных, требующих глубинного понимания внутренних принципов Kubernetes и работы (каким образом опубликовать приложение, развёрнутое в Kubernetes). Давайте пойдём от базы в направлении возрастания сложности.

Собеседования по Kubernetes (k8s) могут иметь различную специфику с акцентом на администрирование и эксплуатацию этой системы. В зависимости от этого, а также от персонального опыта интервьюера, могут задаваться и те вопросы, на которые кандидат не знает ответа.

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

И это нормальный ответ. IT-индустрия достаточно большая, всё в одну голову не вмещается.

Какие вопросы задают на собеседованиях

Что такое Kubernetes?

На самом деле не самый простой и очевидный вопрос. Самый распространённый ответ — «система оркестрации контейнеров». На самом деле контейнер — слишком низкий уровень абстракции для k8s (он управляет POD’адми, на каждый POD создаётся минимум два контейнера). На мой взгляд, вариант, который более точно характеризует эту систему, скорее такой:

«Наиболее популярная на данный момент инфраструктурная платформа, содержащая в себе фреймворк для декларативного управления конфигурациями приложений на основе контейнеризации и (реже) виртуализации, которая в настоящее время является стандартом индустрии».

Что такое POD?

Группа контейнеров, объединённых общей сетью (общий localhost, общий внешний IP). Это может быть нужно для запуска созависимых сервисов, которые работают совместно по сети (например, связки nginx + php-fpm) или используют общие файловые ресурсы (например, продюсер и сборщик логов).

Для чего при старте PODа создаётся контейнер с процессом pause?

Для PODа создаются как минимум два контейнера. Первый контейнер с pause служит для того, чтобы обеспечить общую сеть (для него создаётся network namespace в linux — то есть все контейнеры PODа должны располагаться на одной ноде).

Каким образом мы можем запустить в Kubernetes приложение (варианты: рабочую нагрузку, workload)?

Мы можем запускать PODы поодиночке, но, как правило, для этого используются более функциональные сущности (workloads https://kubernetes.io/docs/concepts/workloads/):

  • Deployment — служит для развёртывания нужного количества PODов на основе единого шаблона.
  • StatefulSet — похож на Deployment, но оптимизирован под работу с stateful-приложениями.
  • DaemonSet — позволяет развернуть один экземпляр приложения на каждом доступном узле (применяется, как правило, для нужд, связанных с обслуживанием узлов).
  • Job — способ запустить конечный процесс в контейнере и дождаться его успешного выполнения (описав при этом политики запуска вроде длительности и количества рестартов в случае падения).
  • CronJob — то же, что и Job, но с запуском по расписанию. При этом ведётся журнал успешности запусков.

Есть ещё ReplicationController, но он считается устаревшим и практически не используется. На смену ему пришёл Deployment.

Что такое ReplicaSet?

ReplicaSet — это сущность, которой управляет Deployment. Deployment создаёт новый ReplicaSet каждый раз, когда в его конфигурации происходят изменения, после чего происходит завершение PODов старого ReplicaSet и запуск PODов нового. При помощи ReplicaSet мы можем (в том числе) делать откат на предыдущие версии Deployment.

Что такое Kubernetes probes?

Это проверки, которые осуществляются в течение жизненного цикла PODа. Они описываются для каждого контейнера PODа. Существуют три вида проверок.

  • Startup probe — запускается сразу после старта PODа и применяется для приложений, которые имеют длительную процедуру инициализации. Пока она не завершена, другие пробы не запускаются.
  • Readiness probe — проверка готовности PODа обрабатывать трафик (POD не добавляется в маршрутизацию трафика в service, если эта проверка не пройдена).
  • Liveness probe — проверяет, функционирует ли приложение (в случае, если проверка не завершилась успехом, процесс в контейнере PODа перезапускается).

Readiness и liveness — независимые и запускаются после прохождения startup probe.

Существуют exec-, http-, tcp- и gprc-пробы. Проверки осуществляются сервисом kubelet на ноде, где запущен целевой POD.

Какие существуют хорошие практики для создания проб?

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

Пробы — это дополнительная нагрузка на инфраструктуру, поэтому они не должны осуществлять тяжёлые, ресурсоёмкие запросы к приложению, равно как и не должны запускаться слишком часто. По возможности пробы также не должны проверять функционал, зависящий от внешних сервисов, — иначе можно получить каскадный сбой. Нужно быть очень осторожным с liveness-пробами, — они перезапускают процесс в контейнере и могут вызвать дополнительные проблемы, когда, например, на приложение пришла высокая нагрузка, оно занято обработкой пользовательских запросов и не может ответить на liveness-пробу (лучше сделать выделенный пул воркеров на прохождение liveness-пробы, если проверяемый сервис это позволяет). Лучше всего создавать сквозные проверки для readiness-пробы — например, если в PODе связка nginx + php-fpm, проверять можно только location nginx, который проксирует php-fpm (одним запросом проверяются оба сервиса).

Что такое Pod Disruption Budget (PDB)?

Это функционал k8s, позволяющий держать запущенными минимально необходимое количество PODов приложения. То есть при возникновении события вроде evict’а PODов с ноды, drain`а ноды, удаления PODов и прочего k8s не удалит PODы, если общее количество PODов приложения ниже PDB.

Что такое priority classes?

Это механизм k8s, позволяющий указать важность PODов для наших процессов. Например, если в одном кластере k8s у нас продуктовый и тестовые окружения, мы можем повысить priority class для PODов продуктового, и если, например, возникнет недостаток ресурсов на ноде, первыми для evict’а будут выбраны PODы тестовых окружений.

Что такое POD eviction?

Это механизм, позволяющий освободить ноду от лишних PODов. Бывают evict’ы по ресурсам (когда на ноде не хватает памяти, места на диске или количества PID для процессов) и посредством вызова API — когда мы запускаем kubectl drain node.

Каким образом мы можем вывести ноду из работы для обслуживания?

Мы можем запретить запуск PODов на ноде с помощью kubectl cordon и удалить PODы с ноды при помощи kubectl drain.

Каким образом мы можем управлять размещением PODов на конкретных нодах кластера k8s?

Для этого существует несколько возможностей.

  • NodeSelector/node affinity — механизм, позволяющий запускать PODы на нодах с определённым набором меток (labels). Это может быть полезно, если, например, PODы требуют определённого оборудования — скажем, у нас есть пул нод с GPU для нужд машинного обучения.
  • Tains/tolerations — механизм, позволяющий запрещать запуск на ноде PODов (taint — описывается на ноде), которые не имеют разрешения (toleration — описывается на POD’е). Это может быть полезно, если у нас в кластере несколько окружений — мы можем выделить ноды под production и при помощи taint запретить запускать там PODы тестовых окружений.
  • Pod affinity/antiAffinity — механизм, позволяющий группировать PODы разных приложений на общих нодах (если, например, им важен быстрый сетевой доступ) или, наоборот — заставлять их запускаться на разных нодах (например, чтобы распределить PODы одного приложения по нодам кластера для повышения отказоустойчивости в случае сбоя на ноде).

Каким образом мы можем управлять вычислительными ресурсами в k8s?

Для управления ресурсами в кластере k8s служат resources requests / limits. Их можно выставить для CPU, memory и в последних версиях k8s — для GPU.

Requests применяются для того, чтобы определить, сколько ресурсов обычно потребляет наше приложение — на основании этих данных Kubernetes scheduler будет осуществлять выбор нод для запуска PODов (сумма request’ов всех контейнеров всех PODов должна быть меньше доступных на ноде ресурсов).

Limits применяются как предохранительный механизм, ограничивающий потребление ресурсов конкретным контейнером PODа. В случае превышения лимита процессорного времени будет применяться механизм thermal throttling, в случае превышения лимита памяти, — механизм OOM.

Модель выделения ресурсов, при которой requests меньше limits, называют burstable QoS, requests равно limits — guaranteed QoS.

Кроме того, мы можем выставить квоты ресурсов (resource quotas) на namespace (квоты по CPU, памяти, количеству запущенных PODов и размеру диска заказанных persistent volume), а также описать требования к resources requests, которые должны быть описаны на POD’ах в namespace при помощи limit ranges.

Также ресурсами приложений можно управлять при помощи автоскейлеров. В k8s из коробки доступны:

  • HPA — horizontal pod autoscaler, который может увеличивать или уменьшать количество PODов приложения в зависимости от потребления ими CPU и/или памяти;
  • VPA — vertical pod autoscaler, который может управлять resources requests / limits (есть разные режимы работы — от рекомендательного до принудительного изменения конфигурации ресурсов и перезапуска PODов с новыми настройками).

Ещё есть реализации автоскейлеров, которые при работе могут использовать какие-то внешние метрики (скажем, длину очереди), например, carpenter или KEDA. Если кластер работает в облачном окружении, мы можем использовать cluster autoscaler для заказа новых нод или удаления лишних, в зависимости от общей утилизации групп нод.

Каким образом мы можем улучшить стабильность работы приложения в k8s?

Прежде всего мы должны описать probe для контейнеров в PODе, а также resources requests / limits. Далее лучше всего описать antiAffinity для PODов наших приложений, чтобы легче переживать сбои на конкретных нодах.

Если у нас в кластере работают как продуктовые, так и тестовые окружения, то хорошей практикой будет описать node selector и taints/tolerations, чтобы запускать production-приложения на выделенных нодах.

Если возможности выделить ноды под production нет или в рамках production мы можем выделить особо важные (core) сервисы, для них стоит поднять priority classes. Также есть смысл описать pod distruption budget для особо важных приложений. В случае с многопользовательской (multitenant) моделью использования кластера в неймспейсах пользователей стоит описывать resourceQuotas и limitRanges.

Что такое Kubernetes service?

Это объект, который выполняет несколько важных функций.

  • Разрешение DNS-имён: сервис предоставляет символьное DNS-имя, формирующееся на основе имени сервиса, неймспейса приложения и DNS-суффикса кластера для единообразного доступа к приложениям. Плюс сервисы с типом externalName могут описывать произвольные A-записи во внутреннем DNS кластера.
  • Маршрутизация и балансировка трафика: сервис может предоставлять IP-адрес, при обращении к которому при помощи алгоритма round robin балансирует трафик на PODы приложения, либо выдавать IP-адреса PODов приложения на запрос к своему DNS-имени для организации балансировки на уровне приложения (режим headless с настройкой clusterIp: no).
  • Service discovery: на основании описанных в нём меток service ищет PODы, которые соответствуют этим меткам и прошли readiness-пробу, и добавляет их в маршрутизацию трафика.
  • Публикация приложений: мы можем использовать сервисы для того, чтобы предоставлять внешним пользователям доступ к приложениям в кластере.

Каким образом мы можем предоставить приложение, которое работает в нашем кластере, k8s нашим пользователям?

— Если наше приложение работает по HTTP, мы можем использовать так называемый ingress-контроллер — реверс-прокси, интегрированный с Kubernetes API, который позволяет на основе описанных custom resources осуществлять маршрутизацию трафика по HTTP.

Можно использовать Kubernetes service для ряда задач.

  • С типом externalIp — чтобы опубликовать порт сервиса на конкретных IP-адресах наших нод; с типом nodePort — чтобы опубликовать порт сервиса на всех нодах на произвольных портах в диапазоне от 30 000 до 64 535 (при этом есть интересная опция trafficPriority: local, позволяющая предпочитать PODы приложения, находящегося на локальной ноде при маршрутизации трафика).
  • С типом loadBalancer — если наш k8s работает в облаке. При этом создаётся сервис, аналогичный nodePort, но вдобавок к нему заказывается load balancer, который осуществляет маршрутизацию на ноды кластера в порты, выбранные для nodePort.
  • Можно использовать kubectl proxy, чтобы пробросить порт сервиса локально на нашу машину (может быть полезно для нужд разработки).
  • Мы можем использовать nodeNetwork: true (проверить, когда появится интернет), чтобы разрешить приложению, работающему в контейнере, использовать сеть ноды и занимать сетевые порты непосредственно на интерфейсах ноды.

Что такое Kubernetes ingress?

Это реверс-прокси, интегрированный с Kubernetes API, который позволяет на основе описанных в кластере специальных custom resources осуществлять доставку пользовательского трафика до приложений, развёрнутых в кластере. Существует множество реализаций этого паттерна. Например, ingress-nginx от создателей k8s на базе nginx или router в openshift на базе haproxy. Помимо доставки трафика, ингрессы позволяют создавать HTTPS-соединения на основе сертификатов, полученных, к примеру, от letsencrypt при помощи cert manager, а также делать многие другие вещи вроде timeouts/retry, limits, session affinity / sticky sessions, маршрутизации трафика для канареечных выкатов и т. п.

Можем ли мы опубликовать приложение, работающее по бинарному протоколу, например postgresql, через ingress?

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

Если нам всё равно нужно описывать Kubernetes service для публикации ingress, то зачем нам ingress?

Преимущество ingress-контроллера в том, что, опубликовав его единожды, мы получим возможность доставлять через него трафик всем нашим приложениям внутри кластера k8s, которые работают по HTTP на основе маршрутизации по URL/locations, HTTP headers и cookie. А также если появится такая необходимость, мы сможем использовать несколько ingress-контроллеров в кластере, разделяя их по ingress class.

Допустим, у нас postgresql в кластере k8s, и разработчики просят к ней доступ. Каким образом мы можем решить этот вопрос?

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

Есть также вариант с использованием какого-нибудь инструмента для доступа разработчика к кластеру (например, Kubernetes dashboard или LENS) с возможностью сделать exec в POD и доступа к базе через утилиту командной строки.

Если разработчикам всё-таки нужен прямой сетевой доступ к базе (например, для использования своего любимого инструмента работы с БД), мы можем завести аккаунт для разработчиков в кластере и использовать kubectl proxy для публикации порта базы на localhost машины разработчика. Либо поднять в кластере сервер vpn.

На худой конец, есть возможность опубликовать базу через Kubernetes service или ingress, но в данном случае нам нужно позаботиться о защите соединения с БД (пользователи, доступы) и протокола (включить шифрование).

Следите за новыми постами
Следите за новыми постами по любимым темам
3К открытий7К показов