Как опубликовать приложение (Services/Ingress)

Публикация приложений внутри кластера и наружу.

Публикация приложения в Kubernetes обычно строится как цепочка нагрузка → Service → Ingress. Нагрузка запускает поды приложения, Service даёт им стабильную точку доступа, а Ingress открывает HTTP/HTTPS-доступ извне кластера.

Если приложение ещё не развёрнуто, сначала создайте Deployment, StatefulSet или другой тип нагрузки по инструкции Как задеплоить приложение.

Как выбрать способ доступа

Service

Способ Когда использовать
ClusterIP Доступ нужен только внутри кластера. Это базовый вариант для связи приложений между собой.
NodePort Нужен внешний доступ через статический порт на каждом узле кластера. Подходит для тестовых и инфраструктурных сценариев.
LoadBalancer Нужен внешний IP через поддерживаемый провайдер инфраструктуры или балансировщик.
ExternalName Нужно создать DNS-алиас Service на внешнее имя, например foo.bar.example.com, без проксирования трафика.

Ingress

Способ Когда использовать
Ingress Нужна публикация HTTP/HTTPS по доменному имени, пути, TLS и правилам маршрутизации к Service.
Gateway API Нужна современная переносимая маршрутизация через Gateway API; разделение ролей между инфраструктурой и приложением. См. Маршруты Gateway API.

В модели Kubernetes каждый Pod получает собственный IP-адрес, но Pod может быть пересоздан. Service скрывает эту изменчивость за стабильным IP-адресом или DNS-именем, а Ingress маршрутизирует внешний HTTP/HTTPS-трафик к Service.

Что подготовить перед публикацией

Перед созданием Service и Ingress проверьте:

  1. Нагрузка приложения создана и поды переходят в состояние Running / Ready.
  2. У подов есть стабильные labels, по которым Service сможет выбрать backend-поды, например app: my-app.
  3. Известен порт контейнера, на котором приложение принимает трафик, например 8080.
  4. У пользователя есть права на создание Service, Ingress и при необходимости Secret в нужном неймспейсе.
  5. Для внешнего доступа настроен Ingress Controller и доступ к его IP-адресу, VIP, NodePort или балансировщику. Подробнее о потоке трафика см. Архитектурные схемы сетей кластера.
  6. Для доступа по доменному имени настроена DNS-запись. До настройки DNS можно проверить Ingress запросом с заголовком Host.
  7. Для HTTPS подготовлен TLS Secret типа kubernetes.io/tls с ключами tls.crt и tls.key.

Создавать Service и Ingress можно из раздела Сети в неймспейсе или с дашборда неймспейса, где отображается связь между нагрузками, сервисами и ингрессами. Подробнее см. Обзор дашборда неймспейса.

Шаг 1. Создайте Service

Service определяет логический набор подов и политику доступа к ним. Для приложения с labels app: my-app Service должен использовать такой же selector, иначе трафик не попадёт в нужные поды.

  1. Откройте кластер и выберите неймспейс, в котором развёрнуто приложение.
  2. Перейдите в СетиServices.
  3. Нажмите + Добавить Service.
  4. Заполните имя Service, например my-app-service.
  5. При необходимости добавьте лейблы и аннотации.
  6. В сведениях о развёртывании выберите тип Service: ClusterIP, NodePort, LoadBalancer или ExternalName.
  7. Оставьте параметр игнорирования статуса Pod в значении по умолчанию, если нет отдельного требования менять его.
  8. Добавьте порт и заполните:
    • Имя порта — например http;
    • AppProtocol — при необходимости;
    • NodePort — для Service типа NodePort, если требуется конкретный порт;
    • Port — порт Service, например 80;
    • TargetPort — порт контейнера, например 8080;
    • ProtocolTCP, UDP или SCTP.
  9. При необходимости задайте внешние IP-адреса.
  10. Добавьте labels селектора подов, например app: my-app.
  11. Нажмите Сохранить.
Скриншот: страница Service

Страница Service

После создания Service можно открыть из списка. На странице Service доступны просмотр и редактирование манифеста на вкладке Манифест, изменение типа Service, внешних IP-адресов, портов, селектора подов, лейблов и аннотаций.

Подготовьте манифест и загрузите его через Импорт манифестов или примените через kubectl.

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

Для внешнего доступа через Service измените spec.type на NodePort или LoadBalancer. Для DNS-алиаса на внешний ресурс используйте ExternalName.

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

Если Deployment уже создан и его поды имеют label app=my-app, Service можно создать командой:

kubectl expose deployment my-app \
  --name my-app-service \
  --port 80 \
  --target-port 8080 \
  -n my-namespace

Или примените YAML-файл:

kubectl apply -f service.yaml

Проверьте Service и backend-поды:

kubectl get svc my-app-service -n my-namespace
kubectl describe svc my-app-service -n my-namespace
kubectl get endpointslices -n my-namespace -l kubernetes.io/service-name=my-app-service

Шаг 2. Настройте TLS Secret

Для HTTPS в Ingress нужен Secret, содержащий закрытый ключ и сертификат TLS. В Secret должны быть ключи tls.crt и tls.key, а сертификат должен соответствовать доменному имени, по которому публикуется приложение.

Ingress использует TLS на порту 443 и завершает TLS-соединение на точке входа. Трафик от Ingress к Service и подам после этого передаётся как внутренний трафик кластера. Если в секции tls указано несколько хостов, они обрабатываются на этом же порту с помощью SNI. Хосты в tls.hosts должны явно соответствовать хостам в rules.

  1. В неймспейсе приложения перейдите в ХранилищеSecrets.
  2. Нажмите + Добавить Secret.
  3. Укажите имя, например my-app-tls.
  4. Выберите тип kubernetes.io/tls.
  5. Добавьте файл сертификата и файл закрытого ключа для публикуемого домена.
  6. Нажмите Сохранить.

Если сертификаты выпускаются через cert-manager, настройку ClusterIssuer или Issuer выполняют отдельно. Подробнее см. ClusterIssuer и Issuer.

apiVersion: v1
kind: Secret
metadata:
  name: my-app-tls
  namespace: my-namespace
type: kubernetes.io/tls
data:
  tls.crt: <base64-encoded-cert>
  tls.key: <base64-encoded-key>
kubectl create secret tls my-app-tls \
  --cert=./tls.crt \
  --key=./tls.key \
  -n my-namespace

Проверьте, что Secret создан в том же неймспейсе, где будет создан Ingress:

kubectl get secret my-app-tls -n my-namespace

Шаг 3. Создайте Ingress

Ingress управляет внешним доступом к сервисам в кластере. Он задаёт HTTP/HTTPS-маршруты извне кластера к Service внутри кластера, может использовать балансировку нагрузки, TLS и виртуальный хостинг на основе имени.

  1. Перейдите в СетиIngresses.
  2. Нажмите + Добавить Ingress.
  3. Укажите имя Ingress, например my-app-ingress.
  4. При необходимости добавьте лейблы и аннотации.
  5. В спецификации объекта заполните:
    • IngressClassName — выберите нужный IngressClass из списка или оставьте пустым, если класс указывать не требуется;
    • Ingress Service Backend — выберите Service my-app-service, имя порта http или номер порта 80.
  6. Добавьте правило:
    • Host — доменное имя, например my-app.example.com;
    • HTTPIngressPath — путь /;
    • Path typePrefix, Exact или ImplementationSpecific;
    • Ingress Backend Service — Service my-app-service и порт 80.
  7. Для HTTPS добавьте IngressTLS:
    • Secret name — выберите созданный TLS Secret, например my-app-tls;
    • Hosts — укажите один или несколько хостов. Значения должны соответствовать хостам в правилах Ingress.
  8. Нажмите Сохранить.
Скриншот: страница Ingress

Страница Ingress

На странице Ingress можно перейти к вкладке Манифест, изменить backend, правила и IngressTLS, а также обновить лейблы и аннотации.

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

Если в вашем кластере требуется конкретный IngressClass, добавьте поле spec.ingressClassName.

spec:
  ingressClassName: nginx

В платформе «Штурвал» в качестве Ingress Controller используется NGINX. Подробнее о базовой настройке Ingress см. в документации NGINX Ingress Controller.

Создайте объект, проставив свои значения параметров:

kubectl create ingress my-app-ingress \
  --namespace=my-namespace \
  --class=nginx \
  --rule="my-app.example.com/*=my-app-service:80,tls=my-app-tls"

Проверьте Ingress:

kubectl get ingress my-app-ingress -n my-namespace
kubectl describe ingress my-app-ingress -n my-namespace

Если DNS ещё не настроен, проверьте маршрут по IP-адресу Ingress Controller или балансировщика с заголовком Host:

curl -H "Host: my-app.example.com" http://<INGRESS_IP>/

Полный пример публикации приложения

В примере Deployment создаёт поды с label app: my-app, Service выбирает эти поды через selector, а Ingress направляет внешний трафик по хосту my-app.example.com на Service my-app-service.

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  namespace: my-namespace
spec:
  replicas: 2
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
        - name: app
          image: registry.example.com/my-app:v1.0.0
          ports:
            - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: my-app-service
  namespace: my-namespace
spec:
  type: ClusterIP
  selector:
    app: my-app
  ports:
    - name: http
      port: 80
      targetPort: 8080
      protocol: TCP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app-ingress
  namespace: my-namespace
spec:
  tls:
    - hosts:
        - my-app.example.com
      secretName: my-app-tls
  rules:
    - host: my-app.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: my-app-service
                port:
                  number: 80

Применение и базовая проверка:

kubectl apply -f publish-my-app.yaml

kubectl get deploy,po,svc,ingress -n my-namespace
kubectl describe svc my-app-service -n my-namespace
kubectl describe ingress my-app-ingress -n my-namespace

Проверка доступа внутри кластера:

kubectl run curl-test --rm -it \
  --image=curlimages/curl \
  --restart=Never \
  -n my-namespace \
  -- curl http://my-app-service.my-namespace.svc.cluster.local

Проверка внешнего доступа:

# До настройки DNS
curl -H "Host: my-app.example.com" http://<INGRESS_IP>/

# После настройки DNS и TLS
curl https://my-app.example.com

Проверка и диагностика

Service

Если приложение недоступно через Service, проверьте:

  1. Service создан в том же неймспейсе, где находятся поды приложения.
  2. spec.selector Service совпадает с labels подов.
  3. port Service и targetPort контейнера соответствуют реальным портам приложения.
  4. У Service появились EndpointSlice для backend-подов. Бэкенд под должны быть в состоянии Running и Ready=true, в противном случае эндпоинт не попадет в Service.
  5. Внутри кластера используется корректное DNS-имя: <service-name>.<namespace>.svc.cluster.local. В том же неймспейсе можно использовать короткое имя Service.
kubectl get svc -n my-namespace
kubectl describe svc my-app-service -n my-namespace
kubectl get pods -n my-namespace --show-labels
kubectl get endpointslices -n my-namespace -l kubernetes.io/service-name=my-app-service

Ingress

Если Ingress не открывает приложение, проверьте:

  1. Ingress Controller установлен и его поды работают.
  2. У Service есть backend-поды.
  3. host, path, pathType, backend Service и порт в Ingress указаны корректно.
  4. DNS-запись указывает на IP-адрес, VIP, NodePort или балансировщик Ingress Controller.
  5. Для HTTPS Secret существует в том же неймспейсе, содержит tls.crt и tls.key, а сертификат выпущен на нужный FQDN.
  6. В событиях Ingress и подов нет ошибок.
kubectl get ingress -n my-namespace
kubectl describe ingress my-app-ingress -n my-namespace
kubectl get events -n my-namespace --sort-by='.lastTimestamp'

Подробные команды для проверки Ingress Controller, Service и логов контроллера см. в разделе Траблшутинг сетей. Частые проблемы приложений описаны в Траблшутинге нагрузок.

Сетевые политики

Если в кластере включены сетевые политики, они могут блокировать трафик от Ingress Controller к backend-подам. Разрешите входящий трафик от Ingress Controller к приложению. Пример политики см. в разделе Разрешить входящий трафик от Ingress Controller.

Рекомендации

  • Для внутренних зависимостей между приложениями используйте ClusterIP и DNS-имя Service.
  • Для production-публикации HTTP/HTTPS-приложений используйте Ingress с доменным именем и TLS.
  • LoadBalancer используйте, когда нужен прямой внешний IP без HTTP/HTTPS-маршрутизации на уровне Ingress.
  • NodePort применяйте для тестирования или специальных инфраструктурных сценариев.
  • Не храните приватные ключи и сертификаты в открытых манифестах. Используйте Kubernetes Secrets.

Дополнительные материалы