[Toy Project] Kubernetes 기반 웹 로그 수집 파이프라인 구축

해당 문서에서는 도커 / 쿠버네티스 온라인 부트캠프에서 수행했던 과제 중 하나인 Kubernetes 기반의 웹 로그 수집 파이프라인 구축 과정을 기술합니다.
일반적으로 웹 로그 수집을 목적으로 구축된 파드의 구조는 다음과 같습니다.

웹서버 컨테이너와 해당 웹서버의 로그를 수집하기 위한 컨테이너가 사이드카 패턴으로 구성되어 있습니다.저는 해당 파드에 elasticsearch와 kibana를 추가적으로 연동하여 웹 로그 수집, 분석, 시각화를 모두 진행할 수 있는 파이프라인을 구축하였습니다.
웹 로그 수집 파이프라인을 구축하기 위해 elasticsearch, kibana 파드와 nginx, filebeat 이미지로 구성된 웹/사이드카 컨테이너를 생성하였습니다.
해당 파이프라인 구축에 사용된 쿠버네티스 오브젝트의 목록입니다.

1. Elasticsearch Pod 배포

먼저, 외부와의 통신을 위한 노드포트 설정이 포함되어 있는 service와 deployment를 통해 elasticsearch 파드를 배포하였습니다. 파드 배포에 사용한 elastic-search.yaml 파일은 다음과 같습니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: elasticsearch
  labels:
    app: elasticsearch
spec:
  replicas: 1
  selector:
    matchLabels:
      app: elasticsearch
  template:
    metadata:
      labels:
        app: elasticsearch
    spec:
      containers:
      - name: elasticsearch
        image: elastic/elasticsearch:7.16.2
        imagePullPolicy: Never
        env:
        - name: discovery.type
          value: "single-node"
        ports:
        - containerPort: 9200
        - containerPort: 9300
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: elasticsearch
  name: elasticsearch-svc
spec:
  ports:
  - name: rest
    port: 9200
    protocol: TCP
    targetPort: 9200
  - name: nodecom
    port: 9300
    protocol: TCP
    targetPort: 9300
  selector:
    app: elasticsearch
  type: NodePort

elasticsearch 버전이 바뀌어도 파드가 정상적으로 동작할 수 있도록 사전에 특정 버전의 elasticsearch 도커 이미지를 로컬 서버에 다운로드받았고, imagePullPolicy를 Never로 설정하여 파드를 재실행하더라도 버전에 영향을 받지 않도록 하였습니다. 또한, service 오브젝트를 추가로 생성하여 외부에서도 접근 가능하도록 노드포트를 설정하였습니다.

2. Kibana Pod 배포

kibana 파드를 구성할 때 사용한 kibana.yaml 파일은 다음과 같습니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: kibana
  labels:
    app: kibana
spec:
  replicas: 1
  selector:
    matchLabels:
      app: kibana
  template:
    metadata:
      labels:
        app: kibana
    spec:
      containers:
      - name: kibana
        image: elastic/kibana:7.16.2
        imagePullPolicy: Never
        env:
        - name: SERVER_NAME
          value: "kibana.kubenetes.example.com"
        - name: ELASTICSEARCH_HOSTS
          value: "http://elasticsearch-svc:9200"
        ports:
        - containerPort: 5601
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: kibana
  name: kibana-svc
spec:
  ports:
  - port: 5601
    protocol: TCP
    targetPort: 5601
  selector:
    app: kibana
  type: NodePort

kibana 또한 elasticsearch와 버전을 일치시켜야 정상 동작하므로 imagePullPolicy를 Never로 설정하였고, elasticsearch 파드와 통신하기 위하여 환경변수에 elasticsearch의 서비스명과 포트번호를 추가하였습니다.
kubectl get svc 명령을 통해 확인한 결과, elasticsearch 파드의 노드포트는 31261, 31096으로, kibana 파드의 노드포트는 30086으로 설정되었습니다.
VM에 할당된 IP주소와 해당 포트를 통해 웹에서도 정상적으로 elasticsearch와 kibana에 접속 가능한 것을 확인하였습니다.

  • elasticsearch 접속 확인
  • kibana 접속 확인

3. 웹 / 사이드카 컨테이너 배포

다음은 nginx 기반 웹/사이드카 컨테이너 배포 과정입니다. 사이드카 컨테이너 배포에 사용한 nginx-sidecar.yaml 파일은 다음과 같습니다.

apiVersion: v1
kind: Pod
metadata:
  name: nginx-sidecar
spec:
  containers:
  - name: nginx
    image: nginx
    ports:
    - containerPort: 80
    volumeMounts:
    - name: varlognginx
      mountPath: /var/log/nginx
  - name: sidecar-access
    image: docker.elastic.co/beats/filebeat:7.16.2
    imagePullPolicy: Never
    volumeMounts:
    - name: varlognginx
      mountPath: /var/log/nginx
  volumes:
  - name: varlognginx
    emptyDir: {}

웹서버인 nginx가 실행되고 있는 컨테이너와 해당 서버에서 웹 로그를 수집하기 위한 filebeat 이미지로 구성된 컨테이너를 사이드카 패턴으로 구축하였습니다. filebeat 이미지 또한 elasticsearch, kibana와 일치시켰으며, 웹서버로부터 로그를 가져오기 위해 웹서버의 로그가 저장되는 디렉토리 경로를 동일하게 마운트하였습니다.
nginx 컨테이너에 접속하여 curl 명령을 실행한 결과 통신이 원활하게 되고, 로그 또한 정상적으로 기록되는 것을 확인할 수 있습니다.

이제 filebeat 컨테이너에서 수집한 웹 로그를 elasticsearch로 전송할 수 있도록 설정하겠습니다. filebeat 컨테이너가 실행되고 있는 노드에 접속하여 root 권한으로 해당 컨테이너에 접속한 뒤, filebeat.yml 내부의 값을 변경합니다.

filebeat.config:
  modules:
    path: ${path.config}/modules.d/*.yml
    reload.enabled: false

processors:
  - add_cloud_metadata: ~
  - add_docker_metadata: ~

output.elasticsearch:
  hosts: ["172.30.5.86:31261"]
  #username: '${ELASTICSEARCH_USERNAME:}'
  #password: '${ELASTICSEARCH_PASSWORD:}'

output.elasticsearch.hosts에 기존에 구축된 elasticsearch 파드의 IP 주소와 포트번호를 입력한 뒤, curl 명령을 통해 elasticsearch 파드와 제대로 통신이 되는지 확인합니다. 아래와 같은 메시지가 출력되었다면, 정상적으로 통신이 되고 있는 것입니다.

그 다음 modules.d 폴더 내부의 nginx.yml.disabled 파일의 이름을 nginx.yml로 변경한 뒤, 기존에 마운트하였던 웹 로그 디렉토리 경로를 입력합니다.

- module: nginx
  access:
    enabled: true
    var.paths: ["/var/log/nginx/access.log*"]
  error:
    enabled: true
    var.paths: ["/var/log/nginx/error.log*"]

모든 작업이 완료되었고, 해당 컨테이너를 재시작하면 변경 내용이 적용됩니다.

4. 실행 결과

이제 nginx 컨테이너에서 생성한 웹 로그가 elasticsearch로 정상적으로 전송이 되는지 확인해보겠습니다.
nginx 웹서버 파드에 접속하여 curl 명령을 반복하여 실행하였을 때, elasticsearch에 해당 로그가 전송되고 kibana를 통해 시각화된 모습을 확인할 수 있습니다.

5. Reference

좋은 웹페이지 즐겨찾기