LEARN X · ЗА 18 МИН

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 # дождаться готовности
Поддержать проект