← Kubernetes: сеть
6 мин · просто · Урок 1 из 8

Service ClusterIP: внутренний адрес для подов

Как группа Pod получает один стабильный адрес и DNS-имя внутри кластера.

Зачем нужен Service

Pod - наименьшая единица, которую запускает Kubernetes. Внутри Pod работает один или несколько контейнеров. Каждый Pod получает свой IP-адрес при старте.

Этот IP меняется при любом пересоздании Pod. Нода упала, прошёл rollout (замена Pod на новую версию), кто-то поменял число реплик. IP станет другим. Запоминать IP Pod в коде соседнего приложения не выйдет: через минуту его уже нет.

Service - объект Kubernetes, который даёт группе Pod стабильный адрес. Этот адрес не зависит от того, какие именно Pod сейчас живы.

Service делает четыре вещи.

Service не проверяет здоровье Pod сам. Он смотрит на readinessProbe Pod. readinessProbe - проверка, которую Kubernetes повторяет, чтобы понять, готов ли контейнер принимать трафик. Pod без readinessProbe считается готовым сразу. Pod с readinessProbe попадает в распределение только после первой успешной проверки.

В Pod нет readinessProbe, а приложение ещё не стартовало. Запрос придёт в Pod, который пока не отвечает. Решает readinessProbe.

Манифест ClusterIP

apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  type: ClusterIP
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 8080
      protocol: TCP

type: ClusterIP

ClusterIP - внутренний адрес Service. Он доступен только из Pod и нод кластера. Снаружи кластера его не видно.

ClusterIP стоит по умолчанию, поле type можно не писать. Другие типы (NodePort, LoadBalancer, ExternalName) решают другие задачи и разбираются в отдельных уроках.

selector

selector - это набор пар ключ: значение. Service ищет в своём namespace все Pod, у которых есть эти метки, и считает их своими.

selector:
  app: web

Если ни один Pod не подошёл, Service существует, но список его адресов пуст. Запросы к нему вернут connection refused. Опечатка в selector ломает связь незаметно: Service при этом выглядит исправным.

ports

клиент curl http://web/ port Service web ClusterIP · 80 targetPort Pod web контейнер · 8080
Клиент стучится в port Service, Service переводит запрос на targetPort контейнера.

ports - список портов Service. Обычно он один. У порта три ключа.

port и targetPort различать обязательно. Клиент видит port. kube-proxy переводит запрос на targetPort. kube-proxy - компонент на каждой ноде, который настраивает правила сети для Service. Если Pod слушает 8080, а в манифесте стоит targetPort: 80, запросы никуда не дойдут.

Что показывает kubectl

kubectl - утилита командной строки для работы с кластером.

kubectl get svc web
# NAME   TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
# web    ClusterIP   10.43.123.45    <none>        80/TCP    10s
kubectl get endpoints web
# Warning: v1 Endpoints is deprecated in v1.33+; use discovery.k8s.io/v1 EndpointSlice
# NAME   ENDPOINTS                              AGE
# web    10.42.0.5:8080,10.42.0.6:8080          10s

Предупреждение в первой строке - норма. Начиная с версии 1.33 объект Endpoints объявлен устаревшим, ему на смену пришёл EndpointSlice. Команда продолжает работать и остаётся самой короткой для быстрой проверки. Тот же список через новый API:

kubectl get endpointslice -l kubernetes.io/service-name=web

Endpoints - объект, который Kubernetes автоматически создаёт рядом с каждым Service. Внутри список реальных адресов IP:port Pod, которые сейчас попали в selector. Если Pod упал, его IP уходит из Endpoints секунд за пять.

Сверяют связку так:

kubectl get pod -l app=web -o wide
kubectl get endpoints web

IP Pod из первого вывода должны совпасть с адресами в Endpoints из второго.

DNS внутри кластера

CoreDNS - DNS-сервер кластера. Он работает в namespace kube-system и переводит имена Service в их ClusterIP. Из любого Pod:

# Короткое имя (только если pod в том же namespace)
curl http://web/

# С namespace
curl http://web.demo/

# Полный FQDN
curl http://web.demo.svc.cluster.local/

Все три варианта работают благодаря строке search в /etc/resolv.conf. Этот файл kubelet кладёт в каждый Pod. kubelet - агент Kubernetes на ноде, который запускает Pod.

Быстрая императивная команда

Если Deployment уже есть, писать YAML руками не нужно. Deployment - объект, который держит заданное число одинаковых Pod.

kubectl expose deploy web --port=80 --target-port=8080 --name=web

Команда создаст Service с правильным selector (он берётся из меток Deployment) и нужными портами. По умолчанию --type=ClusterIP.

Что обычно ломается

Домашка

Теорию закрепляет задание на своём кластере: 4 шага с проверкой. Кластер поднимается по треку Kubernetes локально, задания лежат в репозитории k8s-practice.

./run.sh start k8s-service-clusterip     # развернуть стартовое состояние
./run.sh verify k8s-service-clusterip    # проверить текущий шаг

Коротко

Дрилл · вопросы как на собесе

Проверь себя без подсказок

Сформулируй ответ вслух или письменно, потом раскрой и честно отметь: знал или нет.

01

Что такое Service в Kubernetes и какие у него типы?

K8S-010

Прошли материал до конца? Отметьте урок пройденным.