Развертывание REST API в Kubernetes с Nginx: пошаговое руководство 2026 | AdminWiki

Развертывание REST API в Kubernetes с Nginx: пошаговое руководство 2026

25 августа 2026 7 мин. чтения

Введение: что мы развертываем и зачем

Задача: разместить REST API в кластере Kubernetes и принимать внешний трафик через Nginx. На выходе вы получите отказоустойчивый сервис с проверками жизнеспособности, маршрутизацией по доменному имени и обновлениями без простоя. Все конфигурации проверены на практике и адаптированы под типовые продакшн-сценарии 2026 года.

Мы пройдем полный цикл: упакуем приложение в Docker-образ, опубликуем его в реестре, создадим Deployment и Service, настроим healthcheck-и, подключим Nginx Ingress Controller и зададим стратегию Rolling Update. В конце разберем типичные ошибки, которые чаще всего мешают запуску.

Материал ориентирован на DevOps-инженеров и системных администраторов, которые уже знакомы с базовыми понятиями Kubernetes. Если вы настраивали управление трафиком в Kubernetes через Ingress и Egress, часть шагов покажется знакомой, но здесь мы сфокусируемся именно на связке REST API + Nginx.

Подготовка REST API к развертыванию: упаковка в Docker-образ

Kubernetes работает с контейнерами, поэтому первый шаг - собрать Docker-образ приложения. Без корректного образа дальнейшие шаги теряют смысл: Deployment не сможет запустить поды, а Ingress не получит работоспособный backend.

Создание Dockerfile для REST API

Возьмем типовой REST API на Node.js. Пример Dockerfile с пояснениями:

# Базовый образ с Node.js 18 на Alpine для минимального размера
FROM node:18-alpine

# Создаем рабочую директорию
WORKDIR /app

# Копируем файлы зависимостей отдельно для кеширования слоев
COPY package*.json ./
RUN npm ci --only=production

# Копируем исходный код
COPY . .

# Создаем не-root пользователя
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

# Открываем порт приложения
EXPOSE 3000

# Запускаем API
CMD ["node", "server.js"]

Ключевые моменты: базовый образ на Alpine уменьшает размер, раздельное копирование package.json и исходников ускоряет повторные сборки, не-root пользователь снижает риски безопасности. Порт 3000 - типовой для Node.js, но вы можете указать свой.

Сборка и проверка Docker-образа

Соберите образ и запустите его локально:

docker build -t my-rest-api:1.0.0 .
docker run -p 3000:3000 my-rest-api:1.0.0

Проверьте, что API отвечает на запросы:

curl http://localhost:3000/health

Если endpoint /health отсутствует, создайте его заранее - он понадобится для healthcheck-ов в Kubernetes. Локальная проверка экономит время: ошибки конфигурации проще отловить до загрузки в кластер.

Публикация Docker-образа в реестр

Kubernetes должен получить доступ к образу. Для этого загрузите его в контейнерный реестр: Docker Hub, GitLab Registry или частный Harbor. Пример для Docker Hub:

docker tag my-rest-api:1.0.0 youruser/my-rest-api:1.0.0
docker push youruser/my-rest-api:1.0.0

Для частного реестра создайте секрет в Kubernetes:

kubectl create secret docker-registry regcred \
  --docker-server=registry.example.com \
  --docker-username=youruser \
  --docker-password=yourpassword

Секрет указывается в манифесте Deployment через поле imagePullSecrets. Без этого поды не смогут скачать образ и останутся в статусе ImagePullBackOff.

Создание Deployment в Kubernetes

Deployment описывает желаемое состояние приложения: количество реплик, образ, порты, переменные окружения. Kubernetes поддерживает это состояние, перезапуская упавшие поды и управляя обновлениями.

Настройка ресурсов и переменных окружения

Задайте requests и limits для CPU и памяти. Это предотвращает деградацию соседних подов и помогает планировщику размещать нагрузку равномерно:

resources:
  requests:
    cpu: "100m"
    memory: "128Mi"
  limits:
    cpu: "500m"
    memory: "256Mi"

Переменные окружения передавайте через ConfigMap и Secrets. ConfigMap для несекретных значений:

apiVersion: v1
kind: ConfigMap
metadata:
  name: api-config
data:
  NODE_ENV: "production"
  LOG_LEVEL: "info"

Secret для паролей и ключей API:

apiVersion: v1
kind: Secret
metadata:
  name: api-secrets
type: Opaque
stringData:
  DATABASE_URL: "postgres://user:pass@db:5432/mydb"

Полный манифест Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: rest-api
  labels:
    app: rest-api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: rest-api
  template:
    metadata:
      labels:
        app: rest-api
    spec:
      imagePullSecrets:
        - name: regcred
      containers:
        - name: rest-api
          image: youruser/my-rest-api:1.0.0
          ports:
            - containerPort: 3000
          envFrom:
            - configMapRef:
                name: api-config
            - secretRef:
                name: api-secrets
          resources:
            requests:
              cpu: "100m"
              memory: "128Mi"
            limits:
              cpu: "500m"
              memory: "256Mi"

Labels и selectors должны совпадать: app: rest-api в metadata.labels, selector.matchLabels и template.metadata.labels. Расхождение приводит к ошибке «selector does not match template labels».

Настройка Healthcheck-ов для обеспечения надежности

Healthcheck-и сообщают Kubernetes, готов ли под принимать трафик и не завис ли процесс. Без них обновления и перезапуски работают вслепую: под может числиться Running, но отвечать ошибками.

Liveness, Readiness и Startup Probes: что выбрать?

Три типа проб решают разные задачи:

  • Readiness probe - проверяет готовность принимать трафик. Если проба не проходит, под исключается из Service и не получает запросы.
  • Liveness probe - проверяет, не завис ли процесс. При провале Kubernetes перезапускает контейнер.
  • Startup probe - для медленно стартующих приложений. Пока она не пройдет, liveness и readiness не выполняются.

Для REST API настройте HTTP-пробы на endpoint /health:

readinessProbe:
  httpGet:
    path: /health
    port: 3000
  initialDelaySeconds: 5
  periodSeconds: 10
  timeoutSeconds: 3
  failureThreshold: 3
livenessProbe:
  httpGet:
    path: /health
    port: 3000
  initialDelaySeconds: 15
  periodSeconds: 20
  timeoutSeconds: 3
  failureThreshold: 3
startupProbe:
  httpGet:
    path: /health
    port: 3000
  periodSeconds: 5
  failureThreshold: 30

Readiness срабатывает раньше liveness, чтобы под не получал трафик до полной готовности. Startup probe с большим failureThreshold дает приложению до 150 секунд на старт, что полезно для API с долгой инициализацией подключений к базам данных.

Обеспечение доступности: создание Service

IP-адреса подов меняются при каждом перезапуске. Service создает стабильную точку доступа внутри кластера. Для внутреннего доступа достаточно ClusterIP:

apiVersion: v1
kind: Service
metadata:
  name: rest-api
spec:
  selector:
    app: rest-api
  ports:
    - port: 80
      targetPort: 3000
      protocol: TCP
  type: ClusterIP

Service направляет трафик на поды с меткой app: rest-api. Порт 80 - внутренний порт Service, targetPort 3000 - порт контейнера. Ingress будет обращаться к Service по имени rest-api и порту 80.

Настройка Nginx как обратного прокси через Ingress

Ingress - это набор правил маршрутизации внешнего трафика к Service внутри кластера. Nginx Ingress Controller обрабатывает эти правила и работает как обратный прокси. Если вам нужна более широкая картина по Ingress и Egress в Kubernetes, изучите отдельное руководство.

Установка Nginx Ingress Controller

Самый быстрый способ - Helm:

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

Для установки требуются права администратора кластера. После установки проверьте, что контроллер получил внешний IP:

kubectl get svc -n ingress-nginx

В облачных кластерах IP назначается автоматически через LoadBalancer. В bare-metal окружении потребуется MetalLB или настройка NodePort.

Создание Ingress-ресурса для маршрутизации трафика

Пример Ingress для домена api.example.com:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: rest-api-ingress
  annotations:
    nginx.ingress.kubernetes.io/proxy-body-size: "10m"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
spec:
  ingressClassName: nginx
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: rest-api
                port:
                  number: 80

Аннотации proxy-body-size и proxy-read-timeout полезны для API, принимающих крупные запросы или обрабатывающих долгие операции. Без настройки body-size Nginx отклонит запросы больше 1 МБ с ошибкой 413.

Для HTTPS добавьте TLS. Автоматический выпуск сертификатов настраивается через cert-manager, детали описаны в материале про zero-downtime развертывание и балансировку. Минимальная ручная настройка:

spec:
  tls:
    - hosts:
        - api.example.com
      secretName: api-tls-secret

Секрет api-tls-secret должен содержать сертификат и ключ. Для тестовых сред подойдет самоподписанный сертификат, для продакшена - выпущенный через Let's Encrypt.

Обновление без простоя: стратегия Rolling Update

Rolling Update постепенно заменяет поды со старой версией на новые. Это позволяет обновлять приложение без остановки сервиса. Если вы сравниваете стратегии деплоя, обратите внимание на разбор мифов об обновлениях и отказоустойчивости.

Настройка параметров Rolling Update в Deployment

Добавьте в spec Deployment:

strategy:
  type: RollingUpdate
  rollingUpdate:
    maxSurge: 1
    maxUnavailable: 0

Значения означают: во время обновления можно создать на один под больше, чем задано в replicas, и нельзя допускать недоступных подов. При трех репликах это гарантирует, что минимум три пода всегда обрабатывают трафик. Такая конфигурация требует достаточных ресурсов в кластере, но обеспечивает нулевой простой.

Для корректной работы Rolling Update обязательны readiness probes. Kubernetes не отправит трафик на новый под, пока проба не вернет успех. Без readiness probe под считается готовым сразу после запуска, даже если приложение еще инициализируется.

Типичные ошибки и их решение

Ошибка: поды не становятся Ready

Симптом: под в статусе Running, но не Ready, трафик не поступает. Проверьте статус:

kubectl get pods
kubectl describe pod rest-api-xxxxx

Частая причина - неправильный путь или порт в readiness probe. Убедитесь, что endpoint /health существует и отвечает кодом 200. Проверьте логи контейнера:

kubectl logs rest-api-xxxxx

Если приложение стартует дольше, чем initialDelaySeconds, readiness probe начинает проверять его до готовности и под не включается в Service. Увеличьте initialDelaySeconds или настройте startup probe.

Ошибка: Ingress не маршрутизирует трафик

Симптом: запросы к домену возвращают 404 или connection refused. Проверьте Ingress:

kubectl describe ingress rest-api-ingress

Убедитесь, что Ingress Controller установлен и работает:

kubectl get pods -n ingress-nginx

Проверьте, что host в Ingress совпадает с DNS-записью. Если DNS не настроен, для теста используйте curl с заголовком Host:

curl -H "Host: api.example.com" http://INGRESS_IP/

Ошибка 503 от Nginx обычно означает, что backend Service недоступен: проверьте selector в Service и статус подов.

Заключение: итоги и дальнейшие шаги

Вы развернули REST API в Kubernetes: собрали Docker-образ, опубликовали его в реестре, создали Deployment с healthcheck-ами, настроили Service и Ingress с Nginx, задали Rolling Update для обновлений без простоя. Эта база достаточна для запуска в продакшн-среде.

Дальнейшие шаги: настройте горизонтальное автомасштабирование через HPA, подключите мониторинг с Prometheus и Grafana, интегрируйте сборку и деплой в CI/CD. Для углубления в темы кеширования API и балансировки изучите практические ответы по администрированию динамического контента. Если столкнулись с ошибками 401 или 403 при проксировании, поможет руководство по диагностике ошибок доступа.

Проверяйте конфигурации в тестовом кластере перед применением в продакшене. Это снижает риск простоя и ускоряет внедрение.

Поделиться:
Сохранить гайд? В закладки браузера