Первая часть серии: Kubernetes просто, часть 1: зачем Kubernetes, устройство кластера
https://habr.com/p/1082088/
В первой части мы успели разобрать архитектуру Kubernetes: кластер, ноды, поды, Control Plane, worker Node и так далее.
Как вы помните, главная идея следующая: «Kubernetes постоянно пытается приводить действительное состояние кластера к желаемому»
К примеру, мы хотим видеть три экземпляра приложения:
Desired State: 3 Pods Actual State: 2 Pods
Заметив несоответствие Kubernetes самостоятельно пытается создать еще 1 экземпляр, соответственно получить
Desired State: 3 Pods Actual State: 3 Pods
Однако, вопрос того, как же сообщить Kubernetes то самое желаемое (desired) состояние, мы не затрагивали.
Как раз этим сегодня и займемся! В статье разберем YAML-манифесты, Deployment, ReplicaSet, labels и selectors. Как итог, уже сегодня вы сможете самостоятельно проследить весь путь от kubectl apply до работающих контейнеров.

Императивный и декларативный подходы
Этот вопрос мы уже косвенно рассмотрели в первой статье, просто не зацикливайтесь на названиях.
Так, должно быть, вы помните, в управлении всякой системой есть два подхода.Говорить ей:
1) Подойди к двери 2) Возьмись за ручку 3) Нажми на неё 4) Открой дверь
Называется императивным подходом - здесь мы конкретно руководим и описываем, что именно нужно сделать.
В Kubernetes мы пользуемся другим подходом - декларативным, в нем мы просто сообщаем системе:
«Открой дверь»
А она уже сама решает, как ей это действие удобно сделать. То есть, мы просто описываем результат, который желаем получить.

В Kubernetes преимущества декларативного подхода ощущаются неимоверно. Мы не говорим:
Создай Pod A Создай Pod B Создай Pod C
Вместо этого, он предлагает просто обозначить:
replicas: 3
Что значит:
Я хочу, чтобы приложение было развернуто в трех экземплярах.
Если 2 экземпляра уже есть, то система сама решит, что ей нужно развернуть 1 Pod, если их и так 3, она не будет делать ничего.
Это и есть тот reconciliation loop, о котором мы говорили в первой части.

Kubernetes-манифест
Желаемое состояние Kubernetes-кластера часто описывается в YAML-файлах.К примеру:
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.27
Обычно такие файлы называют manifest.
На первый взгляд здесь довольно много непонятного, это нормально. После того как мы пройдемся по такому манифесту сверху вниз, рассмотрев его детали - все станет куда проще.
YAML просто
Для начала - что же такое YAML?
YAML - формат представления структурированных данных.
К примеру:
name: Boris age: 27
Здесь есть две пары вида Ключ : Значение, которые показывают имя человека и его возраст, соответственно.
Можно объединить их чем-то общим - создать вложенную структуру:
person: name: Boris age: 27
Ну и конечно, можно делать списки, давайте укажем какие языки знает наш Борис:
person: name: Boris age: 27 languages: - english - russian
Дефис здесь означает пункт списка.
Главное, что вам нужно сейчас:
В YAML отступы имеют значение
Так в уже разобранном примере:
person: name: Boris age: 27
Означает, что name и age находятся (принадлежат) в структуре person.

Четыре поля, которые встречаются постоянно
Давайте ознакомимся с началом нашего manifest:
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: replicas: 3
Здесь есть сразу 4 важных понятия:
1. apiVersion
2. kind
3. metadata
4. spec
Поочередно обсудим каждый из них:
apiVersion
apiVersion: apps/v1
Показывает group/version в котором определен тот или иной объект, к примеру объект Deployment, о котором мы подробно поговорим чуть позже. Определен в apps/v1, а Pod, о котором вы уже знаете определен просто в v1.
Запоминать все версии не нужно. Вам достаточно понимать назначение этого поля.
kind
kind: Deployment
Отвечает на вопрос: «Какой Kubernetes-объект мы описываем?»
Это может быть:
kind: Pod
kind:Deployment
kind: Service
kind: ConfigMap
и множество других объектов, которые мы рассмотрим позднее.
metadata
metadata: name: nginx-deployment
Содержит информацию об объекте, который мы создаем, в данном случае его имя - nginx-deployment. Позднее это поле будет нам полезно и другими параметрами, которые можно в нем указать, мы к нему еще вернемся.
spec
spec: replicas: 3
Здесь начинается самое интересное. spec описывает желаемую конфигурацию создаваемого объекта. В нашем случае говорится:
«Я хочу три replicas (экземпляра)»
Иными словами, spec связан напрямую с уже знакомым нам Desired State.

Зачем Deployment?
Должно быть, у вас уже возник вполне логичный вопрос:«Почему в kind мы указываем Deployment? Почему нам просто не создать 3 пода.»
К примеру:
Pod A Pod B Pod C
Проблема появляется тогда, когда один из подов, созданных с помощью kind: Pod исчезает.
Допустим:
Pod A работает Pod B работает Pod C перестал работать
Дело в том , что если мы создаем три отдельных пода, само по себе это не выражает мысль:
Мне всегда нужны три экземпляра
Мы описали три конкретных объекта, а не желаемое количество экземпляров приложения. Поэтому обычно управление происходит с помощью контроллеры более высоких уровней. Один из самых распространенных как раз - Deployment.
Deployment - ReplicaSet - Pod - Container
Когда мы создаем Deployment автоматически создается следующая иерархия:
Deployment -> ReplicaSet -> PodsА
внутри каждого Pod, как мы и говорили, работают контейнеры.
Важно не смешивать уровни:
Deployment управляет обновлением приложений и ReplicaSet.
ReplicaSet поддерживает необходимое количество подов.
Pod - минимальная единица развертывания в Kubernetes.
Container - непосредственная единица, в которой работает процесс приложения.

Если один из Pod исчезнет - контроллер ReplicaSet увидит то, что теперь желаемое состояние не совпадает с действительным и система начнет создавать новый Pod. Снова знакомый нам reconciliation (цикл согласования).
Зачем тогда Deployment, если его задачу выполняет ReplicaSet?
Еще один вопрос, зачем нужен Deployment, если ReplicaSet и так поддерживает необходимое количество подов?Дело в том, что в процессе эксплуатации, не только необходимо поддерживать заданное количество экземпляров приложения, но и обновлять его.Допустим, что сейчас работает 3 экземпляра соответствующих версий:
nginx:1.26 nginx:1.26 nginx:1.26
И теперь мы хотим перейти на nginx:1.27. Как это сделать правильно, ведь одновременная остановка всех действующих экземпляров приведет к временной недоступности приложения, а как следствие, к потере трафика. Deployment позволяет организовать rolling update приложения.
Вместо одновременного уничтожения всех старых экземпляров и запуска новых, Kubernetes может заменить старые поды новыми постепенно.
Концептуально, получается что-то в духе:

Механизм обновления затронем отдельно, пока что, достаточно запомнить:
Deployment управления rollout update и ReplicaSet.
ReplicaSet поддерживает заданное количество приложений.
Разбираем Deployment дальше
Вернемся к нашему манифесту:
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.27
Начало нам уже ясно: создаем Deployment с именем nginx-deployment - хотим получить 3 экземпляра
Но возникает вопрос:
«Три экземпляра чего?»
template
template - шаблон будущего пода.
Обратимся только к нижней части:
template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.27
Template описывает, какие поды необходимо создать. Мысленно, Deployment можно представлять так:
Deployment replicas: 3 template: "вот такой Pod"
Именно поэтому внутри template снова появляются metadata и spec - они относятся не к Deployment, а к внутрилежащим подам.

Что внутри подов?
Внутри template.spec (параметре spec в template) находятся
containers: - name: nginx image: nginx:1.27
Это можно читать, как:
В каждом созданном Pod должен работать контейнер с именем nginx, созданный на образе nginx:1.27
Containers - список, поэтому перед контейнером стоит дефис.
Containers - множественное число. В одном поле может работать сразу несколько контейнеров, это важная концепция на которой строится множество паттернов распределенных приложений.
Labels и selectors
Осталось самое неочевидное:
selector: matchLabels: app: nginx
и
template: metadata: labels: app: nginx
Чтобы понять это, сначала нужно разобраться, что такое labels (метки)
По сути, это обычная пара - Ключ: Значение
К примеру:
app=backend environment=production team=payments
В нашем Deployment каждому созданному поду присваивается:
labels: app: nginx
Поэтому получается:
Pod A [app=nginx] Pod B [app=nginx] Pod C [app=nginx]
Теперь Kubernetes требуется способ для того, чтобы выбрать объекты с определенной меткой:
Для этого нужен selector:
selector: matchLabels: app: nginx
Прочитать его можно так:
Выбрать pods с меткой nginx

Поэтому в Deployment две части связаны:
selector: matchLabels: app: nginx
и
template: metadata: labels: app: nginx
Удобно представить:
selector: то, какими pods я управляю
labels: какие метки получают создаваемые pods
Для Deployment selector должен соответствовать labels для создаваемых подов.
YAML человеческим языком
Наконец, мы можем прочитать наш манифест по-человечески:
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.27
Читаем, как обычный текст:
Создай объект типа Deployment с именем nginx-deployment.
Я хочу три replicas.Deployment должен управлять Pods с меткой app=nginx.
Создаваемые Pods должны получать метку app=nginx.
В каждом Pod должен работать контейнер nginx из образа nginx:1.27.
То, что в начале выглядело как набор непонятных ключей, теперь превратилось в простую структуру
Что происходит после kubectl apply?
Сохраним манифест в файл
deployment.yaml
И выполняем:
kubectl apply -f deployment.yaml
Теперь можно соединить материал этой статьи с предыдущей:
Упрощенно, происходит следующее
Сначала kubectl отправляет описание объекта в API Server.
Kubernetes сохраняет новое желаемое состояние.
Controllers замечают появившийся Deployment.
Deployment controller работает над соответствующим ReplicaSet.
ReplicaSet controller обеспечивает нужное количество Pods.
Мы попросили:
replicas: 3
Значит должны появиться 3 пода.
Но они еще не знают, где запускаться (они не назначены ни на одну Node).
Scheduler выбирает соответствующие NodeПредставим:
Pod A → Node 1 Pod B → Node 2 Pod C → Node 2
После этого kubelet на соответствующих Nodes обеспечивает выполнение назначенных Pods через container runtime.

Обратите внимание: в YAML мы нигде не указывали, на какой Node поставить каждый Pod. Мы описали что хотим получить. Kubernetes сам решил, как этого добиться. И здесь декларативная модель наконец соединяется со всей архитектурой из первой части.
Что будет, если удалить Pod?
В конце, давайте разберем сценарий, который хорошо показывает всю суть Kubernetes.
Теперь у нас есть Deployment, который поддерживает 3 запущенных экземпляра приложения.
Pod A, Pod B и Pod C работают исправно, но мы в ручную удаляем Pod C.
Теперь:
Desired: 3 Actual: 2
Через некоторое время Kubernetes сам создаст новый Pod.
Это происходит благодаря тому, что мы прописали
replicas: 3
Теперь система сама старается поддерживать это состояние.
Kubernetes сам создает новый под и это не обязательно должен быть старый Pod C, он может создать и новый, полностью аналогичный Pod, к примеру Pod D, который соответствует шаблону.
Это важный момент.
Мы управляем не судьбой конкретного экземпляра приложения, а желаемым состоянием системы.
Что стоит запомнить?
В голове должны остаться две следующие модели.
Первая:
YAML ↓ Desired State ↓ Kubernetes ↓ Actual State
Мы описываем результат, а Kubernetes сам пытается его поддерживать.
Вторая:
Deployment ↓ ReplicaSet ↓ Pod ↓ Container
Deployment управляет rollout приложения и ReplicaSets. ReplicaSet поддерживает количество Pods. Pod является минимальной deployable-единицей Kubernetes. Container непосредственно содержит работающий процесс приложения.
А Kubernetes manifest обычно начинается с знакомой структуры:
apiVersion: ... kind: ... metadata: ... spec: ...
Проверьте себя
1. Чем декларативный подход отличается от императивного?
2. Что означает:kind: Deployment
3. Что означает:
spec: replicas: 5
4. В чём разница между Deployment, ReplicaSet, Pod и Container?
5. Что описывает template внутри Deployment?
6. Попробуйте прочитать по-человечески:
spec: replicas: 3 selector: matchLabels: app: backend template: metadata: labels: app: backend spec: containers: - name: api image: my-api:2.4

