본문 바로가기
Cloud Side/Container > Kubernetes

[k8s] DaemonSet으로 fluentd 구축하기 (configMap, pattern not matched)

by developerBeluga 2023. 7. 25.
728x90
반응형

 

 

 

 

어서와 DaemonSet은 처음이지?

회사 서비스 로그 시스템을 구축하는데 벌써 한달이 지나가고 있다.

(물론 로그 시스템만 한거 아님! 메트릭 수집도 해야했고 다른 서비스도 개발해야했으니 한달 허투로 쓴거 아님!)

 

개발은 Docker로만 운영은 k8s로 하기 때문에 로그를 위한 fluentd도 Docker에서 벗어나 k8s에 도입하게 되었다.

근데 k8s의 새로운 kind를 만나게 되었다.

그게 바로 DaemonSet!

 

https://kubernetes.io/ko/docs/concepts/workloads/controllers/daemonset/

k8s 공식 문서에서 확인한 DaemonSet의 설명이다.

한마디로 정리하면 알아서 모든 노드에 실행하게 해주는 아이다.

 

DaemonSet을 사용하지 않을 경우 fleuntd의 pod를 각 노드마다 수동으로 생성해줘야 할것이다.

하지만 DaemonSet를 사용하면 딱히 설정해주지 않아도 n개의 노드에 알아서 생성되서 편하다.

fleuntd처럼 각 노드에 로그를 수집해야할 경우 DaemonSet이 좋다.

(노드가 하나일 경우 굳이 DaemonSet을 사용할 필요는 없어 보인다.)

 

 

 

 

 

DaemonSet을 이용해서 fluentd 사용하기 + configmap

1) DaemonSet 코드

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd
  namespace: kube-system
spec:
  selector:
    matchLabels:
      name: fluentd
  template:
    metadata:
      labels:
        name: fluentd
    spec:
      containers:
        - name: fluentd
          image: fluent/fluentd:v1.16-1 # Replace with your Fluentd image
          securityContext:
            runAsUser: 0
          volumeMounts:
            - name: varlog
              mountPath: /var/log
            - name: varlibdockercontainers
              mountPath: /var/lib/docker/containers
              readOnly: true
      volumes:
        - name: varlog
          hostPath:
            path: /var/log
        - name: varlibdockercontainers
          hostPath:
            path: /var/lib/docker/containers

k8s 공식문서에도 DaemonSet 사양이 적혀있지만 맞지 않는게 있어서 나의 경우 이렇게 적었다.

여기에서 fluentd 이미지 버전 빼고는 주의할게 없다. 

 

 

 

2) configmap

fluentd를 조금 다뤄본 사람이라면 fluentd의 핵심은 conf 파일이라는 것을 알 것이다.

fluentd의 conf 파일에 적힌 대로 로그를 받아들이고 거르고 뱉어낸다.

그럼 그 conf 파일을 k8s에서는 어떻게 해줘야 할까?

 

  fluentd:
    image: arm64v8/fluentd:latest
    volumes:
      - ./fluentd/log:/fluentd/log
      - ./fluentd/conf:/fluentd/etc
    ports:
      - 24224:24224
      - 24224:24224/udp
    restart: always

Docker에서는 volumes에 fluent.conf 파일을 마운팅 해주었다.

즉 로컬에 있는 fluent.conf 파일을 고치고 다시 컨테이너를 재시작 해주면 잘 되었다는 말씀~

하지만 k8s에서는 이렇게 하지 않는다!

k8s에서는 configMap(중요한 정보 x) 혹은 secret(중요한 정보 o)를 사용해야 한다.

 

apiVersion: v1
kind: ConfigMap
metadata:
  name: game-demo
data:
  # 속성과 비슷한 키; 각 키는 간단한 값으로 매핑됨
  player_initial_lives: "3"
  ui_properties_file_name: "user-interface.properties"

  # 파일과 비슷한 키
  game.properties: |
    enemy.types=aliens,monsters
    player.maximum-lives=5    
  user-interface.properties: |
    color.good=purple
    color.bad=yellow
    allow.textmode=true

이렇게 k8s 공식문서에서 제공하는 것처럼 키와 값이 구성되게 작성해주면 된다.

하지만 fluentd의 경우 굳이 yaml 파일에 작성하는게 더 비효율적이라고 생각했다.

왜냐하면 이미 도커에서 사용한다고 작성한 conf 파일이 있기 때문이다.

 

kubectl create configmap fluentd-config --from-file=fluent.conf=fluentd.conf -n kube-system

그래서 내가 선택한 것은 위 코드다!

이 명령어를 사용하면 fluentd.conf 파일을 configmap으로 만들어준다.

 

Azure portal에서 확인해보니 잘 만들어진 것을 볼 수 있다.

인제 이 configmap을 fluentd가 사용할 수 있게 위 yaml 파일을 수정하도록 하자.

 

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd
  namespace: kube-system
spec:
  selector:
    matchLabels:
      name: fluentd
  template:
    metadata:
      labels:
        name: fluentd
    spec:
      containers:
        - name: fluentd
          image: fluent/fluentd:v1.16-1 # Replace with your Fluentd image
          securityContext:
            runAsUser: 0
          volumeMounts:
            - name: varlog
              mountPath: /var/log
            - name: varlibdockercontainers
              mountPath: /var/lib/docker/containers
              readOnly: true=
            - name: config-volume // 여기 추가!
              mountPath: /fluentd/etc # Path in Fluentd configuration
      volumes:
        - name: varlog
          hostPath:
            path: /var/log
        - name: varlibdockercontainers
          hostPath:
            path: /var/lib/docker/containers=
        - name: config-volume // 여기 추가!
          configMap:
            name: fluentd-config

전체 fluentd.yaml 파일이다.

containers와 volumes에 fluentd-config을 추가해줬다.

 

kubectl apply -f k8s-log.yaml

최종 완성된 yaml을 올려주면 잘 된 것이다.

 

 

 

 

 

왜 fluentd가 5개이죠?

fluentd 잘 올라갔겠지~ 하면서 fluentd를 확인해보니 오잉 👀 왜 5개나 만들어진거지?

어리둥절 했었고 이 5개 때문에 포스팅을 하기로 한거다.

 

이유는 아주 간단하다.

n개의 노드만큼 fluentd의 파드가 n개 만들어지는거다 👍

즉, 현재 서비스에서 5개의 노드가 돌아가고 있고 이 5개의 노드의 로그를 수집하기 때문에 5개의 fluentd가 생성된다.

 

그럼 각 fluentd마다 로그가 다르게 찍힐까?

YES!

 

kubectl logs fluentd-4b4gl -n kube-system

로그를 확인해보면 5개 모두 각 서비스마다 마다 다르게 로그가 찍힌다.

 

 

 

 

 

주의! 끝없는 \\\\\\\\\\\\\\\\\\\\\\\\\\\를 만나게 된다면

일주일 동안 날 공포에 떨게 한 상황을 소개하고자 한다 😱

DaemonSet으로 fluentd도 잘 만들어주고 conf 파일도 잘 적용시켜줘서 인제 로그가 잘 빠지는지 확인하기 위해 출력한 파일을 확인하니 위 캡쳐와 같은 \\\\\\\\\만 나오는 상황을 만나게 되었다.

 

pattern not mathched라는 메시지를 힌트로 여러차례 찾아보고 해결방법이라고 알려진 것을 아무리 적용시켜봐도 효과가 없었다.

참고로 pattern not mathched는 fluentd에서 로그 메시지가 주어진 파싱 패턴과 일치하지 않을 때 발생하는 경고라고 한다.

(혹시 모르니 https://github.com/fluent/fluentd-kubernetes-daemonset/issues/434 여기에 가서 트라이 해보시길)

 

파드의 로그 결과를 알고 싶었는데 계속 \\\\\\만 나와서 스트레스를 받아갈 때 쯤 로그 파일에 pod의 이름을 검색하니 나오는게 있었다!

생각해보니 fluentd.conf에서 input의 path가 /var/log/containers/*.log여서 모든 로그 파일을 다 불러오다 보니 그런 것 같았다.

그래서 pod의 로그 파일만 불러오기 위해 /var/log/containers/*_default_*.log로 변경해줬다.

 

<source>
  @type tail
  path /var/log/containers/*_default_*.log
  pos_file /var/log/fluentd-containers.log.pos
  time_format %Y-%m-%dT%H:%M:%S.%NZ
  tag kubernetes.*
  format json
  read_from_head true
</source>

최종 fluentd.conf 파일이다.

이렇게 하니 끝도 없는 \\\\\\\\에 벗어날 수 있게 되었다.

 

2023-07-25 02:08:42.287212135 +0000 fluent.warn: {"message":"pattern not matched: \"2023-07-25T02:08:41.660367512Z stdout F \\e[0mGET / \\e[33m404\\e[0m 0.559 ms - 139\\e[0m\""}

물론 아직도 pattern not matched가 나온다.

그래도 끝에 pod의 로그가 나와 속이 시원해졌다.

 

 

 

+긴급 추가

<source>
  @type tail
  path /var/log/containers/*_default_*.log
  pos_file /var/log/fluentd-containers.log.pos
  tag kubernetes.*
  <parse>
    @type regexp
    expression /^(?<time>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+Z) stdout F (?<log>.*)$/
    time_format %Y-%m-%dT%H:%M:%S.%NZ
  </parse>
</source>

<filter **>
  @type record_transformer
  enable_ruby
  <record>
    log ${record["log"] ? record["log"].gsub(/\033\[\d{1,2}(;\d{1,2}){0,2}[mGK]/, '') : nil}
  </record>
</filter>

혹시 나처럼 pattern not matched가 나온다면 fluentd.conf 파일을 이렇게 바꾸기 바란다.

지긋지긋했던 pattern not matched는 안 나오고 깔끔하게 pod 로그를 볼 수 있다!

 

 

 

 

 

 

 

fin.

 

 

 

 

 

 

728x90
반응형

댓글