Kubernetes

Теория: Сервис и доступ к приложению

В Kubernetes приложения запускаются в Pod, но Pod — временные сущности. Они создаются, умирают, получают новые IP-адреса при перезапуске. Возникает вопрос: как одному приложению найти другое? Как frontend узнает, куда отправлять запросы к backend, если IP backend-Pod меняется?

Для этого в Kubernetes существует Service — объект, который создаёт стабильную точку входа для доступа к группе Pod.

Проблема: как найти Pod

Представьте: frontend должен отправлять запросы на backend. Backend — это Deployment с тремя репликами. Каждый Pod имеет свой IP: 10.244.1.5, 10.244.2.8, 10.244.1.12.

Проблемы начинаются сразу:

  • Pod перезапустился — получил новый IP
  • Нода упала — Pod пересоздался на другой ноде с другим IP
  • Масштабировали Deployment — появились новые Pod с новыми IP

Frontend не может хардкодить эти адреса. Ему нужна стабильная точка входа, которая всегда работает, независимо от того, что происходит с Pod.

Уточнение: для StatefulSet ситуация другая — там Pod получают стабильные сетевые идентификаторы. Но для обычных Deployment проблема динамических IP актуальна.

Service: стабильная точка входа

Service — это объект Kubernetes, который создаёт постоянный IP-адрес и DNS-имя для группы Pod. Вы обращаетесь к Service, а он распределяет запросы между Pod, которые подходят под его селектор.

apiVersion: v1
kind: Service
metadata:
  name: backend-service
spec:
  selector:
    app: backend
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
  type: ClusterIP

Разберём:

  • selector: app: backend — Service направляет трафик на Pod с этой меткой. Тот же механизм, что в Deployment.
  • port: 80 — порт, на котором слушает сам Service.
  • targetPort: 8080 — порт контейнера, на который Service отправляет трафик.
  • type: ClusterIP — тип Service, об этом ниже.

Теперь frontend обращается к backend-service:80, а Service сам находит живые Pod и балансирует между ними.

Типы Service

Это частый вопрос на собеседованиях: какие типы Service бывают и когда какой использовать.

ClusterIP — тип по умолчанию

Service получает внутренний IP, доступный только внутри кластера.

apiVersion: v1
kind: Service
metadata:
  name: backend-service
spec:
  selector:
    app: backend
  ports:
    - protocol: TCP
      port: 8080
      targetPort: 8080
  type: ClusterIP

Когда использовать: коммуникация между микросервисами внутри кластера. Frontend обращается к backend, backend — к базе данных. Всё внутри, ничего наружу.

NodePort — доступ через порт на нодах

Kubernetes открывает указанный порт на всех нодах кластера.

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

Запрос на <любая-нода>:30080 попадает в Service, оттуда — на Pod. Если nodePort не указать, Kubernetes выберет случайный из диапазона 30000-32767.

Когда использовать: тестирование, разработка, простые случаи без Ingress.

Минусы: нестандартные порты (30000+), нужно знать IP нод, порты могут конфликтовать между сервисами.

LoadBalancer — внешний балансировщик

apiVersion: v1
kind: Service
metadata:
  name: web-service
spec:
  selector:
    app: web
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
type: LoadBalancer

Как это работает под капотом:

  1. Kubernetes создаёт ClusterIP для Service
  2. Kubernetes создаёт NodePort (автоматически)
  3. Контроллер (облачный провайдер или MetalLB на bare-metal) создаёт внешний балансировщик
  4. Балансировщик получает внешний IP
  5. Трафик идёт: внешний IP → NodePort на нодах → Pod
kubectl get service web-service

Поле EXTERNAL-IP покажет публичный адрес. Пока балансировщик создаётся — <pending>.

Важно про стоимость: в облаках каждый LoadBalancer — отдельный ресурс, за который вы платите. AWS ELB, GCP Load Balancer, Azure LB — всё стоит денег.

Правильный паттерн: один LoadBalancer на Ingress Controller, который маршрутизирует трафик на все внутренние ClusterIP Service.

Когда LoadBalancer всё же нужен напрямую: не-HTTP протоколы (TCP/UDP-сервисы, базы данных), когда Ingress не подходит.

ExternalName — DNS-алиас на внешний сервис

apiVersion: v1
kind: Service
metadata:
  name: external-db
spec:
  type: ExternalName
  externalName: db.example.com

Это не балансировщик, а CNAME-запись. Обращение к external-db внутри кластера вернёт db.example.com. Полезно для интеграции внешних сервисов без изменения кода приложения.

DNS внутри кластера

Kubernetes автоматически создаёт DNS-записи для каждого Service. Это делает CoreDNS — компонент, который работает в каждом кластере.

Полное DNS-имя Service:

<service-name>.<namespace>.svc.cluster.local

Если Pod и Service в одном namespace, достаточно короткого имени:

# Полное имя
curl http://backend-service.default.svc.cluster.local:8080

# Короткое имя (в том же namespace)
curl http://backend-service:8080

Kubernetes автоматически настраивает /etc/resolv.conf в каждом Pod, чтобы короткие имена резолвились правильно.

Endpoints: как Service находит Pod

Когда вы создаёте Service, Kubernetes автоматически создаёт объект Endpoints — список IP-адресов Pod, которые соответствуют селектору.

kubectl get endpoints backend-service

Service использует этот список для распределения трафика между Pod. Как именно распределяются запросы — зависит от сетевого плагина (CNI), режима kube-proxy и поведения клиента. На практике распределение может быть неравномерным: долгоживущие соединения, keep-alive, особенности iptables/IPVS — всё это влияет на то, какой Pod получит следующий запрос.

Не ожидайте идеально равномерного распределения «по очереди». Если видите, что один Pod получает больше запросов, чем другие — это нормальное поведение, а не поломка Kubernetes.

Важно: если Pod не проходит readinessProbe, его IP убирается из Endpoints. Трафик на неготовый Pod не идёт. Когда Pod снова готов — IP возвращается в список.

Headless Service

Иногда балансировка не нужна, и вы хотите получить IP всех Pod напрямую. Для этого — headless Service:

apiVersion: v1
kind: Service
metadata:
  name: database-headless
spec:
  clusterIP: None
  selector:
    app: database
  ports:
    - port: 5432
      targetPort: 5432

clusterIP: None — ключевое. Service не получает IP. DNS-запрос возвращает IP-адреса всех Pod:

# Вернёт несколько A-записей — по одной на каждый Pod
nslookup database-headless

Когда использовать: StatefulSet с базами данных, где нужен доступ к конкретному инстансу, service discovery без балансировки.

Ingress: HTTP-маршрутизация

NodePort неудобен — нестандартные порты. LoadBalancer дорог — платить за каждый сервис. Ingress решает обе проблемы для HTTP/HTTPS трафика.

Ingress — это правила маршрутизации. Один Ingress может направлять запросы на разные Service в зависимости от хоста или пути:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
spec:
  rules:
    - host: myapp.example.com
      http:
        paths:
          - path: /api
            pathType: Prefix
            backend:
              service:
                name: backend-service
                port:
                  number: 8080
          - path: /
            pathType: Prefix
            backend:
              service:
                name: frontend-service
                port:
                  number: 80

Запросы на myapp.example.com/api/* идут в backend-service, остальные — во frontend-service.

Важно: Ingress сам по себе ничего не делает. Нужен Ingress Controller — компонент, который читает правила и настраивает реальную маршрутизацию.

Ingress Controller: какой выбрать

Это частый вопрос на собеседованиях.

NGINX Ingress Controller — самый популярный. Но есть важный нюанс: существует две разные версии.

kubernetes/ingress-nginx (Community version)

  • Поддерживается сообществом Kubernetes
  • Репозиторий: https://github.com/kubernetes/ingress-nginx
  • Helm chart: ingress-nginx/ingress-nginx
  • Это стандартный выбор для большинства случаев
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm install ingress-nginx ingress-nginx/ingress-nginx

nginxinc/kubernetes-ingress (Nginx Inc version)

  • Поддерживается компанией Nginx Inc (F5)
  • Репозиторий: https://github.com/nginxinc/kubernetes-ingress
  • Есть бесплатная версия и платная (NGINX Plus)
  • Платная версия: JWT-валидация, WAF, продвинутый мониторинг, active health checks

На собеседованиях спрашивают разницу. Ключевое: community-версия от Kubernetes — бесплатная и достаточная для большинства случаев. Версия от Nginx Inc — если нужны enterprise-фичи и готовы платить.

Другие контроллеры:

  • Traefik — написан на Go, автоматическое обнаружение сервисов, встроенная интеграция с Let's Encrypt. Популярен в небольших кластерах и для автоматического получения сертификатов.
  • HAProxy Ingress — если нужны специфические возможности HAProxy или уже есть экспертиза в нём.

Облачные контроллеры:

  • AWS: AWS Load Balancer Controller (создаёт ALB/NLB)
  • GCP: GCE Ingress Controller
  • Azure: Application Gateway Ingress Controller

Они используют нативные балансировщики облака, что может быть эффективнее и дешевле в конкретном облаке.

Для обучения и большинства production-случаев: kubernetes/ingress-nginx.

Установка Ingress Controller

В minikube (самый простой способ для практики):

minikube addons enable ingress

Через Helm:

helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm install ingress-nginx ingress-nginx/ingress-nginx

Проверка:

kubectl get pods -n ingress-nginx
kubectl get service -n ingress-nginx

TLS/HTTPS в Ingress

Ingress поддерживает TLS-терминацию — принимает HTTPS, расшифровывает, отправляет HTTP на backend:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress-tls
spec:
  tls:
    - hosts:
        - myapp.example.com
      secretName: tls-secret
  rules:
    - host: myapp.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: frontend-service
                port:
                  number: 80

Сертификат хранится в Secret:

kubectl create secret tls tls-secret --cert=cert.pem --key=key.pem

Рекомендуемые программы

+7 800 100 22 47

бесплатно по РФ

+7 495 085 21 62

бесплатно по Москве

108813 г. Москва, вн.тер.г. поселение Московский,
г. Московский, ул. Солнечная, д. 3А, стр. 1, помещ. 20Б/3
ОГРН 1217300010476
ИНН 7325174845