Что такое под k8s
Так что же такое pod в Kubernetes?
Прим. перев.: Эта статья продолжает цикл материалов от технического писателя из Google, работающего над документацией для Kubernetes (Andrew Chen), и директора по software engineering из SAP (Dominik Tornow). Их цель — доступно и наглядно объяснить основы организации Kubernetes. В прошлый раз мы переводили статью про high availability, а теперь речь пойдет про такое базовое понятие в Kubernetes, как pod.
Kubernetes — движок оркестровки контейнеров, созданный для запуска контейнеризированных приложений на множестве узлов, которые обычно называют кластером. В этих публикациях мы используем подход системного моделирования с целью улучшить понимание Kubernetes и его нижележащих концепций. Читающим рекомендуется уже иметь базовое представление о Kubernetes.
Pods (Поды) — базовые строительные блоки Kubernetes, однако даже опытные пользователи Kubernetes не всегда могут объяснить, что же это такое.
Данная публикация предлагает лаконичную мысленную модель, которая проливает свет на определяющие характеристики pod’ов Kubernetes. Ради этой краткости пришлось опустить некоторые другие особенности Pod’ов, такие как liveness и readiness probes, разделение ресурсов (включая появившееся недавно namespace sharing — прим. перев.), работу с сетью.
Определение
Pod представляет собой запрос на запуск одного или более контейнеров на одном узле.
Pod определяется представлением запроса на запуск (execute) одного или более контейнеров на одном узле, и эти контейнеры разделяют доступ к таким ресурсам, как тома хранилища и сетевой стек.
Однако в обиходе термин «pod» может употребляться и в смысле этого запроса, и в смысле совокупности контейнеров, которые запускаются в ответ на запрос. Поэтому в публикации мы будем использовать слово «pod», когда говорим о запросе, а для второго случая — употреблять выражение «набор контейнеров».
Pod’ы считаются базовыми строительными блоками Kubernetes, потому что все рабочие нагрузки в Kubernetes — например, Deployments, ReplicaSets и Jobs — могут быть выражены в виде pod’ов.
Pod — это один и единственный объект в Kubernetes, который приводит к запуску контейнеров. Нет pod’а — нет контейнера!
Схема 1. Deployment, ReplicaSet, pod и контейнеры
Архитектура Kubernetes
Схема 2. Pod’ы, планировщик (Scheduler) и Kubelet
На этой иллюстрации выделены соответствующие объекты и компоненты. Pod’ы представлены как Kubernetes Pod Objects, а работой с ними занимаются:
Объекты Kubernetes
Схема 3. Объекты Kubernetes
На этой иллюстрации показаны объекты Kubernetes, ответственные за работу с pod’ом:
Binding Object привязывает Pod Object к Node Object, т.е. назначает pod на узел для последующего запуска.
Node Object представляет узел в кластере Kubernetes.
Обработка pod’а
Схема 4. Обработка pod’а
Когда pod создан пользователем или контроллером вроде ReplicaSet Controller или Job Controller, Kubernetes обрабатывает pod в два этапа:
Планирование pod’а
Схема 5. Управляющий цикл планировщика Kubernetes
Задача планировщика (Scheduler) в Kubernetes — запланировать pod, то есть назначить ему подходящий узел в кластере Kubernetes для последующего запуска.
Связывание объекта pod’а с объектом узла
Pod назначается узлу (или связывается с ним) тогда и только тогда, когда есть объект связывания (binding), у которого:
Запуск pod’а
Схема 6. Управляющий цикл Kubelet
Задача Kubelet — запустить pod, что по сути означает запуск набора контейнеров pod’а. Запуск pod’а Kubelet’ом происходит в две фазы: инициализацию и основную стадию.
Как правило, набор контейнеров на фазе инициализации осуществляет подготовительные работы, такие как подготовку необходимой структуры директорий и файлов. А набор контейнеров на основной фазе выполняет уже «самые главные» задачи.
В обиходе же, хотя это и не совсем корректно, термин «pod» зачастую подразумевает набор контейнеров на основной фазе или же ещё более узкое значение «самого главного» контейнера основной фазы.
Схема 7.1. Запуск pod’а, фаза инициализации (init) и основная фаза (main)
Схема 7.2. Запуск pod’а, подробности этого процесса
У политики рестарта pod’а различная семантика для init-контейнеров и основных контейнеров: если запуск init-контейнеров обязан привести к завершению, то основные контейнеры могут и не завершаться.
Политика рестарта для init-контейнера
Init-контейнер будет перезапущен (т.е. повлечёт за собой запуск нового контейнера с такой же спецификацией) при завершении своей работы только при выполнении следующих условий:
Основной контейнер будет перезапущен (т.е. повлечёт за собой запуск нового контейнера с такой же спецификацией) при завершении своей работы только при выполнении следующих условий:
На иллюстрации показана возможная временная шкала запуска pod’а с двумя спецификациями init-контейнеров и двумя спецификациями основных контейнеров. Также она показывает создание (в соответствии с политикой рестарта) нового контейнера Main Container 1.2 после проблемы с запуском Main Container 1.1.
Фазы pod’а
Схема 9. Взаимодействие Kubelet с объектом pod’а и исполняемой средой контейнера (container runtime)
Фаза pod’а — это проекция состояния контейнеров из набора контейнеров, она зависит от:
Ожидание (Pending)
Фаза Pending
Pod находится в фазе ожидания тогда и только тогда, когда:
Работает (Running)
Фаза Running
Pod находится в фазе работы тогда и только тогда, когда:
Успех (Success)
Фаза Success
Pod находится в фазе успеха тогда и только тогда, когда:
Отказ (Failure)
Фаза Failure
Pod находится в фазе отказа тогда и только тогда, когда:
Неизвестно (Unknown)
В дополнение к описанным выше фазам pod может находиться в неизвестной фазе, что свидетельствует о невозможности определить его текущую фазу.
Сбор мусора для pod’ов
Схема 11. Управляющий цикл сборщика мусора для pod’ов (Pod Garbage Collector)
После того, как pod был запланирован и запущен, специальный контроллер в Kubernetes — Pod Garbage Collector Controller — отвечает за удаление объектов pod’ов из хранилища объектов Kubernetes Object Store.
Заключение
Pod — базовый строительный блок Kubernetes: pod определяется как представление запроса на запуск одного или более контейнеров на одном узле. После того, как pod создан, Kubernetes обрабатывает его в два этапа: сначала планировщик (Scheduler) планирует pod, а затем — Kubelet запускает его. На протяжении своего жизненного цикла pod проходит через разные фазы, сообщая о состоянии — или, точнее говоря, о состоянии своего набора контейнеров — пользователю и системе.
K8S для начинающих. Первая часть
Предисловие
Что такое Kubernetes?
Kubernetes делает следующее:
Управляет и запускает контейнеры
Балансирует сетевой трафик между узлами кластера Kubernetes и количеством реплик контейнеров
Осуществляет контроль состояния, автоматические развертывания и откаты реплик контейнеров внутри узлов кластера Kubernetes
Осуществляет распределение нагрузки между узлами кластера Kubernetes
Предоставляет автоматическое монтирование систем хранения для контейнеров
Предоставляет декларативный API и CLI для управления
И еще множество полезных, и не очень, модулей и сервисов, которые можно развернуть для управления автоматизацией, инфраструктурой и контейнерами
Kubernetes не делает следующее:
Не собирает контейнеры с исходным кодом вашего приложения или сервиса
Не предоставляет процессы и решения непрерывной интеграции (CI)
Не включает в себя решения и системы сбора журналов и метрик
Не включает в себя решения и системы хранения данных
Не включает в себя решения и системы хранения контейнеров (registry)
Не включает в себя решения и системы от всех бед и болячек инфраструктуры
Kubernetes или K8S — это не просто система оркестрации. Техническое определение оркестрации — это выполнение определенного рабочего процесса: сначала сделай A, затем B, затем C. Напротив, Kubernetes содержит набор независимых, компонуемых процессов управления, которые непрерывно переводят текущее состояние к предполагаемому состоянию. Неважно, как добраться от А до С. Не требуется также и централизованный контроль. Это делает систему более простой в использовании, более мощной, надежной, устойчивой и расширяемой.
Но что если у нас таких сервисов тысячи, каждый за что-то отвечает и работает сам по себе? А если еще развернуты несколько реплик для отказоустойчивости? Как управлять всем и уделить внимание каждому из них? Как понять, что сервис правильно работает и взаимодействует с другими? Для этого есть специальные системы, оркестраторы в своем роде, такие как HashiCorp Nomad, Docker Swarm и Kubernetes. Последний используется активнее всего, так как предоставляет более гибкий функционал в контексте управления всей конструкцией приложения.
На самом деле бизнесу нужен не Kubernetes, а система, которая позволит более быстрее подходить к изменению рынка и подстраиваться под его запросы предоставления набора новых услуг или вывода старых. В НАТ.Тех мы давно используем этот инструмент и чувствуем большую разницу, как для нас, так и для наших клиентов. Исходя из нашего опыта, бизнес чаще всего ценит такие возможности К8S как собирать и тестировать только часть приложения, с которой мы работаем, что в разы уменьшает объем необходимых ресурсов; добавлять и убирать сервисы «на лету», тестировать новый функционал в разных регионах и смотреть, как он себя показывает.
Именно для этого нам и нужен Kubernetes, который дает унификацию и гибкость в способе обслуживания и содержания сервисов приложения. Kubernetes предоставляет:
Быструю и автоматическую масштабируемость. При росте нагрузки можно быстро добавить необходимые узлы приложения, а также быстро их вывести, чтобы не тратить драгоценные ресурсы
Гибкий подход в управлении. Kubernetes не потребует перестройки инфраструктуры и прочего, если вы захотели провести тестирование, внедрить новый сервис или сделать деплой по методологии blue-green
Универсальность. С помощью манифестов легко переехать, если вы захотели поменять провайдера или переезжали в свой собственный кластер
Низкий порог вхождения в использование. Kubernetes довольно легок в освоении манифестов, потому что большую часть работы он делает за вас
Если вы задумываетесь о преимуществах, описанных выше, и можете точно сказать, что вам нужна гибкость в разработке и быстрое внедрение сервисов, адаптируемый подход и универсальность в управлении большим количеством сервисов и их реплик, то думаю, что пора попробовать Kubernetes и у себя.
Однако, если ваш проект имеет постоянную нагрузку и не требует высокой степени гибкости и быстрого масштабирования, новый функционал появляется редко и у вас есть команда, уверенно работающая с существующим окружением, то на данный момент возможности K8S для бизнеса избыточны, но «посмотреть» на технологию в фоновом режиме все же стоит, так как те или иные условия могут и поменяться.
Внедрение Kubernetes
Kubernetes, так как он был реализован как облачное решение, предпочтительнее разворачивать именно в облаке.
Если вы решились ставить Kubernetes внутри, на своих собственных ресурсах, то вам придется позаботиться о развёртывании и обслуживании такой инфраструктуры вокруг кластера как: репозиторий для контейнеров, внешние или внутренние балансировщики, сетевые хранилища, хранилище секретов, решения для сбора логов и метрик. Также важна и внутренняя инфраструктура “кубера”: CertManager, Ingress, Istio и другие.
При этом существует много компаний, которые предоставляют Kubernetes как IaaS-решение, где не требуется поднимать и обслуживать окружение для кластера и процессов, которые будут на нем. Или, например, в NUT.Tech, где я сейчас работаю, разрабатываются решения “под ключ” индивидуально под запрос заказчика.
Компоненты и архитектура
Давайте поверхностно коснемся архитектуры самого K8S и его важных компонентов.
Сам кластер K8S состоит из, барабанная дробь, рабочих узлов. В узлах или нодах (Nodes, Worker nodes), помимо контейнеров компонентов самого кластера, размещаются контейнеры наших проектов и сервисов.
Worker nodes состоит из компонентов:
Плоскость управления (Master nodes) управляет рабочими узлами и подами в кластере. Там располагаются компоненты, которые управляют узлами кластера и предоставляют доступ к API.
Control plane состоит из компонентов:
Виды контроллеров
Тестовые кластеры, где потренироваться
Kubectl: как пользоваться, основные команды
При работе с кластером нам потребуется инструмент командной строки kubectl. https://Kubernetes.io/ru/docs/tasks/tools/install-kubectl/
Его можно установить на локальную машину и управлять несколькими кластерами с одной точки.
Основные команды, которые мы часто будем использовать:
Тестовый кластер
Давайте развернем свой k8s кластер на примере kind https://kind.sigs.k8s.io/
После выполнения команды посмотрим и убедимся, что все хорошо и наш кластер появился в списке.
Убедимся, что в kubectl добавился context нашего кластера
Что такое Pods
Pods или поды — это абстрактный объект в кластере K8S, который состоит из одного или нескольких контейнеров с общим хранилищем и сетевыми ресурсами, а также спецификации для запуска контейнеров.
Это главный объект в кластере, в нем прописаны, какие контейнеры должны быть запущены, количество экземпляров или реплик, политика перезапуска, лимиты, подключаемые ресурсы, узел кластера для размещения.
kube-scheduler планирует размещение пода на узлах кластера
kubelet на рабочем узле кластера запускает под
Любители забегать вперед и просто углубиться в тему могут почитать прекрасную статью на github.
Что такое Namespace
Namespace или пространство имен — это абстрактный объект, который логически разграничивает и изолирует ресурсы между подами. Вы можете рассматривать пространство имен как внутренний виртуальный кластер, который поможет вам изолировать проекты или пользователей между собой, применить разные политики квот на свои проекты или выдать права доступа только на определенную область.
default — пространство имён по умолчанию для объектов без какого-либо другого пространства имён
kube-system — пространство имён для объектов, созданных Kubernetes. Там размещаются системные поды кластера
kube-public — создаваемое автоматически пространство имён, которое доступно для чтения всем пользователям (включая также неаутентифицированных пользователей)
Размещение объектов
Давайте разместим свой первый объект в нашем тестовом кластере, для этого создадим свой namespace и положим туда простенький pod с nginx.
Чтобы создать свой namespace, нам нужно передать его спецификацию или манифест в формате yaml.
apiVersion — используемая для создания объекта версия API Kubernetes. Стоит отметить, что из версии к версии версии api могут меняться
kind — тип создаваемого объекта
metadata — данные, позволяющие идентифицировать объект (name, UID и необязательное поле namespace)
spec — требуемое состояние объекта
Давайте создадим test-namespace.yaml со следующим содержанием:
И применим его с помощью команды:
Посмотрим на результат:
Как видим, наш namespace успешно создался. Приступим к созданию своего первого пода (pod). Давайте положим контейнер с NGINX в наш namespace.
Как и с namespace, опишем его. Создадим hello.yaml
Если манифест применился то увидим:
Узнаем развернулся ли наш контейнер
Зайдем через наш браузер на http://localhost:30080/
Если вы видите в браузере:
То контейнер работает, неожиданно. 🙂
Распределяем pod-ы по машинам в kubernetes
Kubernetes иногда называют “операционной системой для дата-центров” – и в этом есть логика. K8s позволяет представить группу серверов (условный ЦОД) как единое вычислительное пространство. Оператор просто бросает задания в K8s, а тот сам выбирает, где тот или иной контейнер лучше разместить. Чаще всего делает это он хорошо. Но иногда появляется необходимость как-то управлять этим процессом. Об этом я и расскажу.
Зачем управлять распределением POD-ов?
Зачем вообще нужно привязывать поды к определенным узлам? Это может быть связано с производительностью, безопасностью или надежность. Например – pod может требовать доступ к специфическому железу (видеокарты и ML-ускорители для задач машинного обучения, аппаратные криптоускорители). Это может быть продиктовано безопасностью: критические части проекта будут размещаться на машинах, где физически не может быть ничего, кроме них. Это снижает шансы на то, что удачный взлом, скажем, сервиса регистраций раскроет данные о платежах. Некоторые стандарты безопасности (включая PCI DSS) имеют даже требования к физической безопасности серверов – датчики вскрытия, пломбы на корпусках, запрет на доступ. Отдельная удобная особенность – tier-инг. Нагрузку в кластере можно разделить на “важную” и “не очень”. Под важную выделять мощные современные машины с резервированием PSU, горячей замены дисков и памяти, под “не очень” – соскрести какой-нибудь хлам. В облаках это делается даже проще за счет spot instances. Такие инстансы дешевле (порой радикально), но их работу никто не гарантирует – инстанс может отключится в любой момент (вместо него появится новый). Это вызовет пересоздание POD-ов, но для чего-то маловажного это, может – и не страшно совсем.
Способы управления
NodeSelector
Это самый простой способ управления аллокацией. Он предельно прямолинеен – запутаться в нем невозможно. Выполняется в 2 этапа. Сначала надо поставить метки на node командой label:
Проверить, какие метки уже есть можно через kubectl describe nodes
Теперь можно указать pod-у требование на привязку к конкретной метке. Для аллокации пода будут использоваться только помеченые узлы, то есть при включении nodeSelector для пода ноды без меток будут игнорироваться:
Для deployment nodeSelector передается в шаблон pod-а, как обычно. Не смотря на удобство и прямолинейность подхода – nodeSelector имеет три минуса:
Taints and Tolerations
Taints – это NodeAffinity наоборот. Если nodeAffinity говорит scheduler-у, где он должен размещать pod-ы, то taint говорит, где pod-ы размещать нельзя. Любой taint запрещает размещение на машине любых подов (есть одно исключение, про него дальше). Однако можно создать под, который будет игнорировать ( tolerate ) этот запрет – и данный pod запустится на данной машине. Даже если у вас есть совершенно пустой нормальный кластер kubernetes – у вас уже есть taint. По умолчанию kubernetes запрещает размещать обычные поды на master nodes – это taint node-role.kubernetes.io/master
taintKey и taintValue – это просто метки, они могут быть произвольными. У taintEffect есть 3 возможных значения:
Теперь о том, как прописываются tolerations. Язык tolerations слегка сложнее прямолинейного подхода nodeAffinity:
в данном примере мы создадим под, который будет игнорировать taint, созданный вот такой командой:
Есть более сложный вариант – можно учитывать не только факт наличия метки, но и ее значение. Создадим пару taint-ов:
Taint-ов можно создать сколь угодно много и условия проверки можно сочетать, как в примере ниже:
В данном примере мы выделяем пул выделенных машин taint-ом “dedicatedNode” и отдельно помечаем группу максимальной безопасности значением secure для группы secGroup.
Удалить taint можно, добавив в конец знак минуса:
nodeAffinity
Не смотря на простоту и эффективность механизма nodeSelector – механизм это прямолинейный и не особенно гибкий. Авторы kubernetes предлагают более мощный, гибкий (а так же – сложный и неудобный) механизм – nodeAffinity. Язык описания nodeAffinity предлагает несколько мощных возомжностей:
Минус nodeAffinity в том, что язык очень многословный и читается тяжело. Общая спецификация выглядит так:
affinityClass влияет на строгость выбора узла:
affinityKey – это метка (ключ), по которой мы будем искать ноды для размещения pod-ов. affinityValues – это значения метки, которые нам подойдут affinityOperator – это тот логический оператор, по которому будет производится выбор метки. Варианты:
affinityClass preferredDuringSchedulingIgnoredDuringExecution слегка отличается – вместо nodeSelectorTerms используется поле preference (синтаксис такой же), плюс есть обязательное поле weight – оно отвечает за приоритет при выборе node.
affinity не учитывается для уже аллоцированных nodes, так что если нужно освободить node-у от всех подов которые там уже есть – поможет команда kubectl node drain
Лирическое отступление – PodAffinitty и PodAntiAffinitty
Механизм, который помогает размещать pod-ы относительно нод – может так же помочь и разместить pod-ы относительно друг друга – за это отвечают классы PodAffinity и PodAntiAffinity. Все три класса можно сочетать друг с другом, синтаксис внутри одинаковый, по этому просто покажу пример:
В этом примере мы размещаем nginx на разных node (как мы сделали с redis), но при этом требуем, чтобы nginx размещался вместе с redis. Это может быть удобно для кешей. Проверим, что получилось:
Static pod allocations
Это очень редкий случай, но не упомянуть его было бы нечестно. Pod-ы можно аллоцировать полностью статически, вручную привязав к конкретной node. В этом случае scheduler никак на них не влияет. На них не действуют taints, nodeSelector и podAffinity. Даже node drain ничего не сможет с такими подами сделать. Зачем это может потребоваться? Ну, во-первых для запуска таких pod-ов не нужен работающий scheduler или apiserver. Это делает размещение таких подов сверхнадежным – они будут работать всегда. Именно так kubeadm устанавливает свои компоненты – это не полноценные демоны, а контейнеры, которые вручную привязаны к master node.
Во-вторых такой подход может потребоваться в случае, если какой-то контейнер надо привязать к конкретной, строго определенной node вручную и ни при каких условиях не давать ему оттуда уезжать. Скажем, у вас какое-то особое шифрование и оно зависит от HSM, который физически подключен к определенной, особо защищенной машине. Вообще – это порочная практика и такой сценарий лучше решается через nodeSelector + taint, но мало ли?
Выполнить статическую аллоакцию очень просто – нужно положить манифесты pod-ов в папку со статическими подами. Этот путь можно задать двумя путями:
Просто создадим манифест статического пода
И проверим, что получилось:
Порядок применения
Первым всегда применяется static pod. Он игнорирует все (taints, affinities, node selectors).
Вторым по списку применяется taint. Если у pod нет toleration – он не будет размещен, по этому taint – это очень эффективный способ “разогнать” pod-ы с определенного узла (или группы узлов).
В случае, если есть nodeAffinity и nodeSelector – должны сработать оба условия сразу (то есть – и метка селектора и условия affinity).
Заключение
Kubernetes – мощный, богатый на возможности инструмент. Он кажется слегка неудобным, но ровно до момента понимания логики его работы. Scheduler у kubernetes практически ключевой компонент, и он достаточно гибок, пусть и не самым лучшим образом описан. Надеюсь – эта статья кому-то поможет. Высокого вам аптайма!