Kubernetes (k8s) ile fluentbit loglama

emre tiryaki
hepsiburadatech
Published in
5 min readFeb 25, 2019

--

Microservice” dünyasına giriş yaptığınızda distributed logging, distributed tracing ve metric gibi problemleri ve bu problemlere karşı ortaya çıkmış bazı uygulamaları duymuşsunuzdur. Tracing için Jaeger ve Zipkin, logging için Elastic Stack ve Splunk bilinen en gelişmiş uygulamalardan birkaçıdır.

Hepsiburada ‘da (Order Management System) Oms Uygulamaları’nda swarm cluster’ından k8s cluster’ına geçiş sonrası logging alt yapısının logstash’den fluentbit’e niçin ve nasıl evrildiğini örnekle göstereceğim. (log bilgilerinin kaydedildiği yer elasticsearch olacaktır)

Logstash, açık kaynak kodlu,logları gösterdiğiniz yerlerden alıp , işleyen ve belirttiğiniz konumlara ileten, yani “collect, parse, aggreate and transform logs ” sağlayan bir yapıdır. Java ile yazılmıştır JVM’e dolayısıyla memory ihtiyaç duyar. Swarm cluster içinde koşan oms uygulamaları ,birçok yazılım ekiplerinin de yaptığı gibi swarm -gelf driver -logstash 3'lüsünü kullanılıyordu. Sizlerinde tahmin edeceği gibi bu uygulamalarda console’a basılan loglar gelf protokolü (UDP veya TCP ) kullanılarak logstash aracılığı elastice atılır (http api). Statik olarak 256mb verilen jvm’de ayrı bir konu…

K8s’e geçiş sonrası (kubespray kullanmanızı tavsiye ederim) , logstash anladığımız kadarıyla k8s’de zayıf kalmış olmalı ki fluentd ve fluentbit karşımıza çıktı. Logstash’ın fazla memory tüketimi, gereksinimizden fazla özelliğe sahip olması sebebiyle karmaşıklığı artırması (karışık configurasyon dosyası), ve k8s dünyasında daha popüler olan diğer 2 uygulamanın olması logstash’ten vazgeçmemize neden oldu.

Fluentbit ve fluentd karşılaştırması ise aşağıda gayet açık :) Log aggregator ihtiyacımız olmadığı için yani sadece container loglarını elastice atmak istediğimiz için fluentbit’i tercih ettik.

Fleuntbit’in kubernetes’e kurulumu: Linkte bulunan yaml dosyalarını sırası ile eklemek gerekmektedir. İsteyenler özelleştirme yapabilmektedir.

Rbac oluşturmak : Bu adımda herhangi bir özelleştirme söz konusu değildir.

kubectl apply -f https://raw.githubusercontent.com/fluent/fluent-bit-kubernetes-logging/master/fluent-bit-service-account.yaml

kubectl apply -f https://raw.githubusercontent.com/fluent/fluent-bit-kubernetes-logging/master/fluent-bit-role.yaml

kubectl apply -f https://raw.githubusercontent.com/fluent/fluent-bit-kubernetes-logging/master/fluent-bit-role-binding.yaml

Fluentbit için config dosyası oluşturmak: Bu alan, makale içindeki en can alıcı yerdir. Loglamak istediğiniz container loglarını ve log parametrelerini, log dosyaları atacağınız yerler (Elastic, Kafka vs) burada oluşturulacaktır. Aslında fluentbit’in default configmap dosyası bulunmaktadır. Fakat bu config dosyasını özelleştirmek gerekmektedir. Yine de default config dosyasının linkine ulaşmak isterseniz bu linki inceleyebilirsiniz.

Aşağıda özelleştirilmiş yaml dosyası bulunmaktadır. Özelleştirdiğimiz yerler şunlardır.

a) Uygulamalarınızın logları farklı elastic indexleriyle oluşturulmalıdır. Bu size elastic’de uygulama bazında logları ayırmanızı sağlar. Aksi takdirde single ve büyük boyutlarda oluşturulan indexlere karmaşık sorgu atmak yada rapor çekmek elastic node’larını oldukça fazla yoracaktır. Kibana’dan timeout almanız kuvvetle muhtemel olacaktır. Aşağıdaki script fleuntbit tarafından her container logunun farkı bir elastic indexine tekabül edeceği şeklinde yorumlanır.

@INCLUDE input-oms-api.conf
@INCLUDE input-oms-consumer.conf içerisindeki
Logstash_Prefix oms-consumer-k8s
Logstash_Prefix oms-api-k8s

b) Loglarının içeriğinde filtreleme yapmalı ve istediğiniz alanları elastic’e gönderilmelidir. Bu alanda Log Objesinin içerisinde “log” alanı ignore edilmektedir. Fluentbit bu alanı gördüğünde elasticsearch’e göndermeyecektir. “Merge_Log” satırı ise log objesi içerisindeki field’lar elastic search’de belirttiğiniz index’in içerisine property olarak eklemek için kullanılır.

[FILTER]
Name record_modifier
Match *
Remove_key log
-------------------------------------Merge_Log On

ConfigMap’in güncel halini aşağıda görebilirsiniz.

apiVersion: v1
kind: ConfigMap
metadata:
name: fluent-bit-config
namespace: logging
labels:
k8s-app: fluent-bit
data:

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-oms-api.conf
@INCLUDE input-oms-consumer.conf
@INCLUDE filter-kubernetes.conf
@INCLUDE output-elasticsearch.conf

input-oms-api.conf: |
[INPUT]
Name tail
Tag oms-api.*
Path /var/log/containers/oms-api*.log
Parser docker
DB /var/log/flb_kube.db
Mem_Buf_Limit 5MB
Skip_Long_Lines On
Refresh_Interval 10

input-oms-consumer.conf: |
[INPUT]
Name tail
Tag oms-consumer.*
Path /var/log/containers/oms-consumer*.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_URL https://kubernetes.default.svc.cluster.local:443
Merge_Log On
K8S-Logging.Parser On
[FILTER]
Name record_modifier
Match *
Remove_key log
output-elasticsearch.conf: |
[OUTPUT]
Name es
Match oms-api.*
Host ${FLUENT_ELASTICSEARCH_HOST}
Port ${FLUENT_ELASTICSEARCH_PORT}
Logstash_Format On
Retry_Limit False
Logstash_Prefix oms-api
[OUTPUT]
Name es
Match oms-consumer.*
Host ${FLUENT_ELASTICSEARCH_HOST}
Port ${FLUENT_ELASTICSEARCH_PORT}
Logstash_Format On
Retry_Limit False
Logstash_Prefix oms-consumer
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
# Command | Decoder | Field | Optional Action
# =============|==================|=================
#Decode_Field_As escaped log
[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

DeamonSet’in oluşturmak: Bu aşamada fleuntbit deamonset’i kurulur . Bu sayede tüm node’larda bir instance fleuntbit container’ı çalışır.

wget https://raw.githubusercontent.com/fluent/fluent-bit-kubernetes-logging/master/output/elasticsearch/fluent-bit-ds.yaml#elastic search host name ve portu güncellenir. kubectl apply -f https://raw.githubusercontent.com/fluent/fluent-bit-kubernetes-logging/master/output/elasticsearch/fluent-bit-ds.yaml

Eğer herşey yolunda ise aşağıdaki ekran görüntüsünü almanız gerekmektedir.

kubectl get pods -n logging

Container’a ait bir log örneği aşağıda görülmektedir. Yukarıdaki pattern’e göre filterelenmiş , merge edilmiş alanlar , k8s’e ait bilgilerle beraber elastice atılmıştır.

Peki fluentbit bu bilgileri nerden yakalıyor : k8s’e bağlanan nodelar içindeki /var/log/containers klasörün altındaki file’larda. Bunu görünce benim aklıma ilk gelen şu olmuştu.

“İyi de docker containers logları aslında /var/lib/docker/containers/<container id> path’de olmalı ama fluentbit başka yerden okuyor?”.

Cevap : Kubrespray k8s’i burda /var/log/containers ‘dan /var/lib/docker/containers/<container id> ‘a mount yapmış. Böylece aslında bu path’a bağlanırken docker container loglarına bakmış oluyorsunuz :)

“Peki herşey iyi güzel, ama docker container logları aslında default silinmiyordu. T zaman sonra ya container logları terabyte’ları bulursa bunun bakımın kim üstlenecek? Ayrıca fleuntbit bunun için IO işlemi gerçekleştirecek, bir container log dosyası terabyte’ları bulursa k8s node’un cpu’su peek yapar :)

Cevap : Kubespray’in güzelliği burda:)kubespray; k8s’e kurarken docker engine kurulumu esnasında default olarak log rotation ekliyor. Buyrunuz resim :). Bu sayede her container için log dosyası 50*5=200 Mgbyte geçmez ve fleuntbit her zaman son oluşan log dosyasını okuyacaktır. Buda node’larınızda fleuntbit cpu tüketimini oldukça düşürecektir.

oluşan 5 adet log dosyası

Özetle Oms takımının kubernetese geçiş sonrası loglama altyapısının logstash’den fleuntbit’e niçin ve nasıl çevirdiğini , bu çevrim esnasında karşılaştığımız problemleri ve çözümlerini anlatmaya çalıştım.

Okuduğunuz için teşekkür ederim.

--

--