Как настроить стек журналов Elasticsearch, Fluentd и Kibana (EFK) в Kubernetes
Введение
При запуске нескольких сервисов и приложений в кластере Kubernetes централизованный стек ведения журналов на уровне кластера может помочь вам быстро отсортировать и проанализировать большой объем данных журналов, создаваемых вашими модулями. Одним из популярных решений для централизованного ведения журналов является стек Elasticsearch, Fluentd и Kibana (EFK).
Elasticsearch — это распределенная и масштабируемая поисковая система, работающая в режиме реального времени, которая обеспечивает полнотекстовый и структурированный поиск, а также аналитику. Он обычно используется для индексирования и поиска в больших объемах данных журнала, но также может использоваться для поиска в различных типах документов.
Elasticsearch обычно развертывается вместе с Kibana, мощным интерфейсом визуализации данных и панелью инструментов для Elasticsearch. Kibana позволяет просматривать данные журналов Elasticsearch через веб-интерфейс и создавать информационные панели и запросы, чтобы быстро отвечать на вопросы и получать представление о ваших приложениях Kubernetes.
В этом руководстве мы будем использовать Fluentd для сбора, преобразования и отправки данных журнала в серверную часть Elasticsearch. Fluentd — это популярный сборщик данных с открытым исходным кодом, который мы настроим на наших узлах Kubernetes, чтобы отслеживать файлы журналов контейнеров, фильтровать и преобразовывать данные журналов и доставлять их в кластер Elasticsearch, где они будут индексироваться и храниться.
Мы начнем с настройки и запуска масштабируемого кластера Elasticsearch, а затем создадим сервис и развертывание Kibana Kubernetes. В заключение мы настроим Fluentd как DaemonSet, чтобы он работал на каждом рабочем узле Kubernetes.
Предпосылки
Прежде чем приступить к работе с этим руководством, убедитесь, что у вас есть следующее:
- Кластер Kubernetes 1.10+ с включенным управлением доступом на основе ролей (RBAC).
- Убедитесь, что в вашем кластере достаточно ресурсов для развертывания стека EFK, а если нет, масштабируйте кластер, добавив рабочие узлы. Мы развернем кластер Elasticsearch с 3 модулями (при необходимости вы можете уменьшить его до 1), а также один модуль Kibana. Каждый рабочий узел также будет запускать модуль Fluentd. Кластер в этом руководстве состоит из 3 рабочих узлов и управляемой плоскости управления.
Инструмент командной строки
kubectl
, установленный на вашем локальном компьютере и настроенный для подключения к вашему кластеру. Подробнее об установкеkubectl
можно прочитать в официальной документации.После настройки этих компонентов вы готовы начать работу с этим руководством.
Шаг 1 — Создание пространства имен
Перед развертыванием кластера Elasticsearch мы сначала создадим пространство имен, в которое мы установим все наши инструменты ведения журнала. Kubernetes позволяет вам разделять объекты, работающие в вашем кластере, с помощью абстракции «виртуального кластера», называемой пространствами имен. В этом руководстве мы создадим пространство имен
kube-logging
, в которое мы установим компоненты стека EFK. Это пространство имен также позволит нам быстро очистить и удалить стек ведения журналов без потери функциональности кластера Kubernetes.Для начала изучите существующие пространства имен в вашем кластере с помощью
kubectl
:- kubectl get namespaces
Вы должны увидеть следующие три исходных пространства имен, которые предустановлены вместе с вашим кластером Kubernetes:
OutputNAME STATUS AGE default Active 5m kube-system Active 5m kube-public Active 5mПространство имен
default
содержит объекты, созданные без указания пространства имен. Пространство именkube-system
содержит объекты, созданные и используемые системой Kubernetes, такие какkube-dns
,kube-proxy
иkubernetes. -панель управления
. Рекомендуется содержать это пространство имен в чистоте и не загрязнять его рабочими нагрузками приложений и инструментов.Пространство имен
kube-public
— это еще одно автоматически созданное пространство имен, которое можно использовать для хранения объектов, которые вы хотите сделать доступными для чтения и доступа во всем кластере, даже для неаутентифицированных пользователей.Чтобы создать пространство имен
kube-logging
, сначала откройте и отредактируйте файл с именемkube-logging.yaml
с помощью вашего любимого редактора, например nano:- nano kube-logging.yaml
В редакторе вставьте следующий объект пространства имен YAML:
kind: Namespace apiVersion: v1 metadata: name: kube-logging
Затем сохраните и закройте файл.
Здесь мы указываем
kind
объекта Kubernetes как объектNamespace
. Чтобы узнать больше об объектахNamespace
, обратитесь к пошаговому руководству по пространствам имен в официальной документации Kubernetes. Мы также указываем версию API Kubernetes, используемую для создания объекта (v1
), и присваиваем емуимя
,kube-logging
.Создав объектный файл пространства имен
kube-logging.yaml
, создайте пространство имен с помощьюkubectl create
с флагом имени файла-f
:- kubectl create -f kube-logging.yaml
Вы должны увидеть следующий вывод:
Outputnamespace/kube-logging createdЗатем вы можете подтвердить, что пространство имен было успешно создано:
- kubectl get namespaces
На этом этапе вы должны увидеть новое пространство имен
kube-logging
:OutputNAME STATUS AGE default Active 23m kube-logging Active 1m kube-public Active 23m kube-system Active 23mТеперь мы можем развернуть кластер Elasticsearch в этом изолированном пространстве имен журналов.
Шаг 2 — Создание Elasticsearch StatefulSet
Теперь, когда мы создали пространство имен для размещения нашего стека журналов, мы можем приступить к развертыванию его различных компонентов. Сначала мы начнем с развертывания кластера Elasticsearch из 3 узлов.
В этом руководстве мы используем 3 модуля Elasticsearch, чтобы избежать проблемы «разделения ресурсов», которая возникает в высокодоступных многоузловых кластерах. На высоком уровне «разделение ресурсов» — это то, что возникает, когда один или несколько узлы не могут взаимодействовать с другими, и избирается несколько «разделенных» мастеров. При 3 узлах, если один из них временно отключается от кластера, два других узла могут выбрать нового мастера, и кластер может продолжать функционировать, пока последний узел пытается повторно присоединиться. Чтобы узнать больше, обратитесь к настройкам голосования.
Создание безголового сервиса
Для начала мы создадим безголовый сервис Kubernetes под названием
elasticsearch
, который определит домен DNS для трех модулей. Безголовый сервис не выполняет балансировку нагрузки и не имеет статического IP-адреса; чтобы узнать больше о безголовых сервисах, обратитесь к официальной документации Kubernetes.Откройте файл с именем
elasticsearch_svc.yaml
в своем любимом редакторе:- nano elasticsearch_svc.yaml
Вставьте следующий сервис Kubernetes YAML:
kind: Service apiVersion: v1 metadata: name: elasticsearch namespace: kube-logging labels: app: elasticsearch spec: selector: app: elasticsearch clusterIP: None ports: - port: 9200 name: rest - port: 9300 name: inter-node
Затем сохраните и закройте файл.
Мы определяем
Service
с именемelasticsearch
в пространстве именkube-logging
и присваиваем ему меткуapp: elasticsearch
. Затем мы устанавливаем для.spec.selector
значениеapp: elasticsearch
, чтобы служба выбирала модули с меткойapp: elasticsearch
. Когда мы связываем наш Elasticsearch StatefulSet с этой службой, служба возвращает записи DNS A, которые указывают на модули Elasticsearch с меткойapp: elasticsearch
.Затем мы устанавливаем
clusterIP: None
, что делает службу безголовой. Наконец, мы определяем порты9200
и9300
, которые используются для взаимодействия с REST API и для связи между узлами соответственно.Создайте сервис с помощью
kubectl
:- kubectl create -f elasticsearch_svc.yaml
Вы должны увидеть следующий вывод:
Outputservice/elasticsearch createdНаконец, еще раз проверьте, что служба была успешно создана с помощью
kubectl get
:kubectl get services --namespace=kube-logging
Вы должны увидеть следующее:
OutputNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE elasticsearch ClusterIP None <none> 9200/TCP,9300/TCP 26sТеперь, когда мы настроили наш автономный сервис и стабильный домен
.elasticsearch.kube-logging.svc.cluster.local
для наших подов, мы можем приступить к созданию StatefulSet.Создание StatefulSet
Kubernetes StatefulSet позволяет назначать стабильные идентификаторы модулям и предоставлять им стабильное постоянное хранилище. Elasticsearch требуется стабильное хранилище для сохранения данных при изменении расписания и перезапусках подов. Чтобы узнать больше о рабочей нагрузке StatefulSet, посетите страницу Statefulsets в документации Kubernetes.
Откройте файл с именем
elasticsearch_statefulset.yaml
в своем любимом редакторе:- nano elasticsearch_statefulset.yaml
Мы будем двигаться по определению объекта StatefulSet раздел за разделом, вставляя блоки в этот файл.
Начните со вставки следующего блока:
apiVersion: apps/v1 kind: StatefulSet metadata: name: es-cluster namespace: kube-logging spec: serviceName: elasticsearch replicas: 3 selector: matchLabels: app: elasticsearch template: metadata: labels: app: elasticsearch
В этом блоке мы определяем StatefulSet с именем
es-cluster
в пространстве именkube-logging
. Затем мы связываем его с нашей ранее созданной службойelasticsearch
с помощью поляserviceName
. Это гарантирует, что каждый под в StatefulSet будет доступен по следующему DNS-адресу:es-cluster-[0,1,2].elasticsearch.kube-logging.svc.cluster.local
, где[0,1,2]
соответствует назначенному поду целочисленному порядковому номеру.Мы указываем 3
replicas
(Pods) и устанавливаем для селектораmatchLabels
значениеapp: elasticseach
, которое затем отражаем в.spec. раздел template.metadata
. Поля.spec.selector.matchLabels
и.spec.template.metadata.labels
должны совпадать.Теперь мы можем перейти к спецификации объекта. Вставьте следующий блок YAML сразу под предыдущим блоком:
. . . spec: containers: - name: elasticsearch image: docker.elastic.co/elasticsearch/elasticsearch:7.2.0 resources: limits: cpu: 1000m requests: cpu: 100m ports: - containerPort: 9200 name: rest protocol: TCP - containerPort: 9300 name: inter-node protocol: TCP volumeMounts: - name: data mountPath: /usr/share/elasticsearch/data env: - name: cluster.name value: k8s-logs - name: node.name valueFrom: fieldRef: fieldPath: metadata.name - name: discovery.seed_hosts value: "es-cluster-0.elasticsearch,es-cluster-1.elasticsearch,es-cluster-2.elasticsearch" - name: cluster.initial_master_nodes value: "es-cluster-0,es-cluster-1,es-cluster-2" - name: ES_JAVA_OPTS value: "-Xms512m -Xmx512m"
Здесь мы определяем поды в StatefulSet. Мы называем контейнеры
elasticsearch
и выбираем образ Dockerdocker.elastic.co/elasticsearch/elasticsearch:7.2.0
. На этом этапе вы можете изменить этот тег изображения, чтобы он соответствовал вашему собственному внутреннему изображению Elasticsearch или другой версии. Обратите внимание, что для целей этого руководства был протестирован только Elasticsearch7.2.0
.Затем мы используем поле
resources
, чтобы указать, что контейнеру гарантированно требуется не менее 0,1 виртуального ЦП, и он может увеличиться до 1 виртуального ЦП (что ограничивает использование ресурсов пода при выполнении первоначальной большой загрузки или работе с скачок нагрузки). Вы должны изменить эти значения в зависимости от ожидаемой нагрузки и доступных ресурсов. Чтобы узнать больше о запросах ресурсов и ограничениях, обратитесь к официальной документации Kubernetes.Затем мы открываем и называем порты
9200
и9300
для REST API и связи между узлами соответственно. Мы указываемvolumeMount
с именемdata
, который будет монтировать PersistentVolume с именемdata
в контейнер по пути/usr/share/elasticsearch/ данные
. Мы определим VolumeClaims для этого StatefulSet в более позднем блоке YAML.Наконец, мы устанавливаем некоторые переменные среды в контейнере:
cluster.name
: имя кластера Elasticsearch, которое в этом руководстве —k8s-logs
.node.name
: имя узла, которое мы установили в поле.metadata.name
с помощьюvalueFrom
. Это приведет кes-cluster-[0,1,2]
, в зависимости от назначенного порядкового номера узла.discovery.seed_hosts
: в этом поле задается список подходящих узлов в кластере, которые будут запускать процесс обнаружения узлов. В этом руководстве, благодаря безголовому сервису, который мы настроили ранее, наши поды имеют домены видаes-cluster-[0,1,2].elasticsearch.kube-logging.svc.cluster.local
, поэтому мы устанавливаем эту переменную соответствующим образом. Используя разрешение DNS локального пространства имен Kubernetes, мы можем сократить его доes-cluster-[0,1,2].elasticsearch
. Чтобы узнать больше об обнаружении Elasticsearch, обратитесь к официальной документации Elasticsearch.cluster.initial_master_nodes
: в этом поле также указывается список узлов, которые могут участвовать в процессе выбора главного узла. Обратите внимание, что для этого поля вы должны идентифицировать узлы по ихnode.name
, а не по их именам хостов.ES_JAVA_OPTS
: здесь мы устанавливаем это значение-Xms512m -Xmx512m
, что указывает JVM использовать минимальный и максимальный размер кучи 512 МБ. Вы должны настроить эти параметры в зависимости от доступности ресурсов и потребностей вашего кластера. Дополнительные сведения см. в разделе Настройка размера кучи.
Следующий блок, который мы вставим, выглядит следующим образом:
. . . initContainers: - name: fix-permissions image: busybox command: ["sh", "-c", "chown -R 1000:1000 /usr/share/elasticsearch/data"] securityContext: privileged: true volumeMounts: - name: data mountPath: /usr/share/elasticsearch/data - name: increase-vm-max-map image: busybox command: ["sysctl", "-w", "vm.max_map_count=262144"] securityContext: privileged: true - name: increase-fd-ulimit image: busybox command: ["sh", "-c", "ulimit -n 65536"] securityContext: privileged: true
В этом блоке мы определяем несколько Init Containers, которые запускаются перед основным контейнером приложения
elasticsearch
. Каждый из этих Init-контейнеров выполняется до завершения в том порядке, в котором они определены. Чтобы узнать больше об Init Containers, обратитесь к официальной документации Kubernetes.Первый, названный
fix-permissions
, запускает командуchown
, чтобы изменить владельца и группу каталога данных Elasticsearch на1000:1000
, UID пользователя Elasticsearch. По умолчанию Kubernetes монтирует каталог данных какroot
, что делает его недоступным для Elasticsearch. Чтобы узнать больше об этом шаге, обратитесь к «Примечаниям по использованию и значениям по умолчанию» Elasticsearch.Второй, с именем
increase-vm-max-map
, запускает команду для увеличения ограничений операционной системы на количество mmap, которые по умолчанию могут быть слишком низкими, что приводит к ошибкам нехватки памяти. Чтобы узнать больше об этом шаге, обратитесь к официальной документации Elasticsearch.Следующим запускаемым контейнером инициализации является
increase-fd-ulimit
, который запускает командуulimit
для увеличения максимального количества дескрипторов открытых файлов. Чтобы узнать больше об этом шаге, обратитесь к \Примечаниям по использованию и значениям по умолчанию в официальной документации Elasticsearch.Примечание. В примечаниях к Elasticsearch для производственного использования также упоминается отключение подкачки по соображениям производительности. В зависимости от вашей установки Kubernetes или поставщика подкачка может быть уже отключена. Чтобы проверить это, введите
exec
в работающий контейнер и запуститеcat /proc/swaps
, чтобы получить список активных устройств подкачки. Если вы ничего там не видите, своп отключен.Теперь, когда мы определили наш основной контейнер приложения и контейнеры Init, которые запускаются перед ним для настройки операционной системы контейнера, мы можем добавить последнюю часть в наш файл определения объекта StatefulSet:
volumeClaimTemplates
.Вставьте следующий блок
volumeClaimTemplate
:. . . volumeClaimTemplates: - metadata: name: data labels: app: elasticsearch spec: accessModes: [ "ReadWriteOnce" ] storageClassName: do-block-storage resources: requests: storage: 100Gi
В этом блоке мы определяем
volumeClaimTemplates
StatefulSet. Kubernetes будет использовать это для создания PersistentVolumes для модулей. В приведенном выше блоке мы назовем егоdata
(этоимя
, на которое мы ссылаемся вvolumeMount
, определенных ранее), и дадим ему имя та же меткаapp: elasticsearch
, что и у нашего StatefulSet.Затем мы указываем его режим доступа как
ReadWriteOnce
, что означает, что он может быть смонтирован только для чтения и записи одним узлом. В этом руководстве мы определяем класс хранилища какdo-block-storage
, поскольку в демонстрационных целях мы используем кластер DigitalOcean Kubernetes. Вы должны изменить это значение в зависимости от того, где вы используете свой кластер Kubernetes. Чтобы узнать больше, обратитесь к документации Persistent Volume.Наконец, мы указываем, что каждый PersistentVolume должен иметь размер 100 ГБ. Вы должны настроить это значение в зависимости от ваших производственных потребностей.
Полная спецификация StatefulSet должна выглядеть примерно так:
apiVersion: apps/v1 kind: StatefulSet metadata: name: es-cluster namespace: kube-logging spec: serviceName: elasticsearch replicas: 3 selector: matchLabels: app: elasticsearch template: metadata: labels: app: elasticsearch spec: containers: - name: elasticsearch image: docker.elastic.co/elasticsearch/elasticsearch:7.2.0 resources: limits: cpu: 1000m requests: cpu: 100m ports: - containerPort: 9200 name: rest protocol: TCP - containerPort: 9300 name: inter-node protocol: TCP volumeMounts: - name: data mountPath: /usr/share/elasticsearch/data env: - name: cluster.name value: k8s-logs - name: node.name valueFrom: fieldRef: fieldPath: metadata.name - name: discovery.seed_hosts value: "es-cluster-0.elasticsearch,es-cluster-1.elasticsearch,es-cluster-2.elasticsearch" - name: cluster.initial_master_nodes value: "es-cluster-0,es-cluster-1,es-cluster-2" - name: ES_JAVA_OPTS value: "-Xms512m -Xmx512m" initContainers: - name: fix-permissions image: busybox command: ["sh", "-c", "chown -R 1000:1000 /usr/share/elasticsearch/data"] securityContext: privileged: true volumeMounts: - name: data mountPath: /usr/share/elasticsearch/data - name: increase-vm-max-map image: busybox command: ["sysctl", "-w", "vm.max_map_count=262144"] securityContext: privileged: true - name: increase-fd-ulimit image: busybox command: ["sh", "-c", "ulimit -n 65536"] securityContext: privileged: true volumeClaimTemplates: - metadata: name: data labels: app: elasticsearch spec: accessModes: [ "ReadWriteOnce" ] storageClassName: do-block-storage resources: requests: storage: 100Gi
Когда вы будете удовлетворены конфигурацией Elasticsearch, сохраните и закройте файл.
Теперь разверните StatefulSet с помощью
kubectl
:- kubectl create -f elasticsearch_statefulset.yaml
Вы должны увидеть следующий вывод:
Outputstatefulset.apps/es-cluster createdВы можете следить за развертыванием StatefulSet, используя
статус развертывания kubectl
:- kubectl rollout status sts/es-cluster --namespace=kube-logging
При развертывании кластера вы должны увидеть следующий вывод:
OutputWaiting for 3 pods to be ready... Waiting for 2 pods to be ready... Waiting for 1 pods to be ready... partitioned roll out complete: 3 new pods have been updated...После развертывания всех модулей вы можете проверить, правильно ли работает ваш кластер Elasticsearch, выполнив запрос к REST API.
Для этого сначала перенаправьте локальный порт
9200
на порт9200
на одном из узлов Elasticsearch (es-cluster-0
), используяkubectl перенаправление портов
:- kubectl port-forward es-cluster-0 9200:9200 --namespace=kube-logging
Затем в отдельном окне терминала выполните запрос
curl
к REST API:- curl http://localhost:9200/_cluster/state?pretty
Вы должны увидеть следующий вывод:
Output{ "cluster_name" : "k8s-logs", "compressed_size_in_bytes" : 348, "cluster_uuid" : "QD06dK7CQgids-GQZooNVw", "version" : 3, "state_uuid" : "mjNIWXAzQVuxNNOQ7xR-qg", "master_node" : "IdM5B7cUQWqFgIHXBp0JDg", "blocks" : { }, "nodes" : { "u7DoTpMmSCixOoictzHItA" : { "name" : "es-cluster-1", "ephemeral_id" : "ZlBflnXKRMC4RvEACHIVdg", "transport_address" : "10.244.8.2:9300", "attributes" : { } }, "IdM5B7cUQWqFgIHXBp0JDg" : { "name" : "es-cluster-0", "ephemeral_id" : "JTk1FDdFQuWbSFAtBxdxAQ", "transport_address" : "10.244.44.3:9300", "attributes" : { } }, "R8E7xcSUSbGbgrhAdyAKmQ" : { "name" : "es-cluster-2", "ephemeral_id" : "9wv6ke71Qqy9vk2LgJTqaA", "transport_address" : "10.244.40.4:9300", "attributes" : { } } }, ...Это означает, что наш кластер Elasticsearch
k8s-logs
успешно создан с тремя узлами:es-cluster-0
,es-cluster-1
, иes-cluster-2
. Текущий главный узел —es-cluster-0
.Теперь, когда ваш кластер Elasticsearch настроен и работает, вы можете перейти к настройке интерфейса Kibana для него.
Шаг 3 — Создание развертывания и службы Kibana
Чтобы запустить Kibana в Kubernetes, мы создадим сервис с именем
kibana
и развертывание, состоящее из одной реплики пода. Вы можете масштабировать количество реплик в зависимости от ваших производственных потребностей и при необходимости указать типLoadBalancer
для службы, чтобы сбалансировать нагрузку запросов между модулями развертывания.На этот раз мы создадим сервис и развертывание в одном файле. Откройте файл с именем
kibana.yaml
в своем любимом редакторе:- nano kibana.yaml
Вставьте следующую спецификацию службы:
apiVersion: v1 kind: Service metadata: name: kibana namespace: kube-logging labels: app: kibana spec: ports: - port: 5601 selector: app: kibana --- apiVersion: apps/v1 kind: Deployment metadata: name: kibana namespace: kube-logging labels: app: kibana spec: replicas: 1 selector: matchLabels: app: kibana template: metadata: labels: app: kibana spec: containers: - name: kibana image: docker.elastic.co/kibana/kibana:7.2.0 resources: limits: cpu: 1000m requests: cpu: 100m env: - name: ELASTICSEARCH_URL value: http://elasticsearch:9200 ports: - containerPort: 5601
Затем сохраните и закройте файл.
В этой спецификации мы определили службу под названием
kibana
в пространстве именkube-logging
и присвоили ей меткуapp: kibana
.Мы также указали, что он должен быть доступен через порт
5601
и использовать меткуapp: kibana
для выбора целевых модулей службы.В спецификации
Deployment
мы определяем Deployment с именемkibana
и указываем, что нам нужна 1 реплика Pod.Мы используем образ
docker.elastic.co/kibana/kibana:7.2.0
. На этом этапе вы можете использовать собственное личное или общедоступное изображение Kibana.Мы указываем, что мы хотели бы гарантировать как минимум 0,1 vCPU для Pod, увеличивая его до предела в 1 vCPU. Вы можете изменить эти параметры в зависимости от ожидаемой нагрузки и доступных ресурсов.
Затем мы используем переменную среды
ELASTICSEARCH_URL
, чтобы установить конечную точку и порт для кластера Elasticsearch. При использовании DNS Kubernetes эта конечная точка соответствует имени службыelasticsearch
. Этот домен будет преобразован в список IP-адресов для 3 модулей Elasticsearch. Чтобы узнать больше о DNS Kubernetes, обратитесь к DNS для служб и модулей.Наконец, мы устанавливаем порт контейнера Kibana на
5601
, на который службаkibana
будет перенаправлять запросы.Когда вы будете удовлетворены своей конфигурацией Kibana, вы можете развернуть сервис и развертывание с помощью
kubectl
:- kubectl create -f kibana.yaml
Вы должны увидеть следующий вывод:
Outputservice/kibana created deployment.apps/kibana createdВы можете проверить успешность развертывания, выполнив следующую команду:
- kubectl rollout status deployment/kibana --namespace=kube-logging
Вы должны увидеть следующий вывод:
Outputdeployment "kibana" successfully rolled outЧтобы получить доступ к интерфейсу Kibana, мы снова переадресуем локальный порт на узел Kubernetes, на котором работает Kibana. Получите информацию о Kibana Pod с помощью
kubectl get
:- kubectl get pods --namespace=kube-logging
OutputNAME READY STATUS RESTARTS AGE es-cluster-0 1/1 Running 0 55m es-cluster-1 1/1 Running 0 54m es-cluster-2 1/1 Running 0 54m kibana-6c9fb4b5b7-plbg2 1/1 Running 0 4m27sЗдесь мы видим, что наш Kibana Pod называется
kibana-6c9fb4b5b7-plbg2
.Перенаправьте локальный порт
5601
на порт5601
в этом модуле:- kubectl port-forward kibana-6c9fb4b5b7-plbg2 5601:5601 --namespace=kube-logging
Вы должны увидеть следующий вывод:
OutputForwarding from 127.0.0.1:5601 -> 5601 Forwarding from [::1]:5601 -> 5601Теперь в веб-браузере перейдите по следующему URL-адресу:
http://localhost:5601
Если вы видите следующую страницу приветствия Kibana, вы успешно развернули Kibana в своем кластере Kubernetes:
Теперь вы можете перейти к развертыванию последнего компонента стека EFK: сборщика журналов Fluentd.
Шаг 4 — Создание набора демонов Fluentd
В этом руководстве мы настроим Fluentd как DaemonSet, тип рабочей нагрузки Kubernetes, который запускает копию определенного модуля на каждом узле в кластере Kubernetes. Используя этот контроллер DaemonSet, мы развернем модуль агента ведения журнала Fluentd на каждом узле в нашем кластере. Чтобы узнать больше об этой архитектуре ведения журнала, обратитесь к разделу «Использование агента ведения журнала узла» в официальной документации Kubernetes.
В Kubernetes потоки журналов контейнерных приложений, которые регистрируются в
stdout
иstderr
, перехватываются и перенаправляются в файлы JSON на узлах. Модуль Fluentd будет отслеживать эти файлы журнала, фильтровать события журнала, преобразовывать данные журнала и отправлять их в серверную часть журнала Elasticsearch, которую мы развернули на шаге 2.Помимо журналов контейнеров, агент Fluentd будет отслеживать журналы системных компонентов Kubernetes, такие как журналы kubelet, kube-proxy и Docker. Чтобы увидеть полный список источников, за которыми следит агент ведения журнала Fluentd, обратитесь к разделу «Ведение журнала на уровне узла» в официальной документации Kubernetes.
Начните с открытия файла
fluent.yaml
в вашем любимом текстовом редакторе:- nano fluentd.yaml
Мы снова будем вставлять определения объектов Kubernetes блок за блоком, предоставляя контекст по мере продвижения. В этом руководстве мы используем Kuberentes Fluentd.
Сначала вставьте следующее определение ServiceAccount:
apiVersion: v1 kind: ServiceAccount metadata: name: fluentd namespace: kube-logging labels: app: fluentd
Здесь мы создаем учетную запись службы с именем
fluentd
, которую модули Fluentd будут использовать для доступа к API Kubernetes. Мы создаем его в пространстве именkube-logging
и снова присваиваем ему меткуapp: fluentd
. Чтобы узнать больше об учетных записях служб в Kubernetes, обратитесь к разделу «Настройка учетных записей служб для модулей» в официальной документации Kubernetes.Затем вставьте следующий блок
ClusterRole
:. . . --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: fluentd labels: app: fluentd rules: - apiGroups: - "" resources: - pods - namespaces verbs: - get - list - watch
Здесь мы определяем ClusterRole с именем
fluentd
, которой мы предоставляем разрешенияget
,list
иwatch
наpods
и объектыnamespaces
. ClusterRoles позволяют вам предоставлять доступ к ресурсам Kubernetes в области кластера, таким как Nodes. Чтобы узнать больше об управлении доступом на основе ролей и ролях кластера, обратитесь к разделу Использование авторизации RBAC в официальной документации Kubernetes.Теперь вставьте следующий блок
ClusterRoleBinding
:. . . --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: fluentd roleRef: kind: ClusterRole name: fluentd apiGroup: rbac.authorization.k8s.io subjects: - kind: ServiceAccount name: fluentd namespace: kube-logging
В этом блоке мы определяем
ClusterRoleBinding
с именемfluentd
, который привязываетfluentd
ClusterRole к сервисному аккаунтуfluentd
. Это предоставляет учетной записи службыfluentd
разрешения, перечисленные в роли кластераfluentd
.На этом этапе мы можем начать вставку фактической спецификации DaemonSet:
. . . --- apiVersion: apps/v1 kind: DaemonSet metadata: name: fluentd namespace: kube-logging labels: app: fluentd
Здесь мы определяем DaemonSet с именем
fluentd
в пространстве именkube-logging
и присваиваем ему меткуapp: fluentd
.Затем вставьте в следующий раздел:
. . . spec: selector: matchLabels: app: fluentd template: metadata: labels: app: fluentd spec: serviceAccount: fluentd serviceAccountName: fluentd tolerations: - key: node-role.kubernetes.io/master effect: NoSchedule containers: - name: fluentd image: fluent/fluentd-kubernetes-daemonset:v1.4.2-debian-elasticsearch-1.1 env: - name: FLUENT_ELASTICSEARCH_HOST value: "elasticsearch.kube-logging.svc.cluster.local" - name: FLUENT_ELASTICSEARCH_PORT value: "9200" - name: FLUENT_ELASTICSEARCH_SCHEME value: "http" - name: FLUENTD_SYSTEMD_CONF value: disable
Здесь мы сопоставляем метку
app: fluentd
, определенную в.metadata.labels
, а затем назначаем DaemonSet учетную запись службыfluentd
. Мы также выбираемapp: fluentd
в качестве подов, управляемых этим DaemonSet.Затем мы определяем допуск
NoSchedule
, чтобы соответствовать эквивалентному заражению на главных узлах Kubernetes. Это гарантирует, что DaemonSet также будет развернут на мастер-серверах Kubernetes. Если вы не хотите запускать модуль Fluentd на своих главных узлах, удалите это допущение. Чтобы узнать больше о недочетах и допусках Kubernetes, ознакомьтесь с \Taints and Tolerations в официальной документации Kubernetes.Затем мы начинаем определять контейнер Pod, который мы называем
fluentd
.Мы используем репозиторий fluentd-kubernetes-daemonset на Github.
Далее мы настраиваем Fluentd, используя некоторые переменные среды:
FLUENT_ELASTICSEARCH_HOST
: мы устанавливаем это значение для адреса безголовой службы Elasticsearch, определенного ранее:elasticsearch.kube-logging.svc.cluster.local
. Это приведет к списку IP-адресов для 3 модулей Elasticsearch. Фактический хост Elasticsearch, скорее всего, будет первым IP-адресом, возвращенным в этом списке. Чтобы распределить журналы по кластеру, вам потребуется изменить конфигурацию подключаемого модуля Fluentd Elasticsearch Output. Чтобы узнать больше об этом подключаемом модуле, обратитесь к подключаемому модулю вывода Elasticsearch.FLUENT_ELASTICSEARCH_PORT
: мы устанавливаем порт Elasticsearch, который мы настроили ранее,9200
.FLUENT_ELASTICSEARCH_SCHEME
: мы установили значениеhttp
.FLUENTD_SYSTEMD_CONF
: мы устанавливаем для этого параметра значениеdisable
, чтобы подавить вывод, связанный с тем, чтоsystemd
не настроен в контейнере.
Наконец, вставьте в следующий раздел:
. . . resources: limits: memory: 512Mi requests: cpu: 100m memory: 200Mi volumeMounts: - name: varlog mountPath: /var/log - name: varlibdockercontainers mountPath: /var/lib/docker/containers readOnly: true terminationGracePeriodSeconds: 30 volumes: - name: varlog hostPath: path: /var/log - name: varlibdockercontainers hostPath: path: /var/lib/docker/containers
Здесь мы указываем ограничение памяти в 512 МБ для модуля FluentD и гарантируем 0,1 ВЦП и 200 МБ памяти. Вы можете настроить эти ограничения ресурсов и запросы в зависимости от ожидаемого объема журнала и доступных ресурсов.
Затем мы монтируем пути хоста
/var/log
и/var/lib/docker/containers
в контейнер, используяvarlog
иvarlibdockercontainers
volumeMounts
. Этитома
определяются в конце блока.Последним параметром, который мы определяем в этом блоке, является
terminationGracePeriodSeconds
, который дает Fluentd 30 секунд для корректного закрытия после получения сигналаSIGTERM
. Через 30 секунд контейнерам отправляется сигналSIGKILL
. Значение по умолчанию дляterminationGracePeriodSeconds
равно 30 с, поэтому в большинстве случаев этот параметр можно не указывать. Чтобы узнать больше о корректном завершении рабочих нагрузок Kubernetes, ознакомьтесь с «Лучшими практиками Kubernetes: изящное завершение» от Google.Вся спецификация Fluentd должна выглядеть примерно так:
apiVersion: v1 kind: ServiceAccount metadata: name: fluentd namespace: kube-logging labels: app: fluentd --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: fluentd labels: app: fluentd rules: - apiGroups: - "" resources: - pods - namespaces verbs: - get - list - watch --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: fluentd roleRef: kind: ClusterRole name: fluentd apiGroup: rbac.authorization.k8s.io subjects: - kind: ServiceAccount name: fluentd namespace: kube-logging --- apiVersion: apps/v1 kind: DaemonSet metadata: name: fluentd namespace: kube-logging labels: app: fluentd spec: selector: matchLabels: app: fluentd template: metadata: labels: app: fluentd spec: serviceAccount: fluentd serviceAccountName: fluentd tolerations: - key: node-role.kubernetes.io/master effect: NoSchedule containers: - name: fluentd image: fluent/fluentd-kubernetes-daemonset:v1.4.2-debian-elasticsearch-1.1 env: - name: FLUENT_ELASTICSEARCH_HOST value: "elasticsearch.kube-logging.svc.cluster.local" - name: FLUENT_ELASTICSEARCH_PORT value: "9200" - name: FLUENT_ELASTICSEARCH_SCHEME value: "http" - name: FLUENTD_SYSTEMD_CONF value: disable resources: limits: memory: 512Mi requests: cpu: 100m memory: 200Mi volumeMounts: - name: varlog mountPath: /var/log - name: varlibdockercontainers mountPath: /var/lib/docker/containers readOnly: true terminationGracePeriodSeconds: 30 volumes: - name: varlog hostPath: path: /var/log - name: varlibdockercontainers hostPath: path: /var/lib/docker/containers
Закончив настройку Fluentd DaemonSet, сохраните и закройте файл.
Теперь разверните DaemonSet с помощью
kubectl
:- kubectl create -f fluentd.yaml
Вы должны увидеть следующий вывод:
Outputserviceaccount/fluentd created clusterrole.rbac.authorization.k8s.io/fluentd created clusterrolebinding.rbac.authorization.k8s.io/fluentd created daemonset.extensions/fluentd createdУбедитесь, что ваш DaemonSet успешно развернут с помощью
kubectl
:- kubectl get ds --namespace=kube-logging
Вы должны увидеть следующий вывод состояния:
OutputNAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE fluentd 3 3 3 3 3 <none> 58sЭто указывает на то, что запущено 3 модуля
fluentd
, что соответствует количеству узлов в нашем кластере Kubernetes.Теперь мы можем проверить Kibana, чтобы убедиться, что данные журнала правильно собираются и отправляются в Elasticsearch.
Не закрывая
kubectl port-forward
, перейдите по адресуhttp://localhost:5601
.Нажмите «Обнаружить» в меню навигации слева:
Вы должны увидеть следующее окно конфигурации:
Это позволяет вам определить индексы Elasticsearch, которые вы хотите исследовать в Kibana. Чтобы узнать больше, обратитесь к разделу «Определение шаблонов индексов» в официальной документации Kibana. На данный момент мы просто используем подстановочный шаблон
logstash-*
для сбора всех данных журнала в нашем кластере Elasticsearch. Введитеlogstash-*
в текстовое поле и нажмите «Следующий шаг».Затем вы попадете на следующую страницу:
Это позволяет вам настроить, какое поле Kibana будет использовать для фильтрации данных журнала по времени. В раскрывающемся списке выберите поле @timestamp и нажмите Создать шаблон индекса.
Теперь нажмите Discover в меню навигации слева.
Вы должны увидеть график гистограммы и некоторые последние записи журнала:
На данный момент вы успешно настроили и развернули стек EFK в своем кластере Kubernetes. Чтобы узнать, как использовать Kibana для анализа данных журнала, обратитесь к Руководству пользователя Kibana.
В следующем необязательном разделе мы развернем простой модуль счетчика, который выводит числа на стандартный вывод, и найдем его журналы в Kibana.
Шаг 5 (необязательно) — Тестирование ведения журнала контейнера
Чтобы продемонстрировать базовый вариант использования Kibana для изучения последних журналов для данного модуля, мы развернем минимальный модуль счетчика, который печатает последовательные числа в стандартный вывод.
Начнем с создания пода. Откройте файл с именем
counter.yaml
в вашем любимом редакторе:- nano counter.yaml
Затем вставьте следующую спецификацию Pod:
apiVersion: v1 kind: Pod metadata: name: counter spec: containers: - name: count image: busybox args: [/bin/sh, -c, 'i=0; while true; do echo "$i: $(date)"; i=$((i+1)); sleep 1; done']
Сохраните и закройте файл.
Это минимальный модуль под названием
counter
, который запускает циклwhile
, последовательно печатая числа.Разверните модуль
counter
с помощьюkubectl
:- kubectl create -f counter.yaml
Как только модуль будет создан и запущен, вернитесь на панель управления Kibana.
На странице Discover в строке поиска введите
kubernetes.pod_name:counter
. Это фильтрует данные журнала для модулей с именемcounter
.Затем вы должны увидеть список записей журнала для модуля
counter
:Вы можете щелкнуть любую запись журнала, чтобы увидеть дополнительные метаданные, такие как имя контейнера, узел Kubernetes, пространство имен и т. д.
Заключение
В этом руководстве мы показали, как установить и настроить Elasticsearch, Fluentd и Kibana в кластере Kubernetes. Мы использовали минимальную архитектуру ведения журнала, состоящую из одного модуля агента ведения журнала, работающего на каждом рабочем узле Kubernetes.
Перед развертыванием этого стека журналов в рабочем кластере Kubernetes лучше всего настроить требования к ресурсам и ограничения, как указано в этом руководстве. Вы также можете настроить X-Pack, чтобы включить встроенные функции мониторинга и безопасности.
Архитектура ведения журнала, которую мы здесь использовали, состоит из 3 модулей Elasticsearch, одного модуля Kibana (без балансировки нагрузки) и набора модулей Fluentd, развернутых в виде DaemonSet. Возможно, вы захотите масштабировать эту настройку в зависимости от вашего производственного варианта использования. Чтобы узнать больше о масштабировании стека Elasticsearch и Kibana, обратитесь к разделу «Масштабирование Elasticsearch».
Kubernetes также позволяет создавать более сложные архитектуры агентов ведения журналов, которые могут лучше подходить для вашего варианта использования. Чтобы узнать больше, обратитесь к разделу «Архитектура ведения журналов» в документации Kubernetes.