Включение внешних сервисов в контекст Kubernetes

Речь о создании объекта Service, за которым стоит не под, а внешний по отношению к кластеру Kubernetes сервер.

Приём позволяет сделать по крайней мере две полезные вещи:

  • подключить внешний сервис к Prometheus с использованием Kubernetes SD;
  • создать для внешнего сервиса запись во внутреннем DNS Kubernetes.

Собственно приём заключается в создании headless сервиса без селектора и соответствующего объекта Endpoints.

Отсутствие селектора отключает управление объектом Endpoints, связанным с сервисом. Значение clusterIP: None отключает создание сервисного IP. Как следствие, внутренняя запись DNS для сервиса будет указывать сразу на адреса конечных точек входа и трафик будет направляться на конечный сервер без трансляции адресов назначения.

Пусть у нас есть кластер Kubernetes, в котором установлен Prometheus со включенным Kubernetes SD. Мы установили внешний сервер Redis с адресом 10.1.0.27, на котором подняты node_exporter, собственно Redis и redis_exporter.

Для подключения сервера к Prometheus создадим следующие ресурсы:

---
apiVersion: v1
kind: Service
metadata:
  name: external-redis-exporter
  labels:
    to-see: in-prometheus
  annotations:
    prometheus.io/scrape: "true"
spec:
  clusterIP: None
  type: ClusterIP
---
apiVersion: v1
kind: Endpoints
metadata:
  name: external-redis-exporter
subsets:
- addresses:
  - ip: 10.1.0.27
  ports:
  - name: node-exporter
    port: 9100
    protocol: TCP
  - name: redis-exporter
    port: 9121
    protocol: TCP

Объект Endpoints связывается с соответствующим Service по metadata.name.

Заглянув в Status -> Targets в интерфейсе Prometheus, наблюдаем в списке kubernetes-service-endpoints

Внешние цели Prometheus

Обратите внимание, что, хотя адреса для мониторинга берутся из объекта Endpoints, метки для метрик берутся из Service.

Пример приведён в синтетическом виде. В реальности для такой инсталляции Redis лучше на сервер поставить только node_exporter, а redis_exporter установить в Kubernetes. При этом для Redis можно создать отдельный Service, после чего к нему можно будет подключаться, используя внутреннее имя DNS.

Список объектов будет выглядеть так:

---
apiVersion: v1
kind: Service
metadata:
  name: external-redis
spec:
  clusterIP: None
  type: ClusterIP
---
apiVersion: v1
kind: Endpoints
metadata:
  name: external-redis
subsets:
- addresses:
  - ip: 10.1.0.27
  ports:
  - name: redis
    port: 6379
    protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
  name: external-redis-node
spec:
  clusterIP: None
  type: ClusterIP
---
apiVersion: v1
kind: Endpoints
metadata:
  name: external-redis-node
subsets:
- addresses:
  - ip: 10.1.0.27
  ports:
  - name: node-exporter
    port: 9100
    protocol: TCP

redis_exporter устанавливается и настраивается отдельно, при этом в качестве адреса сервера Redis теперь можно использовать external-redis:6379 (в том же namespace, что и Service). Имя будет разрешаться в адрес, указанный в Endpoints.

И ещё о профитах. Продолжая рассматривать пример с Redis, можно предположить три возможных вида инсталляции сервера:

  • изначальный пример с Redis на отдельном внешнем сервере,
  • Redis, установленный внутри Kubernetes,
  • облачное managed решение.

Предложенная схема с выделенным Service и redis_exporter, установленным в Kubernetes, позволит менять реализации сервера, не меняя схемы настроек клиентов.