Fluentd көмегімен Kubernetes жүйесінен орталықтандырылған лог жүргізуді қалай баптауға болады
Логтарды контейнерлерге жазу қолайсыз, себебі:
- контейнерлендірілген қолданбаларда жасалатын деректер контейнер бар болғанша ғана сақталады. docker-контейнер қайта жүктелген кезде деректер, соның ішінде қолданба логтары да жойылады;
- логтарды контейнер ішінде ротациялау мүмкін емес, өйткені ротация — лог жүргізілетін процестен бөлек қосымша процесс, ал бір контейнерде бірден артық процесс іске қосыла алмайды.
Kubernetes жүйесіндегі контейнерлендірілген қолданбалардың логтарына қол жеткізу үшін docker-контейнерлер өз логтарын стандартты шығару (stdout) және қате шығару (stderr) ағындарына жіберуі керек. Әдепкі бойынша docker logging driver логтарды нодадағы json-файлға жазады, сол жерден оларды мына команда арқылы алуға болады:
kubectl logs pod_name
docker-контейнерлердің өмірлік циклін Kubernetes оркестраторы басқарған кезде, контейнерлері бар подтар жиі және болжап болмайтындай түрде жасалады, қайта жүктеледі және жойылады. Егер docker лог жүргізу драйверінің баптаулары рұқсат етсе, подтың қайта жүктеуге дейінгі соңғы логтарына --previous аргументі арқылы қол жеткізуге болады:
kubectl logs pod_name --previous
Бірақ осылайша бір қайта жүктеуден де ертеректегі уақыттың логтарын алу мүмкін емес. Подты жою ол туралы бүкіл ақпаратты, соның ішінде логтарды да жоюды білдіреді.
Сондықтан Kubernetes жүйесінде қолданба логтарымен жұмыс істеу үшін логтардан деректерді жинай, агрегаттай, сақтай және пайдалы ақпаратты шығара алатын жүйе қажет. Бұл міндетке Elasticsearch іздеу қозғалтқышы, Fluentd лог жүргізу агенті және Kibana дашбордының байланысы — EFK-стек сай келеді.
Elasticsearch өңдеуіне қажет ақпарат көлеміне байланысты EFK-стекті орнатудың әртүрлі жолдарын таңдауға болады:
- Секундына ондаған мың лог түседі деп күтілсе, Elasticsearch-ті Kubernetes кластеріне орнату ұсынылмайды. Продакшндағы жоғары жүктемелі шешім үшін Elasticsearch кластерінің өзіне бөлек виртуалды машиналар бөліп, логтарды Kubernetes жүйесінен лог-агрегаторлар арқылы жіберген дұрыс.
- Егер жүйе секундына ондаған мың лог өндірмесе және дев-, тест-орталардағы логтарды бақылау керек болса, EFK-стекті Kubernetes кластеріне, қолданбаларға жақынырақ орнатуға болады.
Лог жинаушы агент ретінде fluentd қолданамыз — C және Ruby тілдерінде жазылған, логтарды жинауға, сүзуге және агрегаттауға арналған, қолданбаның базалық функционалын кеңейту үшін түрлі плагиндері бар қосымша.
Kubernetes үшін орталықтандырылған лог жүргізу жүйесін ұйымдастырайық.
kube-loggingnamespace жасаңыз:
kubectl create ns kube-logging
- Kubernetes кластерінде қолжетімді
storage classтүрлерін анықтаңыз:
admin@k8s:~$ kubectl get scNAME PROVISIONER AGEhdd (default) kubernetes.io/cinder 103dhdd-retain kubernetes.io/cinder 103dssd kubernetes.io/cinder 103dssd-retain kubernetes.io/cinder 103d
-
Elasticsearch-ті Kubernetes кластеріне көрсетілген айнымалылармен орнатыңыз:
helm install stable/elasticsearch \--name elastic \--set client.replicas=1 \--set master.replicas=1 \--set data.replicas=1 \--set master.persistence.storageClass=hdd \--set data.persistence.storageClass=hdd \--set master.podDisruptionBudget.minAvailable=1 \--set resources.requests.memory=4Gi \--set cluster.env.MINIMUM_MASTER_NODES=1 \--set cluster.env.RECOVER_AFTER_MASTER_NODES=1 \--set cluster.env.EXPECTED_MASTER_NODES=1 \--namespace kube-loggingНәтижесінде 1 мастер-нода, 1 деректерді сақтау нодасы және 1 клиент нодасынан тұратын Elasticsearch кластері орнатылады.
-
Барлық подтардың жұмысқа дайын екенін тексеріңіз:
kubectl get po -n kube-loggingNAME READY STATUS RESTARTS AGEelastic-elasticsearch-client-c74598797-9m7pm 1/1 Runningelastic-elasticsearch-data-0 1/1 Runningelastic-elasticsearch-master-0 1/1 Running -
kube-logging ішіндегі сервистердің атауын анықтаңыз:
kubectl get svc -n kube-logging
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEelastic-elasticsearch-client ClusterIP 10.233.8.213 <none> 9200/TCP 11melastic-elasticsearch-discovery ClusterIP None <none> 9300/TCP 11m
elastic-elasticsearch-client сервисі kibana және fluentd-мен байланыстыру үшін қолданылады. Kibana дашбордын да helm көмегімен орнатамыз, бірақ оның helm-чартының айнымалыларына elastic-elasticsearch-client сервисінің атауын жазамыз.
- Өңдеу үшін kibana helm-чартының айнымалыларын жүктеп алыңыз:
helm fetch --untar stable/kibana
kibanaдиректориясына өтіп,values.yamlфайлын өңдеңіз:
cd kibana/ && vim values.yaml
elasticsearch hostsбөлімінеelastic-elasticsearch-clientсервисінің атауын жазыңыз:
files:kibana.yml:## Default Kibana configuration from kibana-docker.server.name: kibanaserver.host: "0"## For kibana < 6.6, use elasticsearch.url insteadelasticsearch.hosts: http://elastic-elasticsearch-client:9200
- Өзгертілген параметрлермен Kibana орнатыңыз:
helm install stable/kibana \--name kibana \--namespace kube-logging \-f values.yaml
- Подтың іске қосылғанына көз жеткізіп, дашбордқа қол жеткізу үшін Kibana подының 5601 портын жергілікті машинаңызға бағыттаңыз:
kubectl get pod -n kube-logging
Келесі командада kibana-pod_hash_id орнына алдыңғы команданың шығысынан алынған kibana подының толық атын жазыңыз:
kubectl port-forward --namespace kube-logging kibana-pod_hash_id 5601:5601
- Браузердің мекенжай жолағында бағытталған Kibana дашбордына қосылу жолын көрсетіңіз:
localhost:5601

Kubernetes кластеріндегі Fluend тек подтар, сервистер және хосттар логтарын ғана емес, сонымен қатар kubernetes нысандарының label-дарымен байланыстырылатын метадеректерді де жинай алады.
fluentd конфигурациясында логтарды жинау көздері, пайдалы ақпаратты парсингтеу және сүзу тәсілдері, сондай-ақ осы ақпараттың тұтынушылары туралы мәліметтер бар (біздің жағдайда мұндай тұтынушы — elasticsearch).
fluentd конфигурациялық файлында <source> элементтері аясында логтарды жинау көздері көрсетіледі. Төменде берілген configmap ішінде fluentd қолданба контейнерлерінің логтарынан, kubernetes-тің өз контейнерлерінің логтарынан және kubernetes нодаларының жүйелік логтарынан ақпарат жинайтыны көрсетілген.
- fluentd үшін төмендегі мазмұнмен configmap жасаңыз:
kind: ConfigMapapiVersion: v1data:containers.input.conf: |-<source>@type tailpath /var/log/containers/*.logpos_file /var/log/es-containers.log.postime_format %Y-%m-%dT%H:%M:%S.%NZtag kubernetes.*read_from_head trueformat multi_format<pattern>format jsontime_key timetime_format %Y-%m-%dT%H:%M:%S.%NZ</pattern><pattern>format /^(?<time>.+) (?<stream>stdout|stderr) [^ ]* (?<log>.*)$/time_format %Y-%m-%dT%H:%M:%S.%N%:z</pattern></source>system.input.conf: |-<source>@type tailformat /^time="(?<time>[^)]*)" level=(?<severity>[^ ]*) msg="(?<message>[^"]*)"( err="(?<error>[^"]*)")?( statusCode=($<status_code>\d+))?/path /var/log/docker.logpos_file /var/log/es-docker.log.postag docker</source><source>@type tailformat nonepath /var/log/etcd.logpos_file /var/log/es-etcd.log.postag etcd</source><source>@type tailformat multilinemultiline_flush_interval 5sformat_firstline /^\w\d{4}/format1 /^(?<severity>\w)(?<time>\d{4} [^\s]*)\s+(?<pid>\d+)\s+(?<source>[^ \]]+)\] (?<message>.*)/time_format %m%d %H:%M:%S.%Npath /var/log/kubelet.logpos_file /var/log/es-kubelet.log.postag kubelet</source><source>@type tailformat multilinemultiline_flush_interval 5sformat_firstline /^\w\d{4}/format1 /^(?<severity>\w)(?<time>\d{4} [^\s]*)\s+(?<pid>\d+)\s+(?<source>[^ \]]+)\] (?<message>.*)/time_format %m%d %H:%M:%S.%Npath /var/log/kube-proxy.logpos_file /var/log/es-kube-proxy.log.postag kube-proxy</source><source>@type tailformat multilinemultiline_flush_interval 5sformat_firstline /^\w\d{4}/format1 /^(?<severity>\w)(?<time>\d{4} [^\s]*)\s+(?<pid>\d+)\s+(?<source>[^ \]]+)\] (?<message>.*)/time_format %m%d %H:%M:%S.%Npath /var/log/kube-apiserver.logpos_file /var/log/es-kube-apiserver.log.postag kube-apiserver</source><source>@type tailformat multilinemultiline_flush_interval 5sformat_firstline /^\w\d{4}/format1 /^(?<severity>\w)(?<time>\d{4} [^\s]*)\s+(?<pid>\d+)\s+(?<source>[^ \]]+)\] (?<message>.*)/time_format %m%d %H:%M:%S.%Npath /var/log/kube-controller-manager.logpos_file /var/log/es-kube-controller-manager.log.postag kube-controller-manager</source><source>@type tailformat multilinemultiline_flush_interval 5sformat_firstline /^\w\d{4}/format1 /^(?<severity>\w)(?<time>\d{4} [^\s]*)\s+(?<pid>\d+)\s+(?<source>[^ \]]+)\] (?<message>.*)/time_format %m%d %H:%M:%S.%Npath /var/log/kube-scheduler.logpos_file /var/log/es-kube-scheduler.log.postag kube-scheduler</source><source>@type tailformat multilinemultiline_flush_interval 5sformat_firstline /^\w\d{4}/format1 /^(?<severity>\w)(?<time>\d{4} [^\s]*)\s+(?<pid>\d+)\s+(?<source>[^ \]]+)\] (?<message>.*)/time_format %m%d %H:%M:%S.%Npath /var/log/rescheduler.logpos_file /var/log/es-rescheduler.log.postag rescheduler</source># Logs from systemd-journal for interesting services.<source>@type systemdmatches [{ "_SYSTEMD_UNIT": "docker.service" }]pos_file /var/log/gcp-journald-docker.posread_from_head truetag docker</source><source>@type systemdmatches [{ "_SYSTEMD_UNIT": "kubelet.service" }]pos_file /var/log/gcp-journald-kubelet.posread_from_head truetag kubelet</source><source>@type systemdmatches [{ "_SYSTEMD_UNIT": "node-problem-detector.service" }]pos_file /var/log/gcp-journald-node-problem-detector.posread_from_head truetag node-problem-detector</source>forward.input.conf: |-# Takes the messages sent over TCP<source>@type forward</source><match **>@type elasticsearch@log_level infoinclude_tag_key truehost elastic-elasticsearch-clientport 9200logstash_format truelogstash_prefix fluentd# Set the chunk limitsbuffer_chunk_limit 2Mbuffer_queue_limit 8flush_interval 5s# Never wait longer than 5 minutes between retries.max_retry_wait 30# Disable the limit on the number of retries (retry forever).disable_retry_limit# Use multiple threads for processing.num_threads 2</match>metadata:name: fluentd-es-config-v0.1.1namespace: kube-logginglabels:addonmanager.kubernetes.io/mode: Reconcile
-
Kubernetes жүйесінде configmap қолданыңыз:
kubectl apply -f fluentd-cm.yaml
fluentd бүкіл кластерден ақпарат жинайтындықтан, оған kubernetes ресурстарына қол жеткізу қажет болады. Осы қолжетімділікті қамтамасыз ету үшін fluentd үшін service account пен role жасаңыз. Содан кейін role-ды service account-қа тағайындаңыз.
-
fluentd үшін service account, role және rolebinding сипаттамасы бар
sa-r-crb.yamlфайлын жасаңыз:apiVersion: v1kind: ServiceAccountmetadata:name: fluentdnamespace: kube-logginglabels:app: fluentd---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata:name: fluentdlabels:app: fluentdrules:- apiGroups:- ""resources:- pods- namespacesverbs:- get- list- watch---kind: ClusterRoleBindingapiVersion: rbac.authorization.k8s.io/v1metadata:name: fluentdroleRef:kind: ClusterRolename: fluentdapiGroup: rbac.authorization.k8s.iosubjects:- kind: ServiceAccountname: fluentdnamespace: kube-logging -
Ресурстарды кластерге деплойлаңыз:
kubectl apply -f sa-r-crb.yaml
serviceaccount/fluentd createdclusterrole.rbac.authorization.k8s.io/fluentd createdclusterrolebinding.rbac.authorization.k8s.io/fluentd created
Gatekeeper үшін шектеуге (constraint) өзгерістер енгізіңіз. Бұл өзгерістер нодалардан логтарды оқуға мүмкіндік береді:
-
Кластерде
fluent_patch.yamlфайлын жасап, оны төмендегі мазмұнмен толтырыңыз:spec:match:kinds:- apiGroups:- ""kinds:- Podparameters:allowedHostPaths:- pathPrefix: /k8spspreadOnly: true- pathPrefix: /var/logreadOnly: true- pathPrefix: /var/log/containersreadOnly: true -
Өзгерістерді мына команданы орындап қолданыңыз:
kubectl patch k8spsphostfilesystem.constraints.gatekeeper.sh k8spsphostfilesystem --patch-file fluent_patch.yaml --type merge
fluentd орнатыңыз. fluentd-ті кластердің барлық нодаларына орнату қажет болғандықтан, kubernetes ресурсы ретінде DaemonSet түрін таңдаңыз.
- Келесі мазмұнмен
fluentd-daemonset.yamlманифесін жасаңыз (kubernetes 1.22 нұсқасынан төмен нұсқаларға сай келеді):
apiVersion: apps/v1kind: DaemonSetmetadata:name: fluentdnamespace: kube-logginglabels:app: fluentdspec:selector:matchLabels:app: fluentdtemplate:metadata:labels:app: fluentdspec:serviceAccount: fluentdserviceAccountName: fluentdtolerations:- key: node-role.kubernetes.io/mastereffect: NoSchedulecontainers:- name: fluentdimage: fluent/fluentd-kubernetes-daemonset:v1.4.2-debian-elasticsearch-1.1env:- name: FLUENT_ELASTICSEARCH_HOSTvalue: "elastic-elasticsearch-client"- name: FLUENT_ELASTICSEARCH_PORTvalue: "9200"- name: FLUENT_ELASTICSEARCH_SCHEMEvalue: "http"- name: FLUENTD_SYSTEMD_CONFvalue: disable- name: FLUENT_ELASTICSEARCH_SED_DISABLEvalue: disableresources:limits:memory: 512Mirequests:cpu: 100mmemory: 200MivolumeMounts:- name: varlogmountPath: /var/log- name: varlibdockercontainersmountPath: /var/log/containersreadOnly: true- name: config-volumemountPath: /fluentd/etc/conf.dterminationGracePeriodSeconds: 3volumes:- name: varloghostPath:path: /var/log- name: varlibdockercontainershostPath:path: /var/log/containers- name: config-volumeconfigMap:name: fluentd-es-config-v0.1.1
-
Kubernetes жүйесінде манифесті қолданыңыз:
kubectl apply -f fluentd-daemonset.yaml
Kibana UI интерфейсінде индексті баптаңыз:
-
Kibana дашборды бар қойындыға өтіп, Management мәзірін таңдаңыз:

-
Index Patterns қойындысын таңдап, Create index pattern батырмасын басыңыз да,
logstash-\*атауын енгізіңіз:
-
Next Step батырмасын басып, Create Index Pattern таңдаңыз:

- Discover қойындысына өтіңіз. Сіз Kubernetes кластерінен логтарды көресіз:
