[Kubernetes] 로깅 시스템 구축

[Kubernetes] 로깅 시스템 구축

안녕하세요? 정리하는 개발자 워니즈입니다. 이번시간에는 쿠버네티스안에 app log들을 어떤식으로 수집하고 대시보드 상에서 볼 수 있는지에 대해서 정리를 하고자합니다.

지난 글들은 아래를 참고 해주시면 됩니다.

사실 쿠버네티스 관련 로그들(api, coredns, etcd 등등)에 대한 로그들이 우선적으로 수집이 되어야 합니다. 그런데 필자가 속한 프로젝트에서는 미들웨어를 쿠버네티스 플랫폼 위에 올렸기 때문에 이에 대한 access, error로드에 대한 수집이 필요했습니다.

운영중인 장비가 많아지면 많아질수록 각 서버들을 terminal 로 접속해서 하나씩 로그들을 봐야하는 불편함이 있기때문에 한곳으로 수집하는 로깅 시스템을 구축하기로 했습니다.

1. 로그 수집기 : Fluentd

필자가 아는 로그 수집기에 가장 대표적인것으로 logstash를 알고 있었습니다. 그런데 쿠버네티스를 공부하다보니 fluentd라는 로그(데이터) 수집기(collector)를 알게 되었습니다.

fluentd는 다양한 데이터 소스(HTTP, TCP, TEXT 파일 등)으로부터 데이터를 읽어올 수 있습니다. fluentd로 전달된 데이터는 tag, time recored(JSON)로 구성된 이벤트로 처리되며, 원하는 형태로 가공되어 다양한 목적지로 전달될 수 있습니다.

데이터 유실을 막기 위해 메모리와 파일 기반의 버퍼(Buffer) 시트템을 갖고 있습니다.

2. fluentd 데몬셋

각 로그 수집기는 woker 노드에서 존재해야 되고, 시스템내에 POD로 존재해야합니다. 그래서 데몬셋으로 셋팅을 하고 direct로 elastic search쪽으로 보내주는 방식으로 아키텍처를 세웠습니다.

출처 : 조대협블로그

조대협님의 블로그에서 가져온 사진인데, 보시면, Fluentd가 데몬셋으로 떠있고, 로그를 aggregation해서 Log Storage로 보내주는 방식입니다.

2-1. fluentd 관련 role 생성

쿠버네티스는 api를 통해서 통신을 하기 때문에 계정에 대하여 role을 mapping해주어야 정상적인 api통신이 가능합니다. 따라서, fluentd 전용 계정을 생성하고 role을 binding 해줍니다.

---
apiVersion: v1
kind: ServiceAccount
metadata:
  labels:
    k8s-app: fluentd
  name: fluentd
  namespace: kube-system

---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: fluentd-clusterrole
rules:
  - apiGroups:
      - ""
    resources:
      - "namespaces"
      - "pods"
    verbs:
      - "list"
      - "get"
      - "watch"

---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: fluentd-clusterrole
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: fluentd-clusterrole
subjects:
- kind: ServiceAccount
  name: fluentd
  namespace: kube-system

2-2. fluentd 데몬셋 생성

apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  name: fluentd
  namespace: kube-system
  labels:
    k8s-app: fluentd-logging
    version: v1
    kubernetes.io/cluster-service: "true"
spec:
  template:
    metadata:
      labels:
        k8s-app: fluentd-logging
        version: v1
    spec:
      serviceAccount: fluentd
      serviceAccountName: fluentd
      tolerations:
      - key: node-role.kubernetes.io/master
        effect: NoSchedule
      containers:
      - name: fluentd
        image: fluent/fluentd-kubernetes-daemonset:v1.4.2-debian-elasticsearch-1.1
        env:
          - name:  FLUENT_ELASTICSEARCH_HOST
            value: "http://52.78.101.251:9200/"
          - name:  FLUENT_ELASTICSEARCH_PORT
            value: "9200"
          - name: FLUENT_ELASTICSEARCH_SCHEME
            value: "http"
          - name: FLUENT_UID
            value: "0"
        resources:
          limits:
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 200Mi
        volumeMounts:
        - name: authorlog
          mountPath: /author
        - name: qalog
          mountPath: /qa
        - name: livelog
          mountPath: /live
        - name: config
          mountPath: /fluentd/etc
      terminationGracePeriodSeconds: 30
      volumes:
      - name: authorlog
        hostPath:
          path: /author
      - name: qalog
        hostPath:
          path: /qa
      - name: livelog
        hostPath:
          path: /live
      - name: config
        hostPath:
          path: /config

위의 내용을 보시면, 기존에 미들웨어서 hostPath에 떨군 path들을 각각 mount를 시켰습니다. 그리고 그로그들에 대해서 fluentd가 elasticsearch로 보내도록 수행할 예정입니다.

3. fluentd 설정 변경

fluentd같은 경우 conf 파일내에서 데이터를 어떤식으로 받아들이고, 어떤식으로 전송을 해줄지 명시하고있습니다. 세부적인 사항은 추후에 정리를 하겠지만, 기본적으로 필자가 사용한 내용위주로 정리 하겠습니다.

fluentd는 기본 설치하면 /etc/fluentd.conf 파일이 생성이 됩니다.

다음과 같은 속성을 갖고 있습니다.

  • 태그

  @type tail
  tag dev.sample
  path /var/log/sample.log



  @type stdout

fluentd의 특징 중에 가장 핵심은 태그(Tag) 입니다. 태그는 이벤트가 흘러가면서 적절한 Filter, Parser 그리고 Output 플러그인으로 이동할 수 있는 기준이 됩니다.

  • input 플러그인

대표적인 in_tail 플러그인은 파일을 tail 해서 데이터를 읽어 들입니다. 파일이 만약 로테이팅 되어 새로운 파일을 생성한 경우에는 처음부터 읽게 됩니다.
pos_file 파라미터를 추가 사용할 경우 fluentd가 재실행 되었을 때 파일의 마지막에 읽은 부분부터 다시 처리하게 됩니다.


  @type tail
  path /var/log/nginx/access.log
  pos_file /var/log/fluent/nginx-access.log.pos
  tag nginx.access
  
    @type nginx
  

  • Parser 플러그인

전달 받은 데이터를 파싱하기 위해 섹션을 정의해서 사용합니다. 섹션은 Input 플러그인(), Output 플러그인(), Filter 플러그인 () 안에서 정의하며, @type 파라미터로 사용할 Parser 플러그인 이름을 지정합니다.

기본 내장 Parser : regexp, apache2, nginx, syslog, csv, tsv, json, none 등

  • output 플러그인

Output 플러그인은 섹션에 정의하며, v1.0 부터 Buffering과 Flushing에 대한 설정을 섹션안에 서브 섹션으로 정의한다.

  • Non-Buffered mode : 버퍼에 담지 않음
  • Synchronous Buffered mode : stage 라는 buffer chunk에 담고, chunk 단위로 queue에 쌓아서 처리한다.
  • Asynchronous Buffered mode : Synchronous buffered mode와 동일하게 stage에 queue가 존재하지만, 비동기 방식으로 동작
  • output_elasticsearch

Elasticsearch로 이벤트 레코드를 전송합니다. 레코드 전송은 Bulk 단위로 이뤄지기 때문에 최초 전달받은 이벤트가 즉시 ES로 전송되지 않습니다.


  @type elasticsearch
  hosts 127.0.0.1:9200,127.0.0.1:9201
  index_name django-rest-api
  type_name django-rest-api
  include_timestamp true
  time_key timestamp
  include_tag_key true
  tag_key fluentd_tag

hosts : ES 클러스터의 각 노드 IP와 Port를 콤마로 구분해서 지정
index_name : Index 이름
type_name : Type 이름, 이 값을 지정하지 않을 경우 기본값은 ‘flentd’
include_timestamp:logstash_format 파라미터를 사용 했을 때 추가되는 @timestamp 필드만 별도로 추가
time_key: 기본적으로 로그가 수집된 시간이 @timestmap 필드의 값이 되지만, 이 파라미터에 지정된 필드가 @timestamp 필드의 값으로 사용됩니다.

4. fluentd 적용 내용

필자는 fluentd를 통해서 hostpath에 있는 로그들을 바로바로 elasticsearch에 넣도록 했습니다.

worker node에 /config 폴더를 생성하고, fluentd.conf 파일의 내용을 아래와 같이 수정합니다.

  • input

  @type tail
  path /author/access_log
  format apache2
  tag author_apache.access

  • output

  @type elasticsearch
  logstash_format true
  host {엘라스틱서치 IP}
  port 9200
  index_name fluentd_author
  type_name fluentd_author
 

input과 output 을 통해서 받아들이는대로 ES쪽으로 넣는 내용입니다.

실제로 fluentd를 데몬셋으로 올릴때 config 파일을 마운트 시키는 내용이 있습니다. 그러면, 위의 작성해놓은 config 파일을 물고 데몬셋으로 POD가 생성되기떄문에 정상적으로 각 worker의 hostpath로 마운트된 log들을 수집합니다.

5. 마치며..

이번시간에는 로그수집기인 fluentd의 활용 사례를 정리해봤습니다. 요즘에 SRE(사이트 신뢰성 공학)의 개념이 도입되면서, 로그들에 대한 정리와 visualize에 대한 부분이 강조되고 있습니다. 로그를 수집만 하는것이 아니라 수집된 로그로부터 의미있는 결과를 알아내는 것까지 수행이 된다면 더욱 신뢰있는 소프트웨어가 될 것이라고 생각합니다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다