Kubernetes: шпаргалка для собеседования. Часть 2

Логотип компании Газпромбанк

Продолжение гайда для собеседований по Kubernetes. В этот раз обсудим более сложные вопросы.

Обложка эксперта Олег Вознесенский
Олег Вознесенский

Исполнительный директор по разработке, Газпромбанк

Всем привет! Это снова Олег. Как и обещал, публикую вторую часть текста, посвящённую вопросам, которые могут быть заданы на собеседовании по Kubernetes.

Чтобы было понятно о чём речь, коротко: я работаю исполнительным директором по разработке в Газпромбанке, собеседую инженеров DevOps и системных администраторов. Делюсь вопросами (и ответами, конечно), которые могут быть заданы на собеседовании. Первую часть можно найти здесь. В ней всё начинается с простых заданий. Вторая часть посвящена вопросам посложней. Итак, поехали.

Вы вводите в адресную строку браузера https://company.co. Приложение, работающее по этому адресу, запущено в Kubernetes. Опишите как можно подробнее, что произойдёт после нажатия Enter?

Сначала браузер попробует получить IP-адрес точки входа через DNS запрос (тут можно спросить интервьюера, стоит ли объяснять процесс разрешения DNS-имён).

Допустим, наш Kubernetes запущен в облаке как managed-решение. Скорее всего, там будет облачный load balancer, созданный сервисом с типом loadBalancer. Load balancer может быть application (ALB), и тогда он проксирует запрос, либо network (NLB), и тогда он маршрутизирует запрос.

Если это будет ALB, то, вероятно, на нём будет поддерживаться защищённая сессия (вопрос интервьюеру: стоит ли объяснять установку HTTPS-соединения?).

От Load balancer трафик попадёт на порт одной из нод кластера, который слушает связанный сервис. Скорее всего, для этого порта описаны правила iptables, при помощи которых запрос маршрутизируется в один из PODов ingress-контроллера.

Ingress-контроллер, согласно своей конфигурации, проксирует запрос в один из PODов сервиса, связанного с этим доменом и location. Он может маршрутизировать запрос согласно алгоритму round robin, либо применить какую-то дополнительные правила маршрутизации (например, session affinity или canary на основе весов или соединений, если это описано аннотациями на ингрессе).

Если POD приложения расположен на другой ноде, то пакет пройдёт процесс маршрутизации — нода с ингрессом согласно своей таблице маршрутизации выберет ноду PODа в качестве шлюза и перешлёт ей запрос от ингресса, нода доставит его в приложение в контейнере. Приложение обработает запрос и отошлёт ответ ингрессу, который проксирует это в Load Balancer, а тот, в свою очередь, вернёт его в браузер клиента.

Каким образом организована сеть в k8s?

В k8s существуют три типа сети:

  • node network — сеть, в которую объединены ноды; в зависимости от использования CNI-плагина, ноды могут работать только в одной подсети, либо в нескольких;
  • pod network — сеть, в которой получают IP-адреса запускаемые PODы; 
  • service network — сеть, в которой получают адреса Kubernetes services.

Pod network и service network организуются при помощи так называемых CNI-плагинов.

Что такое CNI-плагин и для чего он нужен?

Аббревиатура CNI расшифровывается как Container Network Interface. Он представляет собой некий уровень абстракции над реализацией сети. Мы можем работать с верхнеуровневыми абстракциями вроде «IP-адрес PODа», Endpoint. За то, как это будет реализовано на физическом уровне, отвечает CNI-плагин. Существует множество CNI-плагинов (например, Flannel, Calico, Cilium), которые реализуют разный функционал и показывают разную сетевую производительность. От простейших, которые используют для работы L3-маршрутизацию, правила iptables и IPVS, до достаточно сложных, которые, например, могут осуществлять шифрование внутреннего трафика в кластере, поддержку VLAN, маршрутизацию на основе IBGP, поддержку EGRESS и прочее.

Что такое egress?

Возможность назначить внешний IP-адрес для исходящего за пределы кластера k8s трафика приложений. Поддержка egress должна быть реализована на уровне CNI-плагина и может быть описана специальным объектом на уровне неймспейса.

Как мы можем ограничить трафик в Kubernetes?

Для ограничения трафика от приложений используется объект networkpolicy. При помощи его мы можем ограничивать входящий и исходящий трафик на уровне неймспейса и компонентов, описанных в нём. Его поддержка должна быть реализована на уровне CNI-плагина.

Что такое service mesh и для чего он нужен?

Основные задачи, в которых используется service mesh, это:

  • гарантия доставки трафика до приложений (service mesh реализует retry / timeouts / circuit breaker и другие подобные механизмы);
  • увеличение безопасности при помощи шифрования трафика, верификация трафика на основе сертификатов, реализация дополнительных правил и разрешений на передачу трафика;
  • tracing запросов, составление схемы и визуализация трафика;
  • реализация переключения трафика при canary, a/b и blue/green стратегий деплоя.

Какие есть недостатки у service mesh?

Избыточная сложность, повышенная ресурсоёмкость и накладные расходы.

В чём отличие меток (labels) от аннотаций (annotations)?

Метки используются для реализации механизмов поиска и группировки объектов, аннотации — для описания метаинформации на объекте (например, при помощи аннотаций мы можем запретить service mesh инжектировать sidecar-контейнеры в некоторые PODы).

Из каких компонентов состоит k 8 s, и каково их назначение?

K8s состоит из control plane и data plane.

Control plane содержит:

  • ETCD — хранилище конфигурации кластера;
  • Kubernetes API — предоставляет API, посредством которого взаимодействуют компоненты k8s, а также клиенты, находящиеся внутри и вне кластера;
  • Kubernetes controller manager — реализует концепцию контроллеров, которые управляют базовыми сущностями кластеров (например, node controller, job controller, endpoint slice controller);
  • Kubernetes scheduler — выбирает ноды, на которых нужно запускать PODы; 
  • cloud controller manager — используется для реализации функций работы с облаком (если кластер k8s запущен в облаке).

Data plane — компоненты, которые запущены на каждой ноде:

  • kubelet — следит за изменениями конфигурации ноды, применяет изменения конфигурации, делает пробы контейнеров, отчитывается о статусе контейнеров, работает с CRI-плагином и реализует функции запуска и остановки контейнеров;
  • kube-proxy — отвечает за сетевой компонент: работает с CNI-плагином и обеспечивает функционирование сущности «сервис» (service) в пределах своей ноды.

Что такое kube-proxy и для чего он нужен?

Это компонент Kubernetes data plane, который работает на каждой ноде. Он взаимодействует с CNI-плагином (обеспечивая функционирование pod network), и обеспечивает функционирование описанных в кластере сервисов (service) в пределах своей ноды, в зависимости от режима, выступая либо как прокси, либо как контроллер правил IPTABLES/IPVS.

Что такое CRI?

Аббревиатура расшифровывается как Container Runtime Interface. Это спецификация, описывающая некий уровень абстракции, который позволяет унифицировано использовать разные версии ПО для работы с контейнерами, например, containerd или CRI-O.

Допустим, у вас есть YAML-файл, в котором описаны deployment и service. Вы вводите команду kubectl create -f file.yaml . Как можно подробнее опишите, что произойдёт после нажатия клавиши Enter

Прежде всего утилита kubectl найдёт и считает kubeconfig, возьмёт оттуда адрес Kubernetes API, сертификаты для его проверки, ключ доступа к нему и сделает запрос.

Kubernetes API получит запрос, проверит права пользователя, которому принадлежит ключ, и если права позволяют, запишет в ETCD информацию о новом deployment и service.

Kubernetes controller manager увидит, что описан deployment, и при помощи Kubernetes API запишет в ETCD информацию о создании replicaset и нужного количества PODов на его основе. На этом этапе в работу могут включиться admission controller’ы, которые способны провалидировать корректность PODов или трансформировать их.

Kubernetes scheduler увидит информацию о новых POD’ах и на основе их параметров (настроек требуемых ресурсов, nodeSelector’ов, taints/tolerations, podAntiAffinity и прочего) назначит PODы на подходящие ноды.

Kubelet, в очередной раз опросив Kubernetes API, увидит изменение конфигурации ноды и начнёт вносить изменения: запустит контейнеры посредством CRI, даст команду kube-proxy на конфигурацию сети, запустит пробы, когда пробы будут пройдены, изменит в ETCD статус POD’а посредством Kubernetes API на Ready.

Kubernetes controller manager увидит, что появились PODы в статусе Ready, которые соответствуют меткам, описанным в service, и создаст записи о новых Endpoint’ах для соответствующих PODов.

Kubelet’ы на всех нодах кластера увидят изменение конфигурации и вызовут kube-proxy для создания Endpoint’ов.

Kube-proxy вызовут CNI-плагины, которые реализуют Endpoint’ы на уровне инфраструктуры.

При обращении к сервису трафик пойдёт на новые PODы.

Можно ли запускать базы данных в k8s?

При определённых условиях. Стоит помнить, что контейнеры не накладывают оверхеда по производительности приложений, но сетевой компонент в k8s, в зависимости от реализации, может накладывать определённые задержки. В контейнерах сложнее делать тонкий тюнинг приложений (менять настройки ядра ОС и прочие низкоуровневые настройки для повышения производительности), но это требуется нечасто. Так что если мы сможем обеспечить дисковое пространство с подходящими параметрами для хранения файлов БД и возможность их переноса между нодами (если такая возможность нужна), в k8s вполне можно запускать stateful-приложения как таковые.

Более того, есть cloud ready / cloud native — базы и очереди, которые оптимизированы для запуска в k8s. Также для управления приложениями в k8s существуют операторы (Kubernetes operators), которые в том числе облегчают эксплуатацию stateful-приложений (например, postgresql stolon для управления кластерами postgresql, strimzi для управления kafka).

В чём разница между deployment и statefulset?

В именах PODов: в deployment в качестве суффикса PODа используется некий случайный хеш, в statefulset — порядковый номер. К каждому PODу в statefulset можно обратиться по особым образом составленным доменным именам (это может быть полезно, например, для mongodb, где клиент должен знать имена всех нод кластера).

В способе работы с дисками (volume): statefulset обеспечивает алгоритм работы at most once — то есть дожидается, пока предыдущий POD с таким именем завершит работу, чтобы занять именно тот диск, который был к нему привязан, тогда как PODы deployment’а могут занять первый подходящий свободный.

Стратегиями перезапуска POD’ов при обновлении.

Какие типы volum’ов можно использовать в k8s?

Можно использовать hostpath, чтобы подключить папку на ноде, но в этом случае POD должен быть привязан к ноде: если он переместится на другую ноду, использует там ту же папку, в которой не будет нужного содержимого.

Можно использовать local-storage — этот тип также использует папку на диске, но привязан к ноде и автоматически привязывает POD, который его использует, к нужной ноде.

Также мы можем использовать сетевые диски при помощи CSI-плагинов.

Что такое CSI-плагин?

Это абстракция, позволяющая унифицировано использовать сетевые файловые системы, построенные на разных технологических базах.

Мы описываем storageClass, соответствующий дискам определённого типа, и деплоим в кластер provisioner — специальное ПО, которое может заказывать сетевые диски в системе, способной их предоставлять (например, NAS или СХД).

Далее мы описываем объект persistentVolumeClaim с указанием нужного storageClass.

Provisioner при появлении PVC заказывает диск нужного размера в системе, которая их предоставляет, создаёт объект persistentVolume и привязывает его к PVC. Когда происходит запуск PODа на ноде, соответствующий диск монтируется на нужную ноду по определённому пути, и этот путь монтируется на файловую систему PODа.

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

Для разделения прав в Kubernetes применяется механизм RBAC (Role Based Access Control). В рамках него есть три группы сущностей — user, или service account, описывающий субъект доступа, role, или clusterRole, описывающий разрешения, и roleBinding, или clusterRoleBinding, для привязки списка разрешений к субъекту.

Что такое role/clusterRole?

Роли описывают права при помощи наборов правил, содержащих:

  • группы API — см. официальную документацию по apiGroups (https://Kubernetes.io/docs/reference/using-api/#api-groups) и вывод kubectl api-resources;
  • ресурсы (resources: pod, namespace, deployment и т. п.);
  • глаголы (verbs: set, update и т. п.);
  • имена ресурсов (resourceNames) — для случая, когда нужно предоставить доступ к какому-то определённому ресурсу, а не ко всем ресурсам этого типа.

Что такое service account?

Это специальные объекты в Kubernetes API (они так и называются — ServiceAccounts), которые привязаны к пространству имён и набору авторизационных данных, хранящихся в кластере в объектах типа Secrets. Такие пользователи (Service Accounts) предназначены в основном для управления правами доступа к Kubernetes API процессов, работающих в кластере Kubernetes.

В чём отличие service account от user?

Users не имеют записей в Kubernetes API: управление ими должно осуществляться внешними механизмами. Они предназначены для людей или процессов, живущих вне кластера. Service Accounts существуют в рамках неймспейса k8s — их именование включает в себя имя неймспейса. Имена пользователей привязаны к кластеру и должны быть уникальны.

Какие механизмы аутентификации используются в k8s?

Kubernetes может использовать большое количество механизмов аутентификации: сертификаты X509, Bearer-токены, аутентифицирующий прокси, HTTP Basic Auth. При помощи этих механизмов можно реализовать большое количество схем авторизации: от статичного файла с паролями до OpenID OAuth2.

Более того, допускается применение нескольких схем авторизации одновременно. По умолчанию в кластере используются:

  • service account tokens — для Service Accounts;
  • X509 — для Users.

Что такое bearer token?

Bearer token, или service account token, — JWT-токен, который автоматически генерируется при создании нового service account (при этом он содержит служебную информацию о service account, которому выдан, и подписывается на корневом сертификате кластера), сохраняется в объект типа secret, который монтируется в POD по стандартному пути, и автоматически ротируется. Используя его, процесс, запущенный в контейнере, может обратиться к Kubernetes API и выполнить разрешённые действия.

Что такое namespace в k8s и для чего он нужен?

Namespace в k8s реализует несколько функций. Прежде всего это способ группировки объектов, относящихся к одному приложению/проекту. Большинство объектов в k8s принадлежат неймспейсам (deployment, secret, service account), после помещения их в определённый неймспейс мы можем совместно их просматривать и применять к ним правила безопасности, квоты ресурсов, сетевые политики, правила service mesh.

Кроме того, неймспейс участвует в формировании DNS-имён внутри кластера. Например, DNS-имена сервисов формируются по принципу [service name].[namespace].[суффикс кластера (по умолчанию svc.cluster.local)]

Что такое финалайзеры (finalizers) и для чего они нужны?

Это специальные ключи в манифесте объекта, описывающие действия, которые требуется совершить до удаления объекта. Например, они используются для того, чтобы невозможно было удалить pvc и pv при запущенном PODе.

Допустим, у нас есть приложение, развёрнутое в k8s, которое доступно по адресу https://company.co. Обращаясь к этому адресу, вы видите задержку в ответе сайта и частые ошибки HTTP 504. Каковы ваши действия по отладке этой проблемы?

Прежде всего определю IP-адрес, куда ссылается https://company.co, и сущность, которая его поддерживает. Если это облачный балансер, посмотрю, куда он ведёт (скорее всего, это будут ноды кластера с сервисом типа nodePort/loadBalancer). Если это нода кластера, то, вероятно, это будет сервис с типом externalIp.

Следующим шагом найду этот сервис и посмотрю, куда он ссылается. Это может быть ingress-контроллер либо непосредственно само приложение.

Определю нэймспейс, в котором развёрнуто приложение / описан ингресс, и прежде всего посмотрю на состояние его PODов — статусы, количество рестартов, время жизни, быстрые метрики, если есть (kubectl top pods).

Если и на этом этапе не обнаружу проблем, взгляну также на статус и метрики PODов ингресс-контроллера.

Если время жизни PODов небольшое, взгляну на статус нод в кластере (всё ли Ready), события в кластере и неймспейсе (kubectl get events), наличие PODов в статусе evicted (кончились ресурсы на какой-то из нод) и быстрые метрики нод (kubectl top nodes). А также проверю проект приложения в CI/CD-системе (или инфраструктурном репозитории, если у нас gitops) на наличие выкатов в обозримом прошлом.

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

Если есть PODы в нерабочем статусе либо с большим количеством перезагрузок, посмотрю по логам PODов, что может мешать нормальной работе.

Если время жизни PODов большое или быстрые метрики показывают проблемы, буду искать проблемы при помощи системы мониторинга — просмотрю базовые показатели производительности нод/PODов, утилизацию ресурсов в разрезе установленных лимитов, данные об ошибках / количестве запросов / времени ответа приложения, полученных на ингресс-контроллере (если он есть), утилизацию сети PODами, состояние сети на нодах. Если есть service mesh — проверю её состояние, статус control plane, утилизацию ресурсов sidecar-контейнерами, зайду в интерфейс администратора service mesh и просмотрю метрики о работе приложения, которые она предоставляет.

Проверю по мониторингу состояние смежных сервисов, которые использует приложение (базы данных, очереди, сервисы).

Если вышеперечисленные действия не позволили обнаружить причину инцидента, начну пристальное изучение логов приложения.

Наверняка на каком-то из этих этапов обнаружится проблема, расследуя которую можно наткнуться на корневую причину инцидента.

Думаю, это достаточно полный, но отнюдь не исчерпывающий список вопросов, которые можно услышать на интервью. А с какими вопросами сталкивались вы (или, возможно, задавали сами)? Пишите в комментариях.

DevOps
Собеседование
Kubernetes
402