Ingress

Уровень: beginner ~50 мин Практика: Gitea (MIT)

Что нужно знать перед уроком

  • M1.03 — Service и порты — Ingress маршрутизирует трафик к Service, не напрямую к Pod'ам

Что нужно знать перед уроком

M1 урок 3 — Service даёт доступ внутри кластера. Этот урок — про то, как открыть приложение снаружи по человеческому доменному имени.

Теория

L4 vs L7: зачем Ingress, если есть Service

Service типа LoadBalancer уже даёт внешний доступ — но по IP-адресу и порту, без осведомлённости о содержимом HTTP-запроса (это уровень L4 модели OSI — транспортный, IP+порт). Ingress работает на уровне L7 (приложения, HTTP) — он смотрит на Host-заголовок и путь запроса и на основе этого решает, к какому Service направить трафик. Это позволяет: обслуживать несколько доменов на одном внешнем IP, терминировать TLS в одном месте, направлять разные пути одного домена к разным Service.

IngressClass — какой контроллер обрабатывает правило

Сам объект Ingress — только декларация правил маршрутизации; их реально применяет отдельный компонент — Ingress-контроллер (например, ingress-nginx в терминах модулей Deckhouse). Один кластер может иметь несколько контроллеров одновременно — spec.ingressClassName явно указывает, какой из них должен обработать конкретный Ingress. Это современный способ, заменивший deprecated аннотацию kubernetes.io/ingress.class.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: gitea
  annotations:
    nginx.ingress.kubernetes.io/proxy-body-size: "50m"
spec:
  ingressClassName: nginx
  rules:
    - host: gitea.course.local
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: gitea
                port:
                  number: 3000
  tls:
    - hosts: ["gitea.course.local"]
      secretName: gitea-tls

Три pathType

ТипСемантика
Exactточное совпадение пути символ-в-символ (с учётом регистра)
Prefixсовпадение по сегментам пути (/foo/bar совпадает с правилом /foo, а /foobarне совпадает)
ImplementationSpecificповедение полностью зависит от конкретного контроллера — может вести себя как Prefix, как regex или иначе
Документированная ловушка ImplementationSpecific + use-regex
У ingress-nginx есть аннотация nginx.ingress.kubernetes.io/use-regex: "true", включающая интерпретацию пути как regex для конкретного Ingress. Официально задокументированный побочный эффект: включение этой аннотации на одном Ingress может изменить сопоставление путей у всех Ingress того же host, включая формально не связанные с ней Exact-пути в других манифестах того же домена — потому что все правила одного host’а в конечном счёте компилируются контроллером в единый набор location-блоков nginx. Это одна из причин, по которой команда Deckhouse рекомендует переходить на Gateway API (следующий урок) там, где важна предсказуемая изоляция правил маршрутизации.

TLS-терминация

1
2
3
4
spec:
  tls:
    - hosts: ["gitea.course.local"]
      secretName: gitea-tls # Secret типа kubernetes.io/tls (см. M1 урок 4)

Контроллер сам терминирует TLS на этом Secret’е — Pod’ы приложения получают уже расшифрованный HTTP-трафик, если не настроено обратное (passthrough).

Частые ошибки и подводные камни

  • Указывать backend.service, ссылающийся на несуществующий Service/порт. Ingress создастся без ошибки на уровне API, но контроллер вернёт 502/503 — первое, что нужно проверить при «Ingress создан, но 502» — реально ли Service существует и в тех же именах/портах.
  • Полагаться на Prefix там, где нужен Exact, и наоборот. Prefix /foo совпадёт и с /foo, и с /foo/bar, но не с /foobar — важно проверять границы сегментов, а не буквальное совпадение символов.
  • Использовать ImplementationSpecific без понимания, как конкретный контроллер его интерпретирует — поведение не гарантировано между разными Ingress-контроллерами.

Практика в кластере

🧩 Практика урока: Gitea · лицензия MIT

Gitea — компактный self-hosted git-сервер на Go — классический пример приложения, которое реально нужно открыть снаружи по домену. В следующем уроке (Gateway API) мы переведём именно этот инстанс с Ingress на ListenerSet+HTTPRoute.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
kubectl create namespace course-m1-ingress
kubectl apply -n course-m1-ingress -f - <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata: { name: gitea }
spec:
  replicas: 1
  selector: { matchLabels: { app: gitea } }
  template:
    metadata: { labels: { app: gitea } }
    spec:
      containers:
        - name: gitea
          image: gitea/gitea:1.22
          ports: [{ containerPort: 3000 }]
          resources:
            requests: { cpu: "150m", memory: "256Mi" }
            limits: { cpu: "500m", memory: "512Mi" }
---
apiVersion: v1
kind: Service
metadata: { name: gitea }
spec:
  selector: { app: gitea }
  ports: [{ port: 3000, targetPort: 3000 }]
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: gitea
spec:
  ingressClassName: nginx
  rules:
    - host: gitea.course.local
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service: { name: gitea, port: { number: 3000 } }
EOF
  1. Проверьте, что Ingress создан и контроллер его подхватил:
1
2
kubectl get ingress -n course-m1-ingress
kubectl describe ingress gitea -n course-m1-ingress
  1. Добавьте запись в /etc/hosts (в реальном кластере — DNS) и проверьте доступ:
1
2
echo "127.0.0.1 gitea.course.local" | sudo tee -a /etc/hosts
curl -H "Host: gitea.course.local" http://localhost/
  1. Проверьте разницу Prefix vs Exact, добавив второе правило на путь /api:
1
2
3
4
5
kubectl patch ingress gitea -n course-m1-ingress --type=json -p '[
  {"op":"add","path":"/spec/rules/0/http/paths/-","value":
    {"path":"/api","pathType":"Exact","backend":{"service":{"name":"gitea","port":{"number":3000}}}}}
]'
kubectl describe ingress gitea -n course-m1-ingress

Практика разработки

Используется официальный образ gitea/gitea; собственная сборка — в M3.

Шпаргалка команд урока

1
2
3
kubectl get ingress [-n <ns>]
kubectl describe ingress <name> [-n <ns>]
kubectl get ingressclass

Вопросы для самопроверки

На каком уровне модели OSI работает Ingress, в отличие от Service?

Ingress анализирует HTTP-заголовки (Host, путь) — это L7, в отличие от Service, который работает на уровне IP+порт (L4).

Источник: Kubernetes — ingress

Путь /foo с pathType: Prefix — совпадёт ли с запросом /foobar?

Prefix требует совпадения по границам сегментов пути — /foo совпадёт с /foo/bar, но не с /foobar (нет разделителя сегмента).

Источник: Kubernetes — ingress

Какой известный побочный эффект имеет аннотация nginx.ingress.kubernetes.io/use-regex на одном Ingress?

Все правила одного host компилируются контроллером в общий набор location-блоков, поэтому включение regex-интерпретации на одном Ingress может затронуть весь host.

Источник: Kubernetes blog — ingress-nginx migration

Что заменила современная опция spec.ingressClassName?

ingressClassName — современный явный способ указать контроллер, обрабатывающий Ingress, заменивший устаревшую аннотацию.

Источник: Kubernetes — ingress

Ingress создан без ошибок, но браузер получает 502 Bad Gateway. Что стоит проверить первым?

502/503 при валидном Ingress почти всегда означает несовпадение backend.service с реально существующим Service/портом — Ingress создаётся успешно даже при ссылке на несуществующий backend.

Источник: Рекомендуемая литература

Рекомендуемая литература

Официальная документация

Статьи и блоги

Книги

  • Marko Lukša. Kubernetes in Action. 2nd ed., 2022. ISBN 978-1617297618.
  • Kelsey Hightower, Brendan Burns, Joe Beda. Kubernetes: Up and Running. 3rd ed., 2022. ISBN 978-1098110201.
  • Michael Hausenblas, Stefan Schimanski. Programming Kubernetes. 2019. ISBN 978-1492047107.

Связанные материалы

  • Предыдущий урок: M1.07 — Probes.
  • Следующий урок: M1.09 — Gateway API — переводим этот же инстанс Gitea на современную альтернативу.
  • Глоссарий: Ingress.