Пишем логи из Managed K8s в Yandex Cloud Logging при помощи Fluentbit
Если вы хотите отгружать логи из Managed Kubernetes в Yandex Cloud, для этого есть как минимум два пути:
- Можно установить helm из магазина приложений.
- Можно разобраться и гибко настроить все самому.
Тут конечно же мы рассмотрим второй способ.
Генератор логов
Для начала нам понадобится генератор логов: простое приложение, чтобы на его примере мы могли рассмотреть как указывается парсер логов.
К счастью у меня в одной из предыдущих статей про fluentbit уже написано такое приложение (шаг 1) и оно нам подойдет. Нужно собрать контейнер и запушить, например, в Yandex Cloud Container Registry.
Деплой в k8s
Надеюсь с развертыванием кластера k8s вы справитесь сами. Если что вот инструкция.
Теперь нам надо создать файл с описанием Deployment’а нашего приложения.
apiVersion: apps/v1
kind: Deployment
metadata:
name: loggen-deployment
labels:
app: loggen
spec:
replicas: 3
selector:
matchLabels:
app: loggen
template:
metadata:
labels:
app: loggen
annotations:
fluentbit.io/parser: app_log_parser
yc.logging.tag: loggen
spec:
containers:
- name: loggen
image: cr.yandex/crpk105d8omff2r209qi/loggen:2022.06.13-234725c
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
Обратите внимание на строку 22 — туда вы должны вставить урл откуда можно скачать докер образ, который вы собрали и запушили в репозиторий на шаге 1. А также на строки 17–18, где указаны аннотации, к которым мы вернемся чуть позднее.
Установка Fluentbit
Тут все просто. Для начала мы будем следовать официальной инструкции.
Тут приведен пример для Kubernetes 1.21 и ниже, так как на текущий момент только такие версии доступны в Yandex Cloud.
kubectl create namespace logging
kubectl create -f https://raw.githubusercontent.com/fluent/fluent-bit-kubernetes-logging/master/fluent-bit-service-account.yaml
kubectl create -f https://raw.githubusercontent.com/fluent/fluent-bit-kubernetes-logging/master/fluent-bit-role.yaml
kubectl create -f https://raw.githubusercontent.com/fluent/fluent-bit-kubernetes-logging/master/fluent-bit-role-binding.yaml
Если, когда вы читаете доступна старшая версия, то в официальной инструкции уже есть пример конфигов и для неё.
Дальше нужно создать ConfigMap yaml-файл.
apiVersion: v1
kind: ConfigMap
metadata:
name: fluent-bit-config
namespace: logging
labels:
k8s-app: fluent-bit
data:
# Configuration files: server, input, filters and output
# ======================================================
fluent-bit.conf: |
[SERVICE]
Flush 1
Log_Level info
Daemon off
Parsers_File parsers.conf
HTTP_Server On
HTTP_Listen 0.0.0.0
HTTP_Port 2020
@INCLUDE input-kubernetes.conf
@INCLUDE filter-kubernetes.conf
@INCLUDE output-yc.conf
input-kubernetes.conf: |
[INPUT]
Name tail
Tag kube.*
Path /var/log/containers/*.log
Parser docker
DB /var/log/flb_kube.db
Mem_Buf_Limit 5MB
Skip_Long_Lines On
Refresh_Interval 10
filter-kubernetes.conf: |
[FILTER]
Name kubernetes
Match kube.*
Kube_URL https://kubernetes.default.svc:443
Kube_CA_File /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
Kube_Token_File /var/run/secrets/kubernetes.io/serviceaccount/token
Kube_Tag_Prefix kube.var.log.containers.
Merge_Log On
Keep_Log Off
K8S-Logging.Parser On
K8S-Logging.Exclude Off
[FILTER]
Name rewrite_tag
Match kube.*
Rule $kubernetes['annotations']['yc.logging.tag'] ^(.+)$ yc.logging.$kubernetes['annotations']['yc.logging.tag'] false
Emitter_Name re_emitted
[FILTER]
Name stdout
Match yc.logging.*
output-yc.conf: |
[OUTPUT]
Name yc-logging
Match yc.logging.*
group_id e23e1gpq9futicl6sna4
resource_type {kubernetes/labels/app}
resource_id {kubernetes/pod_name}
message_key text
level_key severity
default_level WARN
authorization instance-service-account
parsers.conf: |
[PARSER]
Name apache
Format regex
Regex ^(?<host>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^\"]*?)(?: +\S*)?)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?$
Time_Key time
Time_Format %d/%b/%Y:%H:%M:%S %z
[PARSER]
Name apache2
Format regex
Regex ^(?<host>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^ ]*) +\S*)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?$
Time_Key time
Time_Format %d/%b/%Y:%H:%M:%S %z
[PARSER]
Name apache_error
Format regex
Regex ^\[[^ ]* (?<time>[^\]]*)\] \[(?<level>[^\]]*)\](?: \[pid (?<pid>[^\]]*)\])?( \[client (?<client>[^\]]*)\])? (?<message>.*)$
[PARSER]
Name nginx
Format regex
Regex ^(?<remote>[^ ]*) (?<host>[^ ]*) (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^\"]*?)(?: +\S*)?)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?$
Time_Key time
Time_Format %d/%b/%Y:%H:%M:%S %z
[PARSER]
Name json
Format json
Time_Key time
Time_Format %d/%b/%Y:%H:%M:%S %z
[PARSER]
Name docker
Format json
Time_Key time
Time_Format %Y-%m-%dT%H:%M:%S.%L
Time_Keep On
[PARSER]
# http://rubular.com/r/tjUt3Awgg4
Name cri
Format regex
Regex ^(?<time>[^ ]+) (?<stream>stdout|stderr) (?<logtag>[^ ]*) (?<message>.*)$
Time_Key time
Time_Format %Y-%m-%dT%H:%M:%S.%L%z
[PARSER]
Name syslog
Format regex
Regex ^\<(?<pri>[0-9]+)\>(?<time>[^ ]* {1,2}[^ ]* [^ ]*) (?<host>[^ ]*) (?<ident>[a-zA-Z0-9_\/\.\-]*)(?:\[(?<pid>[0-9]+)\])?(?:[^\:]*\:)? *(?<message>.*)$
Time_Key time
Time_Format %b %d %H:%M:%S
[PARSER]
Name app_log_parser
Format regex
Regex ^\[req_id=(?<req_id>[0-9a-fA-F\-]+)\] \[(?<severity>.*)\] (?<code>\d+) (?<text>.*)$
Types code:integer
А теперь давайте разберем подробнее секции конфига.
[INPUT]
Name tail
Tag kube.*
Path /var/log/containers/*.log
Parser docker
DB /var/log/flb_kube.db
Mem_Buf_Limit 5MB
Skip_Long_Lines On
Refresh_Interval 10
В секции INPUT описывается, что Fluentbit будет при помощи инпута tail вычитывать файлы из директории /var/log/containers/, куда k8s складывает логи контейнеров бегущих на текущей ноде. Логи складываются в json-формате Docker’а, поэтому к ним будет применяться парсер docker, который описан ниже в секции парсеров.
[PARSER]
Name docker
Format json
Time_Key time
Time_Format %Y-%m-%dT%H:%M:%S.%L
Time_Keep On
Далее идет несколько настроек для оптимизации кеширования и потребления памяти при инджесте логов. Плагин Tail input не будет добавлять более 5 МБ в движок, пока они не будут сброшены в YC Logging. Это ограничение реализации back pressure.
[FILTER]
Name kubernetes
Match kube.*
Kube_URL [https://kubernetes.default.svc:443](https://kubernetes.default.svc/)
Kube_CA_File /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
Kube_Token_File /var/run/secrets/kubernetes.io/serviceaccount/token
Kube_Tag_Prefix kube.var.log.containers.
Merge_Log On
Keep_Log Off
K8S-Logging.Parser On
K8S-Logging.Exclude Off
Фильтр Kubernetes обогатит записи метаданными Kubernetes, в частности метками и аннотациями. Фильтр ходит в API k8s только в том случае, если он не может найти информацию в кэше.
Тут стоит упомянуть первую из аннотаций из шага 2 — fluentbit.io/parser — именно на основе нее при включенной опции K8S-Logging.Parser будет выбираться парсер, который будет парсит лог нашего приложения. В этом примере app_log_parser.
Теперь подробнее рассмотрим второй фильтр — rewrite_tag.
[FILTER]
Name rewrite_tag
Match kube.*
Rule $kubernetes['annotations']['yc.logging.tag'] ^(.+)$ yc.logging.$kubernetes['annotations']['yc.logging.tag'] false
Emitter_Name re_emitted
В нем мы будем использовать значение второй аннотации из шага 2 — yc.logging.tag. Этот фильтр находит логи в поле по пути kubernetes/annotations/yc.logging.tag есть данные и на основе них формирует новый тег вида yc.logging.*, который мы в свою очередь будем использовать, для того чтобы направлять записи в YC Loggging.
[FILTER]
Name stdout
Match yc.logging.*
Последний фильтр stdout удобен для отладки. Он выводит в логи самого контейнера с Fluentbit записи с тегом совпадающим с фильтром yc.logging.*. В продакшене его стоит убрать.
Теперь разберем конфиг плагина YC Logging.
[OUTPUT]
Name yc-logging
Match yc.logging.*
group_id e23*****
resource_type {kubernetes/labels/app}
resource_id {kubernetes/pod_name}
message_key text
level_key severity
default_level WARN
authorization instance-service-account
Как видно отправлять мы собираемся лишь записи с тегом yc.logging.*. Таким образом если вы хотите настроить отправку логов в разные лог-группы, то вам стоит настроить фильтры так, чтобы им присваивались разные теги.
В полях resource_type и resource_id используется подстановка значений из записи лога. Именно поэтому удобно использовать фильтр stdout, чтобы наглядно видеть какие значения можно использовать.
Также тут можно использовать значения из сервиса метаданных виртуальной машины, на которой бежит под. Для этого используются двойные фигурные скобки. Подробнее про конфиг и синтаксис в репозитории с кодом плагина.
DaemonSet
Ну и напоследок файл конфигурации для деплоя DaemonSet Fluentbit’а. В файле указан самый свежий на текущий момент контейнер с предустановленным плагином для Yandex Cloud Logging.
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluent-bit
namespace: logging
labels:
k8s-app: fluent-bit-logging
version: v1
kubernetes.io/cluster-service: "true"
spec:
selector:
matchLabels:
k8s-app: fluent-bit-logging
template:
metadata:
labels:
k8s-app: fluent-bit-logging
version: v1
kubernetes.io/cluster-service: "true"
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "2020"
prometheus.io/path: /api/v1/metrics/prometheus
spec:
containers:
- name: fluent-bit
image: cr.yandex/yc/fluent-bit-plugin-yandex:v2.0.2-fluent-bit-1.9.3
imagePullPolicy: Always
ports:
- containerPort: 2020
volumeMounts:
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
- name: fluent-bit-config
mountPath: /fluent-bit/etc/
terminationGracePeriodSeconds: 10
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
- name: fluent-bit-config
configMap:
name: fluent-bit-config
serviceAccountName: fluent-bit
tolerations:
- key: node-role.kubernetes.io/master
operator: Exists
effect: NoSchedule
- operator: "Exists"
effect: "NoExecute"
- operator: "Exists"
effect: "NoSchedule"
Развертывание
kubectl apply -f fluent-bit-config.yaml
kubectl apply -f fluent-bit-ds.yaml
kubectl apply -f loggen-deployment.yaml
Отлично. Теперь, если все правильно, вы увидите логи в выбранной вами лог-группе.