Kubernetes: что такое Endpoints

Автор: | 13/03/2021

Практически все знают, что такое Kubernetes Service, но не все могут быть в курсе, что такое Endpoint, так как обычно он работает “за кулисами”, и мы его не видим, аналогично тому, как мы пользуемся Deployment, но редко видим ReplicaSet-ы.

Kubernetes Service

Итак, Service – это абстракция Kubernetes, которая, используя labels, выбирает поды, на которые следует перенаправлять трафик, см. Kubernetes: ClusterIP vs NodePort vs LoadBalancer, Services и Ingress — обзор, примеры и  Kubernetes: Service, балансировка нагрузки, kube-proxy и iptables:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376

Как только в кластере появляется новый под, лейблы которого совпадают с селектором Сервиса, в данном примере  app=MyApp – Service начнёт роутить трафик на него.

Реализуется это путём добавления IP-адреса пода в список Endpoints, который используется Сервисом.

Создадим простейший пример:

---
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
  labels:
    app: nginx
spec:
  containers:
    - name: nginx-container
      image: nginx
      ports:
        - name: web
          containerPort: 80
          protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
spec:
  selector:
    app: nginx
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80

Тут создаётся под с NGINX, и для него – Сервис с дефолтным типом ClusterIP.

Создаём их:

[simterm]

$ kubectl apply -f svc-example.yaml 
pod/nginx-pod created
service/nginx-svc created

[/simterm]

Проверяем Сервис:

[simterm]

$ kubectl get service nginx-svc
NAME        TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
nginx-svc   ClusterIP   172.20.69.253   <none>        80/TCP    26s

[/simterm]

Kubernetes Endpoints

Теперь рассмотрим этот Сервис детальнее:

[simterm]

$ kubectl describe service nginx-svc
Name:              nginx-svc
Namespace:         default
Labels:            <none>
Annotations:       <none>
Selector:          app=nginx
Type:              ClusterIP
IP Families:       <none>
IP:                172.20.69.253
IPs:               <none>
Port:              <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         10.21.56.143:80

[/simterm]

В конце мы собственно и видим Endpoints этого сервиса – IP-адрес пода.

Проверим под:

[simterm]

$ kubectl describe pod nginx-pod
Name:         nginx-pod
Namespace:    default
Priority:     0
Node:         ip-10-21-49-33.us-east-2.compute.internal/10.21.49.33
Start Time:   Sat, 13 Mar 2021 08:37:55 +0200
Labels:       app=nginx
Annotations:  kubernetes.io/psp: eks.privileged
Status:       Running
IP:           10.21.56.143
...

[/simterm]

Вот и наш IP.

А теперь проверим ендпоинты, которые являются отдельными объектами API-сервера – Endpoints, и с которыми можно работать так же, как с Services и Pods:

[simterm]

$ kubectl get endpoints nginx-svc
NAME        ENDPOINTS         AGE
nginx-svc   10.21.56.143:80   18m

[/simterm]

Если мы добавим ещё один под с той же лейблой, в Deployment или просто опишем вторым объектом – он будет добавлен в этот же Ендпоинт:

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deploy
  labels:
    app: nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx-container
        image: nginx
        ports:
          - name: web
            containerPort: 80
            protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
spec:
  selector:
    app: nginx
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80

Создаём:

[simterm]

$ kubectl apply -f svc-example.yaml 
deployment.apps/nginx-deploy created
service/nginx-svc unchanged

[/simterm]

И проверяем ендпоинт:

[simterm]

$ kubectl get endpoints nginx-svc
NAME        ENDPOINTS                                        AGE
nginx-svc   10.21.37.55:80,10.21.54.174:80,10.21.56.143:80   21m

[/simterm]

Тут у нас остался старый под 10.21.56.143:80, и появилось два новых, которые указаны в replicas нашего деплоймента.

Найдём из используя --selector – аналогично Сервис ищет поды для добавления в Ендпоинты:

[simterm]

$ kubectl get pod --selector=app=nginx -o wide
NAME                            READY   STATUS    RESTARTS   AGE     IP          
nginx-deploy-7fcd954c94-gbm6d   1/1     Running   0          2m28s   10.21.54.174
nginx-deploy-7fcd954c94-mg8kr   1/1     Running   0          2m28s   10.21.37.55 
nginx-pod                       1/1     Running   0          23m     10.21.56.143

[/simterm]

Custom Endpoint

Кроме ендпоинтов подов, мы можем создать кастомный ендпоинт, который будет слать трафик на любой ресурс.

К примеру, опишем такой сервис:

kind: Service
apiVersion: v1
metadata:
  name: external-svc
spec:
  ports:
    - name: web
      protocol: TCP
      port: 80
      targetPort: 80

Обратите внимание, что тут мы не описываем selector.

К нему описываем Endpoints:

kind: Endpoints
apiVersion: v1
metadata:
  name: external-svc
subsets: 
  - addresses:
        - ip: 139.59.205.180
    ports:
      - port: 80
        name: web

Тут:

  1. name: должно быть таким же, как у Service
  2. addresses: адрес, на который шлём трафик, в этом примере – IP адрес сервера в DigitalOcean, на котором работает блог rtfm.co.ua, можно указать несколько – тогда Service будет выполнять load balancing между ними, как описано в Kubernetes: Service, балансировка нагрузки, kube-proxy и iptables
  3. ports.port и ports.name такие же, как у Service

Создаём:

[simterm]

$ kubectl apply -f external-endpoint.yaml 
service/external-svc created
endpoints/external-svc created

[/simterm]

Проверяем Service и его Endpoints:

[simterm]

$ kubectl describe svc external-svc
Name:              external-svc
Namespace:         default
Labels:            <none>
Annotations:       <none>
Selector:          <none>
Type:              ClusterIP
IP Families:       <none>
IP:                172.20.45.77
IPs:               <none>
Port:              web  80/TCP
TargetPort:        80/TCP
Endpoints:         139.59.205.180:80

[/simterm]

Запускаем для проверки под:

[simterm]

$ kubectl run pod --rm -i --tty --image ubuntu -- bash

[/simterm]

Устанавливаем в нём curl:

[simterm]

root@pod:/# apt update && apt -y install curl

[/simterm]

И проверяем по имени сервиса:

[simterm]

root@pod:/# curl -Ls external-svc | grep \<title\>
<title>RTFM: Linux, DevOps, and system administration</title>

[/simterm]

Или по его FQDN:

[simterm]

root@pod:/# curl -Ls external-svc.default.svc.cluster.local | grep \<title\>
<title>RTFM: Linux, DevOps, and system administration</title>

[/simterm]

externalName

Другой вариант для доступа к внешним ресурсам – использовать Service с типом externalName:

---
apiVersion: v1
kind: Service
metadata:
  name: rtfm-service
spec:
  ports:
    - port: 80
  type: ExternalName
  externalName: rtfm.co.ua

Применяем, и проверяем:

[simterm]

root@pod:/# curl -Ls rtfm-service | grep \<title\>
<title>RTFM: Linux, DevOps, and system administration</title>

[/simterm]

Готово.