Translate

вівторок, 21 червня 2022 р.

Prometheus. Setup And Monitoring In Kubernetes


Цього разу я не буду починати статтю із історії появи та особливості Prometheus, адже раніше вже опублікував подібну. У ній ми також зупинились на складових частинах моніторингової системи та її конфігурації для звичайних вузлів. Цього разу поговоримо про місце, яке займає Prometheus в моніторингу Kubernetes, його додатків та підсистем.


WHAT IS PROMETHEUS OPERATOR

Prometheus реалізований як оператор Kubernetes. Цю концепцію вперше запропонувала компанія CoreOS, яка була поглинута RedHat-ом. Оператор - це додаткова абстракція, що допомагає менеджити сервіси, які можуть бути дещо "нерідними" для Kubernetes і надає додатковий рівень автоматизації такому програмному продукту. Оператор вводить додаткові типи об'єктів, котрі включають в себе об'єкти більш низького рівня (native resources). Скажімо використавши "kind: Prometeus", оператор (додатковий постійний сервіс) автоматично створить та налаштує StatefulSet, а в його поди буде змонтовано необхідний Secret, що містить конфігураційний файл для старту Прометеуса. Цей контролер також буде слідкувати за подальшою ситуацією щодо ресурсів, створених ним же, і буде їх змінювати за необхідності: скейлити, даунскейлити, видаляти і т.п.

Такі типи об'єктів реалізовуються за допомогою CRD (custom resource definition), що є розширенням до Kubernetes API. Існує велика множина операторів, для деяких продуктів - це офіційний спосіб їх установки і менеджменту, для інших - опціональний чи навіть неофіційний. Ніщо не заважає запакувати оператор в Helm-чарт чи просто виливати його як набір yaml-файлів.

Є декілька варіантів установки Prometheus оператора, і це варто усвідомлювати перед вибором способу інсталяції:
  • prometheus-operator. Основний та офіційний репозиторій, де відбувається розробка. Після його установки  всі ресурси (Prometheus, Alertmanager та ін.) необхідно буде створити власноруч, він описує лише створення оператору та CRDs, якими і маніпулює. Prometheus Operator не тягне за собою купи дашбордів для графани чи щось подібне, як наступний проект в списку.
  • kube-prometheus. Також офіційний спосіб інсталяції, який вже включає приклад конфігурації для повного моніторингу Kubernetes кластеру, створює Prometheus та Alertmanager в HA режимі і навіть активізує нотифікації у разі критичних проблем. Так би мовити мінімальний набір Kubernetes-користувача.
  • kube-prometheus-stack helm chart надає схожі з kube-prometheus  можливості, проте не є офіційним рішенням від проекту.

Із питаннями по першим двом можна звертатись в офіційний Kubernetes slack-канал. Community helm чарт може мати інакшу конфігугацію, інші Grafana-дашборди та навіть інші лейбли для додавання сервісів до моніторингу. Однак всередині все ті ж CRDs та оператор.

У статті буде розглянуто лише два перші способи установки. Основна ціль статті - показати як працювати із примітивами оператора, а не просто встановити Prometheus.


MINIKUBE AND EXPERIMENTAL APP SETUP

У якості дистрибутива Kubernetes оберемо звичайний minikube, але згодиться навіть те, що є під рукою. Ініціюємо його так, як рекомендує сам проєкт:

$ curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
$ sudo install minikube-linux-amd64 /usr/local/bin/minikube

$ minikube delete && minikube start --kubernetes-version=v1.23.0 --memory=6g --bootstrapper=kubeadm --extra-config=kubelet.authentication-token-webhook=true --extra-config=kubelet.authorization-mode=Webhook --extra-config=scheduler.bind-address=0.0.0.0 --extra-config=controller-manager.bind-address=0.0.0.0


Також у разі minikube рекомендується вимкнути metrics-server аддон:

$ minikube addons disable metrics-server

Перевіримо статус роботи K8s:

$ minikube status
minikube
type: Control Plane
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configured


Чудово! Тепер задеплоїмо тестовий додаток example-app, для подальшої демонстрації роботи Prometheus:

$ kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: example-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: example-app
  template:
    metadata:
      labels:
        app: example-app
    spec:
      containers:
      - name: example-app
        image: fabxc/instrumented_app
        ports:
        - name: web
          containerPort: 8080
---
kind: Service
apiVersion: v1
metadata:
  name: example-app
  labels:
    app: example-app
spec:
  # type: NodePort
  selector:
    app: example-app
  ports:
  - name: web
    port: 8080
EOF


Перевіримо чи сервіс справді створився:

$ kubectl get pods
NAME                                   READY   STATUS    RESTARTS   AGE
example-app-56cc7f77dd-56zvm           1/1     Running   0          42s
example-app-56cc7f77dd-bmplr           1/1     Running   0          42s
example-app-56cc7f77dd-l45z7           1/1     Running   0          42s
...

$ kubectl get svc
NAME                  TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
example-app           ClusterIP   10.100.52.216   <none>        8080/TCP   44s
...


На :8080/metrics сервіс має відповідати Prometheus-форматом метрик зі статистикою своєї роботи.


PROMETHEUS-OPERATOR SETUP

Тепер встановимо Prometheus Operator, використавши останній зі стабільних тегів:

$ git clone git@github.com:prometheus-operator/prometheus-operator.git
$ cd prometheus-operator
$ git checkout tags/v0.57.0


$ kubectl create -f bundle.yaml

$ kubectl get crds

NAME                                        CREATED AT
alertmanagerconfigs.monitoring.coreos.com    2022-06-18T11:02:18Z
alertmanagers.monitoring.coreos.com         2022-06-18T11:02:18Z
podmonitors.monitoring.coreos.com           2022-06-18T11:02:18Z
probes.monitoring.coreos.com                2022-06-18T11:02:18Z
prometheuses.monitoring.coreos.com          2022-06-18T11:02:18Z
prometheusrules.monitoring.coreos.com       2022-06-18T11:02:18Z
servicemonitors.monitoring.coreos.com       2022-06-18T11:02:18Z
thanosrulers.monitoring.coreos.com          2022-06-18T11:02:18Z


Із виводу видно які саме CRD буде створено. Оператор створився в default неймспейсі, звісно це не годиться для prod-середовища, але зараз йде мова не про це:

$ kubectl get all

NAME                                       READY   STATUS    RESTARTS   AGE
pod/prometheus-operator-567cd8b6f6-xxvjp   1/1     Running   0          3m31s

NAME                          TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
service/kubernetes            ClusterIP   10.96.0.1    <none>        443/TCP    16m
service/prometheus-operator   ClusterIP   None         <none>        8080/TCP   3m31s

NAME                                  READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/prometheus-operator   1/1     1            1           3m31s

NAME                                             DESIRED   CURRENT   READY   AGE
replicaset.apps/prometheus-operator-567cd8b6f6   1         1         1       3m31s

Це все, абсолютний мінімум. Ніяких web-панелей поки немає, їх ще необхідно буде описати.

RBAC PERMISSIONS

Почнемо із RBAC правил, адже Prometheus потребує доступу до Kubernetes API для опитувань ресурсів (targets) та доступу до майбутнього Alertmanager:

$ kubectl apply -f - <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
  name: prometheus
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: prometheus
rules:
- apiGroups: [""]
  resources:
  - nodes
  - nodes/metrics
  - services
  - endpoints
  - pods
  verbs: ["get", "list", "watch"]
- apiGroups: [""]
  resources:
  - configmaps
  verbs: ["get"]
- apiGroups:
  - networking.k8s.io
  resources:
  - ingresses
  verbs: ["get", "list", "watch"]
- nonResourceURLs: ["/metrics"]
  verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: prometheus
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: prometheus
subjects:
- kind: ServiceAccount
  name: prometheus
  namespace: default
EOF


DEPLOY AND CONFIGURE PROMETHEUS

Маючи працюючий оператор та необхідний ServiceAccount, можемо перейти до установки Prometheus:

$ kubectl apply -f - <<EOF
apiVersion: monitoring.coreos.com/v1
kind: Prometheus
metadata:
  name: prometheus
spec:
  serviceAccountName: prometheus
  serviceMonitorNamespaceSelector: {}
  serviceMonitorSelector: {}
  podMonitorSelector: {}

  resources:
    requests:
      memory: 400Mi
EOF


serviceAccountName вказує який саме ServiceAccount використовувати, serviceMonitorNamespaceSelector описує в які неймспейси дивитись для додавання нових target-ів для моніторингу, serviceMonitorSelector та podMonitorSelector описують по яким лейблам (їх регулярним виразам) додавати serviceMonitor-и та podMonitor-и (CRDs, про котрі поговоримо далі). Цього разу ми не будемо обмежуватись в неймінгу, на що вказує значення {}. Проте щоб обмежити моніторинг лише одним неймспейсом - можна вказати наступне:

...
serviceMonitorNamespaceSelector:
    matchLabels:
      name: some-namespace
...


Перевіримо чи Прометеус працює:

$ kubectl get prometheus
NAME         VERSION   REPLICAS   AGE
prometheus                        64s

$ kubectl get pods
NAME                                   READY   STATUS    RESTARTS   AGE
...
prometheus-prometheus-0                2/2     Running   0          68s

$ kubectl get services

NAME                  TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
prometheus-operated   ClusterIP   None         <none>        9090/TCP   2m49s
...


Відкриємо панель Prometheus, щоб пересвідчитись що він працює та справді поки нічого не моніторить:

$ kubectl port-forward svc/prometheus-operated 9090:9090

 

 DEPLOY AND CONFIGURE SERVICEMONITOR

Оператор використовує ServiceMonitor об'єкти для того, щоб описати цілі (target-и) моніторингу. Він використовує лейбли/селектори для того, щоб описати які K8s сервіси, в яких саме неймспейсах та із якими портами мають бути додані.

Ця блок-схема гарно демонструє логіку роботи ServiceMonitor та і оператора в цілому:

Credits https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/troubleshooting.md#troubleshooting-servicemonitor-changes


Створимо ServiceMonitor для моніторингу самого Прометеусу:

$ kubectl apply -f - <<EOF
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: prometheus
  labels:
    name: prometheus
spec:
  selector:
    matchLabels:
      operated-prometheus: "true"

  namespaceSelector:
    any: true
  endpoints:
    - port: web
EOF


Цей ServiceMonitor буде слідкувати за сервісами із селектором operated-prometheus: "true", що знаходяться в будь-якому нейсмейсі (any: true) та web-портом. operated-prometheus: "true" лейбл додається автоматично до всіх інстансів Prometheus:

$ kubectl get svc prometheus-operated -o yaml

apiVersion: v1
kind: Service
metadata:
  creationTimestamp: "2022-05-18T11:48:51Z"
  labels:
    operated-prometheus: "true"

  name: prometheus-operated
  namespace: default
  ownerReferences:
  - apiVersion: monitoring.coreos.com/v1
    kind: Prometheus
    name: prometheus
    uid: d7412620-855a-489a-9de2-322b0c57e79f
  resourceVersion: "2834"
  uid: 80f66293-0552-41e5-81a2-ce5d2720e722
spec:
  clusterIP: None
  clusterIPs:
  - None
  internalTrafficPolicy: Cluster
  ipFamilies:
  - IPv4
  ipFamilyPolicy: SingleStack
  ports:
  - name: web
    port: 9090
    protocol: TCP
    targetPort: web
  selector:
    app.kubernetes.io/name: prometheus
  sessionAffinity: None
  type: ClusterIP
status:
  loadBalancer: {}


Також додамо до моніторингу тестовий сервіс example-app, котрий ми створили для демонстративних цілей:

$ kubectl apply -f - <<EOF
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: example-app
  labels:
    name: example-app
spec:
  selector:
    matchLabels:
      app: example-app

  namespaceSelector:
    any: true
  endpoints:
    - port: web
EOF


Процес появи ендпоїнтів в веб-панелі Prometheus не миттєвий, зазвичай треба зачекати декілька хвилин:


 

DEPLOY AND CONFIGURE PODMONITOR

Часом необхідно моніторити поди, котрі не мають відповідного сервісу. Це може бути, скажімо, моніторинг через sidecar-контейнер із окремим портом чи просто додатку не потрібен K8s-сервіс взагалі. У такому разі можливості обирати поди за лейблом сервісу немає і у нагоді стає PodMonitor. Опишемо PodMonitor, котрий буде слідкувати за портом 8080 кожного поду із example-app деплойменту:

$ kubectl apply -f - <<EOF
apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
  name: example-app
  labels:
    name: example-app
spec:
  namespaceSelector:
    matchNames:
      - default
  selector:
    matchLabels:
      app: example-app

  podMetricsEndpoints:
  - targetPort: 8080
EOF


selector обере лише поди із лейблом app: example-app в default неймспейсі. Звісно можна також використовувати вибірку по всіх неймспейсах, котру ми використовували раніше.


 ADDITIONAL SCRAPE CONFIGURATION

Хоч це і не найліпший варіант по багатьом причинам, але є можливість додавати конфігурацію Prometheus через Secret файли. Вони зобов'язані мати коректний синтаксис і лише користувач відповідальний за їхню правильність. Інакше вони не будуть включені в загальну конфігурацію.

Створимо таку додаткову конфігурацію:

$ vim prometheus-additional-job.yaml

- job_name: "example-app"   
  static_configs:
    - targets: ["example-app.default:8080"]


Вона описує додатковий target, але у звичному для Prometheus форматі. example-app.default - внутрішнє доменне ім'я нашого демонстраційного сервісу, що лежить в неймспейсі default. Тепер створимо секрет із prometheus-additional-job.yaml та застосуємо його:

$ kubectl create secret generic additional-scrape-configs --from-file=prometheus-additional-job.yaml --dry-run=client -oyaml > additional-scrape-configs.yaml

$ cat additional-scrape-configs.yaml
apiVersion: v1
data:
  prometheus-additional-job.yaml: LSBqb2JfbmFtZTogImV4YW1wbGUtYXBwIiAgIAogIHN0YXRpY19jb25maWdzOiAgICAgCiAgICAtIHRhcmdldHM6IFsiZXhhbXBsZS1hcHAuZGVmYXVsdCJdCg==
kind: Secret
metadata:
  creationTimestamp: null
  name: additional-scrape-configs


$ kubectl apply -f additional-scrape-configs.yaml


Але це ще не все. Потрібно також описати цей секрет в самому Прометеусі:

$ kubectl apply -f - <<EOF
apiVersion: monitoring.coreos.com/v1
kind: Prometheus
metadata:
  name: prometheus
spec:
  serviceAccountName: prometheus
  serviceMonitorNamespaceSelector: {}
  serviceMonitorSelector: {}
  podMonitorSelector: {}
  additionalScrapeConfigs:
    name: additional-scrape-configs
    key: prometheus-additional-job.yaml

  resources:
    requests:
      memory: 400Mi
  enableAdminAPI: false
EOF


Додалась секція additionalScrapeConfigs, все інше залишилось таким, як і було раніше.

Такий конфіг варто використовувати лише якщо ресурси для моніторингу не можна описати попередніми способами, наприклад у разі, якщо вони не лежать у межах одного K8s кластера.

 

DEPLOY AND CONFIGURE ALERTMANAGER

Прометеус оператор також відповідальний за створення Alertmanager ресурсів, що дозволяють декларативно описати Alertmanager кластер. Як і раніше, все це завдяки кастомним ресурсам, що привнесли CRDs.

Створимо конфігураційний файл Alertmanager. Цього разу опишемо ресурс із відправкою повідомлень у вигаданий Wechat чат:

$ kubectl apply -f - <<EOF
apiVersion: monitoring.coreos.com/v1alpha1
kind: AlertmanagerConfig
metadata:
  name: config-alertmanager
  labels:
    alertmanagerConfig: example-app
spec:
  route:
    groupBy: ['job']
    groupWait: 30s
    groupInterval: 5m
    repeatInterval: 12h
    receiver: 'wechat-example-app'
  receivers:
  - name: 'wechat-example-app'
    wechatConfigs:
    - apiURL: 'http://wechatserver:8080/'
      corpID: 'wechat-corpid'
      apiSecret:
        name: 'wechat-config'
        key: 'apiSecret'

---
apiVersion: v1
kind: Secret
type: Opaque
metadata:
  name: wechat-config
data:
  apiSecret: cGFzc3dvcmQK
EOF


Після цього опишемо сам Alertmanager, що використовуватиме цей конфігураційний файл:

$ kubectl apply -f - <<EOF
apiVersion: monitoring.coreos.com/v1
kind: Alertmanager
metadata:
  name: example-app
spec:
  replicas: 1
  alertmanagerConfigSelector:
    matchLabels:
      alertmanagerConfig: example-app

EOF


Це все. Як і раніше, для того, щоб побачити веб-панель, її необхідно прокинути, використовуючи port-forward:

$ kubectl port-forward svc/alertmanager-operated 9093:9093

Після чого за адресою http://localhost:9093/#/status можна побачити новий конфіг.



Окрім всього цього, Alertmanager також має бути прив'язаний до Prometheus, котрий ми створили раніше. Для цього необхідно лиш дещо поправити ресурс останнього:

$ kubectl apply -f - <<EOF
apiVersion: monitoring.coreos.com/v1
kind: Prometheus
metadata:
  name: prometheus
spec:
  serviceAccountName: prometheus
  serviceMonitorNamespaceSelector: {}
  serviceMonitorSelector: {}
  podMonitorSelector: {}
  additionalScrapeConfigs:
    name: additional-scrape-configs
    key: prometheus-additional-job.yaml
  resources:
    requests:
      memory: 400Mi
  enableAdminAPI: false
  alerting:
    alertmanagers:
    - namespace: default
      name: alertmanager-operated
      port: web

  ruleSelector:
    matchLabels:
      role: alert-rules
      prometheus: example-app

EOF



CONFIGURING PROMETHEUS RULES

Ресурс PrometheusRule підтримує опис однієї чи більше RuleGroups. RuleGroups - це множина правил, котрі представляють 2 типи правил: запису чи оповіщення.

Виключно задля демонстрації опишемо наступний PrometheusRule, котрий буде постійно викликати оповіщення:

$ kubectl apply -f - <<EOF
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  creationTimestamp: null
  labels:
    prometheus: example-app
    role: alert-rules
  name: prometheus-example-rules
spec:
  groups:
  - name: example
    rules:
    - alert: SmallHttpResponseSizeBytes
      expr: http_response_size_bytes{job="default/example-app", quantile="0.99"} > 30

      for: 10m
      labels:
        severity: page
      annotations:
        summary: We need more bytes in response!
EOF


ruleSelector.matchLabels в Prometheus описі має збігатись із labels в PrometheusRule. Інакше Prometheus-оператор їх буде ігнорувати.

Тепер можна відкрити http://localhost:9090/alerts та http://localhost:9093/#/alerts і пересвідчитись в тому, що нотифікації справді існують і складові система працюють разом.


 

FEW WORDS REGARDING KUBE-PROMETHEUS

Як я вже говорив, Kube-prometheus розвивається в межах основного проекту. Він надає готові конфігурації для менеджменту всіх найважливіших системних ресурсів Kubernetes на кшалт вузлів кластеру, системних сервісів і т.п. В тому числі цей репозиторій включає установку Графани та дашбордів до неї.

Установка його така ж проста як і основного оператора. Для початку перестворимо minikube, що використовували раніше:

$ minikube delete && minikube start --kubernetes-version=v1.23.0 --memory=6g --bootstrapper=kubeadm --extra-config=kubelet.authentication-token-webhook=true --extra-config=kubelet.authorization-mode=Webhook --extra-config=scheduler.bind-address=0.0.0.0 --extra-config=controller-manager.bind-address=0.0.0.0

Та застосуємо код в репозиторію:

$ git clone git@github.com:prometheus-operator/kube-prometheus.git
$ cd kube-prometheus
$ kubectl apply --server-side -f manifests/setup
$ kubectl apply -f manifests/


Цього разу функціонал metrics-server вже покривається окремим адаптером тому його потрібно заборонити:

$ minikube addons disable metrics-server

Цього разу ввесь стек буде проінтсальовано в monitoring неймспейс:

$ kubectl get deploy -n monitoring
NAME                  READY   UP-TO-DATE   AVAILABLE   AGE
blackbox-exporter     1/1     1            1           11m
grafana               1/1     1            1           11m
kube-state-metrics    1/1     1            1           11m
prometheus-adapter    2/2     2            2           11m
prometheus-operator   1/1     1            1           11m


Додавання своїх сервісів для моніторингу відбувається аналогічним чином, проте попередньо потрібно заглянути в конфіг Прометеуса, щоб побачити які налаштування лейблів він приймає:

$ kubectl get prometheus -n monitoring
NAME   VERSION   REPLICAS   AGE
k8s    2.36.1    2          19m

$ kubectl  -n monitoring get prometheus k8s -o yaml
kind: Prometheus
metadata:
  annotations:
 ...
spec:
  alerting:
    alertmanagers:
    - apiVersion: v2
      name: alertmanager-main
      namespace: monitoring
      port: web
  ...
  podMonitorNamespaceSelector: {}
  podMonitorSelector: {}
  probeNamespaceSelector: {}
  probeSelector: {}

  replicas: 2
  resources:
    requests:
      memory: 400Mi
  ruleNamespaceSelector: {}
  ruleSelector: {}
  scrapeInterval: 30s
  securityContext:
    fsGroup: 2000
    runAsNonRoot: true
    runAsUser: 1000
  serviceAccountName: prometheus-k8s
  serviceMonitorNamespaceSelector: {}
  serviceMonitorSelector: {}

  version: 2.36.1
status:
  availableReplicas: 2
  conditions:
...
  unavailableReplicas: 0
  updatedReplicas: 2


Тут, як і в prometheus-operator, немає обмежень на лейбли podMonitor та ServiceMonitor. Але в операторі, що виливається ком'юніті helm-чартом kube-prometheus-stack такі є:

$ kubectl get prometheus kube-prometheus-stack-prometheus -o yaml

apiVersion: monitoring.coreos.com/v1
kind: Prometheus
...
  podMonitorNamespaceSelector: {}
  podMonitorSelector:
    matchLabels:
      release: kube-prometheus-stack

  portName: http-web
  probeNamespaceSelector: {}
  probeSelector:
    matchLabels:
      release: kube-prometheus-stack

  replicas: 1
  retention: 10d
  routePrefix: /
  ruleNamespaceSelector: {}
  ruleSelector:
    matchLabels:
      release: kube-prometheus-stack
  scrapeInterval: 30s
  securityContext:
    fsGroup: 2000
    runAsGroup: 2000
    runAsNonRoot: true
    runAsUser: 1000
  serviceAccountName: kube-prometheus-stack-prometheus
  serviceMonitorNamespaceSelector: {}
  serviceMonitorSelector:
    matchLabels:
      release: kube-prometheus-stack

...


Toбто podMonitor/ServiceMonitor мають бути із додатковим лейблом 'release: kube-prometheus-stack' (в залежності яка була обрана вами назва Helm реліза) щоб їх помітив оператор Prometheus.


Посилання:
https://blog.container-solutions.com/prometheus-operator-beginners-guide
https://github.com/prometheus-operator/prometheus-operator
https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/user-guides/getting-started.md
https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/user-guides/alerting.md
https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/design.md

Немає коментарів:

Дописати коментар