Kubernetes
Kubernetes за 18 минут: поды, Deployment, Service, ConfigMap, Ingress, пробы и масштабирование на рабочих манифестах и командах kubectl.
Kubernetes (K8s) — система оркестрации контейнеров: она запускает, масштабирует и перезапускает приложения в кластере машин. Ниже — экспресс-тур по манифестам и kubectl: всё в комментариях к коду, минимум прозы.
Что такое Kubernetes
Декларативная модель: вы описываете желаемое состояние в YAML, а Kubernetes приводит кластер к нему и поддерживает.
# Ключевые понятия (концепция, не манифест):
#
# Кластер — набор машин под управлением Kubernetes.
# Нода (node) — одна машина (VM или физический сервер) в кластере.
# Под (pod) — наименьшая единица развёртывания: один или несколько
# контейнеров, делящих сеть и тома. Обычно 1 контейнер = 1 под.
# Объект — любая сущность (Pod, Deployment, Service...), описанная YAML.
#
# Оркестрация = планирование подов по нодам + самовосстановление
# (упал под — поднимется заново) + масштабирование под нагрузку.
Архитектура кластера
# Control plane (управляющий слой) — "мозг" кластера:
# kube-apiserver — единая точка входа, REST API (с ним говорит kubectl).
# etcd — распределённое key-value хранилище состояния кластера.
# kube-scheduler — решает, на какую ноду поставить новый под.
# controller-mgr — следит, чтобы факт совпадал с желаемым состоянием.
#
# Worker-ноды — где реально крутятся контейнеры:
# kubelet — агент на ноде, запускает поды и шлёт их статус в API.
# kube-proxy — сетевые правила для Service.
# container runtime — containerd / CRI-O запускает контейнеры.
Pod — манифест
Под — базовый объект. На практике поды редко создают напрямую (этим занимается Deployment), но важно понимать его структуру.
apiVersion: v1 # версия API для объекта
kind: Pod # тип объекта
metadata:
name: web # имя пода
labels:
app: web # метки — по ним объекты находят друг друга
spec:
containers: # список контейнеров пода
- name: nginx # имя контейнера внутри пода
image: nginx:1.27 # образ из реестра (Docker Hub по умолчанию)
ports:
- containerPort: 80 # порт, который слушает приложение
kubectl apply -f pod.yaml # создать под из файла
kubectl get pods # список подов
Основы kubectl
kubectl — CLI для общения с кластером через API-сервер.
kubectl get pods # список подов в текущем namespace
kubectl get pods -o wide # + ноды и IP
kubectl get all # поды, сервисы, деплойменты разом
kubectl describe pod <pod-name> # подробности и события объекта
kubectl logs <pod-name> # логи контейнера
kubectl logs -f <pod-name> # стримить логи (follow)
kubectl exec -it <pod-name> -- sh # шелл внутри контейнера
kubectl apply -f app.yaml # создать/обновить из манифеста
kubectl delete -f app.yaml # удалить описанное в файле
kubectl delete pod <pod-name> # удалить конкретный под
Deployment
Deployment управляет набором одинаковых подов: держит нужное число реплик и катит обновления без простоя.
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 3 # сколько копий пода держать
selector:
matchLabels:
app: web # какими подами управляет (по меткам)
template: # шаблон пода, который будет создаваться
metadata:
labels:
app: web # метки пода — ДОЛЖНЫ совпасть с selector
spec:
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80
strategy:
type: RollingUpdate # обновление по одному поду (без простоя)
rollingUpdate:
maxSurge: 1 # на сколько подов можно превысить во время катки
maxUnavailable: 0 # сколько подов можно "погасить" одновременно
kubectl apply -f deploy.yaml
kubectl rollout status deployment/web # дождаться завершения катки
kubectl set image deployment/web nginx=nginx:1.28 # обновить образ
kubectl rollout undo deployment/web # откатить на прошлую версию
Service
Поды эфемерны и их IP меняются. Service даёт стабильную точку доступа и балансирует трафик между подами по меткам.
apiVersion: v1
kind: Service
metadata:
name: web
spec:
type: ClusterIP # тип сервиса (см. ниже)
selector:
app: web # на какие поды слать трафик (по меткам)
ports:
- port: 80 # порт самого сервиса
targetPort: 80 # порт контейнера, куда проксировать
# Типы Service:
# ClusterIP — (по умолчанию) виден только внутри кластера.
# NodePort — открывает порт на каждой ноде (30000–32767).
# LoadBalancer — внешний балансировщик облака (внешний IP).
kubectl get svc # список сервисов
kubectl port-forward svc/web 8080:80 # пробросить порт локально
ConfigMap и Secret
Конфигурацию и образ держат раздельно: ConfigMap — для нечувствительных настроек, Secret — для паролей и токенов.
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
APP_ENV: production # обычные пары ключ-значение
LOG_LEVEL: info
---
apiVersion: v1
kind: Secret
metadata:
name: app-secret
type: Opaque
data:
# значения в base64 (это НЕ шифрование, просто кодировка!)
DB_PASSWORD: czNjcjN0 # echo -n 's3cr3t' | base64
# Прокидываем в контейнер как переменные окружения:
spec:
containers:
- name: app
image: myapp:1.0
envFrom:
- configMapRef:
name: app-config # все ключи ConfigMap → env
- secretRef:
name: app-secret # все ключи Secret → env
Namespace
Namespace делит один кластер на изолированные виртуальные пространства (например, dev / staging / prod).
kubectl get namespaces # список пространств
kubectl create namespace dev # создать
kubectl apply -f app.yaml -n dev # применить в namespace dev
kubectl get pods -n dev # смотреть поды в dev
kubectl config set-context --current --namespace=dev # дефолтный ns
Тома (Volumes и PVC)
Контейнер теряет файлы при перезапуске. Тома дают постоянное или общее хранилище.
# PersistentVolumeClaim — "заявка" на постоянный диск:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data-pvc
spec:
accessModes:
- ReadWriteOnce # монтируется на запись одной нодой
resources:
requests:
storage: 1Gi # запрашиваемый размер
---
# Подключаем PVC к поду:
spec:
containers:
- name: app
image: myapp:1.0
volumeMounts:
- name: data
mountPath: /var/lib/data # куда смонтировать внутри контейнера
volumes:
- name: data
persistentVolumeClaim:
claimName: data-pvc # ссылка на заявку выше
Ingress
Ingress маршрутизирует внешний HTTP/HTTPS-трафик на сервисы по хосту и пути (нужен установленный ingress-контроллер, например nginx).
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-ingress
spec:
rules:
- host: example.com # по какому домену принимать
http:
paths:
- path: / # путь URL
pathType: Prefix # совпадение по префиксу пути
backend:
service:
name: web # на какой Service слать
port:
number: 80 # порт сервиса
Ресурсы и лимиты
requests — сколько гарантированно нужно поду (по этому планировщик выбирает ноду). limits — потолок, который под не превысит.
spec:
containers:
- name: app
image: myapp:1.0
resources:
requests:
cpu: "250m" # 250 миллиядер = 0.25 CPU
memory: "128Mi" # гарантированная память
limits:
cpu: "500m" # максимум 0.5 CPU
memory: "256Mi" # при превышении памяти под убивают (OOMKilled)
Пробы (liveness и readiness)
liveness — жив ли контейнер (если нет — перезапуск). readiness — готов ли принимать трафик (если нет — убирается из балансировки Service).
spec:
containers:
- name: app
image: myapp:1.0
livenessProbe:
httpGet:
path: /healthz # эндпоинт проверки
port: 80
initialDelaySeconds: 10 # подождать перед первой проверкой
periodSeconds: 5 # как часто проверять
readinessProbe:
httpGet:
path: /ready
port: 80
periodSeconds: 5
Масштабирование
Меняем число реплик вручную или включаем автоскейлер по нагрузке.
# Ручное масштабирование:
kubectl scale deployment/web --replicas=5
# Горизонтальный автоскейлер (HPA): 2–10 реплик, цель 70% CPU.
# Требует установленного metrics-server в кластере.
kubectl autoscale deployment/web --min=2 --max=10 --cpu-percent=70
kubectl get hpa # текущее состояние автоскейлера
Типичный деплой приложения
Складываем кусочки вместе: ConfigMap + Deployment + Service в одном файле (разделитель ---).
apiVersion: v1
kind: ConfigMap
metadata:
name: api-config
data:
APP_ENV: production
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 3
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: myapp:1.0
ports:
- containerPort: 8080
envFrom:
- configMapRef:
name: api-config
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "300m"
memory: "256Mi"
readinessProbe:
httpGet:
path: /health
port: 8080
---
apiVersion: v1
kind: Service
metadata:
name: api
spec:
type: ClusterIP
selector:
app: api # должно совпасть с labels подов Deployment
ports:
- port: 80
targetPort: 8080
kubectl apply -f app.yaml # развернуть весь стек
kubectl get all -l app=api # проверить объекты по метке
kubectl rollout status deploy/api # дождаться готовности