Перейти к основному содержимому

Kubernetes YAML — минимальные манифесты


Для кого эта статья

Если вы гуглите kubernetes yaml example, deployment yaml nginx, kubectl apply example, service yaml clusterip или минимальный манифест k8s — здесь готовые файлы с разбором каждой строки, как в галерее Turtle для Python и Docker Compose для стеков контейнеров.

Подойдёт, если вы:

  • проходите лабораторную по DevOps, облакам или микросервисам;
  • уже поднимали стек через Compose и хотите тот же сценарий в кластере;
  • учитесь читать манифесты перед курсом по Kubernetes или справочнику.

Kubernetes описывает желаемое состояние кластера в YAML (или JSON): какие контейнеры запустить, сколько копий, как к ним ходить по сети, где хранить настройки.

Сначала теория — потом копирование

Архитектура кластера, Pod, Service, Ingress — Docker Swarm и Kubernetes. Установка Docker Desktop и включение Kubernetes — первые шаги. Локальные стеки без оркестратора — Docker Compose — готовые стеки. Синтаксис YAML (отступы, списки) — глава про YAML.

Загрузка интерактивного демо…

Демо выше проверяет отступы и синтаксис YAML — те же правила, что и в манифестах ниже. В пресетах — Docker Compose и Kubernetes Deployment; полные стеки — в блоках ниже.


Три слова — объект, Pod, контроллер

ТерминПростыми словамиАналогия с Compose
Объект (resource)Запись в API кластера (Pod, Service, …)Сервис или volume в compose.yaml
PodОдин или несколько контейнеров, которые кластер запускает вместе на одной нодеОдин контейнер (или связка sidecar)
Deployment«Держатель» подов: число копий, обновление образаdeploy.replicas + политика перезапуска
ServiceСтабильный DNS и IP для набора подовИмя сервиса db, web в сети compose
NamespaceВиртуальная «папка» объектов в кластереИмя проекта compose (изоляция)
ManifestYAML-файл с apiVersion, kind, metadata, specФайл compose.yaml
kubectl apply -f app.yaml


API-сервер Kubernetes ──► Scheduler ставит Pod на ноду


Service (ClusterIP) ──► другие поды ходят по имени my-app:80

Запомните: внутри кластера приложение к базе подключается по имени Service (postgres), как db в Compose. localhost внутри пода — это сам Pod, а не соседний сервис.


Как читать любой манифест

ПолеСмысл
apiVersionВерсия API (например v1 для Pod, apps/v1 для Deployment)
kindТип объекта: Pod, Deployment, Service, …
metadata.nameИмя в namespace; по нему же часто строится DNS
metadata.labelsМетки для выбора объектов (связь Service ↔ Pod)
metadata.namespaceNamespace (если не указан — default)
specЖелаемое состояние — образ, порты, реплики, …

Связка Deployment → Pod и Service → Pod строится через одинаковые labels:

# В Deployment
spec:
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web

# В Service
spec:
selector:
app: web

Service направляет трафик на все поды с меткой app: web.


Как работать с примерами

  1. Установите kubectl и локальный кластер: Minikube, kind, или Kubernetes в Docker Desktop (Settings → Kubernetes → Enable).
  2. Создайте папку, например k8s-demo/.
  3. Сохраните блок YAML в файл (pod-nginx.yaml, app.yaml, …). Несколько объектов в одном файле разделяют строкой ---.
  4. В терминале в этой папке:
kubectl cluster-info
kubectl apply -f pod-nginx.yaml
kubectl get pods
kubectl get svc

Разбор команд:

КомандаЧто делает
kubectl cluster-infoПроверяет, что kubectl видит кластер
kubectl apply -f файл.yamlСоздаёт или обновляет объекты из манифеста
kubectl get podsСписок подов и статус (Running, CrashLoopBackOff, …)
kubectl describe pod имяСобытия, причина ImagePullBackOff, лимиты
kubectl logs имя-podЛоги контейнера
kubectl delete -f файл.yamlУдаляет объекты, описанные в файле
  1. Для доступа с ПК к Service типа ClusterIP используйте NodePort, port-forward или Ingress (см. примеры ниже).
Minikube и Docker Desktop

Minikube: minikube start, затем minikube kubectl -- get pods или настройте alias kubectl.
Docker Desktop: включите Kubernetes в настройках, дождитесь зелёного статуса — context docker-desktop появится в kubectl config get-contexts.

Секреты в примерах

Пароли вроде secret и base64 в учебных манифестах — только для локального кластера. В Git коммитьте ConfigMap без паролей; секреты — через Sealed Secrets, External Secrets или CI variables.


Обязательный каркас Pod

Любой пример ниже опирается на этот минимум:

apiVersion: v1
kind: Pod
metadata:
name: minimal
spec:
containers:
- name: app
image: alpine:3.20
command: ["sleep", "infinity"]
ФрагментСмысл
apiVersion: v1Стабильное Core API для Pod
kind: PodТип объекта
metadata.nameИмя Pod в текущем namespace
spec.containersСписок контейнеров (минимум один)
name: appИмя контейнера внутри Pod (для kubectl logs app)
image:Образ из registry
command:Переопределяет CMD образа

Проверка до apply (сухой прогон клиента):

kubectl apply -f minimal-pod.yaml --dry-run=client

Стартовые манифесты

Пять конфигураций — от одного Pod до связки Deployment + Service.

Как устроен каждый пример ниже

Задача — что вы получите и типичный поисковый запрос.
YAML — готовый файл для копирования.
Разбор по строкам — таблица «что означает каждая строка».
Проверка — команды kubectl, чтобы убедиться, что объект жив.
Так же устроены Docker Compose и curl / fetch.


1. Один Pod с nginx

Задача: убедиться, что кластер и kubectl apply работают. Запрос в поиске: kubernetes pod nginx yaml example.

apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.27-alpine
ports:
- containerPort: 80

Разбор по строкам:

СтрокаСмысл
kind: PodЗапускается один Pod без автоматического пересоздания при сбое (для прода обычно Deployment)
labels: app: nginxМетка для будущего Service
image: nginx:1.27-alpineТот же образ, что в Compose №1
containerPort: 80Порт внутри контейнера; снаружи кластера сам по себе не открыт

Проверка:

kubectl apply -f nginx-pod.yaml
kubectl get pod nginx-pod
kubectl port-forward pod/nginx-pod 8080:80

Откройте http://localhost:8080 — страница nginx. Остановка: Ctrl+C у port-forward, затем kubectl delete pod nginx-pod.

КомандаСмысл
port-forward pod/… 8080:80Туннель с вашего ПК на порт 80 контейнера
delete podPod исчезает; Deployment бы пересоздал копию

2. Deployment — три копии приложения

Задача: масштабирование и самовосстановление. Запрос: kubernetes deployment yaml replicas.

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

Разбор по строкам:

СтрокаСмысл
apiVersion: apps/v1API группы apps для Deployment
replicas: 3Кластер держит три пода с этим шаблоном
selector.matchLabelsDeployment управляет подами с меткой app: web
templateШаблон Pod — те же поля, что в примере №1
template.metadata.labelsДолжны совпадать с matchLabels

Проверка:

kubectl apply -f web-deployment.yaml
kubectl get deployment web
kubectl get pods -l app=web

Удалите один Pod — Deployment создаст новый:

kubectl delete pod -l app=web --field-selector=status.phase=Running --wait=false
kubectl get pods -l app=web

3. Service ClusterIP — доступ внутри кластера

Задача: стабильное имя web для других подов. Запрос: kubernetes service clusterip yaml.

apiVersion: v1
kind: Service
metadata:
name: web
spec:
type: ClusterIP
selector:
app: web
ports:
- port: 80
targetPort: 80

Разбор по строкам:

СтрокаСмысл
type: ClusterIPIP только внутри кластера (по умолчанию, можно не писать)
selector.app: webТрафик на поды с меткой app: web (из Deployment выше)
port: 80Порт Service (куда стучатся клиенты)
targetPort: 80Порт контейнера (containerPort)

Проверка (нужен запущенный Deployment из примера №2):

kubectl apply -f web-service.yaml
kubectl get svc web
kubectl run curl-test --rm -it --image=curlimages/curl --restart=Never -- curl -s http://web

Внутри временного Pod команда curl http://web обращается к Service по DNS.


4. Service NodePort — доступ с вашего ПК

Задача: открыть приложение на localhost без port-forward. Запрос: kubernetes nodeport yaml example.

apiVersion: v1
kind: Service
metadata:
name: web-nodeport
spec:
type: NodePort
selector:
app: web
ports:
- port: 80
targetPort: 80
nodePort: 30080

Разбор по строкам:

СтрокаСмысл
type: NodePortПроброс порта на каждую ноду кластера
nodePort: 30080Порт на ноде (диапазон обычно 30000–32767)
port / targetPortКак в ClusterIP — маппинг Service → контейнер

Проверка:

kubectl apply -f web-nodeport.yaml
minikube service web-nodeport --url

На Minikube команда выведет URL. На Docker Desktop часто работает http://localhost:30080.


5. ConfigMap — настройки без пересборки образа

Задача: передать конфиг-файл или переменные. Запрос: kubernetes configmap yaml example.

apiVersion: v1
kind: ConfigMap
metadata:
name: web-config
data:
APP_MODE: "development"
index.html: |
Hello from ConfigMap
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-cm
spec:
replicas: 1
selector:
matchLabels:
app: web-cm
template:
metadata:
labels:
app: web-cm
spec:
containers:
- name: nginx
image: nginx:1.27-alpine
env:
- name: APP_MODE
valueFrom:
configMapKeyRef:
name: web-config
key: APP_MODE
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: html
configMap:
name: web-config
items:
- key: index.html
path: index.html

Разбор по строкам:

СтрокаСмысл
data.APP_MODEКлюч → переменная окружения
data.index.htmlМногострочное значение — файл в volume
env.valueFrom.configMapKeyRefПодставить значение ключа в env
volumes.configMapСмонтировать ключи как файлы
items.key / pathКакой ключ ConfigMap стать каким файлом в каталоге

Проверка:

kubectl apply -f web-configmap.yaml
kubectl get configmap web-config
kubectl logs deployment/web-cm
kubectl port-forward deployment/web-cm 8080:80

Страница на localhost:8080 покажет заголовок из ConfigMap.


Связка «приложение + база»

Классический учебный стек — API (или заглушка) и PostgreSQL. Аналог Compose app + db.

Задача: два Deployment, два Service, Secret для пароля БД.

apiVersion: v1
kind: Secret
metadata:
name: pg-secret
type: Opaque
stringData:
POSTGRES_PASSWORD: dev-only-change-me
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres
spec:
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:16-alpine
ports:
- containerPort: 5432
env:
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: pg-secret
key: POSTGRES_PASSWORD
- name: POSTGRES_DB
value: appdb
---
apiVersion: v1
kind: Service
metadata:
name: postgres
spec:
selector:
app: postgres
ports:
- port: 5432
targetPort: 5432
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 2
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: hashicorp/http-echo:0.2.3
args: ["-text=ok", "-listen=:8080"]
ports:
- containerPort: 8080
env:
- name: DB_HOST
value: postgres
- name: DB_PORT
value: "5432"
---
apiVersion: v1
kind: Service
metadata:
name: api
spec:
selector:
app: api
ports:
- port: 80
targetPort: 8080

Разбор ключевых идей:

ФрагментСмысл
stringData в SecretКластер сам кодирует в base64; удобнее для учебных файлов
secretKeyRefПароль не в открытом виде в Deployment
Service name: postgresDNS-имя postgres в namespace
DB_HOST: postgresТо же правило, что DB_HOST=db в Compose
hashicorp/http-echoМинимальный HTTP-ответ без своего Dockerfile
--- между документамиОдин файл — несколько объектов за один apply

Проверка:

kubectl apply -f stack-app-db.yaml
kubectl get pods
kubectl get svc
kubectl port-forward svc/api 8080:80
curl -s http://localhost:8080

Проверка DNS до БД:

kubectl get endpoints postgres

Дополнительные объекты

Namespace — отдельная «песочница»

apiVersion: v1
kind: Namespace
metadata:
name: lab
kubectl apply -f namespace-lab.yaml
kubectl apply -f web-deployment.yaml -n lab
kubectl get pods -n lab

Secret (тип Opaque) — отдельно от Deployment

apiVersion: v1
kind: Secret
metadata:
name: api-token
type: Opaque
data:
TOKEN: dGVzdC10b2tlbg==
СтрокаСмысл
data.TOKENЗначение уже в base64 (test-tokendGVzdC10b2tlbg==)
stringDataАльтернатива — писать текстом, кластер закодирует сам

Подключение в Pod:

env:
- name: API_TOKEN
valueFrom:
secretKeyRef:
name: api-token
key: TOKEN

Ingress — HTTP снаружи (нужен Ingress Controller)

Задача: один входной URL для нескольких Service. На Minikube:

minikube addons enable ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-ingress
spec:
ingressClassName: nginx
rules:
- host: demo.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 80

Разбор:

СтрокаСмысл
ingressClassName: nginxКакой контроллер обрабатывает Ingress (в Minikube addon — nginx)
host: demo.localИмя в HTTP-заголовке Host
backend.service.nameИмя существующего Service
pathType: PrefixПуть / и всё под ним

Добавьте в hosts файл на ПК: 127.0.0.1 demo.local (для Minikube IP возьмите minikube ip).


Liveness и readiness — проверки здоровья

spec:
containers:
- name: nginx
image: nginx:1.27-alpine
ports:
- containerPort: 80
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 10
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 2
periodSeconds: 5
ПробаСмысл
livenessProbePod «завис» — kubelet перезапустит контейнер
readinessProbePod ещё не готов — Service не шлёт трафик
initialDelaySecondsПауза после старта (nginx успевает подняться)

Тот же приём, что healthcheck в Compose.


Один файл — полный минимальный сайт

Deployment + Service NodePort в одном манифесте (удобно для сдачи лабораторной):

apiVersion: apps/v1
kind: Deployment
metadata:
name: hello
labels:
app: hello
spec:
replicas: 2
selector:
matchLabels:
app: hello
template:
metadata:
labels:
app: hello
spec:
containers:
- name: nginx
image: nginx:1.27-alpine
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: hello
spec:
type: NodePort
selector:
app: hello
ports:
- port: 80
targetPort: 80
nodePort: 30081
kubectl apply -f hello-all-in-one.yaml
kubectl get all -l app=hello

Частые ошибки — симптом и лечение

СимптомЧастая причинаЧто сделать
connection refused к localhost из другого PodОбращение к себе, а не к ServiceDB_HOST=postgres, имя Service
ImagePullBackOffНет сети / опечатка в image:kubectl describe pod → Events
CreateContainerConfigErrorSecret/ConfigMap не найденkubectl get secret, имя и ключ
Service есть, endpoints пустыеLabels не совпадаютСравнить selector Service и labels Pod
Forbidden при applyНет прав RBACУчебный кластер — context admin; в проде — RoleBinding
Ingress 404Нет controller или неверный hostkubectl get ingress, включить addon
YAML «ломается» при applyТабы вместо пробелов2 пробела на уровень; демо YAML
Как читать Pending / CrashLoopBackOff

kubectl describe pod имя → блок Events снизу. Там причина: нехватка памяти на ноде, неверный монтирование volume, падение команды в контейнере. Логи — kubectl logs имя и при перезапусках kubectl logs имя --previous.


Шпаргалка — что вставить в отчёт по лабораторной

В кластере Kubernetes развёрнуто приложение по декларативному описанию в YAML. Манифест Deployment задаёт образ контейнера и число реплик; объект Service обеспечивает стабильный сетевой доступ к подам по меткам labels. Команда kubectl apply -f применяет желаемое состояние; kubectl get pods,svc проверяет статус. Для доступа с рабочей станции использован NodePort / kubectl port-forward. Секреты вынесены в объект Secret, конфигурация — в ConfigMap.


Куда дальше

ЗадачаМатериал
Таблицы API, kubectl, архитектураСправочник по Kubernetes
Курс, Helm, прод-стекРеализация Kubernetes
Локально без k8sDocker Compose — стеки
CI с деплоем в кластерGitHub Actions — рецепты
Вопросы для самопроверки200 вопросов по Kubernetes
Официальная документацияНавигатор kubernetes.io

См. также

Другие статьи этого же раздела в боковом меню (как на странице "О разделе").