Развёртывание приложений в Kubernetes

KubernetesBeginner
Практиковаться сейчас

Введение

В первом разделе курса вы исследовали кластер Kubernetes, не изменяя его. Вы узнали, что kubectl отправляет запросы на сервер API, а контроллеры Kubernetes постоянно сопоставляют требуемое состояние с фактическим состоянием.

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

Сначала вы создадите один Pod, чтобы было проще разобраться в базовой структуре манифеста. Затем определите Deployment, управляющий двумя Pod. Сравнение обычного Pod с Pod, управляемыми Deployment, покажет, почему для приложений обычно предпочитают контроллеры более высокого уровня.

В этой лабораторной работе основное внимание уделяется созданию рабочих нагрузок. Позже вы научитесь предоставлять приложения через Services — после того как хорошо освоите Pod, метки и Deployment.

Изучение декларативных объектов Kubernetes

Запуск среды: В этой лабораторной работе для вас запускается полноценный кластер Kubernetes. Настройка плоскости управления, узла и сетевых компонентов обычно занимает 2–3 минуты. Дождитесь полной загрузки среды, прежде чем начинать работу.

На этом шаге вы свяжете идею требуемого состояния из предыдущей лабораторной работы с манифестами Kubernetes и подготовите рабочую область для первых определений приложений.

От команд к требуемому состоянию

Kubernetes поддерживает два основных стиля управления:

  • С помощью императивной команды вы напрямую запрашиваете действие, например: «создать Pod с именем first-nginx».
  • С помощью декларативного манифеста вы сохраняете требуемую конфигурацию объекта в файле и просите Kubernetes привести кластер в соответствие с ней.

Декларативные файлы удобны тем, что их можно изучить до внесения изменений, применять повторно, просматривать различия и хранить в системе контроля версий. В этом курсе основное внимание уделяется именно декларативному подходу.

У каждого объекта Kubernetes, возвращаемого API, есть важные поля верхнего уровня:

  • apiVersion выбирает группу и версию API Kubernetes.
  • kind определяет тип объекта, например Pod или Deployment.
  • metadata задаёт идентичность объекта, включая его имя и метки.
  • spec описывает требуемое состояние объекта.
  • status содержит наблюдаемое состояние. Обычно это поле заполняется Kubernetes после создания объекта и не указывается в манифесте.

Таким образом формируется следующий цикл:

manifest spec -> API server stores desired state -> controllers act -> object status reports actual state

Подготовка каталога манифестов

Перейдите в каталог, подготовленный для этой лабораторной работы:

cd /home/labex/project/k8s-manifests

Проверьте текущий каталог с помощью pwd, что означает print working directory:

pwd
/home/labex/project/k8s-manifests

Создайте небольшой файл с заметками, содержащий четыре поля манифеста, которые вы будете использовать. Команда printf записывает каждую строку в кавычках на отдельной строке, а > перенаправляет этот вывод в файл, заменяя его содержимое, если файл уже существует.

printf '%s\n' apiVersion kind metadata spec > manifest-fields.txt

Выведите содержимое файла, чтобы проверить его:

cat manifest-fields.txt
apiVersion
kind
metadata
spec

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

Написание и проверка манифеста Pod

На этом шаге вы создадите YAML-манифест для одного Pod и проверите его структуру до отправки в кластер.

Знакомство с Pod

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

В этой лабораторной работе Pod будет запускать NGINX — небольшой веб-сервер. Используется образ nginx:1.27-alpine. Фиксация версии делает результат более воспроизводимым, чем использование изменяющегося тега latest. Образ уже кэширован в кластере, поэтому выполнение задания не зависит от загрузки из интернета.

Создание YAML-файла

Убедитесь, что вы находитесь в каталоге манифестов:

cd /home/labex/project/k8s-manifests

Для создания файла вы воспользуетесь here-документом. Оболочка перенаправляет каждую строку между <<'EOF' и закрывающим EOF в файл first-pod.yaml. Кавычки вокруг первого EOF не позволяют оболочке подставлять специальные символы внутри YAML.

cat <<'EOF' > first-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: first-nginx
  namespace: default
  labels:
    app: first-nginx
spec:
  containers:
    - name: nginx
      image: nginx:1.27-alpine
      imagePullPolicy: IfNotPresent
      ports:
        - name: http
          containerPort: 80
          protocol: TCP
EOF

В YAML иерархия задаётся отступами. Используйте пробелы единообразно: табуляция может сделать YAML недействительным. Дефис, как в - name: nginx, обозначает элемент списка.

Рассмотрим объект сверху вниз:

  • apiVersion: v1 выбирает основную группу API, используемую Pod.
  • kind: Pod объявляет тип ресурса.
  • metadata.name задаёт Pod стабильное имя first-nginx.
  • metadata.namespace: default помещает его в обычное пространство имён для приложений этого курса, а не в системное пространство имён.
  • metadata.labels добавляет метку app=first-nginx, по которой позднее можно будет выбрать этот Pod.
  • spec.containers представляет список контейнеров, которые должен запускать Pod.
  • imagePullPolicy: IfNotPresent использует кэшированный образ, если он доступен.
  • Именованный порт http использует containerPort: 80 и протокол TCP. Он документирует порт, на котором NGINX принимает соединения внутри контейнера, но не предоставляет Pod за пределами кластера.

Проверка перед созданием

Используйте пробный запуск на стороне клиента, чтобы разобрать файл, не создавая Pod. Параметр -f означает file, а --dry-run=client оставляет запрос локальным:

kubectl apply --dry-run=client -f first-pod.yaml
pod/first-nginx created (dry run)

Слова dry run здесь принципиальны: синтаксис корректен, но кластер ещё не изменён.

Попросите kubectl вывести нормализованный объект в формате YAML:

Опция вывода -o означает output format. Значение yaml просит kubectl представить разобранный объект в виде YAML, а не вывести только результат в одну строку:

kubectl apply --dry-run=client -f first-pod.yaml -o yaml

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

Создание и исследование первого Pod

На этом шаге вы примените проверенный манифест, проследите, как Kubernetes приближает Pod к требуемому состоянию, и изучите созданный объект.

Применение манифеста

При необходимости перейдите в каталог манифестов:

cd /home/labex/project/k8s-manifests

Примените файл без параметра пробного запуска:

kubectl apply -f first-pod.yaml
pod/first-nginx created

kubectl apply отправляет объект на сервер API. Сервер API сохраняет требуемую спецификацию Pod, а планировщик и kubelet совместно запускают его на узле.

Ожидание готовности

Создание Pod выполняется асинхронно: kubectl apply может завершиться до того, как контейнер будет готов. Используйте kubectl wait, чтобы дождаться условия Ready. Команда успешно завершится, когда условие станет истинным, или завершится с ошибкой через 60 секунд:

kubectl wait --for=condition=Ready pod/first-nginx --timeout=60s
pod/first-nginx condition met

Теперь выведите список Pod. В этой лабораторной работе параметр -o wide повторяется, чтобы каждый раздел можно было выполнять независимо: -o выбирает формат вывода, а wide добавляет такие поля, как IP-адрес Pod и имя узла:

kubectl get pod first-nginx -o wide
NAME          READY   STATUS    RESTARTS   AGE   IP           NODE
first-nginx   1/1     Running   ...        ...   ...          labex-v135

READY=1/1 означает, что единственный контейнер готов, а STATUS=Running — это фаза Pod. В расширенном представлении также отображаются IP-адрес Pod и назначенный узел. IP-адреса и время существования генерируются автоматически, поэтому их значения могут отличаться.

Изучение меток и владельцев

Выведите метки Pod:

kubectl get pod first-nginx --show-labels

Найдите app=first-nginx. Метки хранятся вместе с объектом и станут особенно важны, когда Deployment и Services начнут выбирать Pod.

Проверьте, управляет ли этим Pod какой-либо другой объект. Параметр -o jsonpath='...' извлекает выбранные поля вместо вывода всего объекта. Выражение проходит по metadata.ownerReferences; {"\n"} добавляет завершающий перевод строки, чтобы приглашение оболочки появилось на следующей строке:

kubectl get pod first-nginx -o jsonpath='Owner: {.metadata.ownerReferences[0].kind}{"\n"}'
Owner:

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

Контрольный пункт шага

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

Определение Deployment

На этом шаге вы определите Deployment, который попросит Kubernetes поддерживать две копии Pod с NGINX.

Зачем нужен Deployment?

Обычный Pod полезен для обучения, но приложениям обычно требуется контроллер. Deployment задаёт требуемое количество реплик и шаблон Pod. Он создаёт ReplicaSet, а ReplicaSet поддерживает нужное количество Pod.

Цепочка владения выглядит так:

Deployment -> ReplicaSet -> Pods -> containers

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

Создание манифеста Deployment

Вернитесь в каталог манифестов:

cd /home/labex/project/k8s-manifests

Создайте course-web-deployment.yaml с помощью here-документа:

cat <<'EOF' > course-web-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: course-web
  labels:
    app: course-web
spec:
  replicas: 2
  selector:
    matchLabels:
      app: course-web
  template:
    metadata:
      labels:
        app: course-web
    spec:
      containers:
        - name: nginx
          image: nginx:1.27-alpine
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 80
EOF

Deployment использует apps/v1 — стабильную версию API для Deployment. В его spec появляются три важных поля:

  • replicas: 2 — требуемое количество Pod.
  • selector.matchLabels — критерий выбора Pod, которыми управляет Deployment.
  • template — шаблон, используемый для создания каждого Pod.

Селектор и template.metadata.labels используют одну и ту же метку app: course-web. Они должны совпадать, иначе Deployment не сможет идентифицировать Pod, созданные по собственному шаблону.

Проверка Deployment

Разберите манифест, не изменяя кластер:

kubectl apply --dry-run=client -f course-web-deployment.yaml
deployment.apps/course-web created (dry run)

Используйте kubectl diff, чтобы сравнить манифест с текущим состоянием кластера. Ненулевой код завершения означает лишь, что объект будет изменён; || true не позволяет оболочке считать ожидаемое различие ошибкой:

kubectl diff -f course-web-deployment.yaml || true

Поскольку course-web ещё не существует, вывод показывает весь объект как добавление, а строки начинаются с +. В отличие от apply, команда diff не вносит изменений в кластер.

Развёртывание и исследование управляемого приложения

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

Применение Deployment и ожидание завершения

Сначала перейдите в каталог с манифестом. Затем примените сохранённое требуемое состояние; параметр -f указывает kubectl прочитать данные из этого файла:

cd /home/labex/project/k8s-manifests
kubectl apply -f course-web-deployment.yaml
deployment.apps/course-web created

Дождитесь завершения развёртывания Deployment. Развёртывание — это процесс приведения Pod Deployment к требуемому шаблону и количеству реплик:

kubectl rollout status deployment/course-web --timeout=60s
deployment "course-web" successfully rolled out

Выведите Deployment:

kubectl get deployment course-web
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
course-web   2/2     2            2           ...

READY=2/2 означает, что обе требуемые реплики готовы. UP-TO-DATE=2 означает, что обе используют текущий шаблон Pod, а AVAILABLE=2 — что обе доступны.

Исследование связанных ресурсов

Используйте селектор меток -l app=course-web, чтобы вывести связанные ресурсы:

kubectl get deployment,replicaset,pods -l app=course-web

В выводе будут один Deployment, один ReplicaSet и два Pod. Сгенерированные суффиксы ReplicaSet и Pod могут отличаться:

NAME                         READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/course-web   2/2     2            2           ...

NAME                                    DESIRED   CURRENT   READY   AGE
replicaset.apps/course-web-...          2         2         2       ...

NAME                              READY   STATUS    RESTARTS   AGE
pod/course-web-...-...            1/1     Running   ...        ...
pod/course-web-...-...            1/1     Running   ...        ...

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

Контрольный пункт шага

Вы применили манифест Deployment и дождались требуемого состояния. Kubernetes создал ReplicaSet и два Pod, а общая метка app=course-web позволила вывести их как единую группу приложения.

Прослеживание владения контроллеров

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

Проверка владельца Pod

Сохраните имя одного сгенерированного Pod в переменную оболочки. Синтаксис NAME=$(command) — это подстановка команды: оболочка выполняет команду и сохраняет её вывод в NAME. В данном случае -l app=course-web выбирает подходящие Pod, а JSONPath извлекает имя первого Pod:

POD_NAME=$(kubectl get pods -l app=course-web -o jsonpath='{.items[0].metadata.name}')

Выведите имя, чтобы знать, какой Pod был выбран:

echo "$POD_NAME"

Теперь проверьте его непосредственного владельца. Взятие "$POD_NAME" в кавычки передаёт сохранённое имя как один безопасный аргумент команды:

kubectl get pod "$POD_NAME" -o jsonpath='Owner: {.metadata.ownerReferences[0].kind}/{.metadata.ownerReferences[0].name}{"\n"}'
Owner: ReplicaSet/course-web-...

В отличие от обычного Pod first-nginx, Pod, управляемый Deployment, имеет владельцем ReplicaSet. Сам ReplicaSet принадлежит Deployment.

Выведите владельца ReplicaSet. В первой команде повторяется тот же шаблон подстановки команды, но теперь имя ReplicaSet сохраняется в RS_NAME:

RS_NAME=$(kubectl get replicaset -l app=course-web -o jsonpath='{.items[0].metadata.name}')
kubectl get replicaset "$RS_NAME" -o jsonpath='Owner: {.metadata.ownerReferences[0].kind}/{.metadata.ownerReferences[0].name}{"\n"}'
Owner: Deployment/course-web

Повторное применение требуемого состояния

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

kubectl apply -f course-web-deployment.yaml
deployment.apps/course-web unchanged

unchanged демонстрирует важное свойство декларативного подхода: многократное применение одного и того же требуемого состояния безопасно. Kubernetes предпринимает действия только тогда, когда требуемая конфигурация отличается от фактической.

Контрольный пункт шага

Теперь у вас есть обычный Pod и приложение, управляемое Deployment. Оба запускают контейнеры, но Deployment добавляет иерархию контроллеров, которая поддерживает две реплики и создаёт основу для дальнейшего масштабирования и постепенных обновлений.

Итоги

Вы перешли от исследования кластера в режиме только чтения к декларативному управлению приложениями. Вы изучили назначение apiVersion, kind, metadata и spec; проверили манифесты с помощью пробного запуска на стороне клиента; создали и исследовали обычный Pod; а также развернули две управляемые реплики с помощью Deployment.

Самое главное — вы увидели цепочку владения контроллеров от Deployment к ReplicaSet и далее к Pod. Эта основа, построенная на требуемом состоянии, подготовит вас к диагностике приложений, предоставлению к ним доступа через Services, масштабированию реплик и выполнению постепенных обновлений в следующих разделах курса.