Кратко

kubectl

  • Валидирует запрос — проверяет что он понимает что это за запрос.
  • Составляет http запрос к apiserver в соответствии с вычисленным типом ресурса — под, деплоймент и прочие разные штуки kube’а.
  • Договаривается с apiserver об используемой версии api.
  • Аутентифицируется в apiserver — по сертификатам, токену или паролю.

kube-apiserver

  • Аутентифицирует клиента разрешенными = указанными при старте способами.
  • Авторизует запрос ­ — что вам его можно выполнять. По цепочке авторизаторов — по файлам политик и проч.
  • Контроль допуска — проверяет что вообще такого типа запросы на этом кластере разрешены — лимиты, квоты и т.п.
  • Формирует соответствующий запросу ресурс — runtime представления заказанного пода, деплоймента и т.п.
  • Сохраняет его в etcd через “поставщик хранилища”, но в неинициализированном виде — не ставится галка.
  • Поставщик хранилища его сохраняет и get’ает, чтобы проверить успешное сохранение.
  • Apiserver отправляет http ответ.
  • Поставщик хранилища вызывает post-hooks.

Инициализаторы

Если у вновь записанного типа ресурса есть какие-то предварительные шаги, то на его создание зарегистрированы соответствующие инициалайзеры, как например, ижектеры проксей для пода и т.п.

  • Эти инициализаторы записываются в pending список этому ресурсу.
  • И начиная с первого выполняются по одному — они подписаны на объекты и если видят себя в голове списка, то отрабатывают.
  • Пустой список означает, что ресурс initialized. И он становится доступен контроллерам.

Контроллеры

  • Каждый из них считывает состояние каких-то etcd нод, за которые он отвечает. Например, Deployment controller видит, что появился новый деплоймент nginx’ов и его ответственность — создать нужные ReplicaSet’ы. Создать в смысле записать в etcd в нужном виде.
  • Когда записаны новые ReplicaSet’ы, то в свою очередь ReplicaSet controller видит, что его понимание о том какие поды существуют не соответствует тому какие ReplicaSet’ы указаны в etcd. И он начинает создавать нужные pod’ы согласно правилам, например, соблюдая ReplicaSet burst count — количество подов создаваемых за раз.
  • Контроллеры узнают об изменении в хранилище через Informer’ы — специальный механизм листенеров над etcd.

Шедулер

  • Когда все контроллеры отработали и завели все нужные поды, поды будут тем не менее находиться в Pending состоянии, т.к. они еще не были назначены на конкретные ноды.
  • Шедулер работает примерно так же как другие контроллеры — он слушает все события про поды и отфильтровывает поды, у которых NodeName пустой и пытается найти им подходящие Ноды.
  • Шедулинг происходит так:
    • Прежде всего проверяется цепочка предикатов, которые отфильтровывают неподходящие ноды. Например, если в PodSpec’е указаны CPU ресурс, а на какой-то ноде столько нет.
    • Дальше выполняются функции приоритета, по которым оставшиеся ноды ранжируются. Например, выше ставится нода с большим количеством свободных ресурсов. Когда нода выбрана, шедулер создает Binding запрос к apiserver’у — который запишет имя ноды в NodeName PodSpec’и и установит PodScheduled статус в True .

kubelet

  • Это компонент запущенный на всех нодах Кубернетес кластера и занимающийся среди прочего жизненным циклом подов на этой ноде — он транслирует все необходимое из абстрации “Под” в реальные контейнеры, volum’ы и т.п.
  • kubelet так же как контроллеры и шедулер получает статус всех подов назначеных его ноде от apiserver’а и проверяет, что всё это соответсвует его локальной действительности.
  • А если что-то не соответствует, то он пытается отсинхронизовать действительность под желаемые аписервером требования. Вот что он делает:
    • Создает объект PodStatus, в котором отслеживается изменения его состояния, такие как Pending, Running, Succeeded, Failed. Например, определив какие контейнеры надо запустить, кублет понимает, что они еще не созданы, и поэтому Pod находится в Pending состоянии.
    • Затем этот PodStatus отдается статус-менеджеру Пода, который ассинхронно будет записывать обновления в apiserver.
    • Запускаются секьюрити хендлеры, которые, например, проверяют профили Apparmor. Поды не прошедшие эти хендлеры навсегда остаются в состоянии Pending.
    • Если был установлен флаг cgroups-per-qos, то kubelet создает cgroup’ы для пода и ставит туда ограничения ресурсов.
    • Кублет создает дата-директории для пода — /var/run/kubelet/pods/ для самого пода, /volumes для вольюмов и/plugins для плагинов.
    • Volume manager будет ожидать соответствующих PodSpec’е вольюмов.
    • Кублет получит все соответвующие поду секреты от аписервера, чтобы позже их заинжектить в контейнер.
    • Container runtime запускает контейнер:

ContainerRuntimeInterface

  • CRI это абстракция между kubelet’ом и, например, docker’ом, которая нужна, чтобы кубернетес мог работать не только поверх docker’а, но и поверх rkt или вообще вирутальных машин.
  • kubelet запускает вызовRunPodSandbox, где “sandbox” это CRI термин обозначающий под. Т.е. это может быть как группа контейнеров, так и виртуалка или что-то подобное.
  • В случае docker’а создание sandbox’а соответствует созданию запаузенного (paused) контейнера — родительского контейнера для пода, через который все контейнеры пода будут шарить ресурсы — Linux неймспейсы (IPC, network, PID).

ContainerNetworkInterface

  • CNI это тоже интерфейс абстракция, которая позволяет подключать различные провайдеры организации сетей.
  • Если у Pod’а был сконфигурирован интерфейс типа bridge:
    • Плагин bridge создаст локальный линуксовый сетевой мост на ноде.
    • Затем вторая пара от veth будет заинжектена внутрь paused родительского контейнера, т.е. в его network namespace.
    • CNI присвоит этому интерфейсу IP и назначит роутинг. Делает это он через ipam (address manager) плагин, который, например, создает host-local конфигурацию.
    • CNI получив информацию о DNS сервере от kublet’а, пропишет ее в resolve.conf контейнеру.

Межнодовое сетевое взаимодействие

Обычно оно организовывается с помощью overlay сети, например, это может быть layer-3 IPv4 межнодовая сеть, которая энкапсюлирует ip пакеты в UDP реальной сети.

Запуск контейнеров

После того как sandbox подготовлен, kubelet запускает уже реальные контейнеры Пода — сначала запускаются init контейнеры, если такие есть в спеке Пода, и только потом основные контейнеры.

  • Pull’ится соответствующий образ.
  • CRI создает контейнер внутри родительского paused контейнера.
  • Если нужно, контейнер регистрируется в CPU manager’е — ему приписывается набор ЦПУ, на которых он будет работать.
  • Контейнера запускается!
  • Запускаются post-start хуки.