Cам себе облако: установка ELK и TICK стеков в Kubernetes

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

Казалось бы простая задача - развернуть Kubernetes кластер и в нем запустить централизованный мониторинг TICK стек и логирование ELK стек. Но для меня она осложнилась тем, что с этим технологиями я столкнулся впервые. Чтобы понять, как все работает, хотел пройти простые Quick Start инструкции. К своему удивлению, не нашел детальных актуальных описаний - либо отдельные статьи со своей спецификой, либо многостраничные книги по Kubernetes. Пока собирал пазл из многочисленных материалов, решил написать статью, в которой рассмотреть общую концепцию развертывания TICK и ELK стеков в Kubernetes. Для чего, вообще, решать задачу централизованного мониторинга и логирования в Kubernetes?

Архитектура Kubernetes кластера

IT индустрия пришла к консенсусу, что cloud native приложения - это такие, которые могут эффективно работать в Kubernetes кластере (K8S). И одним из требований к cloud native приложением - это их построение в соответствии с принципами наблюдаемости (observability). В Kubernetes приложения работают в контейнерах и в любой момент контейнер может быть остановлен и его внутренние данные, включая логи, пропадут. Поэтому мониторинг работоспособности и логирование должны осуществляться внешними системами. В настоящее время наиболее популярны системы, построенные на TICK и ELK стеках. Поэтому задача установки и настройки этих систем в Kubernetes кластере является актуальной.

Но для начала надо обзавестись Kubernetes кластером. Самый простой способ - установить под Windows Docker Desktop и Minikube. Но как показала практика, Minikube предназначен только для изучения Kubernetes, а как среда для развертывания промышленных систем, не тянет. Эксперименты с увеличением вычислительных ресурсов для Minikube не помогли. Пришлось разворачивать кластер на Amason EKS (Elastic Kubernetes System), что, конечно, не бесплатно, но что ни сделаешь ради статьи!

Начнем с теории и посмотрим на архитектуру Kubernetes. Я нарисовал свой вариант архитектуры со всеми элементами и связями, т.к. в документации и книгах включены только упрощенные схемы:

Подробное описание элементов и связей можно найти в официальной документации Kubernetes и многочисленных статьях, поэтому здесь приведу только краткое описание. Kubernetes кластер содержит одну или несколько Master Node. В Master Node работают следующие элементы:

  • api-server - предоставляет REST API для взаимодействия с кластером Kubernetes.

  • Cluster Store (etcd) - хранит состояние и конфигурацию кластера.

  • Scheduler - получает от api-server новые задания по запуску Pods и назначает им рабочие ноды.

  • Controller - следит за состоянием кластера и приводит его в нужные состояния.

Kubernetes кластер содержит одну или несколько Worker Node. В Worker Node работают следующие элементы:

  • Kubelet - k8s agent регистрирует ноды в кластере, обрабатывает вызовы api-server, запускает Pods. При ошибках нотифицирует мастера, который решает, что делать дальше.

  • Container Engine - управляет контейнерами, обычно используется Docker.

  • kube-proxy - обеспечивает сетевую связность, выдает IP для Pods, распределяет вызовы (Load balances) между Pods в Service.

Pod

  • Pod - единица запуска и масштабирования в Kubernetes кластере.

  • Может содержать один или несколько Docker контейнеров.

Deployment

  • С помощью Deployment обеспечивается запуск, обновление и удаление Pods.

Services

  • Каждый Pod имеет свой IP. Pods объединяются в Service для распределения нагрузки и организации единой точки интеграции.

  • Service - стандартный способ доступа к Pods снаружи кластера.

  • Service обеспечивает для Pods доступ к внешним сетевым ресурсам.

Persistent Volume

  • Используются для организации постоянных хранилищ данных для Pods

На локальном компьютере обычно работают:

  • Kubectl - обеспечивает связь с Kubernetes кластером.

  • Helm - обеспечивает запуск Helm charts, которые загружаются из внешних репозиториев.

Запуск кластера в Amazon EKS

Создание Kubernetes кластера в Amazon не так очевидно, как с помощью Minikube, но на все шаги есть хорошие инструкции. Для того, чтобы развернуть Kubernetes кластер в Amazon необходимо создать:

  • Cluster для начала без Nodes.

  • Пользовательскую роль для управления кластером со стороны Amazon.

  • Node Group.

  • Пользовательскую роль для управления вычислительными ресурсами в Node Group.

  • В Node Group выделить вычислительные ресурсы (виртуальные машины).

Для того, чтобы управлять кластером и запускать Pods со своего локального компьютера необходимо получить доступ к кластеру с помощью kubectl. Для этого идем по очередной инструкции "Create a kubeconfig for Amazon EKS"

  • Устанавливаем aws консоль локально, проверяем, что установка успешна:

aws --version

  • Вводим API key и API secret которые получаем в онлайн консоли:

aws configure

  • Консоль aws пропишет в Kubernetes setup файл свои настройки, проверяем, что есть доступ к нашему кластеру:

kubectl cluster-info

  • Теперь для доступа к кластеру можно использовать как kubectl, так и графический интерфейс. В своей работе я использую open source приложение Lens (https://k8slens.dev/)

Helm

Устанавливать наши приложения будем с помощью Helm Charts https://helm.sh/. Они значительно упрощают процесс установки и конфигурирования, т.к. скрывают шаги по созданию многих сущностей, которые иначе пришлось бы делать в Kubernetes вручную:

  • Deploymet - конфигурации развертываний.

  • ConfigMap - конфигурации приложений.

  • Secrets - чувствительные к разглашению данные.

  • Persistent Volume - постоянные диски.

  • Service - сервисы для доступа к приложениям.

  • Пользователи и их роли.

Установка Helm

Инструкция по установке Helm. Под Windows Helm устанавливается одной командой:

choco install kubernetes-helm

Helm еще удобен тем, что после установки его можно использовать как для работы с локальным Kubernetes в Minikube, так и в облачном Amazon EKS.

Поиск Helm charts

Не нашел структурированного места для поиска Helm charts. Думал, что есть аналог Docker Hub, который используется для поиска docker контейнеров. Официальная команда поиска Helm charts:

helm search hub influxdb

выдает список из нескольких десятков чартов для InfluxDB непонятного происхождения. Поэтому лучше всего, если производитель сам сделал чарт для разворота приложения в Kubernetes. Для наших задач есть официальные чарты от производителей:

  • Чарты для InfluxDB и Telegraf

  • Grafana

  • Elastic Search и Kibana

  • Для Fluentd чарты находятся на GitHub

Посмотреть список установленных локальных репозиториев можно командой:

helm repo list

Установка TICK стека

Классическая схема TICK стека находится на официальном сайте InfuxData и состоит из Telegraf, InfluxDB, Chronograf и Kapacitor:

Вместо Chronograf будем использовать Grafana, как более функциональное приложение, а Kapacitor вообще для наших целей не понадобится. Таким образом у нас получатся не TICK, а TIG стек.

Для установки TIG стека изучил несколько инструкций:

  • Monitor your infrastructure with InfluxDB and Grafana on Kubernetes

  • Официальная инструкция от производителя InfluxDB

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

Установка InfluxDB

  • Создаем в кластере namespace для мониторинга:

kubectl create namespace monitoring

  • Добавляем репозиторий в Helm:

helm repo add influxdata https://helm.influxdata.com/

  • Обновляем список чартов репозитория:

helm repo update

  • Запускаем чарт на выполнение:

helm upgrade --namespace monitoring --install influxdb influxdata/influxdb

Если в чарте надо поменять параметры по умолчанию, есть два способа, первый:

  • Скачать чарт локально:

helm pull influxdata/influxdb -d c:/work/influxdb

  • Поменять параметры в файле values.yaml и запустить на выполнение из локального каталога:

helm upgrade --namespace monitoring --install influxdb .

Второй способ: при вызове helm использовать параметры "set". Например, для ограничения использования 1Gi оперативной памяти и 1 виртуального процессора в командную строку надо добавить два параметра "set":

helm upgrade --namespace monitoring --install influxdb influxdata/influxdb --set resources.requests.memory=1Gi --set resources.requests.cpu=1000m

Оба способа рабочие и используются в зависимости от решаемых задач.

  • Проверяем, что Pod InfluxDB установился в кластер. Для этого смотрим, что он есть в списке Pods:

kubectl get pods --namespace monitoring

  • Смотрим, что сервис для InfluxDB есть в списке сервисов:

kubectl get services --namespace monitoring

  • Теперь необходимо получить доступ к установленной базе InfluxDB и сделать первоначальные настройки. Для этого запускаем порт форвардинг с именем нашего сервиса:

kubectl port-forward --namespace monitoring svc/influxdb 8086:8086

  • Теперь можно запускать InfluxDB CLI и делать настройки. Для этого, опять же, есть два способа. Первый - заходим внутрь контейнера и запускаем CLI изнутри:

kubectl exec -i -t --namespace monitoring influxdb-0 /bin/sh

influx

Второй способ работает, если есть локально установленная база influxDB. В этом случае можно подключится сразу из под Windows, при условии, что работает порт форвардинг:

C:\Program Files\InfluxData\influxdb\influxdb>influx.exe

Настройки в базе делаем для последующего подключения к Telegraf и Grafana. Для этого выполняем последовательность команд:

  • Создаем базу telegraf:

CREATE DATABASE telegraf

  • Смотрим что она есть в списке:

SHOW DATABASES

  • Делаем ее активной:

USE telegraf

  • Для проверки работоспособности вставляем произвольное значение:

insert key1 value=1

  • Сморим, что данные отображаются:

SHOW MEASUREMENTS

SELECT * FROM telegraf

На этом с InfluxDB закончили, далее устанавливаем Telegraf.

Установка Telegraf

  • Устанавливаем Telegraf из того же репозитория, что и InfluxDB:

helm upgrade --namespace monitoring --install telegraf influxdata/telegraf

  • С первого раза чарт не установился, смотрим логи:

kubectl logs --namespace monitoring --tail=20 telegraf-64fdc74887-22ld9

  • Видим строку:

Error running agent: Error loading config file /etc/telegraf/telegraf.conf: error parsing statsd, line 40: (statsd.Statsd.Percentiles) cannot unmarshal TOML integer into float64

Что-то не то с конфигурацией в telegraf.conf. Подозреваю, что Telegraf ожидает в конфиге float числа, а в нем integer. Проверим гипотезу, для этого добавляем в конфигурацию точки с нулем:

  • Перестартовываем чарт:

kubectl rollout restart --namespace monitoring deployment/telegraf

Как ни странно, но это помогло... как же они тестировали свой конфиг?) Теперь надо добавить системные метрики, которые Telegraf будет передавать в InfluxDB. Для этого в конец того же конфига добавляем метрики, описанные в статье:

[[inputs.cpu]]
  percpu = true
  totalcpu = true
  collect_cpu_time = false
  report_active = false
[[inputs.disk]]
  ignore_fs = ["tmpfs", "devtmpfs", "devfs"]
[[inputs.diskio]]
[[inputs.kernel]]
[[inputs.mem]]
[[inputs.processes]]
[[inputs.swap]]
[[inputs.system]]
[[inputs.docker]]
  endpoint = "unix:///var/run/docker.sock"
  • Опять перестартовываем чарт:

kubectl rollout restart --namespace monitoring deployment/telegraf

  • Чтобы убедиться, что появились нужные нам метрики снова заходим в CLI IngluxDB и выполняем команды:

USE telegraf
SHOW MEASUREMENTS

Смотрим, что есть нужные нам метрики cpu, disk и mem:

Теперь устанавливаем графический интерфейс.

Установка Grafana

  • Добавляем репозиторий:

helm repo add grafana https://grafana.github.io/helm-charts

  • Обновляем список чартов:

helm repo update

  • Если надо, скачиваем чарт локально или устанавливаем сразу:

helm upgrade --namespace monitoring --install grafana grafana/grafana

  • Cмотрим имя Pod

kubectl get pods --namespace monitoring

  • Для доступа к интерфейсу делаем порт форвардинг. 3001 я сделал т.к 3000 порт у меня занят под локальную Grafana:

kubectl port-forward --namespace monitoring grafana-pod-name 3001:3000

  • Идем в Secrets, смотрим user / password:

  • Запускаем GUI в браузере: http://127.0.0.1:3001

  • Вводим полученные user / password

  • Добавляем DataSource, при этом в качестве IP указываем внутренний адрес InflixDB в кластере: http://10.100.35.97:8086

  • В качестве базы данных указываем ранее созданную telegraf:

Теперь можно создавать dashboard. Существуют уже готовые dashboards для разных метрик. Например, в нашем случае можно сделать импорт dashboard по ранее упомянутой инструкции. Для этого надо нажать на знак "+" слева и выбрать пункт меню Import, далее ввести номер нужной dashboard 928:

Теперь смотрим на красивые графики по потреблению системных ресурсов нашим кластером:

Создание Service для Grafana

В промышленном кластере удобно получать доступ к интерфейсу Grafana по внешнему IP адресу, а не делать форвард портов. Для этого в Kubernetes используются Services. Шаги для создания Service:

  • В Pod Grafana делаем метку, по которой Service будет находить необходимый Pod, например "app: my-grafana":

  • Делаем файл grafana-loadbalancer-svc.yaml для сервиса с типом LoadBalancer, чтобы он получил внешний IP адрес. В селектор записываем нашу метку. Внутренний и внешний порт указываем стандартный 3000:

apiVersion: v1
kind: Service
metadata:
  name: my-grafana-loadbalancer
spec:
  type: LoadBalancer
  ports:
  - port: 3000
    targetPort: 3000
  selector:
    app: my-grafana
  • Запускаем на выполнение

kubectl apply --namespace monitoring -f ./grafana-loadbalancer-svc.yaml

  • Смотрим список сервисов, убеждаемся, что присутствует наш my-grafana-loadbalancer, смотрим его внешний IP.

  • Смотрим, что в списке endpoints сервиса присутствует IP, который указывает на Pod с Grafana:

  • Вводим полученный адрес в браузере, убеждаемся что появилось GUI Grafana:

http://a26ddb02e81fd4956867aea4a0178679-256392.us-east-1.elb.amazonaws.com:3000

  • Если сервис больше не нужен, то удаляем:

kubectl delete svc --namespace monitoring my-grafana-loadbalancer

Схема Pods в Kubernetes

В результате получили полную цепочку для централизованного мониторинга в Kubernetes:

Установка ELK стека

Классическая цепочка логирования в ELK стеке.

Вместо Logstash будем использовать стандартного для Kubernetes агента Fluentd и поэтому у нас получится не ELK, а EFK стек. Также в Kubernetes есть несколько схем развертывания логирования. Они описаны в официальной документации "Kubernetes Logging Architecture". Мы будем реализовывать следующую стандартную схему:

Начнем установку EFK стека с базы данных Elastic Search.

Установка Elastic Search

  • Создаем namespace efk в Kubernetes:

kubectl create namespace efk

  • Добавляем репозиторий:

helm repo add elastic https://helm.elastic.co

  • Обновляем список чартов:

helm repo update

  • Для минимизации ресурсов делаем конфигурацию с одной Master Node, одной репликой и ограничиваем объём диска в 10Gb. При промышленном развёртывании, конечно, параметры должны быть рассчитаны из реальных требований к надежности Elastic Search кластера и предполагаемого объёма данных:

helm install elasticsearch elastic/elasticsearch --namespace efk --set volumeClaimTemplate.resources.requests.storage=10Gi --set replicas=1 --set minimumMasterNodes=1

  • Если с первого раза не получилось, смотрим логи, удаляем chart и persistent volume:

helm delete elasticsearch --namespace efk

kubectl delete -n efk persistentvolumeclaim elasticsearch-master-elasticsearch-master-0

  • Правим параметры, запускаем заново.

  • После успешной установки проверяем, для этого делаем форвард потов:

kubectl port-forward --namespace efk svc/elasticsearch-master 9200

  • Запускаем в браузере: http://127.0.0.1:9200/

В ответ должен вернуться JSON с версией Elastic Search.

Установка Fluentd

Чтобы разобраться с Fluentd я использовал несколько материалов:

  • Kubernetes Logging with Elasticsearch, Fluentd and Kibana

  • Cluster-level Logging in Kubernetes with Fluentd

Краткий алгоритм, который при удачном стечении обстоятельств должен привести к успеху:

  • Клонируем Helm чарты для fluentd из Git Hub:

git clone https://github.com/fluent/fluentd-kubernetes-daemonset

  • Создаем ServiceAccount в namespace kube-system с именем fluentd.

  • Создаем ClusterRole и записываем в него конфигурацию с правилами доступа:

    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
      name: fluentd
      uid: 3458b789-e888-4473-9b48-04550404ea30
      resourceVersion: '36487'
      creationTimestamp: '2021-07-20T14:02:13Z'
      selfLink: /apis/rbac.authorization.k8s.io/v1/clusterroles/fluentd
      namespace: kube-system
    rules:
    - apiGroups:
      - ""
      resources:
      - pods
      - namespaces
      verbs:
      - get
      - list
      - watch
  • Создаем ClusterRoleBinding, в которой связываем ServiceAccount и ClusterRole.

  • Далее в файле fluentd-daemonset-elasticsearch.yaml в параметре FLUENT_ELASTICSEARCH_HOST указываем внутренний IP сервиса elasticsearch-master.

  • Там же правим на настройки с путями.

  • После нескольких неудачных попыток у меня получился следующий работоспособный YAML:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd
  namespace: kube-system
  labels:
    k8s-app: fluentd-logging
    version: v1
spec:
  selector:
    matchLabels:
      k8s-app: fluentd-logging
      version: v1
  template:
    metadata:
      labels:
        k8s-app: fluentd-logging
        version: v1
    spec:
      serviceAccount: fluentd
      serviceAccountName: fluentd
      tolerations:
      - key: node-role.kubernetes.io/master
        effect: NoSchedule
      containers:
      - name: fluentd
        image: fluent/fluentd-kubernetes-daemonset:v1-debian-elasticsearch
        env:
          - name:  FLUENT_ELASTICSEARCH_HOST
            value: "10.100.73.75"
          - name:  FLUENT_ELASTICSEARCH_PORT
            value: "9200"
          - name: FLUENT_ELASTICSEARCH_SCHEME
            value: "http"
          - name: FLUENT_UID
            value: "0"           
        resources:
          limits:
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 200Mi
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: dockercontainerlogdirectory
          mountPath: /var/lib/docker/containers
          readOnly: true
      terminationGracePeriodSeconds: 30
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: dockercontainerlogdirectory
        hostPath:
          path: /var/lib/docker/containers
  • Устанавливаем Helm чарт:

kubectl apply --namespace kube-system -f fluentd-daemonset-elasticsearch.yaml

  • Если попытка не получилось, разбираемся, удалям:

kubectl delete --namespace efk -f fluentd-daemonset-elasticsearch.yaml

  • Правим, устанавливаем заново.

Установка Kibana

  • Установка оказалось на удивление проста. В параметрах сразу подрезаем требуемые системные ресурсы:

helm install kibana elastic/kibana --namespace efk --set resources.requests.cpu=500m --set resources.requests.memory=1Gi

  • При ошибках, как всегда, разбираемся, удаляем:

helm delete kibana --namespace efk

  • Запускаем заново. После успешной установки проверяем, делаем порт-форвардинг:

kubectl port-forward --namespace efk deployment/kibana-kibana 5601

  • Запускаем GUI в браузере: http://127.0.0.1:5601

А вот с конфигурированием Kibana все оказалось не очевидно. Чтобы разобраться, установил EFK стек локально под Windows, прошел там всю цепочку и потом вернулся в Kubernetes. Но для тех, кто до этого работал с Kibana, разумеется, все просто:

  • После захода в GUI Kibana делаем новую Index Pattern (Management -> Index Patterns -> Create New Index Pattern).

  • При создании индекса указываем Logstash index, который сгенерировал Fluentd.

  • Идем в Discover, выбираем созданный индекс.

  • Ставим фильтр на определенную дату / время .

  • Выбираем поля kubernetes.container_name и log и смотрим на логи от fluentd и самой Kibana:

Создание Service для Kibana

Для удобного доступа к GUI Kibana делаем сервис, как мы ранее сделали для GUI Grafana. Шаги те же, тут просто перечислю без подробностей:

  • В Pod Kibana делаем метку, по которой Service будет его находить.

  • Далаем и запускаем файл kibana-loadbalancer-svc.yaml для сервиса с типом LoadBalancer. В селектор записываем нашу метку. Внутренний и внешний порт указываем стандартный для Kibana 5601.

  • Смотрим выданный Kubernetes внешний IP адрес, с помощью него получаем доступ к GUI Kibana.

Полная схема Pods в Kubernetes

В результате получили классическую цепочку для централизованного логирования в Kubernetes. Полная схема, развернутых в Kubernetes стеков:

В результате получили в Kubernetes

Pods
Pods
Services
Services

Заключение

  • В статье мы рассмотрели, как развернуть в Kubernetes кластере классические цепочки для централизованного логирования и мониторинга.

  • Для себя сделал вывод, что в Kubernetes устанавливать программы сложнее, чем локально или на виртуалки, т.к. появляется новая прослойка абстракции и не всегда сразу понятно, почему что-то не работает.

  • Что дальше? Не просто же так мы поднимали эту инфраструктуру! Дальше рассмотрим все требования к cloud native приложениям, а не только требование observability, сделаем само приложение и запустим его в нашей инфраструктуре.

It's only the beginning!

Источник: https://habr.com/ru/post/569124/


Интересные статьи

Интересные статьи

Коллеги, всем привет! На этой неделе состоялся релиз очередной версии нашего плагина DevOpsProdigy KubeGraf v1.4.0. Он разработан для Grafana и предназначен для мониторинга kuberne...
Tree of Dragons II by surrealistguitarist Для тех, кто каждый день использует Git, но чувствует себя неуверенно, команда Mail.ru Cloud Solutions перевела статью фронтенд-разрабо...
Примечание: эта статья не претендует на статус лучшей практики. В ней описан опыт конкретной реализации инфраструктурной задачи в условиях использования Kubernetes и Helm, который может быть ...
Приветствую вас (лично вас, а не всех кто это читает)! Сегодня мы: Создадим приложение (навык) Алисы с использованием нового (октябрь 2019) сервиса Yandex Cloud Functions. Настроим н...
Сегодня, в среду, состоится очередной релиз Kubernetes — 1.16. По сложившейся для нашего блога традиции, вот уже в юбилейный — десятый — раз мы рассказываем о наиболее значимых изменениях в н...