Четыре типа Service в Kubernetes решают одну задачу разными способами: ClusterIP даёт стабильный адрес внутри кластера, NodePort открывает порт на каждом узле, LoadBalancer запрашивает внешний IP у инфраструктуры, ExternalName подставляет DNS-имя внешнего ресурса. Выбор между ними определяется одним вопросом: откуда должен приходить трафик.
Короткий ответ: трафик только внутри кластера — ClusterIP; доступ извне без облачного балансировщика — NodePort; доступ извне в облаке или с MetalLB — LoadBalancer; ссылка на внешнюю базу или API по DNS — ExternalName. Ниже разбираем каждый тип, приводим рабочие манифесты, команды проверки и сводную таблицу.
Что такое Service в Kubernetes и зачем он нужен
Pod не переживает пересоздание. При обновлении Deployment, падении контейнера или переезде на другой узел Pod получает новый IP. Клиент, который прописал конкретный адрес в конфиге, после первого же рестарта теряет связь. Service убирает зависимость от отдельного Pod'а: группа Pod'ов получает один виртуальный IP и DNS-имя, которые не меняются при пересоздании.
Service выбирает Pod'ы по селектору меток (spec.selector). Kubernetes следит за подходящими Pod'ами и хранит их адреса в объекте EndpointSlice. Балансировка идёт на уровне L4: Service распределяет TCP и UDP-соединения, а не HTTP-запросы. Заголовки, cookie и путь URL он не разбирает. Для маршрутизации HTTP/HTTPS по хостам и путям нужен Ingress или Gateway API — разбор уровней L4 и L7.
DNS-имя строится по шаблону my-svc.my-namespace.svc.cluster.local. Внутри того же namespace достаточно короткого имени my-svc. CoreDNS разрешает его в виртуальный IP Service.
Как kube-proxy обеспечивает маршрутизацию
kube-proxy работает на каждом узле и подписан на изменения Service и EndpointSlice. При обращении на виртуальный IP он подменяет адрес назначения на IP конкретного Pod'а. Режимов два: iptables (правила DNAT через Netfilter) и IPVS (хеш-таблица в ядре, устойчивее к десяткам тысяч сервисов). Режим задаётся при запуске kube-proxy.
Поведение внутреннего трафика настраивает поле InternalTrafficPolicy. При значении Local kube-proxy использует только эндпоинты, расположенные на том же узле, откуда идёт трафик. Внутренним считается трафик от Pod'ов текущего кластера. Если на узле нет ни одного эндпоинта нужного Service, Pod'ы этого узла видят сервис без эндпоинтов, даже когда эндпоинты есть на других узлах. Значение по умолчанию учитывает все эндпоинты кластера — политика внутреннего трафика Service.
Смысл Local в том, чтобы не гонять пакеты через сеть кластера в обе стороны. Это снижает задержку, повышает пропускную способность и уменьшает расходы на межзональный трафик.
ClusterIP: внутренний доступ по умолчанию
ClusterIP — тип по умолчанию. Если в манифесте не указать spec.type, Kubernetes создаст Service именно с ним. Адрес выделяется из диапазона service-cidr, который задают при инициализации кластера, например kubeadm init --service-cidr 10.96.0.0/12. Такой Service доступен только изнутри, снаружи к нему не подключиться — настройка kubelet через kubeadm.
Пример манифеста ClusterIP
apiVersion: v1
kind: Service
metadata:
name: my-clusterip
namespace: default
spec:
type: ClusterIP
selector:
app: web
ports:
- name: http
port: 80
targetPort: 8080
protocol: TCP
Поле port — порт, который слушает сам Service. targetPort — порт контейнера, куда уходит трафик. Если targetPort не указан, берётся значение port. Применяем и проверяем:
kubectl apply -f my-clusterip.yaml kubectl get svc my-clusterip kubectl describe svc my-clusterip
Проверка трафика изнутри кластера: запустите временный Pod и обратитесь по DNS-имени.
kubectl run nettest --image=busybox:1.36 --restart=Never --rm -it -- wget -qO- http://my-clusterip.default.svc.cluster.local
Headless-режим включается полем clusterIP: None. Виртуального IP тогда нет, DNS возвращает адреса всех Pod'ов, и клиент выбирает адресата сам. Так подключают StatefulSet с базами данных, где клиенту нужен конкретный экземпляр.
Сценарии: обмен данными между микросервисами, доступ приложения к PostgreSQL и Redis, внутренние API и сбор метрик.
NodePort: доступ снаружи через порт на каждом узле
NodePort открывает один и тот же порт на всех узлах кластера. Запрос на NODE_IP:nodePort попадает в kube-proxy, тот перенаправляет его на Pod. Порт выбирается из диапазона 30000-32767, что снижает вероятность конфликтов с зарезервированными портами и другими портами на узле. Границы диапазона меняются в конфигурации kube-apiserver; проверить действующее значение можно командой kubectl cluster-info dump -n kube-system | grep service-node-port-range — диапазон портов NodePort.
NodePort строится поверх ClusterIP: Service получает и внутренний виртуальный адрес, и внешний порт. Когда нужен внешний доступ без балансировщика и без публикации порта на каждом узле, используют externalIPs — настройка внешнего доступа через externalIPs.
Пример манифеста NodePort
apiVersion: v1
kind: Service
metadata:
name: my-nodeport
spec:
type: NodePort
selector:
app: web
ports:
- name: http
port: 80
targetPort: 8080
nodePort: 30080
kubectl apply -f my-nodeport.yaml kubectl get svc my-nodeport -o wide curl -I http://NODE_IP:30080
Если nodePort не задавать, Kubernetes выделит свободный порт сам. Фиксированное значение удобно для правил файрвола и записей DNS, но следите за конфликтами: один порт нельзя занять дважды в пределах кластера.
Ограничения: балансировка идёт по соединениям, без учёта HTTP-логики; номер порта приходится помнить пользователям; порт слушает каждый узел, что расширяет поверхность атаки; привязка к IP узлов усложняет работу при автомасштабировании.
Сценарии: тестовые стенды, on-premise без внешнего балансировщика, отладка и временная публикация сервиса.
LoadBalancer: автоматический внешний балансировщик
LoadBalancer запрашивает у инфраструктуры внешний балансировщик и получает внешний IP. В облаке это делают встроенные контроллеры AWS, GCP и Azure. В своём дата-центре нужен MetalLB или аналог: без контроллера Service останется со статусом EXTERNAL-IP: pending — диагностика External IP Pending.
Тип LoadBalancer включает NodePort и ClusterIP. Трафик приходит на балансировщик, тот передаёт его на порты узлов, дальше работает kube-proxy.
Пример манифеста LoadBalancer
apiVersion: v1
kind: Service
metadata:
name: my-lb
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: nlb
spec:
type: LoadBalancer
selector:
app: web
ports:
- name: http
port: 80
targetPort: 8080
kubectl apply -f my-lb.yaml kubectl get svc my-lb kubectl describe svc my-lb
Аннотации зависят от провайдера: они задают тип балансировщика, схему internal или external, таймауты, TLS-сертификаты. Сверяйтесь с документацией вашего cloud-controller-manager, универсального набора аннотаций нет.
Сценарии: продакшн-сервисы для интернета, публичные API, входная точка для Ingress-контроллера. Для маршрутизации по путям и хостам обычно ставят один LoadBalancer перед Ingress-контроллером — настройка Service и Ingress.
ExternalName: DNS-псевдоним для внешних сервисов
ExternalName не создаёт прокси, не выделяет IP и не балансирует трафик. Он возвращает CNAME-запись на внешнее DNS-имя. Приложение обращается к внутреннему имени, а DNS ведёт его наружу — описание поля externalName в API Service.
Пример манифеста ExternalName
apiVersion: v1 kind: Service metadata: name: my-db namespace: default spec: type: ExternalName externalName: my.database.example.com
kubectl apply -f my-externalname.yaml kubectl get svc my-db nslookup my-db.default.svc.cluster.local
Ограничения: нет селектора, нет портов, нет проверки доступности. Если DNS-имя не разрешается, соединения падают, а kubectl get endpoints покажет пустой список. Скорость смены адреса зависит от TTL в ответе CoreDNS.
Сценарии: доступ к управляемой внешней БД (RDS, Managed PostgreSQL), абстракция на время миграции. Меняете externalName, когда база переезжает, код приложения остаётся прежним.
Сравнительная таблица типов Service
| Тип | Доступность извне | Требования к инфраструктуре | Сложность настройки | Типичный сценарий |
|---|---|---|---|---|
| ClusterIP | Только внутри кластера | Нет | Низкая | Микросервисы, базы данных, кэши |
| NodePort | IP узла и порт 30000-32767 | Нет | Средняя | Тестовые стенды, on-premise |
| LoadBalancer | Внешний IP от балансировщика | Облачный провайдер или MetalLB | Высокая | Продакшн для интернета |
| ExternalName | CNAME на внешний DNS | Нет | Низкая | Внешняя БД, миграция |
Отдельно настраивается политика трафика. Поле InternalTrafficPolicy со значением Local ограничивает внутренний трафик эндпоинтами своего узла, а ExternalTrafficPolicy управляет тем, как распределяется трафик снаружи для NodePort и LoadBalancer. С политикой внутреннего трафика связаны Topology Aware Routing и политика внешнего трафика Service — политика внутреннего трафика Service.
Как выбрать тип Service под задачу
Алгоритм из четырёх шагов:
- Трафик нужен только внутри кластера? Берите ClusterIP.
- Трафик идёт извне, и есть облачный провайдер или MetalLB? Берите LoadBalancer.
- Трафик идёт извне, внешнего балансировщика нет? Берите NodePort.
- Нужно сослаться на внешний сервис по имени? Берите ExternalName.
Примеры подстановки: микросервисы внутри кластера — ClusterIP; веб-приложение в облаке — LoadBalancer; тестовый стенд на своём железе — NodePort; внешняя управляемая БД — ExternalName. Для продакшна с HTTP-маршрутизацией комбинируйте один LoadBalancer с Ingress-контроллером и набором ClusterIP-сервисов за ним.
Оптимизация трафика добавляется поверх выбранного типа: LoadBalancer с InternalTrafficPolicy: Local не гоняет внутренние запросы между зонами, а Topology Aware Routing держит соединения внутри одной зоны. Разбор критериев выбора с примерами собран в отдельном материале — как выбрать тип Service в Kubernetes.
Типичные ошибки при настройке Service
- Селектор не совпадает с метками Pod'ов. Service создан, но EndpointSlice пустой, и трафика нет.
- targetPort указывает на порт, который контейнер не слушает, либо приложение слушает только 127.0.0.1 вместо 0.0.0.0.
- Конфликт nodePort: значение уже занято другим Service в кластере.
- LoadBalancer без cloud-controller-manager или MetalLB: EXTERNAL-IP остаётся в статусе pending — как исправить External IP Pending.
- ExternalName без рабочего DNS за пределами кластера: соединения падают без внятного сообщения.
- InternalTrafficPolicy: Local на узле без эндпоинтов: Pod'ы этого узла не получают трафик, даже когда эндпоинты есть на других узлах — политика внутреннего трафика Service.
Диагностика по шагам, от Service к Pod'у:
kubectl get svc my-svc -o wide kubectl describe svc my-svc kubectl get endpoints my-svc kubectl get endpointslices -l kubernetes.io/service-name=my-svc kubectl logs -n kube-system -l k8s-app=kube-proxy --tail=100
Пустой список эндпоинтов указывает на селектор или на неготовность Pod'ов. Непустой список при недоступном сервисе указывает на targetPort, NetworkPolicy или на сам процесс в контейнере.
Актуальность в 2026 году: что изменилось
Dockershim удалён из проекта Kubernetes начиная с релиза 1.24, поэтому в кластерах используется containerd или CRI-O. Старые инструкции с настройкой Docker как рантайма kubelet не работают — настройка kubelet через kubeadm.
kubelet не определяет автоматически cgroup-драйвер, который использует контейнерный рантайм. Драйвер kubelet обязан совпадать с драйвером рантайма, иначе Pod'ы не стартуют. Проверяйте значение в конфиге kubelet и в конфиге containerd. Там же задаётся подсеть виртуальных IP сервисов: kubeadm init --service-cidr 10.96.0.0/12 выделяет адреса ClusterIP из этого диапазона.
Из экспериментальных возможностей стоит держать в поле зрения Pod-группы: функция управления приоритетом и disruption доступна с версии 1.37 и по умолчанию отключена. Чтобы её включить, нужен feature gate на всех соответствующих компонентах кластера, а все Pod'ы одной группы должны иметь тот же приоритет и ту же политику вытеснения, что и сама группа — Pod-группы, disruption и приоритет.
Четыре базовых типа Service, InternalTrafficPolicy и Topology Aware Routing остаются рабочим инструментом повседневной настройки. Начните с ClusterIP для внутренних связей, добавьте NodePort или LoadBalancer только там, где нужен внешний доступ, и держите диагностику в виде готового набора команд под рукой.