Merhaba, Kubernetes’i kullanırken özellikle managed service olarak kullanırken karşılaştığımız en önemli problemlerden biri logların nasıl saklanacağıdır. İşin aslı Kubernetes pod loglarını ilgili podun çalıştığı node üzerinde bir dosyaya yazıyor. Ancak bu dosyalar kalıcı olarak saklanmıyor, podun silinmesi ya da nodeun down olması gibi durumlarda dosyalar siliniyor. Bu durum logları sağlıklı bir şekilde yönlendirme ihtiyacı doğuruyor. Bu yazıda da en popüler stacklerden biri olan ELK Stack’ini inceleyeceğiz.
Logların dosyalarda geçici olarak saklandığını az önce söylemiştim. Burada yapacağımız ilk işlem bu dosyaları takip edip işleme almak olacak. Bunun için Elastic Beats’i kullanacağız. Burada Beats, Kubernetes’in logları geçici olarak sakladığı dosyaları sürekli takip ederek yeni logları işleme alıp stackin bir sonraki elemanına iletmekle yükümlü olacaktır. Stackin bir sonraki elemanı ise Elastic Logstash olacak ve Logstash’in yükümlülüğü de Beats’ten aldığı logları işleyerek Elasticsearch’e göndermek olacaktır. Buradaki işleme eylemini ham logu parse edip içinden bilgi elde etmek olarak düşünebilirsiniz. Logstash, Elasticsearch’e işlenmiş logları ilettikten sonra Elasticsearch bu logları diskte saklama işini üstlenecektir. Son olaraksa Elastic Kibana da görselleştirme ve arayüz işlemleri için yardımımıza koşacaktır. Uzun bir girizgahtan sonra kuruluma geçebiliriz 🙂
1.) Elastic Cloud’un Deploy Edilmesi:
İlk olarak Elastic Cloud’un CRD’sini deploy edelim.
kubectl create -f https://download.elastic.co/downloads/eck/2.3.0/crds.yaml
Ardından Elastic Cloud’un operatörünü deploy edelim.
kubectl apply -f https://download.elastic.co/downloads/eck/2.3.0/operator.yaml
Aşağıdaki komut ile operatörün durumunu izleyebiliriz.
kubectl get po -n elastic-system
Operatör aşağıdaki gibi hazır olduğunda devam edebiliriz.
NAME READY STATUS RESTARTS AGE
elastic-operator-0 1/1 Running 0 4m
2.) Elasticsearch’ün Deploy Edilmesi
Aşağıdaki komut ile de Elasticsearch’ü deploy edelim.
cat <<EOF | kubectl apply -f -
apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
name: blog
spec:
version: 8.3.2
nodeSets:
- name: default
count: 1
config:
node.store.allow_mmap: false
volumeClaimTemplates:
- metadata:
name: elasticsearch-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: standard
http:
tls:
selfSignedCertificate:
disabled: true
EOF
Burada ihtiyaca göre yapmamız gereken birkaç değişiklik olabilir.
- metadata.name: Deploy edeceğimiz Elasticsearch instance’ının ön adını belirliyoruz.
- spec.nodeSets.count: Elasticsearch cluster’ının eleman sayısını belirliyoruz.
- spec.nodeSets[0].volumeClaimTemplates[0].spec.resources.requests.storage: Elasticsearch için ayırılacak volume’ün boyutunu belirliyoruz.
- spec.nodeSets[0].volumeClaimTemplates[0].spec.storageClassName: Elasticsearch’ün verileri diske yazacağı volume’ü ayıracak olan storageClass’ı belirtiyoruz. Bu kısım çok önemli eğer clusterınızda olmayan bir storageClass girerseniz deployment Pending aşamasında takılı kalacaktır.
3.) Logstash’in Deploy Edilmesi
Logstash Elastic Cloud bünyesinde bulunan bir paket olmadığı için Helm ile deploy edeceğiz. İlk olarak aşağıdaki bloku values.yaml adında bir dosyaya kaydedin.
logstashPipeline:
logstash.conf: |
input {
beats {
host => "0.0.0.0"
port => 5044
}
}
filter {
mutate {
rename => ["host", "hostname"]
convert => {"hostname" => "string"}
}
if [kubernetes][namespace] == "ingress-nginx" {
if [stream] == "stdout" {
grok {
match => { "message" => ["%{IPORHOST:[nginx][access][remote_ip]} - %{DATA:[nginx][access][user_name]} \[%{HTTPDATE:[nginx][access][time]}\] \"%{WORD:[nginx][access][method]} %{DATA:[nginx][access][url]} HTTP/%{NUMBER:[nginx][access][http_version]}\" %{NUMBER:[nginx][access][response_code]} %{NUMBER:[nginx][access][body_sent][bytes]} \"%{DATA:[nginx][access][referrer]}\" \"%{DATA:[nginx][access][agent]}\" %{NUMBER:[nginx][access][request_length]} %{NUMBER:[nginx][access][request_time]} \[%{DATA:[nginx][access][proxy_upstream_name]}\] \[%{DATA:[nginx][access][proxy_alternative_upstream_name]}\] %{NOTSPACE:[nginx][access][upstream_addr]} %{NUMBER:[nginx][access][upstream_response_length]} %{NUMBER:[nginx][access][upstream_response_time]} %{NUMBER:[nginx][access][upstream_response_code]} %{NOTSPACE:[nginx][access][req_id]}"] }
remove_field => "message"
}
mutate {
add_field => { "read_timestamp" => "%{@timestamp}" }
}
date {
match => [ "[nginx][access][time]", "dd/MMM/YYYY:H:m:s Z" ]
remove_field => "[nginx][access][time]"
}
useragent {
source => "[nginx][access][agent]"
target => "[nginx][access][user_agent]"
remove_field => "[nginx][access][agent]"
}
geoip {
source => "[nginx][access][remote_ip]"
target => "[nginx][access][geoip]"
}
}
else if [stream] == "stderr" {
grok {
match => { "message" => ["%{DATA:[nginx][error][time]} \[%{DATA:[nginx][error][level]}\] %{NUMBER:[nginx][error][pid]}#%{NUMBER:[nginx][error][tid]}: (\*%{NUMBER:[nginx][error][connection_id]} )?%{GREEDYDATA:[nginx][error][message]}"] }
remove_field => "message"
}
mutate {
rename => { "@timestamp" => "read_timestamp" }
}
date {
match => [ "[nginx][error][time]", "YYYY/MM/dd H:m:s" ]
remove_field => "[nginx][error][time]"
}
}
}
}
output {
stdout { codec => rubydebug }
elasticsearch {
hosts => [ "blog-es-http:9200" ]
user => 'elastic'
password => 'P@$$w0rD'
}
}
extraEnvs:
- name: XPACK_MONITORING_ENABLED
value: "false"
service:
type: ClusterIP
loadBalancerIP: ""
ports:
- name: beats
port: 5044
protocol: TCP
targetPort: 5044
values.yaml dosyasının içeriğini biraz açıklamak gerekirse aslında temelde 3 farklı değer verdik. Bunlar logstash.conf dosyasının içeriği, eklemek istediğimiz ortam değişkenleri ve açmak istediğimiz servis portlarıdır. Son iki değeri açıklayacak olursak XPACK_MONITORING_ENABLED: “false” diyerek kullanmayacağımız monitoring modülünü kapatmış olduk, servis tarafında ise Filebeat’in logları göndereceği 5044 portunu serviste tanımlamış olduk. Gelelim logstash.conf dosyasına bu dosya logstash ile kullanacağımız pipelineları tanımladığımız bir dosyadır. Peki bizim pipeline’ımız nasıl işleyecek? Öncelikle beats tipinde bir input tanımlıyoruz buraya istediğimiz portu tanımlayabiliriz ama service kısmında da o portu tanımlamayı unutmayalım. Filter kısmı ise logları Elasticsearch’e göndermeden önce düzenlediğimiz kısım. Burada ilk satırdaki mutate scope’unda inputtan yani bizim için Filebeat’ten gelen fieldlardan host fieldının adını hostname ve tipini de string olarak değiştiriyoruz. Alttaki if scope’u ise logların ingress-nginx’ten gelip gelmediğini kontrol ediyor ve eğer öyleyse gerekli fieldları parse ediyor son olaraksa output bölümünde elasticsearch servisinin bilgilerini giriyoruz burada ek olarak elasticsearch clusterına ait bir parolaya ihtiyacımız var bu parolayı aşağıdaki komut ile öğrenebilirsiniz.
kubectl get secret blog-es-elastic-user -o=jsonpath='{.data.elastic}' | base64 --decode; echo
Şimdi sıra geldi Helm ile deploy etmeye… Bunun için values.yaml dosyasını kaydettiğimiz dizinde şu komutu çalıştırmamız yeterli olacaktır.
helm repo add elastic https://helm.elastic.co
helm install logstash elastic/logstash -f values.yaml
4.) Filebeat’in Deploy Edilmesi
Filebeat de Logstash gibi Elastic Cloud’a dahil bir paket değil o yüzden yine Helm kullnarak deploy edeceğiz.
Aşağıdaki bloku values.yaml olarak kaydedelim.
---
daemonset:
filebeatConfig:
filebeat.yml: |
filebeat.inputs:
- type: container
paths:
- /var/log/containers/*.log
processors:
- add_kubernetes_metadata:
host: ${NODE_NAME}
matchers:
- logs_path:
logs_path: "/var/log/containers/"
- drop_event:
when:
or:
- equals:
kubernetes.namespace: "kube-system"
- equals:
kubernetes.namespace: "elastic-system"
- regexp:
kubernetes.pod.name: "filebeat-*"
- regexp:
kubernetes.pod.name: "elasticsearch-*"
- regexp:
kubernetes.pod.name: "logstash-*"
output.logstash:
host: '${NODE_NAME}'
hosts: '[logstash-logstash]'
deployment:
enabled: false
secretMounts: []
values.yaml dosyasında container loglarının hangi dizinden alınacağını, hariç tutulacak logları ve logstash adresini tanımladık. Helm ile deploy edelim. Aşağıdaki komutu yeni values.yaml dosyamızın olduğu dizinde çalıştıralım.
helm install filebeat elastic/filebeat -f values.yaml
5.) Kibana’nın Deploy Edilmesi
cat <<EOF | kubectl apply -f -
apiVersion: kibana.k8s.elastic.co/v1
kind: Kibana
metadata:
name: blog
spec:
version: 8.3.2
count: 1
elasticsearchRef:
name: blog
EOF
Kibana’yı deploy etmek için yukarıdaki komutu çalıştırmanız yeterli olacaktır. Burada dikkat etmeniz gereken kısım koddaki elasticsearchRef.name kısmı ikinci aşamadaki Elasticsearh’ün name’i ile aynı olmalıdır.
Arayüze erişmek için blog-kb-http servisinin 5601 portu için bir ingress tanımlayabilirsiniz. Arayüze eriştiğinizde sizden kullanıcı adı ve parola isteyecektir. Parolayı 3. aşamadaki gibi elde edebilirsiniz.
Bir yanıt yazın