🏗️ Лекция: Kubernetes для начинающих

Pod, Deployment, Service и как это всё работает.

🚀 Цель лекции

Понять, что такое Kubernetes (K8s) и зачем он нужен. Мы разберем базовые "кубики" архитектуры: что такое `Pod`, как `Deployment` им управляет, и как `Service` и `Ingress` открывают к ним доступ. Это обзор архитектуры, а не глубокое погружение в команды.

1. Что такое Kubernetes?

Kubernetes (K8s) — это "оркестратор" для контейнеров.

Проблема: У вас есть 100 Docker-контейнеров. Как их запускать, обновлять, следить, чтобы они не упали, и соединять их друг с другом? Делать это вручную — кошмар.

Решение (K8s): Вы не говорите "запусти контейнер". Вы декларативно (в YAML-файле) описываете "желаемое состояние": "Я хочу, чтобы у меня всегда было 3 копии этого веб-сервера". K8s сам разбирается, как это обеспечить.

Аналогия: K8s — это дирижер оркестра. Он не играет на инструментах, но говорит, кому, когда и как громко играть, чтобы получилась музыка (ваше приложение).

Компонент Что это Роль
Control Plane (Мастер) "Мозг" кластера Принимает решения, хранит "желаемое состояние" (в `etcd`).
Nodes (Воркеры) "Рабочие" серверы Запускают ваши контейнеры (в `Pod`-ах) по команде Мастера.

2. Pod (Атом)

Pod (По-русски: "капсула", "стручок") — это самая маленькая единица, которую K8s может развернуть.

Это не контейнер. Это "домик" для одного или (редко) нескольких контейнеров.

  • В 99% случаев у вас будет 1 Pod = 1 контейнер (ваше приложение).
  • Контейнеры внутри одного Pod-а делят общий IP-адрес и могут общаться через `localhost`.
  • K8s управляет Pod-ами, а не отдельными контейнерами.

Важно: Pod-ы "смертны". Если Node падает, Pod на ней умирает. Если Pod умирает, K8s создает новый (с новым IP), а не воскрешает старый.

# Пример минимального Pod-а
apiVersion: v1
kind: Pod
metadata:
  name: my-nginx-pod
spec:
  containers:
  - name: my-nginx-container
    image: nginx:latest
    ports:
    - containerPort: 80

3. Deployment (Завод)

Проблема: Мы только что узнали, что Pod-ы умирают. Если запустить Pod (как в примере выше) и он упадет, он не перезапустится. Как обеспечить стабильную работу?

Решение: Deployment. Это объект, который управляет Pod-ами.

Вы говорите Deployment-у:

  • Какой Pod (шаблон) нужно запускать.
  • Сколько копий (реплик) нужно: `replicas: 3`.
  • Как проводить обновления (Rolling Update).

Аналогия: Deployment — это "начальник цеха". Он следит, чтобы 3 станка (Pod-а) всегда работали. Если один станок ломается, он его не чинит, а просто выбрасывает и заказывает новый.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx-deployment
spec:
  replicas: 3 # <-- Вот "желаемое состояние"
  selector:
    matchLabels:
      app: nginx
  template: # <-- А вот шаблон Pod-а, который нужно создавать
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80

4. Service (Внутренняя связь)

Проблема: У нас есть 3 Nginx Pod-а от Deployment-а. У каждого свой, постоянно меняющийся IP (т.к. Pod-ы умирают и создаются заново). Как другим Pod-ам (например, вашему Backend-у) найти эти Nginx Pod-ы?

Решение: Service.

Service — это стабильный внутренний IP-адрес и DNS-имя (`my-nginx-service`) для группы Pod-ов. Он работает как внутренний балансировщик нагрузки.

Аналогия: Service — это "внутренний номер отдела" в компании. Вам не нужно знать телефон каждого сотрудника (Pod-а) в отделе продаж. Вы звоните на общий номер (Service), и вас соединяют со свободным сотрудником.

apiVersion: v1
kind: Service
metadata:
  name: my-nginx-service
spec:
  type: ClusterIP # <-- Доступен только ВНУТРИ кластера
  selector:
    app: nginx # <-- "Искать все Pod-ы с этой меткой"
  ports:
  - protocol: TCP
    port: 80       # <-- На какой порт приходят запросы
    targetPort: 80 # <-- На какой порт Pod-а их отправлять

5. Ingress (Внешний доступ)

Проблема: `Service` (типа `ClusterIP`) дает доступ только *внутри* кластера. Как дать доступ пользователям из Интернета к нашему Nginx?

Решение: Ingress. Это "L7-маршрутизатор" (уровень HTTP). Он позволяет вам настроить правила, как внешний трафик должен попадать на ваши внутренние Service.

Он не работает сам по себе. Ему нужен Ingress Controller (например, `nginx-ingress`, `traefik`) — это, по сути, Pod с Nginx/Traefik, который смотрит на правила Ingress и применяет их.

Аналогия: Ingress — это "администратор" на входе в ваш бизнес-центр (кластер). Он спрашивает "Вы к кому?". "А, вы на `app.com/api`? Вам в `backend-service` (офис 101). А вы на `app.com`? Вам в `frontend-service` (офис 102)".

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app-ingress
spec:
  rules:
  - host: my-app.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: my-nginx-service # <-- Отправляем на наш Service
            port:
              number: 80

6. ConfigMap и Secret

Проблема: Приложение нужно как-то конфигурировать (адрес базы данных, ключи API, пароли). "Зашивать" это в Docker-образ — ужасная практика (12 factors app).

Решение: Использовать K8s для "внедрения" конфигурации в Pod во время его запуска.

Объект Назначение Пример
ConfigMap Для нечувствительных данных. `config.ini`, `APP_MODE=production`, URL-ы
Secret Для конфиденциальных данных. Пароль от БД, ключ API, TLS-сертификат

Оба объекта можно "примонтировать" в Pod либо как переменные окружения (environment variables), либо как файлы (например, в `/etc/config/`).

💡 Pro-фишка: В чем "секрет" Secret-а?

По умолчанию, данные в `Secret` просто кодируются в Base64. Это не шифрование, а просто способ представить бинарные данные в тексте. Любой, у кого есть доступ к K8s API, может их прочитать. Для настоящей безопасности нужно настраивать шифрование `etcd` (базы данных K8s) и жестко ограничивать права доступа через RBAC.

7. PersistentVolume (Память)

Проблема: Pod-ы "смертны". Когда Pod (например, с базой данных) умирает, все данные на его диске исчезают. Как сохранить состояние?

Решение: `PersistentVolume` (PV) и `PersistentVolumeClaim` (PVC).

Это работает в двух частях — как "спрос" и "предложение":

  • PersistentVolume (PV): "Предложение". Это сам "кусок" хранилища. Его настраивает Администратор кластера. (Например: "Вот 100GB на быстром SSD-диске в нашем NFS").
  • PersistentVolumeClaim (PVC): "Спрос". Это "заявка" от вашего приложения (Pod-а). (Например: "Мне нужно 10GB хранилища для моей базы данных").

K8s "связывает" (bind) вашу заявку (PVC) с подходящим куском хранилища (PV). Ваш Pod "видит" только заявку (PVC).

Аналогия: PV — это "склад" с жесткими дисками. PVC — это ваша "заявка на выдачу" диска. Pod — это "компьютер", в который вы этот диск вставляете. Когда компьютер (Pod) сгорает, вы берете новый, вставляете в него тот же диск (PVC) и продолжаете работу.

# Заявка (PVC), которую мы пишем в YAML-е нашего приложения:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-db-pvc
spec:
  accessModes:
    - ReadWriteOnce # <-- Диск можно подключить к 1 Pod-у на R/W
  resources:
    requests:
      storage: 10Gi # <-- "Прошу 10 Гигабайт"