Циклы: обеспечение непрерывных запросов с помощью Observability FaaS


Все мы знакомы с этим небольшим фрагментом кода, который добавляет разумную ценность вашему бизнес-подразделению. Он может материализоваться в виде сценария, программы, строки кода… и будет создавать отчет, новые показатели, ключевые показатели эффективности или создавать новые составные данные. Этот код предназначен для периодического запуска, чтобы соответствовать требованиям к актуальной информации.

В команде Observability мы встречаем эти фрагменты как запросы в базе данных временных рядов (TSDB), чтобы выражать непрерывные запросы, которые отвечают за автоматизацию различных вариантов использования, таких как: удаление, сведение или любая бизнес-логика, которая должна манипулировать данными временных рядов.

Мы уже представили  TSL в предыдущем сообщении блога , в котором было продемонстрировано, как наши клиенты используют доступные протоколы OVH Metrics, такие как Graphite, OpenTSDB, PromQL и WarpScript ™, но когда дело доходит до манипулирования или даже создания новых данных , у вас нет много вариантов, хотя вы можете использовать WarpScript ™ или TSL в качестве языка сценариев вместо языка запросов.

В большинстве случаев эта бизнес-логика требует создания приложения, что требует больше времени, чем выражение логики в виде запроса, нацеленного на TSDB. Создание кода базового приложения — это первый шаг, за которым следует CI / CD (или любой процесс доставки) и настройка его мониторинга. Однако управление сотнями таких небольших приложений приведет к увеличению органических затрат из-за необходимости  поддерживать их вместе с базовой инфраструктурой.

Мы хотели, чтобы эти ценные задачи не ложились на головы нескольких разработчиков, которым в таком случае пришлось бы нести ответственность за владение данными и вычислительные ресурсы , поэтому мы задавались вопросом, как мы можем автоматизировать вещи, не полагаясь на команду в настройке вычислять задания каждый раз, когда кому-то что-то нужно.

Нам нужно было решение, которое сфокусировалось бы на бизнес-логике без необходимости запускать все приложение. Таким образом, кому-то, кто хочет создать файл JSON с ежедневным отчетом (например), нужно будет только выразить соответствующий запрос.



Вы не должны FaaS!

Планирование работы — старая привычная рутина. Будь то задания bash cron, раннеры или специализированные планировщики, когда дело доходит до упаковки фрагмента кода и его периодического запуска, для этого есть название: FaaS.

FaaS был создан с простой целью: сократить время разработки. Мы могли бы найти реализацию с открытым исходным кодом для оценки (например, OpenFaas ), но большинство из них полагалось на управляемый стек контейнеров. Наличие одного контейнера на запрос было бы очень дорогостоящим, плюс разогрев контейнера для выполнения функции, а затем его замораживание было бы очень контрпродуктивным.

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

#def <Циклы>

Именно тогда мы решили создать «Loops»: платформу приложений, на которой вы можете отправить код, который хотите запустить. Вот и все. Цель состоит в том, чтобы продвинуть функцию (буквально!), А не модуль, как это делают все текущие решения FaaS:

function dailyReport(event) {
    return Promise.resolve('Today, everything is fine !')
}


Затем вы можете выполнить его вручную, используя HTTP-вызов или планировщик, подобный Cron.

Оба эти аспекта необходимы, поскольку у вас может быть (например) ежемесячный отчет, но в один прекрасный день потребуется дополнительный, через 15 дней после последнего отчета. Циклы упростят создание нового отчета вручную в дополнение к ежемесячному.

Когда мы начали создавать Loops, были некоторые необходимые ограничения:

  • Эта платформа должна легко масштабироваться , чтобы поддерживать производственную нагрузку OVH.
  • Он должен быть высокодоступным
  • Он должен быть независимым от языка , потому что некоторые из нас предпочитают Python, а другие JavaScript.
  • Это должно быть надежно
  • Часть планирования не должна коррелировать с частью выполнения ( культура μService )
  • Он должен быть безопасным и изолированным, чтобы любой мог размещать непонятный код на платформе.

Реализация петель

Мы решили построить нашу первую версию на V8 . Мы выбрали JavaScript в качестве первого языка, потому что его легко изучить, а асинхронными потоками данных легко управлять с помощью Promises. Кроме того, он очень хорошо сочетается с FaaS, поскольку функции Javascript очень выразительны. Мы построили его на основе нового модуля виртуальной машины NodeJS , который позволяет выполнять код в выделенном контексте V8 .

Контекст V8 похож на объект (JSON), изолированный от вашего исполнения. В контексте вы можете найти собственные функции и объекты. Однако, если вы создадите новый контекст V8, вы увидите, что некоторые переменные или функции изначально недоступны (например, setTimeout (), setInterval () или Buffer ). Если вы хотите их использовать, вам придется внедрить их в свой новый контекст. Последнее, что нужно помнить, это то, что когда у вас есть новый контекст, вы можете легко выполнить сценарий JavaScript в строковой форме.

Контексты выполняют самую важную часть нашего первоначального списка требований: изоляцию . Каждый контекст V8 изолирован, поэтому он не может взаимодействовать с другим контекстом. Это означает, что глобальная переменная, определенная в одном контексте, недоступна в другом. Вам придется построить между ними мост, если вы хотите, чтобы это было так.

Мы не хотели выполнять скрипты с eval () , поскольку вызов этой функции позволяет выполнять JS-код в основном общем контексте с вызывающим его кодом. Затем вы можете получить доступ к тем же объектам, константам, переменным и т. Д. Эта проблема безопасности стала нарушением условий сделки для новой платформы.

Теперь мы знаем, как выполнять наши скрипты, давайте реализуем для них некоторое управление. Чтобы не иметь состояния , каждый экземпляр рабочего цикла Loops (то есть движок JavaScript, способный запускать код в контексте виртуальной машины) должен иметь последнюю версию каждого цикла (цикл — это сценарий, который нужно выполнить). Это означает, что когда пользователь отправляет новый цикл, мы должны синхронизировать его с каждым рабочим циклом. Эта модель хорошо согласуется с парадигмой pub / sub, и, поскольку мы уже используем Kafka в качестве инфраструктуры pub / sub, оставалось лишь создать специальную тему и использовать ее у рабочих. В этом случае публикация включает API, в который пользователь отправляет свои циклы, которые создают событие Kafka, содержащее тело функции. Поскольку у каждого воркера есть своя группа потребителей Kafka, все они получают одинаковые сообщения.

Рабочие подписываются на обновления Loops как потребители Kafka и поддерживают хранилище Loop, которое представляет собой встроенный ключ (хэш Loop) / значение (текущая версия функции). В части API хэши цикла используются в качестве параметров URL для определения того, какой цикл выполнять. После вызова цикл извлекается из карты, затем вводится в контекст V8 , выполняется и отбрасывается. Этот механизм перезагрузки горячего кода гарантирует, что каждый цикл может быть выполнен для каждого рабочего. Мы также можем использовать возможности наших балансировщиков нагрузки для распределения нагрузки на рабочих. Эта простая модель распределения позволяет избежать сложного планирования и упрощает обслуживание всей инфраструктуры.

Для защиты от перезагрузки мы используем очень удобную функцию сжатия журналов Kafka . Сжатие журнала позволяет Kafka сохранять последнюю версию каждого сообщения с ключом. Когда пользователь создает новый цикл, ему будет присвоен уникальный идентификатор, который используется в качестве ключа сообщения Kafka. Когда пользователь обновляет цикл, это новое сообщение будет перенаправлено всем потребителям, но поскольку ключ уже существует, Kafka сохранит только последнюю версию. Когда рабочий перезапускается, он будет использовать все сообщения для восстановления своего внутреннего KV, поэтому будет восстановлено предыдущее состояние. Кафка здесь используется как постоянное хранилище .



Время выполнения циклов

Даже если базовый движок может запускать собственный Javascript, как указано выше, мы хотели, чтобы он выполнял более идиоматические запросы временных рядов, такие как TSL или WarpScript ™ . Для этого мы создали абстракцию Loops Runtime, которая оборачивает не только Javascript, но также запросы TSL и WarpScript ™ в код Javascript. Пользователи должны объявить цикл с его средой выполнения, после чего остается лишь вопрос работы оберток. Например, выполнение цикла WarpScript ™ включает в себя использование простого сценария WarpScript ™ и его отправку посредством HTTP-вызова запроса узла.



Обратная связь по петлям

Безопасное выполнение кода — это начало, но когда дело доходит до выполнения произвольного кода, также полезно получить некоторую обратную связь о состоянии выполнения . Удачно это было или нет? В функции есть ошибка? Если цикл находится в состоянии сбоя, пользователь должен быть немедленно уведомлен.

Это приводит нас к одному особому условию: скрипты пользователя должны уметь определять, все ли в порядке или нет. В базовом движке JavaScript есть два способа сделать это : обратные вызовы и обещания.
Мы выбрали Promises, который предлагает лучшее асинхронное управление. Каждый цикл возвращает обещание в конце скрипта. Отклоненное обещание приведет к статусу ошибки HTTP 500, а решенное — к статусу HTTP 200.

Планирование циклов

При публикации Loops вы можете объявить несколько триггеров аналогично Cron. Каждый триггер будет выполнять HTTP-вызов вашего цикла с дополнительными параметрами.

На основе этой семантики для создания нескольких отчетов мы можем зарегистрировать одну функцию, которая будет планироваться в разных контекстах, определяемых различными параметрами (регион, скорость и т. Д.). См. Пример ниже:

functions:
  warp_apps_by_cells:
    handler: apps-by-cells.mc2
    runtime: ws
    timeout: 30
    environment:
    events:
      - agra:
          rate: R/2018-01-01T00:00:00Z/PT5M/ET1M
          params:
            cell: ovh-a-gra
      - abhs:
          rate: R/2018-01-01T00:00:00Z/PT5M/ET1M
          params:
            cell: ovh-a-bhs


Планирование основано на Metronome, который представляет собой планировщик событий с открытым исходным кодом, с особым упором на планирование, а не на выполнение. Это идеально подходит для циклов, поскольку циклы обрабатывают выполнение, а метроном управляет вызовами выполнения.

Петли трубопроводов

В проекте Loops может быть несколько циклов. Одним из распространенных вариантов использования наших клиентов было использование Loops в качестве платформы данных в режиме потока данных. Поток данных — это способ описания конвейера этапов выполнения. В контексте Loops есть глобальный объект Loop, который позволяет сценарию выполнять другой цикл с этим именем. Затем вы можете связать выполнение цикла, которое будет действовать как пошаговые функции.

Болевые точки: масштабирование приложения NodeJS

Рабочие циклов — это приложения NodeJS. Большинство разработчиков NodeJS знают, что NodeJS использует однопоточный цикл обработки событий . Если вы не позаботитесь о потоковой модели своего приложения nodeJS, вы, скорее всего, пострадаете из-за недостаточной производительности, поскольку будет использоваться только один поток хоста.

NodeJS также имеет доступный модуль кластера , который позволяет приложению использовать несколько потоков. Вот почему в воркере Loops мы начинаем с потока N-1 для обработки вызовов API, где N — общее количество доступных потоков, в результате чего один остается выделенным для главного потока.

Главный поток отвечает за использование тем Kafka и поддержание хранилища Loops , а рабочий поток запускает сервер API. Для каждого запрошенного выполнения цикла он запрашивает у мастера содержимое скрипта и выполняет его в выделенном потоке.

При такой настройке на каждом сервере запускается одно приложение NodeJS с одним потребителем Kafka, что позволяет очень легко масштабировать инфраструктуру, просто добавляя дополнительные серверы или облачные экземпляры.

Вывод

В этом посте мы предварительно ознакомились с Loops, масштабируемой, ориентированной на метрики FaaS с встроенной поддержкой JavaScript и расширенной поддержкой WarpScript ™ и TSL .

У нас еще есть несколько вещей, которые нужно улучшить, например импорт зависимостей в стиле ES5 и предварительный просмотр показателей для проектов циклов наших клиентов. Мы также планируем добавить больше сред выполнения , особенно WASM , что позволит использовать многие другие языки, которые могут быть ориентированы на него, например Go, Rust или Python , в соответствии с предпочтениями большинства разработчиков.

Платформа Loops была частью требования по созданию высокоуровневых функций вокруг продуктов OVH Observability. Это первый шаг к предложению более автоматизированных сервисов, таких как накопление метрик , конвейеры агрегации или экстракторы журналов в метрики .

Этот инструмент был создан как часть набора продуктов Observability с учетом более высокого уровня абстракции, но вам также может потребоваться прямой доступ к API, чтобы реализовать собственную автоматизированную логику для ваших показателей. Вас заинтересует такая функция? Посетите наш канал Gitter, чтобы обсудить это с нами!

Как отслеживать свой кластер Kubernetes с помощью OVH Observability

Наши коллеги из команды K8S на прошлой неделе запустили решение OVH Managed Kubernetes, в котором они управляют главными компонентами Kubernetes и создают ваши узлы поверх нашего решения Public Cloud. Я не буду описывать здесь детали того, как это работает, но в блогах уже есть много сообщений об этом ( здесь и здесь, чтобы вы начали).

В команде Prescience мы используем Kubernetes уже больше года. Наш кластер включает 40 узлов, работающих поверх PCI. Мы постоянно запускаем около 800 модулей и в результате генерируем множество показателей.

Сегодня мы рассмотрим, как мы обрабатываем эти метрики для мониторинга нашего кластера Kubernetes, и (что не менее важно!) Как это сделать с вашим собственным кластером.

Показатели OVH

Как и в любой другой инфраструктуре, вам необходимо отслеживать свой кластер Kubernetes. Вам необходимо точно знать, как ведут себя ваши узлы, кластер и приложения после их развертывания, чтобы предоставлять надежные услуги вашим клиентам. Чтобы сделать это с нашим собственным кластером, мы используем OVH Observability.

OVH Observability не зависит от серверной части, поэтому мы можем передавать метрики в одном формате и запрашивать в другом. Он может обрабатывать:

  • Graphite
  • InfluxDB
  • Metrics2.0
  • OpentTSDB
  • Prometheus
  • Warp10

Он также включает управляемую Grafana для отображения показателей и создания панелей мониторинга.

Узлы мониторинга

Первое, что нужно контролировать, — это состояние узлов. Все остальное начинается с этого.

Чтобы контролировать ваши узлы, мы будем использовать Noderig и Beamium, как описано здесь. Мы также будем использовать Kubernetes DaemonSets для запуска процесса на всех наших узлах.



Итак, приступим к созданию пространства имен…

kubectl create namespace metrics


Затем создайте секрет с метриками токена записи, которые вы можете найти в панели управления OVH.

kubectl create secret generic w10-credentials --from-literal=METRICS_TOKEN=your-token -n metrics


Скопируйте metrics.ymlв файл и примените конфигурацию с помощью kubectl

metrics.yml
# This will configure Beamium to scrap noderig
# And push metrics to warp 10
# We also add the HOSTNAME to the labels of the metrics pushed
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: beamium-config
  namespace: metrics
data:
  config.yaml: |
    scrapers:
      nodering:
        url: http://0.0.0.0:9100/metrics
        period: 30000
        format: sensision
        labels:
          app: nodering

    sinks:
      warp:
        url: https://warp10.gra1.metrics.ovh.net/api/v0/update
        token: $METRICS_TOKEN

    labels:
      host: $HOSTNAME

    parameters:
      log-file: /dev/stdout
---
# This is a custom collector that report the uptime of the node
apiVersion: v1
kind: ConfigMap
metadata:
  name: noderig-collector
  namespace: metrics
data:
  uptime.sh: |
    #!/bin/sh
    echo 'os.uptime' `date +%s%N | cut -b1-10` `awk '{print $1}' /proc/uptime`
---
kind: DaemonSet
apiVersion: apps/v1
metadata:
  name: metrics-daemon
  namespace: metrics
spec:
  selector:
    matchLabels:
      name: metrics-daemon
  template:
    metadata:
      labels:
        name: metrics-daemon
    spec:
      terminationGracePeriodSeconds: 10
      hostNetwork: true
      volumes:
      - name: config
        configMap:
          name: beamium-config
      - name: noderig-collector
        configMap:
          name: noderig-collector
          defaultMode: 0777
      - name: beamium-persistence
        emptyDir:{}
      containers:
      - image: ovhcom/beamium:latest
        imagePullPolicy: Always
        name: beamium
        env:
        - name: HOSTNAME
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
        - name: TEMPLATE_CONFIG
          value: /config/config.yaml
        envFrom:
        - secretRef:
            name: w10-credentials
            optional: false
        resources:
          limits:
            cpu: "0.05"
            memory: 128Mi
          requests:
            cpu: "0.01"
            memory: 128Mi
        workingDir: /beamium
        volumeMounts:
        - mountPath: /config
          name: config
        - mountPath: /beamium
          name: beamium-persistence
      - image: ovhcom/noderig:latest
        imagePullPolicy: Always
        name: noderig
        args: ["-c", "/collectors", "--net", "3"]
        volumeMounts:
        - mountPath: /collectors/60/uptime.sh
          name: noderig-collector
          subPath: uptime.sh
        resources:
          limits:
            cpu: "0.05"
            memory: 128Mi
          requests:
            cpu: "0.01"
            memory: 128Mi

Не стесняйтесь менять уровни коллектора, если вам нужна дополнительная информация.

Затем примените конфигурацию с помощью kubectl…

$ kubectl apply -f metrics.yml
# Then, just wait a minutes for the pods to start
$ kubectl get all -n metrics
NAME                       READY   STATUS    RESTARTS   AGE
pod/metrics-daemon-2j6jh   2/2     Running   0          5m15s
pod/metrics-daemon-t6frh   2/2     Running   0          5m14s

NAME                          DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE AGE
daemonset.apps/metrics-daemon 40        40        40      40           40          122d


Вы можете импортировать нашу панель управления в свою Grafana отсюда и сразу же просматривать некоторые показатели о ваших узлах.



OVH Метрики

Поскольку OVH Kube — это управляемая служба, вам не нужно контролировать apiserver, etcd или панель управления. Об этом позаботится команда OVH Kubernetes. Поэтому мы сосредоточимся на показателях cAdvisor и показателях состояния Kube.

Самым зрелым решением для динамического считывания метрик внутри Kube (на данный момент) является Prometheus.


В следующем выпуске Beamium мы сможем воспроизвести функции скребка Prometheus.


Чтобы установить сервер Prometheus, вам необходимо установить Helm в кластере…

kubectl -n kube-system create serviceaccount tiller
kubectl create clusterrolebinding tiller \
    --clusterrole cluster-admin \
    --serviceaccount=kube-system:tiller
helm init --service-account tiller


Затем вам нужно создать следующие два файла: prometheus.yml и values.yml.

# Based on https://github.com/prometheus/prometheus/blob/release-2.2/documentation/examples/prometheus-kubernetes.yml
serverFiles:
  prometheus.yml:
    remote_write:
    - url: "https://prometheus.gra1.metrics.ovh.net/remote_write"
      remote_timeout: 120s
      bearer_token: $TOKEN
      write_relabel_configs:
      # Filter metrics to keep
      - action: keep
        source_labels: [__name__]
        regex: "eagle.*|\
            kube_node_info.*|\
            kube_node_spec_taint.*|\
            container_start_time_seconds|\
            container_last_seen|\
            container_cpu_usage_seconds_total|\
            container_fs_io_time_seconds_total|\
            container_fs_write_seconds_total|\
            container_fs_usage_bytes|\
            container_fs_limit_bytes|\
            container_memory_working_set_bytes|\
            container_memory_rss|\
            container_memory_usage_bytes|\
            container_network_receive_bytes_total|\
            container_network_transmit_bytes_total|\
            machine_memory_bytes|\
            machine_cpu_cores"

    scrape_configs:
    # Scrape config for Kubelet cAdvisor.
    - job_name: 'kubernetes-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:
      - 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
        
      metric_relabel_configs:
      # Only keep systemd important services like docker|containerd|kubelet and kubepods,
      # We also want machine_cpu_cores that don't have id, so we need to add the name of the metric in order to be matched
      # The string will concat id with name and the separator is a ;
      # `/;container_cpu_usage_seconds_total` OK
      # `/system.slice;container_cpu_usage_seconds_total` OK
      # `/system.slice/minion.service;container_cpu_usage_seconds_total` NOK, Useless
      # `/kubepods/besteffort/e2514ad43202;container_cpu_usage_seconds_total` Best Effort POD OK
      # `/kubepods/burstable/e2514ad43202;container_cpu_usage_seconds_total` Burstable POD OK
      # `/kubepods/e2514ad43202;container_cpu_usage_seconds_total` Guaranteed POD OK
      # `/docker/pod104329ff;container_cpu_usage_seconds_total` OK, Container that run on docker but not managed by kube
      # `;machine_cpu_cores` OK, there is no id on these metrics, but we want to keep them also
      - source_labels: [id,__name__]
        regex: "^((/(system.slice(/(docker|containerd|kubelet).service)?|(kubepods|docker).*)?);.*|;(machine_cpu_cores|machine_memory_bytes))$"
        action: keep
      # Remove Useless parents keys like `/kubepods/burstable` or `/docker`
      - source_labels: [id]
        regex: "(/kubepods/burstable|/kubepods/besteffort|/kubepods|/docker)"
        action: drop
        # cAdvisor give metrics per container and sometimes it sum up per pod
        # As we already have the child, we will sum up ourselves, so we drop metrics for the POD and keep containers metrics
        # Metrics for the POD don't have container_name, so we drop if we have just the pod_name
      - source_labels: [container_name,pod_name]
        regex: ";(.+)"
        action: drop
    
    # Scrape config for service endpoints.
    - job_name: 'kubernetes-service-endpoints'
      kubernetes_sd_configs:
      - role: endpoints
      
      relabel_configs:
      - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
        action: keep
        regex: true
      - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme]
        action: replace
        target_label: __scheme__
        regex: (https?)
      - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path]
        action: replace
        target_label: __metrics_path__
        regex: (.+)
      - source_labels: [__address__, __meta_kubernetes_service_annotation_prometheus_io_port]
        action: replace
        target_label: __address__
        regex: ([^:]+)(?::\d+)?;(\d+)
        replacement: $1:$2
      - action: labelmap
        regex: __meta_kubernetes_service_label_(.+)
      - source_labels: [__meta_kubernetes_namespace]
        action: replace
        target_label: namespace
      - source_labels: [__meta_kubernetes_service_name]
        action: replace
        target_label: kubernetes_name

    # Example scrape config for pods
    #
    # The relabeling allows the actual pod scrape endpoint to be configured via the
    # following annotations:
    #
    # * `prometheus.io/scrape`: Only scrape pods that have a value of `true`
    # * `prometheus.io/path`: If the metrics path is not `/metrics` override this.
    # * `prometheus.io/port`: Scrape the pod on the indicated port instead of the
    # pod's declared ports (default is a port-free target if none are declared).
    - job_name: 'kubernetes-pods'
      kubernetes_sd_configs:
      - role: pod

      relabel_configs:
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
        action: keep
        regex: true
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
        action: replace
        target_label: __metrics_path__
        regex: (.+)
      - source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
        action: replace
        regex: ([^:]+)(?::\d+)?;(\d+)
        replacement: $1:$2
        target_label: __address__
      - action: labelmap
        regex: __meta_kubernetes_pod_label_(.+)
      - source_labels: [__meta_kubernetes_namespace]
        action: replace
        target_label: namespace
      - source_labels: [__meta_kubernetes_pod_name]
        action: replace
        target_label: pod_name
      - source_labels: [__meta_kubernetes_pod_node_name]
        action: replace
        target_label: host
      - action: labeldrop
        regex: (pod_template_generation|job|release|controller_revision_hash|workload_user_cattle_io_workloadselector|pod_template_hash)


values.yml
alertmanager:
  enabled: false
pushgateway:
  enabled: false
nodeExporter:
  enabled: false
server:
  ingress:
    enabled: true
    annotations:
      kubernetes.io/ingress.class: traefik
      ingress.kubernetes.io/auth-type: basic
      ingress.kubernetes.io/auth-secret: basic-auth
    hosts:
    - prometheus.domain.com
  image:
    tag: v2.7.1
  persistentVolume:
    enabled: false


Не забудьте заменить свой токен!

Скребок Прометей довольно мощный. Вы можете изменить метку своего временного ряда, оставить несколько, которые соответствуют вашему регулярному выражению, и т. Д. Эта конфигурация удаляет множество бесполезных метрик, поэтому не стесняйтесь настраивать его, если вы хотите увидеть больше метрик cAdvisor (например).

Установите его с помощью Helm…

helm install stable/prometheus \
    --namespace=metrics \
    --name=metrics \
    --values=values/values.yaml \
    --values=values/prometheus.yaml


Добавить добавить секрет базовой аутентификации…

$ htpasswd -c auth foo
New password: <bar>
New password:
Re-type new password:
Adding password for user foo
$ kubectl create secret generic basic-auth --from-file=auth -n metrics
secret "basic-auth" created


Вы можете получить доступ к интерфейсу сервера Prometheus через prometheus.domain.com.



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

Интерфейсы Prometheus — хороший способ изучить ваши метрики, поскольку довольно просто отображать и контролировать вашу инфраструктуру. Вы можете найти нашу панель управления здесь.



Метрики ресурсов

Как сказал @ Martin Schneppenheim в этом посте, для правильного управления кластером Kubernetes вам также необходимо отслеживать ресурсы пода.

Мы установим Kube Eagle, который будет извлекать и предоставлять некоторые метрики о запросах и ограничениях ЦП и ОЗУ, чтобы они могли быть получены только что установленным сервером Prometheus.



Создайте файл с именем eagle.yml.

apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  labels:
    app: kube-eagle
  name: kube-eagle
  namespace: kube-eagle
rules:
- apiGroups:
  - ""
  resources:
  - nodes
  - pods
  verbs:
  - get
  - list
- apiGroups:
  - metrics.k8s.io
  resources:
  - pods
  - nodes
  verbs:
  - get
  - list
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  labels:
    app: kube-eagle
  name: kube-eagle
  namespace: kube-eagle
subjects:
- kind: ServiceAccount
  name: kube-eagle
  namespace: kube-eagle
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: kube-eagle
---
apiVersion: v1
kind: ServiceAccount
metadata:
  namespace: kube-eagle
  labels:
    app: kube-eagle
  name: kube-eagle
---
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: kube-eagle
  name: kube-eagle
  labels:
    app: kube-eagle
spec:
  replicas: 1
  selector:
    matchLabels:
      app: kube-eagle
  template:
    metadata:
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "8080"
        prometheus.io/path: "/metrics"
      labels:
        app: kube-eagle
    spec:
      serviceAccountName: kube-eagle
      containers:
      - name: kube-eagle
        image: "quay.io/google-cloud-tools/kube-eagle:1.0.0"
        imagePullPolicy: IfNotPresent
        env:
        - name: PORT
          value: "8080"
        ports:
        - name: http
          containerPort: 8080
          protocol: TCP
        livenessProbe:
          httpGet:
            path: /health
            port: http
        readinessProbe:
          httpGet:
            path: /health
            port: http


$ kubectl create namespace kube-eagle
$ kubectl apply -f eagle.yml


Затем добавьте импорт этой панели управления Grafana (это та же панель, что и Kube Eagle, но перенесена на Warp10).



Теперь у вас есть простой способ контролировать ресурсы вашего модуля в кластере!

Пользовательские показатели

Как Прометей узнает, что ему нужно соскрести куба-орла? Если вы посмотрите на метаданные eagle.yml, вы увидите, что:

annotations:
  prometheus.io/scrape: "true"
  prometheus.io/port: "8080" # The port where to find the metrics
  prometheus.io/path: "/metrics" # The path where to find the metrics


Эти аннотации запустят процесс автоматического обнаружения Prometheus (описанный в prometheus.ymlстроке 114).

Это означает, что вы можете легко добавить эти аннотации к модулям или службам, которые содержат экспортер Prometheus, а затем пересылать эти метрики в OVH Observability. Вы можете найти неполный список экспортеров Прометея здесь.



Объемный анализ

Как вы видели на странице prometheus.yml, мы попытались отфильтровать множество бесполезных показателей. Например, cAdvisor в новом кластере, всего с тремя настоящими производственными модулями, а также со всей системой kube и пространством имен Prometheus, имеет около 2600 метрик на узел. Используя интеллектуальный подход к очистке, вы можете сократить это количество до 126 серий.

Вот таблица, показывающая приблизительное количество метрик, которые вы создадите, в зависимости от количества узлов (N) и количества производственных модулей (P), которые у вас есть:



Например, если вы запустите три узла с 60 подами, вы сгенерируете 264 * 3 + 32 * 60 ~ = 2700 метрик.

NB: модуль имеет уникальное имя, поэтому при повторном развертывании развертывания вы будете каждый раз создавать 32 новых метрики.

(1) Метрики Noderig: os.mem / os.cpu / os.disk.fs / os.load1 / os.net.dropped (in/out) / os.net.errs (in/out) / os.net.packets (in/out) / os.net.bytes (in/out)/ os.uptime

(2) метрики узлов cAdvisor: machine_memory_bytes / machine_cpu_cores

(3) Метрики узлов состояния Kube: kube_node_info

(4) Показатели узлов Kube Eagle: eagle_node_resource_allocatable_cpu_cores / eagle_node_resource_allocatable_memory_bytes / eagle_node_resource_limits_cpu_cores / eagle_node_resource_limits_memory_bytes / eagle_node_resource_requests_cpu_cores / eagle_node_resource_requests_memory_bytes / eagle_node_resource_usage_cpu_cores / eagle_node_resource_usage_memory_bytes

(5) С помощью наших фильтров мы будем отслеживать около пяти system.slices.

(6) Показатели указываются для каждого контейнера. Pod — это набор контейнеров (минимум два): ваш контейнер + контейнер паузы для сети. Таким образом, мы можем рассматривать (2 * 10 + 6) для количества метрик в пакете. 10 показателей из cAdvisor и шесть для сети (см. Ниже) и для system.slice у нас будет 10 + 6, потому что он рассматривается как один контейнер.

(7) cAdvisor предоставит следующие показатели для каждого контейнера :container_start_time_seconds / container_last_seen / container_cpu_usage_seconds_total / container_fs_io_time_seconds_total / container_fs_write_seconds_total / container_fs_usage_bytes / container_fs_limit_bytes / container_memory_working_set_bytes / container_memory_rss / container_memory_usage_bytes

(8) cAdvisor предоставит следующие показатели для каждого интерфейса: container_network_receive_bytes_total * per interface / container_network_transmit_bytes_total * per interface

(9) kube-dns / beamium-noderig-metrics / kube-proxy / canal / metrics-server

(10) Показатели модулей Kube Eagle: eagle_pod_container_resource_limits_cpu_cores / eagle_pod_container_resource_limits_memory_bytes / eagle_pod_container_resource_requests_cpu_cores / eagle_pod_container_resource_requests_memory_bytes / eagle_pod_container_resource_usage_cpu_cores / eagle_pod_container_resource_usage_memory_bytes

Вывод

Как видите, отслеживать кластер Kubernetes с помощью OVH Observability очень просто. Вам не нужно беспокоиться о том, как и где хранить свои метрики, и вы можете сосредоточиться на использовании кластера Kubernetes для эффективной обработки бизнес-нагрузок, как это делаем мы в группе обслуживания машинного обучения.

Следующим шагом будет добавление системы предупреждений, чтобы уведомить вас, когда ваши узлы не работают (например). Для этого вы можете использовать бесплатный инструмент OVH Alert Monitoring.

Рекомендации по мониторингу наблюдаемости OVH

В команде OVH Observability (ранее Metrics) мы собираем, обрабатываем и анализируем большую часть данных мониторинга OVH. Он представляет около 500 миллионов уникальных показателей, перемещая точки данных с постоянной скоростью 5 миллионов в секунду.

Эти данные можно классифицировать двумя способами: мониторинг хоста или приложения. Мониторинг хоста в основном основан на аппаратных счетчиках (ЦП, память, сеть, диск…), тогда как мониторинг приложений основан на сервисе и его масштабируемости (запросы, обработка, бизнес-логика…).

Мы предоставляем эту услугу для внутренних команд, которые пользуются тем же опытом, что и наши клиенты. По сути, наша служба Observability — это SaaS с уровнем совместимости (поддерживающим InfluxDB, OpenTSDB, Warp10, Prometheus и Graphite), который позволяет ему интегрироваться с большинством существующих решений. Таким образом, команде, которая привыкла к определенному инструменту или уже развернула решение для мониторинга, не нужно будет тратить много времени или усилий при переходе на полностью управляемую и масштабируемую службу: они просто выбирают токен, используют правильный конечная точка, и все готово. Кроме того, наш уровень совместимости предлагает выбор: вы можете отправить свои данные с помощью OpenTSDB, а затем запросить их в PromQL или WarpScript. Подобное комбинирование протоколов приводит к уникальной совместимости с открытым исходным кодом, которая обеспечивает большую ценность.

Scollector, Snap, Telegraf, Graphite, Collectd…

Опираясь на этот опыт, мы коллективно опробовали большинство инструментов сбора данных, но всегда приходили к одному и тому же выводу: мы наблюдали утечку показателей. Каждый инструмент ориентирован на очистку каждого доступного бита данных, что отлично, если вы зависим от графиков, но может оказаться контрпродуктивным с операционной точки зрения, если вам нужно контролировать тысячи хостов. Хотя их можно отфильтровать, командам все равно необходимо понимать весь набор показателей, чтобы знать, что нужно фильтровать.

В OVH мы используем наборы показателей, вырезанные лазером. У каждого хоста есть определенный шаблон (веб-сервер, база данных, автоматизация…), который экспортирует заданное количество показателей, которые можно использовать для диагностики работоспособности и мониторинга производительности приложений.

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

Beamium & Noderig — Идеальная посадка

Наши требования были довольно простыми:
Масштабируемость : мониторинг одного узла так же, как мы наблюдаем за тысячами
Laser-Cut : собирать только релевантные метрики
Надежность : мы хотим, чтобы метрики были доступны даже в наихудших условиях
Просто : Несколько компонентов plug-and-play вместо сложных
Эффективность : мы верим в беспроблемный сбор показателей

Первым решением был Beamium

Beamium обрабатывает два аспекта процесса мониторинга: данные приложения слом и показатели пересылки.

Данные приложения собираются в хорошо известном и широко используемом формате Prometheus. Мы выбрали Prometheus, поскольку в то время сообщество быстро росло, и для него было доступно множество инструментальных библиотек. В Beamium есть две ключевые концепции: источники и приемники.

Источники, из которых Beamium будет извлекать данные, — это просто конечные точки HTTP Prometheus. Это означает, что это так же просто, как предоставить конечную точку HTTP и, в конечном итоге, добавить несколько параметров. Эти данные будут направляться в приемники, что позволяет нам фильтровать их во время процесса маршрутизации между источником и приемником. Приемники — это конечные точки Warp 10 ®, куда мы можем отправлять данные.

После очистки метрики сначала сохраняются на диске, а затем направляются в приемник. Механизм переключения диска при отказе (DFO) позволяет выполнять восстановление после сбоя в сети или удаленно. Таким образом, локально мы сохраняем логику вытягивания Prometheus, но децентрализованно, и мы обращаем ее вспять, чтобы протолкнуть для подачи на платформу, которая имеет много преимуществ:

  • поддержка транзакционной логики на платформе метрик
  • восстановление после разделения сети или недоступности платформы
  • двойная запись с согласованностью данных (поскольку в противном случае нет гарантии, что два экземпляра Prometheus будут очищать одни и те же данные с одной и той же меткой времени)

У нас много разных клиентов, некоторые из которых используют хранилище временных рядов для продукта Observability для управления потреблением продукта или транзакционными изменениями при лицензировании. Эти варианты использования не могут быть обработаны с помощью экземпляров Prometheus, которые лучше подходят для мониторинга на основе метрик.



Второй был Noderig

В ходе бесед с некоторыми из наших клиентов мы пришли к выводу, что существующие инструменты нуждаются в определенном уровне знаний, чтобы их можно было использовать в больших масштабах. Например, команда с кластером из 20 тыс. Узлов с Scollector в итоге получит более 10 миллионов метрик только для узлов… Фактически, в зависимости от конфигурации оборудования, Scollector будет генерировать от 350 до 1000 метрик из одного узла.

Это причина создания Noderig. Мы хотели, чтобы он был таким же простым в использовании, как экспортер узлов от Prometheus, но с более детализированным производством метрик по умолчанию.

Noderig собирает метрики ОС (ЦП, память, диск и сеть), используя семантику простого уровня. Это позволяет собирать нужное количество метрик для любого типа хоста, что особенно подходит для контейнерных сред.

Мы сделали его совместимым с пользовательскими сборщиками Scollector, чтобы упростить процесс миграции и обеспечить расширяемость. Внешние сборщики — это простые исполняемые файлы, которые действуют как поставщики данных, собираемых Noderig, как и любые другие показатели.

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



Это работает?

Beamium и Noderig широко используются в OVH и поддерживают мониторинг очень больших инфраструктур. На момент написания мы собираем и храним сотни миллионов показателей с помощью этих инструментов. Так что они определенно работают!

Фактически, в настоящее время мы работаем над выпуском 2.0, который будет доработкой, включающей автообнаружение и горячую перезагрузку.