Введение
В первом разделе курса вы исследовали кластер 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, масштабированию реплик и выполнению постепенных обновлений в следующих разделах курса.


