저의 경우 fluentd까지는 필요하지 않다고 판단하여 경량버전인 fluent bit으로 구성하였습니다.
EFK스택을 간단하게 helm chart로 구성하는 방법에 관해 포스팅해보려고 합니다.
준비하기
EFK를 구성할 네임스페이스를 생성합니다.
kubectl create ns efk
해당 네임스페이스로 이동합니다.
kubens efk
kubens는 `brew install kubectx` 로 설치 후 사용할 수 있습니다.
참고 : https://blog.secuof.net/37
Elastic Search
헬름차트로 elastic search를 설치합니다.
helm install elasticsearch bitnami/elasticsearch
명령어를 실행한 후 조금 기다려주면 elasticsearch라는 이름으로 efk 네임스페이스에 구성 요소들이 설치됩니다.
기본으로 설정된 내용으로 설치되기 때문에 사용량이 더 많을 것 같거나 적을 것 같다면 bitnami/elasticsearch repo의 value.yaml을 확인하고 변경할 내용을 아래와 같이 명령어를 실행하면 됩니다.
helm upgrade elasticsearch --set {설정내용} bitnami/elasticsearch
Kibana
헬름차트로 Kibana를 설치합니다.
noglob helm install kibana bitnami/kibana --set elasticsearch.hosts[0]=elasticsearch.efk --set elasticsearch.port=9200
elasticsearch.hosts[0]=실제 설치한 elastic search 서비스의 cluster ip를 입력해 주시면 됩니다.
elasticsearch.port=변경하지 않으셨다면 9200으로 해주시면 됩니다.
대 괄호가 명령어에 들어가서 noglob이라는 명령어를 함께 사용하지 않으면 에러가 발생합니다.
조금 기다려주면 kibana라는 이름으로 efk 네임스페이스에 구성 요소들이 설치됩니다.
kubectl port-forward svc/kibana 8080:5601
위 명령어를 실행한 후 웹브라우저로 localhost:8080에 접속했을 때 잘 나온다면 성공입니다.
Fluent Bit
fluent bit은 헬름차트로 설치하지 않고 직접 yaml을 작성합니다.
service-account.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: fluent-bit
namespace: efk
role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: fluent-bit-read
rules:
- apiGroups: [""]
resources:
- namespaces
- pods
verbs: ["get", "list", "watch"]
role-binding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: fluent-bit-read
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: fluent-bit-read
subjects:
- kind: ServiceAccount
name: fluent-bit
namespace: efk
configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: fluent-bit-config
namespace: efk
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-elasticsearch.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
Merge_Log_Key log_processed
K8S-Logging.Parser On
K8S-Logging.Exclude Off
[FILTER]
Name nest
Match kube.*
Operation lift
Nested_under kubernetes
Add_prefix Kube.
[FILTER]
Name modify
Match kube.*
Remove Kube.container_hash
Remove Kube.docker_id
Remove Kube.pod_id
Remove Kube.namespace_name
Remove Kube.pod_name
Remove Kube.host
Remove stream
Remove logtag
[FILTER]
Name nest
Match kube.*
Operation nest
Wildcard Kube.*
Nested_under kubernetes
Remove_prefix Kube.
output-elasticsearch.conf: |
[OUTPUT]
Name es
Match *
Host ${FLUENT_ELASTICSEARCH_HOST}
Port ${FLUENT_ELASTICSEARCH_PORT}
Logstash_Format On
Replace_Dots On
Retry_Limit False
Suppress_Type_Name On
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
daemonset.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluent-bit
namespace: efk
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: fluent/fluent-bit:latest
imagePullPolicy: Always
ports:
- containerPort: 2020
env:
- name: FLUENT_ELASTICSEARCH_HOST
value: "elasticsearch.efk"
- name: FLUENT_ELASTICSEARCH_PORT
value: "9200"
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"
ENV값을 유효한 값으로 변경해 줍니다.
위 yaml을 순서대로 apply 합니다.
kubectl apply -f service-account.yaml
kubectl apply -f role.yaml
kubectl apply -f role-binding.yaml
kubectl apply -f configmap.yaml
kubectl apply -f daemonset.yaml
모두 설치 후 성공하였다면 port forwarding 해둔 키바나 페이지로 갑니다.
Kibana 기본 설정 하기
키바나의 왼쪽 사이드바 부분을 열어 가장 하단으로 내려보면 Management 섹션에 Stack Management라는 탭이 있습니다.
해당 탭으로 진입한 후 Data -> Index Management 탭으로 진입하면 indies 화면이 나옵니다.
해당 화면에 logstash- 로 시작하는 항목이 존재한다면 fluent-bit가 정상적으로 로그를 수집하여 elastic search에 적재하고 있다는 것입니다. 만약 생성 되지 않았다면 fluent bit의 daemon set 로그를 확인하여 왜 작동되지 않는지 찾아보아야 합니다.
제가 겪었던 문제는 elastic search의 버전과 fluent bit의 버전이 맞지 않아 elastic search에 적재를 하지 못하는 문제가 발생하였고 fluent bit의 이미지 버전을 latest로 변경하여 해결하였습니다.
indies가 존재한다면 view를 생성하면 됩니다.
좌측 메뉴에서 Kibana -> Data Views를 클릭하여 해당 화면으로 진입한 뒤 create data view를 하면 됩니다.
name : 원하시는 대로
index pattern: logstash-*
이렇게 입력하고 생성해 주시면 로그가 잘 나오는 것을 확인할 수 있습니다.
'개발' 카테고리의 다른 글
[NestJS] 슬랙 알람 데코레이터 리팩토링 (0) | 2023.05.12 |
---|---|
Select marker with drag in Google map (0) | 2023.05.12 |
Naming Convention (0) | 2023.05.12 |
REST API Convention (0) | 2023.05.12 |