Kubernetes: Service, Ingress, NetworkPolicy
Сеть в Kubernetes — типы Service, порты, Ingress с путями и TLS, NetworkPolicy и default-deny
Поды появляются и исчезают, их IP меняются. Поэтому к ним обращаются не напрямую, а через объекты сети:
| Объект | Зачем |
|---|---|
| Service | постоянное имя и IP для группы подов, выбранных по меткам; балансирует запросы между ними |
| Ingress | HTTP(S)-маршрутизация снаружи кластера: домен и путь → Service; TLS |
| NetworkPolicy | файрвол между подами: кто к кому может ходить |
Внутри кластера Service доступен по DNS-имени <сервис> из своего неймспейса и <сервис>.<неймспейс>.svc.cluster.local из любого.
Синтаксис
apiVersion: v1
kind: Service
metadata:
name: web
spec:
selector: {app: web} # на какие поды слать трафик (по меткам)
ports:
- port: 80 # порт сервиса
targetPort: 8080 # порт в контейнере
Заготовки Service и Ingress генерирует kubectl create service … и kubectl create ingress … с --dry-run=client -o yaml. Для NetworkPolicy генератора нет — пиши руками.
Service: типы
spec.type | Как доступен | Заметки |
|---|---|---|
ClusterIP | виртуальный IP внутри кластера | по умолчанию |
ClusterIP + clusterIP: None | headless: IP нет, DNS-имя сервиса возвращает IP подов | для StatefulSet и клиентской балансировки |
NodePort | порт на каждом узле: IP-узла:nodePort | заодно получает ClusterIP |
LoadBalancer | внешний балансировщик облака | заодно NodePort и ClusterIP; без облачного контроллера EXTERNAL-IP останется <pending> |
ExternalName | DNS-псевдоним (CNAME) на внешний хост из externalName | без селектора и портов-прокси |
Service: поля
| Поле | Что значит | По умолчанию / заметки |
|---|---|---|
spec.selector | метки подов, на которые идёт трафик | без селектора Kubernetes не знает подов: адреса задают вручную (EndpointSlice) |
spec.ports[].port | порт, на котором слушает сервис | обязательно |
spec.ports[].targetPort | порт контейнера: число или имя из containerPort | равен port |
spec.ports[].nodePort | порт на узлах для NodePort/LoadBalancer | выбирается сам из 30000–32767 |
spec.ports[].protocol | TCP / UDP / SCTP | TCP |
spec.ports[].name | имя порта | обязательно, если портов больше одного |
spec.clusterIP | конкретный IP или None (headless) | выдаётся автоматически |
spec.externalName | хост для ExternalName | |
spec.sessionAffinity | ClientIP — один клиент всегда на один под | None |
spec.externalTrafficPolicy | Cluster / Local — для внешнего трафика только поды своего узла, сохраняется IP клиента | Cluster |
spec.publishNotReadyAddresses | включать в DNS поды, которые не Ready | false |
Ingress
Ingress — только правила. Работают они, если в кластере установлен Ingress-контроллер (ingress-nginx, Traefik и др.).
| Поле | Что значит | По умолчанию / заметки |
|---|---|---|
spec.ingressClassName | какой контроллер обрабатывает этот Ingress (nginx, traefik) | класс, помеченный в кластере как default |
spec.rules[].host | домен: example.com, *.example.com | без host — любой домен |
spec.rules[].http.paths[].path | путь URL | |
spec.rules[].http.paths[].pathType | Prefix / Exact / ImplementationSpecific | обязательно |
…paths[].backend.service.name | куда направить запрос | |
…paths[].backend.service.port.number / .name | порт сервиса (не контейнера) — числом или именем | одно из двух |
spec.defaultBackend | куда идут запросы, не подошедшие ни под одно правило | зависит от контроллера |
spec.tls[].hosts | для каких доменов включить HTTPS | |
spec.tls[].secretName | Secret типа kubernetes.io/tls с сертификатом и ключом | см. k8s-config |
metadata.annotations | настройки конкретного контроллера: nginx.ingress.kubernetes.io/rewrite-target и т. п. |
pathType:
| Значение | Что совпадает с path: /api |
|---|---|
Exact | только /api |
Prefix | /api, /api/, /api/users — по целым сегментам пути; /apiv2 не совпадает |
ImplementationSpecific | как решит контроллер |
Если путей несколько, выигрывает самый длинный совпавший.
NetworkPolicy
Пока на под не действует ни одна политика, ему разрешено всё. Как только его выбирает хотя бы одна политика нужного направления (Ingress — входящий трафик, Egress — исходящий), в этом направлении разрешено только то, что разрешают политики. Запрещающих правил нет — политики только добавляют разрешения.
| Поле | Что значит | По умолчанию / заметки |
|---|---|---|
spec.podSelector | к каким подам своего неймспейса применяется политика | обязательно; {} — ко всем |
spec.policyTypes | [Ingress], [Egress] или оба | если не указано: Ingress, плюс Egress, если есть раздел egress |
spec.ingress[].from[] | от кого разрешён входящий трафик | пустой список правил = запрещено всё входящее |
spec.ingress[].ports[] | {protocol: TCP, port: 8080}; port — число или имя порта; endPort для диапазона | без ports — все порты |
spec.egress[].to[], spec.egress[].ports[] | куда разрешён исходящий трафик |
Элементы from / to:
| Элемент | Что выбирает |
|---|---|
podSelector: {matchLabels: …} | поды с этими метками в том же неймспейсе |
namespaceSelector: {matchLabels: …} | все поды неймспейсов с этими метками; у каждого неймспейса есть метка kubernetes.io/metadata.name: <имя> |
podSelector + namespaceSelector в одном элементе | поды с метками и в подходящих неймспейсах |
ipBlock: {cidr: 10.0.0.0/8, except: [10.1.0.0/16]} | диапазон IP (обычно — адреса вне кластера) |
Разные элементы списка from соединяются через «или», а селекторы внутри одного элемента — через «и».
Примеры
# Service разных типов: --tcp=порт_сервиса:порт_контейнера
kubectl create service clusterip web --tcp=80:8080 --dry-run=client -o yaml > svc.yaml
kubectl create service nodeport web --tcp=80:8080 --node-port=30080 --dry-run=client -o yaml
kubectl create service clusterip db --clusterip=None --dry-run=client -o yaml # headless
kubectl create service externalname mail --external-name=smtp.example.com --dry-run=client -o yaml
# генератор берёт селектор app=<имя сервиса>; если у подов другие метки — поправь
yq -i '.spec.selector = {"app": "frontend"}' svc.yaml
# Ingress: путь с * — Prefix, без * — Exact; tls=секрет включает HTTPS для host
kubectl create ingress site --class=nginx \
--rule='example.com/api*=api:80' \
--rule='example.com/*=web:80,tls=site-tls' \
--dry-run=client -o yaml > ingress.yaml
# посмотреть пути и типы
yq '.spec.rules[].http.paths[] | .path + " " + .pathType + " -> " + .backend.service.name' ingress.yaml
kubeconform -strict -summary svc.yaml ingress.yaml
# в кластере (здесь не сработают):
# kubectl get svc,endpointslices -l app=web # нашлись ли поды за сервисом
# kubectl get ingress site
# kubectl run tmp --rm -it --image=busybox -- wget -qO- http://web
Сервис с двумя именованными портами, targetPort — по имени порта контейнера:
apiVersion: v1
kind: Service
metadata:
name: api
spec:
type: NodePort
selector: {app: api}
ports:
- name: http
port: 80
targetPort: http # containerPort с name: http
nodePort: 30080
- name: metrics
port: 9090
targetPort: 9090
Ingress с HTTPS и двумя сервисами:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: site
spec:
ingressClassName: nginx
tls:
- hosts: [example.com]
secretName: site-tls
rules:
- host: example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api
port: {number: 80}
- path: /
pathType: Prefix
backend:
service:
name: web
port: {name: http}
Запретить всё входящее в неймспейсе, затем разрешить точечно:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
spec:
podSelector: {} # все поды неймспейса
policyTypes: [Ingress] # правил ingress нет — входящее запрещено
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: db-from-api
spec:
podSelector:
matchLabels: {app: db}
policyTypes: [Ingress]
ingress:
- from:
- podSelector:
matchLabels: {app: api}
- namespaceSelector: # отдельный элемент: «или» — весь неймспейс monitoring
matchLabels:
kubernetes.io/metadata.name: monitoring
ports:
- {protocol: TCP, port: 5432}
Исходящий трафик только к DNS и к своей базе:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-egress
spec:
podSelector:
matchLabels: {app: api}
policyTypes: [Egress]
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector: # тот же элемент: «и» — kube-dns в kube-system
matchLabels: {k8s-app: kube-dns}
ports:
- {protocol: UDP, port: 53}
- {protocol: TCP, port: 53}
- to:
- podSelector:
matchLabels: {app: db}
ports:
- {protocol: TCP, port: 5432}
Подводные камни
- Селектор сервиса не совпадает с метками подов — ошибки нет, сервис просто без адресов, запросы получают
connection refusedили таймаут.kubeconformтакое не ловит. Первым делом сравниspec.selectorсервиса сlabelsв шаблоне пода. portиtargetPortперепутаны.port— порт сервиса, к нему обращаются клиенты;targetPort— порт, который слушает контейнер. Ingress ссылается наportсервиса, а не на порт контейнера.- Порт строкой.
port: "80"не пройдёт схему. АtargetPort: "8080"пройдёт, но будет считаться именем порта. - Несколько портов без
nameAPI-сервер отвергнет, а схема пропустит. nodePortвне30000–32767API отвергнет (это диапазон по умолчанию), схема — пропустит.LoadBalancerбез облака (minikube, kind, голое железо) навсегда в<pending>, если не установлен MetalLB или аналог.kubectl create ingressбез*в пути даётpathType: Exact: правилоexample.com/=web:80поймает только корень, а/aboutуже нет. Для «всего сайта» пиши/*.pathType: Prefixсравнивает сегменты, а не строки:/apiне совпадёт с/apiv2.- Ingress без контроллера ничего не делает, без ошибок. А без
ingressClassNameи без класса по умолчанию его может не подхватить ни один контроллер. podSelectorв NetworkPolicy обязателен по схеме: политику «на все поды» пиши явноpodSelector: {}.- Egress default-deny ломает DNS. Запретил весь исходящий трафик — поды перестали резолвить имена сервисов. Разреши UDP и TCP 53 к kube-dns.
- Один дефис меняет смысл.
- namespaceSelector: …и- podSelector: …отдельными элементами — это «или», аpodSelectorбез своего дефиса подnamespaceSelector— «и». Отступы проверяй особенно внимательно. - NetworkPolicy работает, только если сетевой плагин их поддерживает (Calico, Cilium и др.). С плагином без поддержки (например, flannel) политики принимаются API, но ничего не блокируют.
podSelectorиnamespaceSelectorне выбирают узлы и внешние адреса. Для трафика извне кластера нуженipBlock.
См. также
kubectl · kubeconform · yq · k8s-workloads · k8s-config · nginx · dig · curl · openssl