Kubernetes: мониторинг с Prometheus

Автор: | 08/04/2020
 

Следующая задача – настроить мониторинг Kubernetes.

Задача осложняется тем, что у нас есть целый набор ресурсов, которые требуется мониторить:

  • инфраструктура – ЕС2 инстасы WokerNodes, их ЦПУ, память, сеть
  • ключевые сервисы самого Kubernetes – состояние API сервера, etcd, scheduler
  • состояние подов и контейнеров
  • состояние деплойментов
  • сбор метрик непосредственно с приложений

Для мониторинга всего этого доступны следующие сервисы:

  • metrics-server – метрики использования ресурсов CPU, памяти, файл-дескрипторы, etc
  • cAdvisor – метрики Docker-демона, мониторинг контейнеров
  • kube-state-metrics – состояние деплойментов, нодов, под
  • node-exporter: метрики серверов (CPU, memory, network)

Что именно будем делать?

  • запустим Prometheus сервер
  • запустим Redis и redis-exporter и настроим сбор его метрик
  • настроим ClusterRole для Prometheus
  • настроим Prometheus Kubernetes Service Discovery и настроим сбор метрик с:
    • посмотрим на Service Discovery роли
    • запустим експортеры:
      • node-exporter
      • kube-state-metrics
      • метрики cAdvisor
    • запустим metrics-server

Как всё будет работать?

Используем Prometheus Federation:

  • у нас уже есть Prometheus-Grafana стек, который занимается мониторингом существующих ресурсов – это будет наш “центральный” сервер мониторинга, который будет собирать метрики с других серверов Prometheus (все VPC сети объединены через VPC peering, и передача метрик идёт внутри сетей)
  • в EKS-кластер будет отдельный Prometheus-сервер, который будет собирать метрики кластера и експортёров в нём, а потом будет отдавать их центральному серверу

helm install

В 99% всех встреченных гайдов по Prometheus и Kubernetes вся установка и настройка сводились к “helm install“.

Helm это, конечно, круто – и шаблонизатор, и “менеджер пакетов”, но проблема в том, что он слишком много делает “под капотом”, а сейчас хочется посмотреть, что и как вообще выполняется для настройки мониторинга Kubernetes в Prometheus.

По сути, этот пост – продолжение постов AWS: Elastic Kubernetes Service — автоматизация создания кластера, часть 1 — CloudFormation и AWS: Elastic Kubernetes Service — автоматизация создания кластера, часть 2 — Ansible, eksctl, поэтому местами будет Ansible.

Используемые версии:

  • Kubernetes: (AWS EKS): v1.15.11
  • Prometheus: 2.17.1
  • kubectl: v1.18.0

kubectl conext

При необходимости – настраиваем новый контекст:

[simterm]

$ aws --region eu-west-2 eks update-kubeconfig --name bttrm-eks-dev-2
Added new context arn:aws:eks:eu-west-2:534***385:cluster/bttrm-eks-dev-2 to /home/admin/.kube/config

[/simterm]

Или переключаем kubectl на нужный кластер:

[simterm]

$ kubectl config get-contexts
...
arn:aws:eks:eu-west-2:534****385:cluster/bttrm-eks-dev-1
...
$ kubectl config use-context arn:aws:eks:eu-west-2:534***385:cluster/bttrm-eks-dev-2
Switched to context "arn:aws:eks:eu-west-2:534***385:cluster/bttrm-eks-dev-2".

[/simterm]

ConfigMap Reloader

Так как мы постоянно будем вносить изменения в ConfigMap нашего Prometheus – имеет смысл сразу добавить Reloader, что бы поды подхватывали изменения в конфиге.

Создаём для него каталог:

[simterm]

$ mkdir -p roles/reloader/tasks

[/simterm]

И одну задачу – установку самого Reloader. Используем kubectl, вызываем просто через Ansible command.

В roles/reloader/tasks/main.yml добавляем:

- name: "Install Reloader"
  command: "kubectl apply -f https://raw.githubusercontent.com/stakater/Reloader/master/deployments/kubernetes/reloader.yaml"

Пока таким костылём – нет времени на k8s и его проблемы с импортом python-requests.

Добавляем вызов в плейбук:

- hosts:
  - all
  become:
    true
  roles:
    - role: cloudformation
      tags: infra
    - role: eksctl
      tags: eks
    - role: reloader
      tags: reloader

Запускаем:

[simterm]

$ ansible-playbook eks-cluster.yml --tags reloader

[/simterm]

Проверяем:

[simterm]

$ kubectl get po
NAME                                 READY   STATUS    RESTARTS   AGE
reloader-reloader-55448df76c-9l9j7   1/1     Running   0          3m20s

[/simterm]

Запуск Prometheus сервера в Kubernetes

Сначала запустим сам Prometheus в кластере.

Файл настроек будем передавать через ConfigMap.

Кластер уже создан, и в будущем всё будет управляться Ansible – создаём структуру каталогов:

[simterm]

$ mkdir -p roles/monitoring/{tasks,templates}

[/simterm]

Namespace

Все связанные с мониторингом ресурсы будем держать в отдельном namespace.

В каталоге roles/monitoring/templates/ создаём для него конфиг, назовём prometheus-ns.yml.j2:

---
apiVersion: v1
kind: Namespace
metadata:
  name: monitoring

Создаём файл roles/monitoring/tasks/main.yml и добавляем создание namespace:

- name: "Create the Monitoring Namespace"
  command: "kubectl apply -f roles/monitoring/templates/prometheus-ns.yml.j2"

Добавляем вызов в плейбук:

- hosts:
  - all
  become:
    true
  roles:
    - role: cloudformation
      tags: infra
    - role: eksctl
      tags: eks
    - role: reloader
      tags: reloader
    - role: monitoring
      tags: monitoring

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

[simterm]

$ ansible-playbook eks-cluster.yml --tags monitoring

[/simterm]

Проверяем:

[simterm]

$ kubectl get ns
NAME              STATUS   AGE
default           Active   24m
kube-node-lease   Active   25m
kube-public       Active   25m
kube-system       Active   25m
monitoring        Active   32s

[/simterm]

prometheus.yml ConfigMap

Файл настроек Prometheus будем хранить в отдельном ConfigMap.

Создаём файл roles/monitoring/templates/prometheus-configmap.yml.j2 — это минимальный конфиг для запуска сервера:

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: prometheus-config
  namespace: monitoring
data:
  prometheus.yml: |
    global:
      scrape_interval:     15s
      external_labels:
        monitor: 'eks-dev-monitor'
    scrape_configs:
      - job_name: 'prometheus'
        scrape_interval: 5s
        static_configs:
          - targets: ['localhost:9090']

Добавляем его в roles/monitoring/tasks/main.yml:

- name: "Create the Monitoring Namespace"
  command: "kubectl apply -f roles/monitoring/templates/prometheus-ns.yml.j2"

- name: "Create prometheus.yml ConfigMap"
  command: "kubectl apply -f roles/monitoring/templates/prometheus-configmap.yml.j2"

Можно ещё раз проверить – запускаем, и глянем ConfigMap:

[simterm]

$ kubectl -n monitoring get configmap prometheus-config -o yaml
apiVersion: v1
data:
  prometheus.yml: |
    global:
      scrape_interval:     15s
      external_labels:
        monitor: 'eks-dev-monitor'
    scrape_configs:
      - job_name: 'prometheus'
        scrape_interval: 5s
        static_configs:
          - targets: ['localhost:9090']
kind: ConfigMap
...

[/simterm]

Prometheus Deployment и LoadBalancer Service

Теперь можно запускать сам Prometheus.

Создаём файл деплоймента – roles/monitoring/templates/prometheus-deployment.yml.j2:

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: prometheus-server
  labels:
    app: prometheus
  namespace: monitoring
  annotations:
    reloader.stakater.com/auto: "true"
#    service.beta.kubernetes.io/aws-load-balancer-internal: "true"  
spec:
  replicas: 2
  selector:
    matchLabels: 
      app: prometheus
  template:
    metadata:
      labels:
        app: prometheus
    spec:
      containers:
      - name: prometheus-server
        image: prom/prometheus
        volumeMounts:
          - name: prometheus-config-volume
            mountPath: /etc/prometheus/prometheus.yml
            subPath: prometheus.yml
        ports:
        - containerPort: 9090
      volumes:
        - name: prometheus-config-volume
          configMap:
            name: prometheus-config
---
kind: Service
apiVersion: v1
metadata:
  name: prometheus-server-alb
  namespace: monitoring
spec:
  selector:
    app: prometheus
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9090
  type: LoadBalancer

Тут:

  1. запускаем два пода с Prometheus
  2. подключаем им prometheus-config ConfigMap
  3. создаём Service типа LoadBalancer для доступа к Prometheus на порт 9090 – создаст AWS LoadBalancer типа Classic (не Application LB) с Listener на порту 80

Анноттации:

  • reloader.stakater.com/auto: «true» — используется для Reloader.
  • service.beta.kubernetes.io/aws-load-balancer-internal: «true» пока комментируем — Internal используем позже, когда будем настраивать VPC peering и Prometheus federation, пока делаем Internet-facing.

Добавляем вызов в tasks:

...
- name: "Deploy Prometheus server and its LoadBalancer"
  command: "kubectl apply -f roles/monitoring/templates/prometheus-deployment.yml.j2"

Запускаем:

[simterm]

$ ansible-playbook eks-cluster.yml --tags monitoring
...
TASK [monitoring : Deploy Prometheus server and its LoadBalancer] ****
changed: [localhost]
...

[/simterm]

Проверяем поды:

[simterm]

$ kubectl -n monitoring get pod
NAME                                 READY   STATUS    RESTARTS   AGE
prometheus-server-85989544df-pgb8c   1/1     Running   0          38s
prometheus-server-85989544df-zbrsx   1/1     Running   0          38s

[/simterm]

LoadBalancer Service:

[simterm]

$ kubectl -n monitoring get svc
NAME                    TYPE           CLUSTER-IP       EXTERNAL-IP                                                               PORT(S)        AGE
prometheus-server-alb   LoadBalancer   172.20.160.199   ac690710a9747460abc19cd999812af8-1463800400.eu-west-2.elb.amazonaws.com   80:30190/TCP   42s

[/simterm]

Он же в AWS:

Проверяем — открываем в браузере:

Если с LoadBalancer проблемы — пробуем через порт-форвад:

[simterm]

$ kubectl -n monitoring port-forward prometheus-server-85989544df-pgb8c 9090:9090
Forwarding from 127.0.0.1:9090 -> 9090
Forwarding from [::1]:9090 -> 9090

[/simterm]

И открываем через URL localhost:9090.

Настройка мониторинга

Хорошо, сам сервер для сбора метрик мы запустили,  теперь надо в него собрать собственно метрики.

Начнём с самого простого — запустим сервис, к нему добавим експортёр, а потом соберём метрики приложения в Prometehus.

Redis && redis_exporter

Используем Redis server и для него — redis_exporter.

Создадим деплоймент, например roles/monitoring/templates/tests/redis-server-and-exporter-deployment.yml.j2:

---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: redis
spec:
  replicas: 1
  template:
    metadata:
      annotations:
        prometheus.io/scrape: "true"
      labels:
        app: redis
    spec:
      containers:
      - name: redis
        image: redis
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
        ports:
        - containerPort: 6379
      - name: redis-exporter
        image: oliver006/redis_exporter:latest
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
        ports:
        - containerPort: 9121
---
kind: Service
apiVersion: v1
metadata:
  name: redis
spec:
  selector:
    app: redis
  ports:
  - name: redis
    protocol: TCP
    port: 6379
    targetPort: 6379
  - name: prom
    protocol: TCP
    port: 9121
    targetPort: 9121

В аннотациях тут:

  • prometheus.io/scrape – используется в фильтрах pod и services, см. Roles
  • prometheus.io/port – если порт для сбора метрик отличается от заданного в шаблоне (containerPort, видимо) – то можно переопределить его тут
  • prometheus.io/path – если путь метрик отличается от стандартного /metrics – можно переопределить его тут

См. Per-pod Prometheus Annotations.

Обратите внимание, что мы не указываем namespace — и Redis, и его екпортёр будут запущены в default namespace, скоро увидим — к каким ограничениям это приведёт.

Деплоим – тут руками, задача тестовая, в мониторинге не нужна:

[simterm]

$ kubectl apply -f roles/monitoring/templates/tests/redis-server-and-exporter-deployment.yml.j2 
deployment.extensions/redis created
service/redis created

[/simterm]

Проверяем – должен быть под и в нём два контейнера.

В default namespace находим под:

[simterm]

$ kubectl get pod
NAME                                 READY   STATUS    RESTARTS   AGE
redis-698cd557d5-xmncv               2/2     Running   0          10s
reloader-reloader-55448df76c-9l9j7   1/1     Running   0          23m

[/simterm]

В проверяем в нём контейнеры:

[simterm]

$ kubectl get pod redis-698cd557d5-xmncv -o jsonpath='{.spec.containers[*].name}'
redis redis-exporter

[/simterm]

Окей.

Теперь добавим сбор метрик с этого експортёра – обновляем prometheus-configmap.yml.j2 ConfigMap — добавляем новый таргет, redis:

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: prometheus-config
  namespace: monitoring
data:
  prometheus.yml: |

    global:
      scrape_interval:     15s
      external_labels:
        monitor: 'eks-dev-monitor'
    
    scrape_configs:
      
      - job_name: 'prometheus'
        scrape_interval: 5s
        static_configs:
          - targets: ['localhost:9090']

      - job_name: 'redis'
        static_configs:
          - targets: ['redis:9121']

Деплоим (руками, или через Ansible), и проверяем Targets нашего Prometheus:

Окей — таргет появился, но почему ошибка «Get http://redis:9121/metrics: dial tcp: lookup redis on 172.20.0.10:53: no such host»?

Prometheus ClusterRole, ServiceAccount и ClusterRoleBinding

Как помним, наш Prometheus запущен в namespace с именем monitoring:

[simterm]

$ kubectl get ns monitoring
NAME         STATUS   AGE
monitoring   Active   25m

[/simterm]

Тогда как в деплойменте Redis namespace задан не был и, соотвественно, под был создан в default namespace:

[simterm]

$ kubectl -n default get pod
NAME                                 READY   STATUS    RESTARTS   AGE
redis-698cd557d5-xmncv               2/2     Running   0          12m

[/simterm]

Или так:

[simterm]

$ kubectl get pod redis-698cd557d5-xmncv -o jsonpath='{.metadata.namespace}'
default

[/simterm]

Что бы Prometheus имел доступ ко всем пространствам имён кластера — создадим ему ClusterRole, ServiceAccount и ClusterRoleBinding, см. Kubernetes: знакомство, часть 5 — RBAC авторизация и примеры Role и RoleBinding.

Этот же ServiceAccount позже будем использовать для Kubernetes Service Discovery.

Добавляем файл roles/monitoring/templates/prometheus-rbac.yml.j2:

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  name: prometheus
rules:
- apiGroups: [""]
  resources:
  - services
  - endpoints
  - pods
  - nodes
  - nodes/proxy
  - nodes/metrics
  verbs: ["get", "list", "watch"]
- apiGroups:
  - extensions
  resources:
  - ingresses
  verbs: ["get", "list", "watch"]
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: prometheus
  namespace: monitoring
---  
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: prometheus
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: prometheus
subjects:
- kind: ServiceAccount
  name: prometheus
  namespace: monitoring

Добавляем его вызов после создания ConfigMap и перед деплойментом самого Prometheus-сервера:

- name: "Create the Monitoring Namespace"
  command: "kubectl apply -f roles/monitoring/templates/prometheus-ns.yml.j2"

- name: "Create prometheus.yml ConfigMap"
  command: "kubectl apply -f roles/monitoring/templates/prometheus-configmap.yml.j2"

- name: "Create Prometheus ClusterRole"
  command: "kubectl apply -f roles/monitoring/templates/prometheus-rbac.yml.j2"

- name: "Deploy Prometheus server and its LoadBalancer"
  command: "kubectl apply -f roles/monitoring/templates/prometheus-deployment.yml.j2"

Обновляем prometheus-deployment.yml — в spec добавляем serviceAccountName:

...
  template:
    metadata:
      labels:
        app: prometheus
    spec:
      serviceAccountName: prometheus
      containers:
      - name: prometheus-server
        image: prom/prometheus
...

Кроме того, для обращения к подам в другом namespace адрес пода надо указывать в виде полного FQDN, с указанием пространства имён, в нашем случае адрес будет redis.default.svc.cluster.local, см. DNS for Services and Pods.

Обновляем ConfigMap – меняем адрес Redis:

...
    scrape_configs:

      - job_name: 'prometheus'
        scrape_interval: 5s
        static_configs:
          - targets: ['localhost:9090']

      - job_name: 'redis'
        static_configs:
          - targets: ['redis.default.svc.cluster.local:9121']

Деплоим с Ansible, что бы обновилось всё:

[simterm]

$ ansible-playbook eks-cluster.yml --tags monitoring

[/simterm]

Проверяем таргеты:

И метрики:

Prometheus Kubernetes Service Discovery

static_configs в Prometheus — хорошо, но что, если надо будет собирать метрики с десятков или сотен сервисов?

Решение — использовать kubernetes_sd_config.

kubernetes_sd_config роли

Kubernetes SD в Prometheus имеет набор “ролей”, согласно которым выполняется сбор метрик.

Каждая такая роль имеет собственный набор лейблов, см. документацию:

  • node: обнаружит по одному таргету на каждой WorkerNode кластера, соберёт метрики kubelet
  • service: обнаружит и вернёт каждый сервис и его порт
  • pod: выполнит поиск всех подов и вернёт их контейнеры как таргеты для сбора метрик
  • endpoints: создаст таргеты из каждого ендпоинта каждого сервиса
  • ingress: создаст таргеты каждого path в каждом ingress

Различия по сути только в том, какие лейблы будут добавлены к собираемым метрикам, и какой адрес таргета будет использоваться.

Примеры конфигов:

Кроме того, для подключения к API-серверу с использованием SSL/TLS потребуется указать CA-сертификат сервера для проверки валидности этого API-сервера, см. Accessing the API from a Pod.

А для самой авторизации на API-сервере – используем токен из файла bearer_token_file, который смонтируется из serviceAccountName: prometheus, который мы указали в деплойменте.

node role

Посмотрим что нам вернёт каждая такая роль, начнём с node – добавляем в scrape_configs, можно прямо скопипастить из примера:

...
      - job_name: 'kubernetes-nodes'
        scheme: https
        tls_config:
          ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
        bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token

        kubernetes_sd_configs:
        - role: node

Пока без релейблов – запускаем, проверяем таргеты:

Доступные лейблы и их значения – в Status > Service Discovery:

И метрики kubelet_*:

Добавим релейбл, см. relabel_config и пост Life of a Label.

Что нам предлагают?

...
        relabel_configs:
        - action: labelmap
          regex: __meta_kubernetes_node_label_(.+)
        - target_label: __address__
          replacement: kubernetes.default.svc:443
        - source_labels: [__meta_kubernetes_node_name]
          regex: (.+)
          target_label: __metrics_path__
          replacement: /api/v1/nodes/${1}/proxy/metrics
...
  1. собираем лейблы __meta_kubernetes_node_label_alpha_eksctl_io_cluster_name, __meta_kubernetes_node_label_alpha_eksctl_io_nodegroup_name и т.д., вырезаем (.+) – получим лейблы типа alpha_eksctl_io_cluster_name, alpha_eksctl_io_nodegroup_name, etc
  2. обновляем лейблу __address__ – вносим значение kubernetes.default.svc:443 что бы сформировать адрес запроса к таргетам
  3. получаем занчение из __meta_kubernetes_node_name и обновляем лейблу` __metrics_path__` – вносим в неё /api/v1/nodes/__meta_kubernetes_node_name/proxy/metrics

В результате Prometheus выстроит запрос вида kubernetes.default.svc:443/api/v1/nodes/ip-10-1-57-13.eu-west-2.compute.internal/proxy/metrics – и соберёт метрики с этой WorkerNode.

Обновляем, проверяем:

Отлично.

pod role

Оттуда же берём пример для pod:

...
      - job_name: 'kubernetes-pods'
        kubernetes_sd_configs:
        - role: pod

        relabel_configs:
        - action: labelmap
          regex: __meta_kubernetes_pod_label_(.+)
        - source_labels: [__meta_kubernetes_namespace]
          action: replace
          target_label: kubernetes_namespace
        - source_labels: [__meta_kubernetes_pod_name]
          action: replace
          target_label: kubernetes_pod_name

Проверяем:

Нашлись вообще все поды, но зачем так много?

Добавим проверку аннотации prometheus.io/scrape: “true”:

...
        relabel_configs:
        - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
          action: keep
          regex: true
...

Которая есть в нашем Редисе:

...
  template:
    metadata:
      annotations:
        prometheus.io/scrape: "true"

...

И результат:

http://10.1.44.135:6379/metrics – сам Redis-сервер, метрик у которого нет.

Хотим вырезать и его? Добавим ещё один фильтр:

...
        - source_labels: [__meta_kubernetes_pod_container_name]
          action: keep
          regex: .*-exporter
...

Т.е. собираем только метрики, у которых в лейбле __meta_kubernetes_pod_container_name есть “-exporter“.

Проверяем:

В целом тут вроде разобрались.

Что осталось?

  • node-exporter
  • kube-state-metrics
  • cAdvisor
  • metrics-server

node-exporter metrics

Добавим node_exporter, что бы собирать метрики с ЕС2-инстансов.

Так как pod с екпортёром надо разместить на каждой WorkerNode — используем тип DaemonSet.

Создаём файл roles/monitoring/templates/prometheus-node-exporter.yml.j2 — запуcкаем по одному поду на каждой WorkerNode, в monitoring namespace, и к ним добавляем Service, что бы Prometheus смог собрать метрики с ендпоинтов:

---
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  name: node-exporter
  labels:
    name: node-exporter
  namespace: monitoring
spec:
  template:
    metadata:
      labels:
        name: node-exporter
        app: node-exporter
      annotations:
         prometheus.io/scrape: "true"
    spec:
      hostPID: true
      hostIPC: true
      hostNetwork: true
      containers:
        - ports:
            - containerPort: 9100
              protocol: TCP
          resources:
            requests:
              cpu: 0.15
          securityContext:
            privileged: true
          image: prom/node-exporter
          args:
            - --path.procfs
            - /host/proc
            - --path.sysfs
            - /host/sys
            - --collector.filesystem.ignored-mount-points
            - '"^/(sys|proc|dev|host|etc)($|/)"'
          name: node-exporter
          volumeMounts:
            - name: dev
              mountPath: /host/dev
            - name: proc
              mountPath: /host/proc
            - name: sys
              mountPath: /host/sys
            - name: rootfs
              mountPath: /rootfs
      volumes:
        - name: proc
          hostPath:
            path: /proc
        - name: dev
          hostPath:
            path: /dev
        - name: sys
          hostPath:
            path: /sys
        - name: rootfs
          hostPath:
            path: /

---
kind: Service
apiVersion: v1
metadata:
  name: node-exporter
  namespace: monitoring
spec:
  selector:
    app: node-exporter
  ports:
  - name: node-exporter
    protocol: TCP
    port: 9100
    targetPort: 9100

В roles/monitoring/tasks/main.yml добавляем вызов:

...
- name: "Deploy node-exporter to WorkerNodes"
  command: "kubectl apply -f roles/monitoring/templates/prometheus-node-exporter.yml.j2"

И подумаем – как собирать метрики.

Первое – какую роль использовать? Нам надо указать порт 9100 – значит роль node отпадает – у неё нет порта:

[simterm]

$ kubectl -n monitoring get node
NAME                                        STATUS   ROLES    AGE     VERSION
ip-10-1-47-175.eu-west-2.compute.internal   Ready    <none>   3h36m   v1.15.10-eks-bac369
ip-10-1-57-13.eu-west-2.compute.internal    Ready    <none>   3h37m   v1.15.10-eks-bac369

[/simterm]

Service?

The address will be set to the Kubernetes DNS name of the service and respective service port

Посмотрим, что там:

[simterm]

$ kubectl -n monitoring get svc
NAME                    TYPE           CLUSTER-IP       EXTERNAL-IP  PORT(S)        AGE
node-exporter           ClusterIP      172.20.242.99    <none>       9100/TCP       37m

[/simterm]

Смотрим, какие мета-лейблы есть у роли service – всё хорошо, но нет лейбл под Worker Nodes кластера – а нам надо собрать метрики с каждого node_exporter на каждой WorkerNode.

Смотрим дальше – endpoints:

[simterm]

$ kubectl -n monitoring get endpoints
NAME                    ENDPOINTS                          AGE
node-exporter           10.1.47.175:9100,10.1.57.13:9100   44m
prometheus-server-alb   10.1.45.231:9090,10.1.53.46:9090   3h24m

[/simterm]

10.1.47.175:9100,10.1.57.13:9100 – ага, вот эти ребята!

Значит – можем использовать endpoints, кроме того – у этой роли будет лейбла __meta_kubernetes_endpoint_node_name.

Пробуем:

...
      - job_name: 'node-exporter'
        kubernetes_sd_configs:
          - role: endpoints
        relabel_configs:
        - source_labels: [__meta_kubernetes_endpoints_name]
          regex: 'node-exporter'
          action: keep

Проверяем таргеты:

И метрики:

Примеры запросов для графиков с node_exporter есть в посте Grafana: создание dashboard.

kube-state-metrics

Для сбора метрик о ресурсах самого Kubernetes используем kube-state-metrics.

В roles/monitoring/tasks/main.yml добавляем его установку:

...
- git:
    repo: 'https://github.com/kubernetes/kube-state-metrics.git'
    dest: /tmp/kube-state-metrics

- name: "Install kube-state-metrics"
  command: "kubectl apply -f /tmp/kube-state-metrics/examples/standard/"

Сам деплоймент можно помотреть в файле https://github.com/kubernetes/kube-state-metrics/blob/master/examples/standard/deployment.yaml.

Тут Service Discovery не обязателен, т.к. у нас будет только один сервис kube-state-metrics.

Используем static_configs:

...
      - job_name: 'kube-state-metrics'
        static_configs:
          - targets: ['kube-state-metrics.kube-system.svc.cluster.local:8080']

Проверяем таргеты:

И метрики, например – kube_deployment_status_replicas_available:

cAdvisor

Сам cAdvisor, думаю, в представлении не особо нуждается — одна из наиболее используемых систем для сбора метрик контейнеров.

Он уже встроен в Kubernetes, поэтому експортёр не нужен — только добавить сбор метрик. Пример можно взять в том же https://github.com/prometheus/prometheus/blob/master/documentation/examples/prometheus-kubernetes.yml#L102.

Обновляем roles/monitoring/templates/prometheus-configmap.yml.j2:

...
      - job_name: 'cAdvisor'
        scheme: https
        tls_config:
          ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
        bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
        kubernetes_sd_configs:
        - role: node

        relabel_configs:
        - action: labelmap
          regex: __meta_kubernetes_node_label_(.+)
        - target_label: __address__
          replacement: kubernetes.default.svc:443
        - source_labels: [__meta_kubernetes_node_name]
          regex: (.+)
          target_label: __metrics_path__
          replacement: /api/v1/nodes/${1}/proxy/metrics/cadvisor

Деплоим, проверяем:

У нас две Kubernetes WorkerNode, с каждой собираем метрики контейнеров – отлично.

Сами метрики:

metrics-server

Для него есть екпортеры — но сейчас нам его метрики не важны, просто добавим его установку, что бы работали лимиты с kubectl top.

Раньше установка была немного сложнее, см. Kubernetes: запуск metrics-server в AWS EKS для Kubernetes Pod AutoScaler – надо было вносить правки в деплоймент, но сейчас на EKS завелось из коробки.

Обновляем roles/monitoring/tasks/main.yml:

...
- git:
    repo: "https://github.com/kubernetes-sigs/metrics-server.git"
    dest: "/tmp/metrics-server"

- name: "Install metrics-server"
  command: "kubectl apply -f /tmp/metrics-server/deploy/kubernetes/"
...

Деплоим, проверяем поды в kube-system namespace:

[simterm]

$ kubectl -n kube-system get pod
NAME                                 READY   STATUS    RESTARTS   AGE
aws-node-s7pvq                       1/1     Running   0          4h42m
...
kube-proxy-v9lmh                     1/1     Running   0          4h42m
kube-state-metrics-6c4d4dd64-78bpb   1/1     Running   0          31m
metrics-server-7668599459-nt4pf      1/1     Running   0          44s

[/simterm]

Под metrics-server появлися — окей.

Через пару минут можно пробовать top node:

[simterm]

$ kubectl top node
NAME                                        CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%   
ip-10-1-47-175.eu-west-2.compute.internal   47m          2%     536Mi           14%       
ip-10-1-57-13.eu-west-2.compute.internal    58m          2%     581Mi           15%

[/simterm]

И поды:

[simterm]

$ kubectl top pod
NAME                                 CPU(cores)   MEMORY(bytes)   
redis-6d9cf9d8cb-dfnn6               2m           5Mi             
reloader-reloader-55448df76c-wsrfv   1m           7Mi

[/simterm]

В целом вроде всё.

Можно приводить в порядок, и допиливать по ходу дела.

Ссылки по теме

Configs

Misc