Сеть в K8s
Презентация
Ссылка на презентацию
Последняя редакция: 18.09.26
Теория
Probes
Probes - механизмы, которыми Kubernetes отслеживает состояние пода изнутри, а не просто "жив процесс или нет"
- Liveness - когда перезапускать контейнер (завис, но процесс формально жив)
- Readiness - готово ли приложение принимать трафик прямо сейчас
- Startup - запустилось ли приложение вообще (для медленно стартующих сервисов)
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness-exec
spec:
containers:
- name: liveness
image: registry.k8s.io/busybox:1.27.2
args:
- /bin/sh
- -c
- touch /tmp/healthy; sleep 30; rm -f /tmp/healthy; sleep 600
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 5
periodSeconds: 5
readinessProbe и startupProbe устроены точно так же - те же exec/httpGet/tcpSocket и
те же поля таймингов (initialDelaySeconds, periodSeconds и так далее), отличается только имя
ключа верхнего уровня
readinessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
startupProbe:
httpGet:
path: /healthz
port: 8080
failureThreshold: 30
periodSeconds: 10
Пока startupProbe не пройдёт хотя бы один раз успешно, livenessProbe и readinessProbe
вообще не запускаются - это специально для медленно стартующих приложений, чтобы их не
перезапустили и не убрали из трафика ещё до того, как они успели подняться
Readiness - не просто диагностика, а прямое условие для сети: именно от неё зависит, попадёт
ли под в Endpoints Service'а (см. ниже) и получит ли он вообще трафик снаружи
Endpoints и EndpointSlice
Ingress и Gateway API (дальше в этой статье) в итоге вс егда указывают на Service, а не
напрямую на поды
Service сам по себе - только стабильный адрес (имя, порт, тип), реальный список живых подов за
ним хранится в отдельном объекте - Endpoints (или его более новой версии, EndpointSlice)
Как Service находит свой Endpoints - через одинаковое имя: kubectl get endpoints my-svc
всегда возвращает объект с тем же metadata.name, что и у Service my-svc
У EndpointSlice имя другое (слайсов на один Service может быть несколько), поэтому связь идёт
через лейбл kubernetes.io/service-name: my-svc
kube-proxy на каждой ноде следит за этими объектами и на их основе программирует реальные
правила перенаправления трафика
Зачем Endpoints вообще нужен как отдельный объект, если у Service уже есть selector - дело
в том, что selector у Service необязателен, Service можно создать вообще без него, и тогда
Kubernetes не будет сам вычислять его Endpoints
Типичный случай - Service указывает не на поды в кластере, а на внешний backend за его
пределами, например на базу данных
apiVersion: v1
kind: Service
metadata:
name: external-db
spec:
ports:
- port: 5432
---
apiVersion: v1
kind: Endpoints
metadata:
name: external-db
subsets:
- addresses:
- ip: 192.0.2.42
ports:
- port: 5432
Здесь Endpoints создаётся и поддерживается руками (или отдельным контроллером), а не
автоматически по selector - именно поэтому Endpoints не поле Service, а отдельный ресурс
Ещё один довод в пользу отдельного объекта - масштабирование: если пользоваться старым
Endpoints, все адреса живут в одном объекте, который целиком рассылается на каждую ноду при
любом изменении
EndpointSlice по умолчанию ограничивает один слайс 100 адресами (--max-endpoints-per-slice
на kube-controller-manager, можно поднять до 1000) и режет большой список на несколько слайсов -
при изменении одного пода kube-proxy на нодах получает маленький дифф, а не пересылку всего
списка целиком
Как это связано с Probes: если под не проходит readiness-проверку, control plane убирает
его из Endpoints/EndpointSlice - трафик через Service (а значит, и через Ingress/Gateway
API) на такой под просто не пойдёт, при этом сам под продолжает работать и не перезапускается
Именно так K8s не даёт "недоготовым" репликам приложения попадать под реальную нагрузку во
время деплоя или медленного старта
Ingress
Прямой доступ снаружи к каждому Service по отдельности неудобно масштабировать - под каждый
сервис пришлось бы городить свой NodePort или LoadBalancer
Ingress - единая точка входа для HTTP(S)-трафика снаружи, которая распределяет запросы по
разным Service внутри кластера по правилам маршрутизации
client -> Ingress-managed load balancer -> Ingress -> Service -> Pod
Правила маршрутизации бывают двух видов
- По пути (
path) -/fooуходит на один Service,/bar- на другой, на одном и том же хосте - По хосту (
host) -foo.bar.comиbar.foo.comуходят на разные Service, хотя оба приходят на один и тот же Ingress
Минимальный манифест - без fan-out и виртуальных хостов, весь трафик на один Service
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-ingress
spec:
ingressClassName: nginx
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx-service
port:
number: 80
ingressClassName - какой именно Ingress Controller должен разбирать этот манифест (см. ниже) -
без него, если в кластере несколько контроллеров одновременно, непонятно, кто из них должен
подхватить правило
Ещё одно полезное поле - defaultBackend: куда слать запрос, если ни одно правило из rules не
подошло (без него балансировщик на несовпавший путь просто ответит своим дефолтным 404)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: test-ingress
spec:
defaultBackend:
service:
name: test
port:
number: 80
Ingress также умеет терминировать TLS - подробнее про сертификаты для этого ниже, в разделе про HTTPS
Ingress Controller
Сам ресурс Ingress - это только манифест с правилами, кто-то должен их реально исполнять -
этим занимается Ingress Controller, отдельный компонент, который нужно установить отдельно
Реализаций несколько, и они не взаимозаменяемы один в один
nginx- самая распространённаяtraefikistio- облачные (AWS, GCE и другие) - управляются самим облаком
Важно про nginx: проект ingress-nginx объявлен retired - с марта 2026 у него больше нет
ни релизов, ни исправлений безопасности, хотя уже развёрнутые кластеры продолжают работать как
раньше
Сам ресурс Ingress от этого не deprecated и никуда не делся, deprecated только конкретно эта
реализация контроллера - официальная рекомендация - переходить на Gateway API (см. ниже), либо
использовать другой Ingress Controller (traefik, облачный и так далее)
На minikube - через встроенный addon (он ставит именно ingress-nginx из примечания выше)
minikube addons enable ingress
В YCloud Ingress Controller ставит ся отдельно, вместе с менеджером сертификатов Let's Encrypt® (см. "Дополнительно")
CRD (Custom Resources)
Ingress - штатный ресурс Kubernetes API, но большая часть более сложных вещей (включая
Gateway API дальше) реализована через CRD (Custom Resource Definition) - механизм, который
позволяет добавлять в API k8s свои собственные типы объектов
- Свои типы объектов, не встроенные в Kubernetes изначально
- Доступ к ним - через тот же
kubectl/API, что и к штатным ресурсам (Pod,Serviceи так далее) - Есть стандарт описания самого CRD и валидация манифестов - k8s отклонит объект, если он не соответствует схеме
Gateway API целиком построен на CRD - Gateway, HTTPRoute, GatewayClass не входят в
apps/v1/networking.k8s.io/v1, это отдельные CRD, которые ставятся в кластер до того, как
ими можно будет пользоваться
Gateway API
Gateway API - более новая замена Ingress, тоже отвечает за вход трафика в кластер, но
устроена иначе
- Разделена на несколько ролей вместо одного плоского манифеста -
GatewayClass(реализация, обычно её ставит облако/платформа),Gateway(точка входа - слушает конкретный порт/протокол),HTTPRoute(правила маршрутизации, привязанные к конкретномуGateway) - Разные команды могут владеть разными частями - платформенная команда управляет
Gateway, разработчики - своимиHTTPRoute, не трогая инфраструктурную часть - Поддерживает больше протоколов и более гибкие правила маршрутизации, чем классический
Ingress
Как и с Ingress, сам ресурс - это только манифест, кто-то должен реализовывать эти правила
Установка одних CRD Gateway API (kubectl apply --server-side -f .../standard-install.yaml)
трафик обрабатывать не заставит - нужен отдельно установленный Gateway API controller, точно
так же как Ingress Controller для Ingress
Готового addon для minikube, как у Ingress, здесь нет
Один из вариантов без Helm (в курсе Helm пока не разбирали)
kubectl apply -f https://projectcontour.io/quickstart/contour-gateway-provisioner.yaml
kubectl get pods -n projectcontour
Этот же манифест ставит и CRD, и сам контроллер (Contour), а связывает их GatewayClass
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: contour
spec:
controllerName: projectcontour.io/gateway-controller
GatewayClass в большинстве случаев уже создан за вас платформой (в YCloud - при установке
Gateway API для managed-кластера, см. "Дополнительно") - дальше нужны только Gateway и
HTTPRoute, которые ссылаются на него по имени через gatewayClassName
Gateway - точка входа, слушает конкретный порт и протокол
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: example-gateway
spec:
gatewayClassName: example-class
listeners:
- name: http
protocol: HTTP
port: 80
HTTPRoute - правила маршрутизации, привязанные к конкретному Gateway через parentRefsУмеет то же, что и path/host-based роутинг у Ingress выше - hostnames отвечает за хост,
matches внутри rules - за путь
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: example-httproute
spec:
parentRefs:
- name: example-gateway
hostnames:
- "www.example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /login
backendRefs:
- name: example-svc
port: 8080
Полный рабочий пример, эквивалентный тому же fan-out Ingress'у выше - ниже, в разделе "Примеры"
Доменные имена и DNS
DNS в контексте этой темы - не внутрикластерный DNS подов и Service, а обычный интернет-DNS, без которого к приложению нельзя будет зайти по человеческому имени, а не IP
- ICANN координирует TLD (Top-Level Domain -
.ru,.comи подобные) - Операторы реестров управляют конкретным TLD (для
.RU/.РФ- Координационный центр доменов, а также РКН как реестр доменов) - Регистраторы аккредитованы реестром и продают домены конечным пользователям - именно здесь домен покупают
Доменное имя само по себе бесполезно без сопоставления с IP-адресом - за это отвечают
DNS-серверы
Их адрес обычно не выбирают руками - он приходит от интернет-провайдера, зашит в роутер или на
устройстве по умолчанию, но его всегда можно переопределить явно (например, использовать
DNS-серверы Яндекса)
DNS-записи
Один домен может хранить в DNS не только IP - разные типы записей отвечают за разные вещи
- A - доменное имя -> IPv4-адрес
- AAAA - доменное имя -> IPv6-адрес
- CNAME - домен/поддомен -> другое доменное имя (алиас)
- MX - куда слать почту для этого домена
- TXT - произвольная текстовая информация (подтверждение владения доменом, SPF и так далее)
- NS - какие серверы отвечают за DNS этого домена
Локальный DNS и свой домен
Локально, ещё до всякого DNS-се рвера, есть файл /etc/hosts - он сопоставляет имя и IP только
для собственной машины, в обход настоящего DNS
127.0.0.1 {{your_domain}}
Удобно, чтобы проверить Ingress по имени хоста ещё до того, как домен настроен по-настоящему (см. "Практика")
Чтобы заходить в приложение с реального домена
- Купить домен у регистратора
- Прописать у регистратора адреса DNS-серверов Яндекса
- В Yandex DNS создать нужные записи (
Aна IP балансировщика,CNAMEдля поддоменов)
Шифрование и HTTPS
Чтобы понять, что такое SSL-сертификат и зачем он нужен Ingress, стоит начать с самого шифрования
Шифр - сообщение превращается в нечитаемый вид и обратно с помощью ключа и алгоритма
Простой пример - шифр Цезаря со сдвигом 5 (ROT5): hello world -> mjqqt btwqi
Проблема любого такого шифра одна - если один и тот же ключ нужен и чтобы зашифровать, и чтобы
расшифровать, что делать, если он попадёт не в те руки?
Отсюда - асимметричное шифрование
- Создаётся пара связанных ключей - публичный и приватный
- Публичным ключом данные шифруют, приватным - расшифровывают
- Публичный ключ можно раздавать всем, приватный не покидает сервер
Остаётся один вопрос - как убедиться, что публичный ключ, который вам прислали, действительно
принадлежит тому серверу, а не подделан по пути
Отвечает на него сертификат: авторитетная третья сторона ("арбитр", центр сертификации)
подписывает публичный ключ сервера, подтверждая, что он принадлежит именно ему
Для HTTPS-трафика такой подписанный сертификат обязателен - именно его получает и подключает
Ingress из TLS-примера выше
Let's Encrypt - один из таких арбитров, причём бесплатный, поэтому и упоминается в
инструкции по установке Ingress Controller в YCloud