В первой лабораторной мы подняли кластер с помощью kind, нашли знакомые компоненты Kubernetes и запустили первый Pod hello-k8s.

Но, как вы уже знаете Kubernetes нужен не для 1 пода, а для целых распределенных систем, так что нашему приложению одного Pod мало. Нужно три, а может даже больше. Ну и естественно хочется, чтобы их падения отслеживались автоматически - мы стремимся к уменьшению ручной работы.

Во второй теоретической статье мы разбирали Deployment, ReplicaSet и принцип желаемого состояния. Теперь проверим всё это в работающем кластере

На лабораторную стоит заложить около 20–30 минут. Если образ nginx ещё не загружен, первый запуск может занять дополнительное время.

Сегодня попробуем не просто запустить несколько Pod, а своими глазами увидеть, как Kubernetes поддерживает желаемое (desired) состояние. И для этого даже немного ему помешаем.

Что понадобится

Используем те же инструменты, что и в первой лабораторной: Docker, kind и kubectl.

Если после первой лабы кластер k8s-first-lab у вас остался - отлично, ничего пересоздавать не нужно. Если успели удалить - поднимем заново, предварительно, не забывайте включить Docker Desktop, если пользуетесь Windows:

kind create cluster --name k8s-first-lab --wait 180s

Не забудьте выбрать нужное подключение:

kubectl config use-context kind-k8s-first-lab

Проверим ноду:

kubectl get nodes

В колонке STATUS должно быть Ready. Если видите NotReady, немного подождите и повторите команду.

Помните наш hello-k8s из первой лабы? Если он всё ещё живёт в кластере, давайте его уберём. Сегодня у нас будет другой подопытный:

kubectl delete pod hello-k8s --ignore-not-found
Картинка 1
Картинка 1

Шаг 1. Описываем приложение в YAML

В прошлый раз хватило одной команды kubectl run. На этот раз сделаем чуть серьёзнее - опишем приложение в YAML. Не пугайтесь объёма файла, большую часть полей мы уже встречали в теории.

Создайте папку для второй лабораторной и перейдите в неё:

mkdir k8s-lab-2
cd k8s-lab-2

Создайте файл deployment.yaml и добавьте в него:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: hello-nginx
  template:
    metadata:
      labels:
        app: hello-nginx
    spec:
      containers:
        - name: nginx
          image: nginx:stable-alpine
          ports:
            - containerPort: 80

Да, после одной команды из прошлой лабы такой YAML выглядит внушительно. Но давайте посмотрим, за что именно мы здесь заплатили несколькими лишними строками.

Разберём основные части:

Поле

Что означает

kind: Deployment

Создаём Deployment, а не отдельный Pod

metadata.name

Имя нашего Deployment

replicas: 3

Хотим три экземпляра приложения

selector.matchLabels

По каким меткам Deployment определяет свои Pod

template

Шаблон, по которому будут создаваться Pod

image

Образ контейнера, знакомый нам по первой лабораторной

Особое внимание обратите на две части:

selector:
  matchLabels:
    app: hello-nginx

и:

template:
  metadata:
    labels:
      app: hello-nginx

Значения меток совпадают. Это важно: selector Deployment должен соответствовать меткам в шаблоне Pod, мы уже обсуждали причины во второй части теоретической серии.

И ещё одна строка:

replicas: 3

Здесь и начинается самое интересное. Мы не говорим Kubernetes: «Создай три Pod и можешь отдыхать». Мы говорим: у приложения должно быть три Pod. Не только сейчас, а пока мы не изменим это требование. Иными словами - мы задали desired (желаемое) состояние нашего кластера. Kubernetes будет стараться его поддерживать

Картинка 2
Картинка 2

Шаг 2. Применяем YAML и смотрим на результат

Выполните из папки с файлом:

kubectl apply -f deployment.yaml

Ожидаемый ответ:

deployment.apps/hello-deployment created

Команда apply отправила описание объекта в Kubernetes API. Теперь посмотрим, что получилось:

kubectl get deployments

Пример вывода:

NAME               READY   UP-TO-DATE   AVAILABLE   AGE
hello-deployment   3/3     3            3           1m

В колонке READY нас интересует 3/3: три готовых Pod из трёх желаемых. Если сразу после создания видите 0/3 или 1/3, немного подождите - контейнеры ещё запускаются.

Чтобы дождаться готовности Deployment, можно выполнить:

kubectl rollout status deployment/hello-deployment

А теперь посмотрим сами Pod:

kubectl get pods

Пример:

NAME                                READY   STATUS    RESTARTS   AGE
hello-deployment-xxxxxxxxxx-abc12   1/1     Running   0          1m
hello-deployment-xxxxxxxxxx-def34   1/1     Running   0          1m
hello-deployment-xxxxxxxxxx-ghi56   1/1     Running   0          1m

Имена у вас будут другими. Важнее, что Pod три и все они перешли в Running.

Итак, мы попросили создать один Deployment, а в результате получили три Pod. Неплохо, но кто именно проделал остальную работу?

Картинка 3
Картинка 3

Шаг 3. Находим ReplicaSet

Вспомним схему из теории:

Deployment -> ReplicaSet -> Pods

ReplicaSet мы своими руками не создавали. Но ведь он должен где-то быть? Проверим:

kubectl get replicasets

Пример вывода:

NAME                          DESIRED   CURRENT   READY   AGE
hello-deployment-xxxxxxxxxx   3         3         3       2m

Здесь три полезные колонки:

Колонка

Что показывает

DESIRED

Сколько Pod должно быть

CURRENT

Сколько Pod сейчас находится под управлением ReplicaSet

READY

Сколько из них готовы

Получается, Deployment создал ReplicaSet, а уже тот в свою очередь обеспечил появление трёх Pod.

Посмотрим на связь объектов подробнее:

kubectl describe deployment hello-deployment

Найдите сведения о ReplicaSet в выводе.

Теперь узнаем имя ReplicaSet:

kubectl get rs

И посмотрим его описание, подставив настоящее имя:

kubectl describe rs <имя-replicaset>

В выводе найдите строку Controlled By: Deployment/hello-deployment.

Она показывает, что ReplicaSet управляется нашим Deployment. А если выполнить kubectl describe pod <имя-pod>, в описании Pod можно найти Controlled By: ReplicaSet/.... Без уточнения имени конкретного пода она выведет информацию по всем трем существующим у нас подам.

Вот теперь схема Deployment -> ReplicaSet -> Pods перестала быть просто картинкой из теоретической статьи. Связи мы нашли прямо в кластере.

Картинка 4
Картинка 4

Шаг 4. Удаляем Pod и наблюдаем за Kubernetes

Перейдём к самой интересной части. Давайте попробуем немного помешать Kubernetes

Сейчас у нас три Pod. В YAML по-прежнему указано:

replicas: 3

Как думаете, что будет, если мы возьмём и удалим один из этих Pod?

Откройте второй терминал и запустите в нём:

kubectl get pods -w

Параметр -w означает watch: команда будет показывать изменения, пока мы её не остановим.

В первом терминале получим список Pod:

kubectl get pods

Скопируйте имя одного из них и удалите его:

kubectl delete pod <имя-pod>

Вернитесь во второй терминал. Вы можете увидеть, как удаляемый Pod переходит в Terminating, а новый - в Pending, затем ContainerCreating и Running.

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

Проверим итог:

kubectl get pods

Снова три Pod, но у одного теперь другое имя. Почему так?

В какой-то момент фактическое состояние перестало соответствовать желаемому:

Желаемое состояние (desired state): 3 Pod
Фактическое состояние (actual state): 2 Pod

ReplicaSet обнаружил нехватку и создал новый Pod. Kubernetes не восстановил старый Pod с тем же именем - он создал замену.

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

Картинка 5
Картинка 5

Остановить наблюдение во втором терминале можно сочетанием Ctrl+C.

Шаг 5. Меняем количество реплик

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

Выполните:

kubectl scale deployment hello-deployment --replicas=5

Команду можно прочитать так: «Измени количество желаемых реплик Deployment hello-deployment на пять».

Проверим:

kubectl get deployments
kubectl get pods

После завершения масштабирования ожидаем READY 5/5 и пять Pod.

Обратите внимание! Мы не создавали ещё два Pod вручную. Поменяли одно число - остальное Kubernetes сделал сам

Теперь уменьшим количество:

kubectl scale deployment hello-deployment --replicas=2

Снова посмотрим:

kubectl get pods

Через некоторое время останутся два Pod. ReplicaSet не старается держать как можно больше экземпляров - он поддерживает столько, сколько мы указали - иными словами, вернулись к циклу согласования (reconciliation loop) из теории.

Картинка 6
Картинка 6

Шаг 6. Возвращаемся к YAML

Тут есть небольшая ловушка. Причём довольно жизненная.

В файле deployment.yaml у нас по-прежнему записано:

replicas: 3

А в кластере после последней команды сейчас две реплики.

Команда kubectl scale поменяла количество реплик в кластере, а наш YAML об этом, конечно, ничего не знает. Сам себя файл не перепишет. Давайте вернёмся к декларативному подходу.

Откройте deployment.yaml и замените:

replicas: 3

на:

replicas: 4

Сохраните файл и выполните:

kubectl apply -f deployment.yaml

Проверим результат:

kubectl get deployments

После запуска новых Pod должно появиться READY 4/4.

Именно поэтому в реальных проектах важно понимать, где хранится актуальная конфигурация. Если вручную менять ресурсы в кластере и забывать обновлять YAML, легко запутаться.

Картинка 7
Картинка 7

Шаг 7. Проверяем labels и selector

В нашем YAML есть метка:

app: hello-nginx

Посмотрим, какие метки получили Pod:

kubectl get pods --show-labels

В колонке LABELS найдите app=hello-nginx.

Теперь выведем только Pod с этой меткой:

kubectl get pods -l app=hello-nginx

Параметр -l позволяет выбирать объекты по меткам.

Вот почему метки так удобны. Имена Pod после пересоздания меняются, а искать их по app=hello-nginx мы всё ещё можем. Не нужно каждый раз копировать длинное имя.

Важно: совпадение метки само по себе ещё не означает, что произвольный Pod автоматически станет частью нашего Deployment. Kubernetes также учитывает связи управления объектами.

Картинка 8
Картинка 8

Если что-то не получилось

Что произошло

С чего начать

kubectl не подключается к кластеру

Выполните kubectl config use-context kind-k8s-first-lab и проверьте, что Docker запущен

kubectl apply сообщает об ошибке YAML

Проверьте отступы: в YAML они имеют значение; табуляцию лучше не использовать

Deployment долго показывает 0/3

Выполните kubectl get pods и kubectl describe pod <имя-pod>

Pod имеет статус ImagePullBackOff

Проверьте образ nginx:stable-alpine и сообщения в разделе Events

Команда удаления Pod сообщает NotFound

Повторите kubectl get pods: имя могло измениться после пересоздания

После kubectl scale число Pod не совпадает с ожидаемым

Посмотрите kubectl get deployments, kubectl get rs и события через kubectl describe deployment hello-deployment

Если что-то пошло не так, не спешите удалять кластер и начинать всё заново. Для начала посмотрим, что Kubernetes пытается нам сказать:

kubectl describe pod <имя-pod>

В конце вывода найдите Events. Там часто видно, на каком этапе возникла проблема. Если контейнер уже запускался, его сообщения можно посмотреть через kubectl logs <имя-pod>.

Материалы лабы на GitHub

Материалы лабораторной на GitHub

Как и в прошлый раз, все необходимые материалы я собрал в отдельном репозитории на GitHub.

Для этой лабораторной нас интересует папка labs/02-deployment.

Там вы найдёте:

deployment.yaml - готовый манифест нашего Deployment, если не хочется переписывать его из статьи вручную.

commands.md - небольшую шпаргалку с командами, которые мы использовали.

tasks.md - задания для самостоятельной практики.

solutions.md - решения этих заданий с пояснениями.

README.md - краткую инструкцию по работе с материалами.

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

А если в процессе экспериментов что-то пошло не так, всегда можно вернуться к исходному deployment.yaml и начать заново.

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

Проверьте себя

Попробуйте выполнить несколько действий без подсказок:

  1. Покажите Deployment, ReplicaSet и Pod нашего приложения тремя командами kubectl.

  2. Определите, сколько реплик желает Deployment и сколько Pod сейчас готовы.

  3. Удалите один Pod и объясните, почему через некоторое время их количество восстановилось.

  4. Измените количество реплик через YAML и примените файл.

  5. Выведите только Pod с меткой app=hello-nginx.

И главный вопрос: чем создание одного Pod через kubectl run из первой лабораторной отличается от создания Deployment с replicas: 3?

Если теперь можете своими словами объяснить, зачем между Deployment и Pod стоит ReplicaSet, значит, мы сегодня собрались не зря

Что делать с ресурсами после лабы

Удалим созданный Deployment:

kubectl delete -f deployment.yaml

Проверим:

kubectl get deployments
kubectl get replicasets
kubectl get pods

Наш Deployment и принадлежащие ему ReplicaSet и Pod должны исчезнуть. Если в кластере остались другие объекты, созданные не этой лабораторной, удалять их не нужно.

Сам кластер можно оставить для следующих занятий. Если хотите полностью удалить учебный стенд, выполните:

kind delete cluster --name k8s-first-lab

В следующей лабораторной перейдём к Service. Посмотрим, как обращаться к приложению, если его Pod могут исчезать, пересоздаваться и получать новые IP-адреса.

Документация

Комментарии (0)