k8s

Kubernetes, EFK (ElasticSearch, Fluent-bit, Kibana) 구성

089 2024. 2. 26. 10:17

도입 이유

쿠버네티스는 시스템을 유지하기 위해 자체적으로 오류를 감지하고 복구하는 메커니즘을 가지고 있습니다. (self-healing기능) 예를 들어 컨테이너가 비정상적으로 종료되거나 반응이 없을 때, 해당 Pod를 자동으로 재시작 합니다. Pod가 재시작되면 해당 Pod가 사용하던 컨테이너 인스턴스는 제거되고, 새로운 컨테이너와 인스턴스가 생성되는 과정에 기존 컨테이너 로컬 저장소에 있던 로그 파일들은 삭제가 됩니다. Pod안에 임시 볼륨이 삭제되고 새로운 파일 시스템이 생성되기 때문입니다. 하지만 운영을 하는 입장에서 왜 Pod가 재시작이 되었는지 알아야 되기 때문에 컨테이너가 살아있을때 외부 로그 저장소에 로그를 수집,저장,분석하여 서비스의 안정성 및 로그 관리를 위해 EFK를 도입 했습니다.

 

개념

클러스터 내의 모든 노드에서 로그를 수집하기 위해 Fluent Bit을 DaemonSet으로 배포하여 로그를 수집하여 중앙의 Elasticsearch로 전달 합니다. Elasticsearch는 전달받은 로그에 대해 저장 및 검색 기능을 제공하며 Kibana는 이 로그들을 쿼리하고 시각화 할수 있게 해줍니다. 이 세가지 컴포넌트가 함께 작동하여 로그 관리 및 분석 파이프라인을 구성합니다.

 

설치 과정

Elasticsearch 최신버전은 helm을 통해 설치해주자

helm repo add elastic https://helm.elastic.co
helm install elasticsearch elastic/elasticsearch

설치가 완료되었으면 설치된 모든 오브젝트를 확인하자 서비스에 elasticsearch-master 가 있어야 한다. 그리고 모든 pods 가 running 으로 전환되는 것을 확인하자.

kubectl get all

 

port-forward 를 하고, index 리스트를 조회해서 정상적으로 동작하는지 확인하자.

포트포워딩을 하고 인덱스를 조회할 때 8. 이후 버전부터 보안이 강화되어 helm차트에서 다운받을때 자동으로 생성되는 패스워드와 기본 아이디값을 입력해줘야된다.

 

# 자동으로 생성된 패스워드 조회
kubectl get secret elasticsearch-master-credentials -o go-template='{{.data.password | base64decode}}'

# 고정 아이디 값은 elastic 이지만 조회하는 명령어이다.
kubectl get secret elasticsearch-master-credentials -o go-template='{{.data.username | base64decode}}'


-k 옵션을 줘서 cURL 인증서 검증을 무시하고 -u 명령어를 통해 아이디와 패스워드를 입력하여 조회하자

# -k 옵션을 줘서 cURL에 인증서 검증을 무시
curl -k -u elastic:vpyBZSyphcx1fjiG https://localhost:30000/_cat/indices\?v

성공적으로 조회가 되었다. 

health status index uuid pri rep docs.count docs.deleted store.size pri.store.size

 

다음으로 Kibana를 설치하자

helm install kibana elastic/kibana

 

마찬가지로 포트포워딩을 하여 Kibana에 접속해보자

 

다음은 Fluent-bit을 설치해보자

Fluent-bit은 helm차트가 아닌 yaml파일로 구성하였다.

 

fluentbit-serviceaccount.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: fluent-bit
  namespace: monitoring

 

fluentbit-role.yaml

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: fluent-bit-read
rules:
- apiGroups: [""]
  resources:
  - namespaces
  - pods
  verbs: ["get", "list", "watch"]

 

fluentbit-rolebinding.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: monitoring

 

fluentbit-configmap.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: fluent-bit-config
  namespace: monitoring
  labels:
    k8s-app: fluent-bit
data:
  fluent-bit.conf: |
    [SERVICE]
        Flush         1
        Log_Level     info
        Daemon        off
        Parsers_File  parsers.conf
    @INCLUDE filter-kubernetes.conf
    @INCLUDE input-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
        Merge_Log           On
        Keep_Log            Off
        K8S-Logging.Parser  On
        K8S-Logging.Exclude Off

  output-elasticsearch.conf: |
    [OUTPUT]
        Name            es
        Match           *
        Host            ${FLUENT_ELASTICSEARCH_HOST}
        Port            ${FLUENT_ELASTICSEARCH_PORT}
        Logstash_Format On
        Logstash_Prefix fluent-bit
        Retry_Limit     False
        HTTP_User       elastic
        HTTP_Passwd     vpyBZSyphcx1fjiG
        tls             on
        tls.verify      off
        Suppress_Type_Name On
  parsers.conf: |
    [PARSER]
        Name        docker
        Format      json
        Time_Key    time
        Time_Format %Y-%m-%dT%H:%M:%S.%L
        Time_Keep   On

 

fluentbit-daemonset.yaml

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluent-bit
  namespace: monitoring
spec:
  selector:
    matchLabels:
      k8s-app: fluent-bit-logging
  template:
    metadata:
      labels:
        k8s-app: fluent-bit-logging
    spec:
      containers:
      - name: fluent-bit
        image: fluent/fluent-bit:latest
        imagePullPolicy: Always
        env:
        - name: FLUENT_ELASTICSEARCH_HOST
          value: "elasticsearch-master"
        - name: FLUENT_ELASTICSEARCH_PORT
          value: "9200"
        - name: FLUENT_ELASTICSEARCH_HTTPS
          value: "on"
        - name: FLUENT_ELASTICSEARCH_USER
          value: "elastic"
        - name: FLUENT_ELASTICSEARCH_PASSWORD
          value: "S77RMGFWQo2geB1O"
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
        - name: journal
          mountPath: /journal
          readOnly: true
        - name: fluent-bit-config
          mountPath: /fluent-bit/etc/
      terminationGracePeriodSeconds: 10
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: journal
        hostPath:
          path: /var/log/journal
      - 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
        effect: NoSchedule

 

모든 설정을 apply 해주고 kibana에 접속해 Management → Stack Management → Data views 에서 Create data view에서 fluent-bit- Index pattern 추가해주고 Discover에서 확인하면 끝이 난다!

 

다음은 Kibana dashboard 설정하는 방법을 정리할 예정이다.