В продолжение поста Kubernetes: мониторинг с Prometheus, в котором мы настроили мониторинг вручную, и более-менее разобрались с тем, как оно всё внутри работает – теперь попробуем прикрутить Prometheus Operator из Helm-репозитория.
Напомню, задача – поднять Prometheus и все необходимые експортёры в AWS Elastic Kubernetes Cluster, и с него через /federation
передавать метрики на наш “центровой” Prometheus, где уже есть Alertmanager и Grafana.
Смутил целый набор чартов – есть просто Prometheus, есть kube-prometheus
, есть prometheus-operator
, причём от разных разработчиков:
- Bitnami Prometheus Operator – https://github.com/bitnami/charts/tree/master/bitnami/prometheus-operator/
- CoreOS Prometheus Operator – https://github.com/coreos/prometheus-operator
- Helm Community Prometheus Operator – https://github.com/prometheus-community/helm-charts
- CoreOS kube-prometheus – https://github.com/coreos/kube-prometheus
- И “просто Prometheus” – https://github.com/helm/charts/tree/master/stable/prometheus
Хотя в репозитории находится только один prometheus-operator
:
[simterm]
$ helm search repo stable/prometheus-operator -o yaml - app_version: 0.38.1 description: Provides easy monitoring definitions for Kubernetes services, and deployment and management of Prometheus instances. name: stable/prometheus-operator version: 8.14.0
[/simterm]
Разница между stable/prometheus
и stable/prometheus-operator
в том, что в Operator включена Grafana с набором готовых дашборд и набор ServiceMonitors
для сбора метрик с сервисов кластера, таких как CoreDNS, API Server, Scheduler, etc.
Собственно – используем stable/prometheus-operator
.
Содержание
Prometheus Operator deployment
Деплоим:
[simterm]
$ helm install --namespace monitoring --create-namespace prometheus stable/prometheus-operator manifest_sorter.go:192: info: skipping unknown hook: "crd-install" manifest_sorter.go:192: info: skipping unknown hook: "crd-install" manifest_sorter.go:192: info: skipping unknown hook: "crd-install" manifest_sorter.go:192: info: skipping unknown hook: "crd-install" manifest_sorter.go:192: info: skipping unknown hook: "crd-install" manifest_sorter.go:192: info: skipping unknown hook: "crd-install" NAME: prometheus LAST DEPLOYED: Mon Jun 15 17:54:27 2020 NAMESPACE: monitoring STATUS: deployed REVISION: 1 NOTES: The Prometheus Operator has been installed. Check its status by running: kubectl --namespace monitoring get pods -l "release=prometheus" Visit https://github.com/coreos/prometheus-operator for instructions on how to create & configure Alertmanager and Prometheus instances using the Operator.
[/simterm]
Проверяем поды:
[simterm]
$ kk -n monitoring get pod NAME READY STATUS RESTARTS AGE alertmanager-prometheus-prometheus-oper-alertmanager-0 2/2 Running 0 41s prometheus-grafana-85c9fbc85c-ll58c 2/2 Running 0 46s prometheus-kube-state-metrics-66d969ff69-6b7t8 1/1 Running 0 46s prometheus-prometheus-node-exporter-89mf4 1/1 Running 0 46s prometheus-prometheus-node-exporter-bpn67 1/1 Running 0 46s prometheus-prometheus-node-exporter-l9wjm 1/1 Running 0 46s prometheus-prometheus-node-exporter-zk4cm 1/1 Running 0 46s prometheus-prometheus-oper-operator-7d5f8ff449-fl6x4 2/2 Running 0 46s prometheus-prometheus-prometheus-oper-prometheus-0 3/3 Running 1 31
[/simterm]
Note: alias kk="kubectl" >> ~/.bashrc
Итак, Prometheus Operator деплоит нам целый набор сервисов – и сам Prometheus, и Alertmanager, и Grafana, плюс набор ServiceMonitors:
[simterm]
$ kk -n monitoring get servicemonitor NAME AGE prometheus-prometheus-oper-alertmanager 3m53s prometheus-prometheus-oper-apiserver 3m53s prometheus-prometheus-oper-coredns 3m53s prometheus-prometheus-oper-grafana 3m53s prometheus-prometheus-oper-kube-controller-manager 3m53s prometheus-prometheus-oper-kube-etcd 3m53s prometheus-prometheus-oper-kube-proxy 3m53s prometheus-prometheus-oper-kube-scheduler 3m53s prometheus-prometheus-oper-kube-state-metrics 3m53s prometheus-prometheus-oper-kubelet 3m53s prometheus-prometheus-oper-node-exporter 3m53s prometheus-prometheus-oper-operator 3m53s prometheus-prometheus-oper-prometheus 3m53s
[/simterm]
Роль ServiceMonitors рассмотрим ниже, когда будем добавлять свой ServiceMonitor в Добавление сервиса в мониторинг.
Grafana access
Пока всё это тестируем – пробросим порт на Grafana, что бы посмотреть какие дашборды там есть.
Находим под с Grafana:
[simterm]
$ kk -n monitoring get pod NAME READY STATUS RESTARTS AGE alertmanager-prometheus-prometheus-oper-alertmanager-0 2/2 Running 0 103s prometheus-grafana-85c9fbc85c-wl856 2/2 Running 0 107s ...
[/simterm]
И вызываем port-forward
:
[simterm]
$ kk -n monitoring port-forward prometheus-grafana-85c9fbc85c-wl856 3000:3000 Forwarding from 127.0.0.1:3000 -> 3000 Forwarding from [::1]:3000 -> 3000
[/simterm]
Открываем в браузере localhost:3000, логинимся с admin и паролем prom-operator, и получаем целый набор готовых графиков, например:
На момент написания – Prometheus Operator запускал Grafana 7.0.3 (на нашем “центровом” сервере мониторинга всё ещё 6.5).
Собственно, из всего этого в будущем просто можно будет надёргать примеров всяких запросов.
На сейчас нам тут не нужны ни сама Grafana, ни Alertmanager, так что попозже мы их выпилим.
Prometheus Operator configuration
Prometheus Operator использует Custom Resource Definitions, которые описывают ресурсы:
[simterm]
$ kk -n monitoring get crd NAME CREATED AT alertmanagers.monitoring.coreos.com 2020-06-15T14:47:44Z eniconfigs.crd.k8s.amazonaws.com 2020-04-10T07:21:20Z podmonitors.monitoring.coreos.com 2020-06-15T14:47:45Z prometheuses.monitoring.coreos.com 2020-06-15T14:47:46Z prometheusrules.monitoring.coreos.com 2020-06-15T14:47:47Z servicemonitors.monitoring.coreos.com 2020-06-15T14:47:47Z thanosrulers.monitoring.coreos.com 2020-06-15T14:47:48Z
[/simterm]
Например, в prometheuses.monitoring.coreos.com
описывается Custom Resource с именем Prometheus:
[simterm]
$ kk -n monitoring get crd prometheuses.monitoring.coreos.com -o yaml apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition ... spec: ... names: kind: Prometheus listKind: PrometheusList plural: prometheuses singular: prometheus ...
[/simterm]
И далее к нему можно обращаться, как к обычному ресурсу Kubernetes, используя имя из names
:
[simterm]
$ kk -n monitoring get prometheus -o yaml apiVersion: v1 items: - apiVersion: monitoring.coreos.com/v1 kind: Prometheus metadata: annotations: meta.helm.sh/release-name: prometheus meta.helm.sh/release-namespace: monitoring ... spec: alerting: alertmanagers: - apiVersion: v2 name: prometheus-prometheus-oper-alertmanager namespace: monitoring pathPrefix: / port: web baseImage: quay.io/prometheus/prometheus enableAdminAPI: false externalUrl: http://prometheus-prometheus-oper-prometheus.monitoring:9090 listenLocal: false logFormat: logfmt logLevel: info paused: false podMonitorNamespaceSelector: {} podMonitorSelector: matchLabels: release: prometheus portName: web replicas: 1 retention: 10d routePrefix: / ruleNamespaceSelector: {} ruleSelector: matchLabels: app: prometheus-operator release: prometheus securityContext: fsGroup: 2000 runAsGroup: 2000 runAsNonRoot: true runAsUser: 1000 serviceAccountName: prometheus-prometheus-oper-prometheus serviceMonitorNamespaceSelector: {} serviceMonitorSelector: matchLabels: release: prometheus version: v2.18.1 ...
[/simterm]
Нам тут интересно значение serviceMonitorSelector
:
... serviceMonitorSelector: matchLabels: release: prometheus
Которое определяет какие ServiceMonitors будут включены под “наблюдение” Prometheus.
Добавление сервиса в мониторинг
Теперь попробуем добавить новый сервис в наш мониторинг:
- запустим Redis server
- запустип
redis_exporter
- добавим ServiceMonitor
- настроим Prometheus Operator использование этого ServiceMonitor для сбора метрик с експортёра
Запуск Redis server
Создаём неймспейс, что бы максимально приблизить условия к “боевым”, когда мониторинг и приложения работают не в одном default namespace, а в разных:
[simterm]
$ kk create ns redis-test namespace/redis-test created
[/simterm]
Запускаем Redis:
[simterm]
$ kk -n redis-test run redis --image=redis deployment.apps/redis created
[/simterm]
Создаём для него сервис:
[simterm]
$ kk -n redis-test expose deploy redis --type=ClusterIP --name redis-svc --port 6379 service/redis-svc exposed
[/simterm]
Проверяем:
[simterm]
$ kk -n redis-test get svc redis-svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE redis-svc ClusterIP 172.20.237.116 <none> 6379/TCP 25s
[/simterm]
Окей – сам Redis работает, добавляем его експортер.
Запуск redis_exporter
Запускаем из Helm:
[simterm]
$ helm install -n monitoring redis-exporter --set "redisAddress=redis://redis-svc.redis-test.svc.cluster.local:6379" stable/prometheus-redis-exporter
[/simterm]
Проверяем его сервис:
[simterm]
$ kk -n monitoring get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE alertmanager-operated ClusterIP None <none> 9093/TCP,9094/TCP,9094/UDP 89m ... redis-exporter-prometheus-redis-exporter ClusterIP 172.20.239.67 <none> 9121/TCP 84s
[/simterm]
Его ендпоинт:
[simterm]
$ kk -n monitoring get endpoints redis-exporter-prometheus-redis-exporter -o yaml apiVersion: v1 kind: Endpoints metadata: ... labels: app: prometheus-redis-exporter app.kubernetes.io/managed-by: Helm chart: prometheus-redis-exporter-3.4.1 heritage: Helm release: redis-exporter name: redis-exporter-prometheus-redis-exporter namespace: monitoring ... ports: - name: redis-exporter port: 9121 protocol: TCP
[/simterm]
Поверим доступ к метрикам – запустим дебаг-под с Debian, устанавливаем в нём curl
:
[simterm]
$ kk -n monitoring run --rm -ti debug --image=debian --restart=Never bash If you don't see a command prompt, try pressing enter. root@debug:/# apt update && apt -y install curl
[/simterm]
И обращаемся к ендпоинту сервиса redis_exporter
:
[simterm]
root@debug:/# curl redis-exporter-prometheus-redis-exporter:9121/metrics ... # HELP redis_up Information about the Redis instance # TYPE redis_up gauge redis_up 1 # HELP redis_uptime_in_seconds uptime_in_seconds metric # TYPE redis_uptime_in_seconds gauge redis_uptime_in_seconds 2793
[/simterm]
Либо без дополнительного пода – делаем port-forward
на redis-svc
:
[simterm]
$ kk -n monitoring port-forward svc/redis-exporter-prometheus-redis-exporter 9121:9121 Forwarding from 127.0.0.1:9121 -> 9121 Forwarding from [::1]:9121 -> 9121
[/simterm]
И проверяем с локальной машины:
[simterm]
$ curl localhost:9121/metrics ... redis_up 1 # HELP redis_uptime_in_seconds uptime_in_seconds metric # TYPE redis_uptime_in_seconds gauge redis_uptime_in_seconds 8818
[/simterm]
Хорошо – у нас есть приложение – Редис, есть его експортёр, который отдаёт нам метрики на порту 9121 по URI /metrics
– теперь надо настроить Prometheus Operator на сбор метрик с него.
Создание Kubernetes ServiceMonitor
Проверим лейблы нашего redis_exporter
:
[simterm]
$ kk -n monitoring get deploy redis-exporter-prometheus-redis-exporter -o yaml apiVersion: extensions/v1beta1 kind: Deployment metadata: ... generation: 1 labels: app: prometheus-redis-exporter app.kubernetes.io/managed-by: Helm chart: prometheus-redis-exporter-3.4.1 heritage: Helm release: redis-exporter ...
[/simterm]
И ещё раз глянем селектор serviceMonitorSelector
ресурса prometheus
:
[simterm]
$ kk -n monitoring get prometheus -o yaml
[/simterm]
В конце манифеста находим:
... serviceMonitorSelector: matchLabels: release: prometheus
Т.е. Prometheus ищет ServiceMonitor
-ы с тегом release, у которых значение prometheus.
Далее – создаём ServiceMonitor
:
apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: labels: serviceapp: redis-servicemonitor release: prometheus name: redis-servicemonitor namespace: monitoring spec: endpoints: - bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token interval: 15s port: redis-exporter namespaceSelector: matchNames: - monitoring selector: matchLabels: release: redis-exporter
В его labels
задаём release: prometheus, что бы Prometheus его обнаружил, а в selector.matchLabels
– указываем поиск сервисов с тегом release: redis-exporter.
Применяем:
[simterm]
$ kk -n monitoring apply -f redis-service-monitor.yaml servicemonitor.monitoring.coreos.com/redis-servicemonitor created
[/simterm]
И проверяем таргеты Prometheus:
Что надо дальше?
А дальше надо выпилить из Operator запуск Alertmanager и Grafana.
Prometheus Operator Helm deployment и конфигурация сервисов
Хотя – а зачем выпиливать Графану? Там уже есть пачка готовых дашборд – пусть остаётся.
Давайте сделаем иначе:
- на каждый EKS кластер выкатываем Operator с Grafana, но без Alertmanager
- на локальных кластеру Prometheus retention period для хранения метрик используем дефолтный – 2 недели, и локальные Grafana будут выводить графики за две недели
- Alertmanager выпиливаем – будем использовать его с центрального сервера мониторинга – там уже настроены роуты, каналы, и прочее – надо будет только добавить алерты
- центральный сервер мониторинга хранит метрики год, и там нарисуем свою дашборду(ы) Grafana
Значит надо будет создать два LoadBalancer – один с типом internet-facing для Grafana, и один для Prometheus – internal, т.к. к Prometheus в кластере будет ходить “центральный” Prometheus через AWS VPC Peering, и с /federation
забирать у него метрики.
И всё это надо заинтегрировать с нашей автоматизацией – Ansible, см. AWS Elastic Kubernetes Service: — автоматизация создания кластера, часть 2 — Ansible, eksctl.
Но сначала сделаем вручную, конечно.
Итак, что нам надо изменить в дефолтном деплое Prometheus Operator?
- убрать деплой Alertmanager
- добавить настройки для:
- Prometheus и Grafana – должны деплоится за LoadBalancer
- логин-пароль для Grafana
Дальше смотрим доступные параметры в документации – https://github.com/helm/charts/tree/master/stable/prometheus-operator.
Пока начнём с того, что передеплоим стек без Алертменеджера, для этого надо передать alertmanager.enabled
.
Проверяем под сейчас:
[simterm]
$ kk -n monitoring get pod NAME READY STATUS RESTARTS AGE alertmanager-prometheus-prometheus-oper-alertmanager-0 2/2 Running 0 24h ...
[/simterm]
Редеплоим, через --set
задаём alertmanager.enabled=false:
[simterm]
$ helm upgrade --install --namespace monitoring --create-namespace prometheus stable/prometheus-operator --set "alertmanager.enabled=false"
[/simterm]
Проверяем поды ещё раз – Алертменеджера нет, отлично.
Настройка LoadBalancer
Итак, мы хотим открыть доступ из мира к Grafana – значит нужен будет ресурс Ingress
для неё, и отдельный Ingress
для Prometheus.
Что есть в доке:
grafana.ingress.enabled |
Enables Ingress for Grafana | false |
grafana.ingress.hosts |
Ingress accepted hostnames for Grafana | [] |
Добавляем Ingress
для Графаны:
[simterm]
$ helm upgrade --install --namespace monitoring --create-namespace prometheus stable/prometheus-operator --set "alertmanager.enabled=false" --set grafana.ingress.enabled=true ... Error: UPGRADE FAILED: failed to create resource: Ingress.extensions "prometheus-grafana" is invalid: spec: Invalid value: []networking.IngressRule(nil): either `backend` or `rules` must be specified
[/simterm]
Эм…
Думаете – в документации указано как его настроить? Счас…
Пригорает от такой “документации” иногда.
Какую-то подсказку удалось нагуглить тут: https://zero-to-jupyterhub.readthedocs.io/en/latest/administrator/advanced.html#ingress
Пробуем – теперь уже через values.yaml
, что бы не городить кучу --set
, добавляем hosts
:
grafana: ingress: enabled: true annotations: kubernetes.io/ingress.class: "alb" alb.ingress.kubernetes.io/scheme: "internet-facing" hosts: - "dev-0.eks.monitor.example.com"
Деплоим:
[simterm]
$ helm upgrade --install --namespace monitoring --create-namespace prometheus stable/prometheus-operator -f oper.yaml
[/simterm]
И смотрим логи ALB Controller:
I0617 11:37:48.272749 1 tags.go:43] monitoring/prometheus-grafana: modifying tags { ingress.k8s.aws/cluster: “bttrm-eks-dev-0”, ingress.k8s.aws/stack: “monitoring/prometheus-grafana”, kubernetes.io/service-name: “prometheus-grafa
na”, kubernetes.io/service-port: “80”, ingress.k8s.aws/resource: “monitoring/prometheus-grafana-prometheus-grafana:80”, kubernetes.io/cluster/bttrm-eks-dev-0: “owned”, kubernetes.io/namespace: “monitoring”, kubernetes.io/ingress-name
: “prometheus-grafana”} on arn:aws:elasticloadbalancing:us-east-2:534***385:targetgroup/96759da8-e0b8253ac04c7ceacd7/35de144cca011059
E0617 11:37:48.310083 1 controller.go:217] kubebuilder/controller “msg”=”Reconciler error” “error”=”failed to reconcile targetGroups due to failed to reconcile targetGroup targets due to prometheus-grafana service is not of type NodePort or LoadBalancer and target-type is instance” “controller”=”alb-ingress-controller” “request”={“Namespace”:”monitoring”,”Name”:”prometheus-grafana”}
Хорошо – давайте добавим /target-type: "ip"
, что бы AWS ALB слал трафик прямо на под с Grafana, а не на WorkerNodes, заодно добавим валидные коды ответов:
grafana: ingress: enabled: true annotations: kubernetes.io/ingress.class: "alb" alb.ingress.kubernetes.io/scheme: "internet-facing" alb.ingress.kubernetes.io/target-type: "ip" alb.ingress.kubernetes.io/success-codes: 200,302 hosts: - "dev-0.eks.monitor.example.com"
Либо, что бы использовать Instance type – можно переопределить тип Service для Grafana, и задать его в NodePort:
grafana: service: type: NodePort port: 80 annotations: {} labels: {} ingress: enabled: true annotations: kubernetes.io/ingress.class: "alb" alb.ingress.kubernetes.io/scheme: "internet-facing" alb.ingress.kubernetes.io/success-codes: 200,302 hosts: - "dev-0.eks.monitor.example.com"
Передеплоиваем:
[simterm]
$ helm upgrade --install --namespace monitoring --create-namespace prometheus stable/prometheus-operator -f oper.yaml
[/simterm]
Через минуту поднялся ALB:
[simterm]
$ kk -n monitoring get ingress NAME HOSTS ADDRESS PORTS AGE prometheus-grafana dev-0.eks.monitor.example.com 96759da8-monitoring-promet-***.us-east-2.elb.amazonaws.com 80 30m
[/simterm]
Но теперь при открытии страницы dev-0.eks.monitor.example.com – ALB отдаёт 404:
[simterm]
$ curl -vL dev-0.eks.monitor.example.com * Trying 3.***.***.247:80... * Connected to dev-0.eks.monitor.example.com (3.***.***.247) port 80 (#0) > GET / HTTP/1.1 > Host: dev-0.eks.monitor.example.com > User-Agent: curl/7.70.0 > Accept: */* > * Mark bundle as not supporting multiuse < HTTP/1.1 302 Found ... < Location: /login ... * Connection #0 to host dev-0.eks.monitor.example.com left intact * Issue another request to this URL: 'http://dev-0.eks.monitor.example.com/login' ... > GET /login HTTP/1.1 ... < HTTP/1.1 404 Not Found < Server: awselb/2.0
[/simterm]
Что тут происходит?
- ALB принимает запрос к dev-0.eks.monitor.example.com, отправляет его на TargetGroup с Grafana
- Grafana возвращает 302
/login
- мы возвращаемся к ALB, но теперь в URI передаём
/login
Проверяем правила Listener:
Ну, да – а в правилах балансера на все запросы кроме /
мы возвращаем 404. Соответсвенно, и на /login
тоже возвращается 404.
Очень хотелось бы увидеть комментарии разработчика, который дефолтным рулом path
задал именно такое.
Возвращаемся к нашему values.yaml
, добавляем path
равным /*
.
А hosts
, что бы избежать ошибки “Invalid value: []networking.IngressRule(nil): either `backend` or `rules` must be specified” и не привязываться к конкретному домену можно задать просто в виде ""
:
grafana: ingress: enabled: true annotations: kubernetes.io/ingress.class: "alb" alb.ingress.kubernetes.io/target-type: "ip" alb.ingress.kubernetes.io/scheme: "internet-facing" alb.ingress.kubernetes.io/success-codes: 200,302 hosts: - "" path: /*
Редеплоим, проверяем:
[simterm]
$ kk -n monitoring get ingress NAME HOSTS ADDRESS PORTS AGE prometheus-grafana * 96759da8-monitoring-promet-bb6c-2076436438.us-east-2.elb.amazonaws.com 80 15m
[/simterm]
И открываем в браузере:
Prometheus и Lens
И даже в Lens появились все графики вместо ошибок “Metrics are not available due to missing or invalid Prometheus configuration” и “Metrics not available at the moment“:
В целом – на этом, думаю, всё основное рассмотрели.
Можно прикручивать автоматизацию, и выкатывать в тестирование.
Ссылки по теме
- Kubernetes monitoring with Prometheus – Prometheus operator tutorial
- Prometheus Operator – Installing Prometheus Monitoring Within The Kubernetes Environment
- Quick Start with Prometheus Monitoring
- Prometheus Operator — How to monitor an external service
- How to Set Up DigitalOcean Kubernetes Cluster Monitoring with Helm and Prometheus Operator
- Configuring prometheus-operator helm chart with AWS EKS
- Zero to JupyterHub with Kubernetes
- Howto expose prometheus, grafana and alertmanager with nginx ingress
- Howto expose prometheus, grafana and alertmanager with nginx ingressEKS: failed to reconcile targetGroups due to failed to load serviceAnnotation due to no object matching key