Зачем Service
Pod в k8s, эфемерный. Killed/rescheduled, новый IP. Если другой
pod хочет с ним общаться, по IP, гонка. Service, это стабильный
VIP (ClusterIP) и DNS-имя (my-svc.my-ns.svc.cluster.local),
за которым стоит динамическая группа подов, выбранных по
label selector'у.
apiVersion: v1
kind: Service
metadata:
name: web
spec:
selector:
app: web # выбрать поды с этой label
ports:
- port: 80 # порт самого Service
targetPort: 8080 # порт в pod'е
type: ClusterIP # default
Под капотом control-plane (kube-controller-manager) поддерживает
Endpoints (или EndpointSlice с k8s 1.21+), список IP:port
всех живых подов, прошедших readinessProbe. Это «живой DNS» Service.
Типы Service
| Тип | Где доступен | Как работает |
|---|---|---|
| ClusterIP | внутри кластера | VIP в service-CIDR, не маршрутится наружу |
| NodePort | <node-IP>:<30000-32767> | open-port на каждой ноде, форвардит на ClusterIP |
| LoadBalancer | внешний IP облачного LB | NodePort + cloud-controller вешает LB перед нодами |
| ExternalName | DNS CNAME | Service резолвится в внешнее DNS-имя, без VIP |
Headless (clusterIP: None) | DNS A-records на pod IPs | DNS возвращает все pod IPs, балансирует клиент (StatefulSet, БД-кластеры) |
NodePort в проде используют редко, некрасивые порты, не HTTPS. Обычно либо LoadBalancer (в облаке), либо за Ingress (для HTTP).
Headless Service
spec:
clusterIP: None
selector: { app: cassandra }Полезен для stateful-кейсов где клиенту нужны конкретные pod'ы:
Cassandra, Kafka, etcd. DNS-запрос отдаёт A-records на каждый pod,
без балансировки на уровне kube-proxy. Часто пара с
[[kubernetes-pod-lifecycle|StatefulSet]] (даёт стабильные имена
cassandra-0.cassandra.default.svc).
kube-proxy, кто реализует Service
На каждой ноде живёт kube-proxy (DaemonSet или systemd-unit).
Его задача, обеспечить, чтобы пакет на ClusterIP/NodePort долетел
до одного из pod'ов из Endpoints. Делает это в data plane,
без user-space-проксирования.
Три режима:
| Режим | Что использует | Performance | Где default |
|---|---|---|---|
| iptables | DNAT-цепочки cmd-iptables | O(N) на правило | большинство кластеров |
| ipvs | LVS (Linux Virtual Server, kernel L4 LB) | O(1), hash-based | EKS большие кластеры, kops |
| nftables | cmd-nft (k8s 1.31+) | как iptables, новее | новый backend kube-proxy |
При iptables на каждый Service-port создаётся chain типа
KUBE-SVC-XXX, в нём random-DNAT (statistic mode random) на один
из endpoints (KUBE-SEP-YYY). На 5000+ Service'ов начинаются
тормоза, каждое iptables-update пересобирает всю таблицу.
ipvs масштабируется лучше: правила в hash-table, не в linear-chain.
Включается флагом kube-proxy --proxy-mode=ipvs. Требует kernel-модулей
ip_vs, ip_vs_rr, nf_conntrack.
Проверка режима:
kubectl logs -n kube-system kube-proxy-xxxx | grep "Using"
# или
kubectl get cm -n kube-system kube-proxy -o yaml | grep mode
ExternalTrafficPolicy
У NodePort/LoadBalancer есть важный параметр:
Cluster(default), пакет может пойти на pod на ЛЮБОЙ ноде, через SNAT. Source IP теряется, balancing equal.Local, только на pod'ы текущей ноды. Source IP сохраняется, но если на ноде нет pod'а, пакет дропается. Обычно используют с healthcheck облачного LB чтобы он перестал слать на пустые ноды.
Для серверов где нужен client IP (rate-limit, ban-list, geo)
всегда Local. И добавить podAntiAffinity или DaemonSet чтобы pod'ы
были на каждой ноде.
Ingress, L7-роутинг
Service работает на L4 (TCP/UDP). Для HTTP-приложений нужен reverse-proxy с роутингом по host/path, TLS-termination и т.д. Это Ingress.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
tls:
- hosts: [api.example.com]
secretName: api-tls # k8s Secret с cert+key
rules:
- host: api.example.com
http:
paths:
- path: /v1
pathType: Prefix
backend:
service:
name: api-v1
port: { number: 80 }- path: /v2
pathType: Prefix
backend:
service: { name: api-v2, port: { number: 80 } }Сам по себе Ingress, это просто декларация. Чтобы что-то заработало, нужен Ingress Controller, pod, который читает Ingress-объекты и конфигурирует реальный proxy.
Сравнение Ingress Controllers
| Controller | Под капотом | Сильные стороны | Слабые |
|---|---|---|---|
| ingress-nginx | nginx + Lua | де-факто стандарт, огромная база annotations | reload при каждом изменении, не для тысяч хостов |
| traefik | Go | автоматический Let's Encrypt, K8s CRDs (IngressRoute) | свой DSL, разная семантика с annotations |
| HAProxy ingress | HAProxy | максимальный throughput, runtime-API без reload | меньше комьюнити |
| Envoy-based (Contour, Emissary) | Envoy | xDS, HTTP/2/gRPC first-class | сложнее, больше ресурсов |
| cloud-native (AWS ALB, GKE GCLB) | external LB | интеграция с облаком | vendor lock-in |
Для большинства команд, ingress-nginx. Если нужен auto-TLS
без cert-manager, traefik. Для high-throughput API-gateway
Envoy-based (Contour/Gloo/Istio).
Gateway API, следующее поколение
В k8s 1.25+ стандартизирован Gateway API, наследник Ingress с разделением ответственности:
- GatewayClass (cluster-admin задаёт implementation: nginx/envoy/...)
- Gateway (infra-team: «вот listener на 443, TLS, ACL»)
- HTTPRoute / TCPRoute (app-team: «route /api на этот Service»)
Implementation: Istio, Contour, Cilium Service Mesh, kong. Ingress остаётся stable и поддерживается, но новые фичи (header modification, traffic split) идут в Gateway API.
Service Mesh, когда Ingress уже мало
Когда нужно между сервисами: mTLS, retry, circuit-break, observability, это уже не Service/Ingress, а service-mesh (Istio, Linkerd, Cilium). Sidecar Envoy/proxy в каждом pod'е плюс control-plane. Платишь complexity и CPU, получаешь zero-trust + tracing.
Когда что-то пошло не так
- Service есть, pod'ы есть, но трафика нет
kubectl get endpoints my-svc. Если пусто, selector не матчит (опечатка в label) или pod не прошёл readinessProbe. connection refusedизнутри кластера, pod слушает на127.0.0.1, не на0.0.0.0. Service шлёт на pod IP, не localhost.- NodePort недоступен снаружи, host firewall (firewalld, iptables INPUT chain) дропает 30000-32767. Нужно разрешить.
- Ingress 503 Service Temporarily Unavailable, Endpoints пусты
или backend Service неправильный (другой namespace, опечатка).
Лог nginx-controller в
kubectl logs -n ingress-nginx .... - TLS cert mismatch, secretName в Ingress есть, но Secret
другого типа (надо
kubernetes.io/tls) или хост в SAN не совпадает. - Source IP всегда node IP,
externalTrafficPolicy: Cluster, включиLocalили возьми X-Forwarded-For/PROXY-protocol на Ingress-уровне. - kube-proxy iptables CPU 100%, кластер 1000+ Service'ов,
переключай на
ipvs. - Кросс-namespace Service не резолвится, короткое имя
my-svcработает только в своём namespace; используй FQDNmy-svc.other-ns.svc.cluster.local.
Полезные диагностические команды
kubectl get svc -A, все Service в кластереkubectl describe svc my-svc, selector, endpoints, portskubectl get endpoints my-svc, реальные pod IPs за Servicekubectl get endpointslice -l kubernetes.io/service-name=my-svckubectl run -it --rm debug --image=nicolaka/netshoot --, pod с curl/dig/tcpdump для тестов изнутри кластера