![](https://uzilog-upload.s3.ap-northeast-2.amazonaws.com/private/ap-northeast-2%3Ab6c10628-1f45-492c-a9eb-f54020bc8014/1592711073932-image.png) ## EFK Stack 이란 EFK Stack은 Elasticsearch, Fluentd, Kibana의 첫글자들의 합성으로 로그 데이터들을 수집하고 분석하여 시각화 해주는 것을 의미한다. 비슷한 스택으로는 ELK Stack이 있으며 로그를 수집하는 미들웨어가 Fluentd 냐 Logstash냐의 차이점이 존재하고 분석한 후 시각화 하는 도구는 Elasticsearch 와 Kibana로 동일하다. ## Elasticsearch Elasticsearch는 Shay Banon이 2010년 OSS(Open Source Software)로 발표한 분산형 전문 검색 서버 이다. Apache Lucene 라는 전문 검색 라이브러리를 코어로 이용하고 있으며 도큐먼트 단위로 필드를 정의하여 유연한 데이터 등록이 가능하다. Elasticsearch의 가장 큰 특징이자 장점은 데이터를 실시간으로 검색 가능 하다는 것인데 이것은 데이터 구조 설계와 적절한 검색 엔진, 그리고 분산 시스템을 고려하여 설계되었기 때문이다. 구조를 간단히 보자면 하나의 클러스터 안에 여러개의 노드가 위치해 있고 해당 노드에 도큐먼트의 집합인 인덱스가 위치한다. Elasticsearch는 기본적으로 인덱스 단위로 데이터를 관리하고 인덱스를 노드에 분산하기 위한 단위로 샤드를 사용한다. ## Fluentd Fluentd는 로그 수집 미들웨어로서 분산되어 있는 환경에 수집되어야 하는 데이터와 로그를 손쉽게 수집할 수 있다는 특징이 있다. Fluentd의 가장 큰 장점으로는 확장성이 뛰어나다는 점인데 플러그인 방식을 이용하여 어떤 데이터든 제한 없이 수집할 수 있다는 특징이 있다. Fluentd의 데이터 구조는 \[tag, time, record] 요소로 구성되어 있다. tag는 레코드의 라우팅에 사용하는 문자열, time은 UNIX 타임스탬프로 저장하고, record는 JSON과 유사한 Key-Value 형태로 연관 배열을 저장한다. Fluentd 가 데이터를 수집해서 가공하는 방식은 첫째, 수집되는 로그/데이터에 태그를 붙여 수집한다 (Input). 둘째, 필터를 통해 데이터 가공과 집계 처리를 한다 (Filter). 마지막으로 데이터 저장소로 출력하는 형태이다(Output). 태그를 통한 라벨링 덕분에 안정적으로 비동기 처리가 가능하며 스트리밍 방식으로 데이터를 처리하여 실시간으로 수집이 용이하다는 특징이 있다. ## Kibana Kibana는 Elastic 에서 개발한 시각화 도구 이다. 웹 기반으로 동작하며 수집된 데이터의 시각화 및 분석을 담당하고 있다. Elasticsearch와 Fluentd를 이용하여 실시간으로 데이터를 수집하면 Kibana에서 대시보드를 통해 실시간 데이터를 시각화 하여 보여주는 형태로 동작할 수 있다. 이 외에도 대시보드를 직접 수정하여 원하는 형태로 보여주거나, 저장되어 있는 로그를 검색하는 등의 기능을 가지고 있다. ## 설치환경 - `kubectl` : v1.14.7 - `AWS EC2` - `helm` : v2.16.8 - `AWS EKS` Terraform으로 AWS EC2에서 EKS 배포 후 helm chart 배포 방식으로 EFK 스택 구현할 예정이다. 완성된 최종적인 목표는 아래와 같다. helm이 설치되어 있지 않다면 바로 Kubernetes에 배포하면 된다. ![](https://uzilog-upload.s3.ap-northeast-2.amazonaws.com/private/ap-northeast-2%3Ab6c10628-1f45-492c-a9eb-f54020bc8014/1592117975758-image.png) ## Namespace 설정하기 네임스페이스는 가상의 클러스터이다. 동일한 클러스터 내에서 네임스페이스로 작업 환경을 분리해준다. 배포하고자 하는 쿠버네티스에 네임스페이스를 지정해줘서 하나로 묶어준다. 네임스페이스를 지정하지 않을 경우 `default` 네임스페이스에 배포 되지만 다른 서비스와 같이 사용하는 환경이라면 구분을 해서 따로 관리하는 것이 좋다. 또한 네임스페이스로 묶어주게 되면 나중에 삭제를 할때도 네임스페이스만 지우면 관련된 리소스들도 같이 삭제가 된다. 네임스페이스 리스트는 아래와 같이 확인할 수 있다. ```shell $ kubectl get ns NAME STATUS AGE default Active 3m kube-system Active 3m kube-public Active 3m ``` Namespace를 새로 생성하기 위해 `yaml` 파일을 생성한다. 여기서는 `efk-stack` 이라는 네임스페이스를 지정해 주도록 하겠다. ``` shell $ vi efk-stack.yaml ``` ### efk-stack.yaml ```yaml kind: Namespace apiVersion: v1 metadata: name: efk-stack ``` `kind`로 리소스 타입을 지정해주며 `metadata`로 해당 네임스페이스의 이름은 `efk-stack`이라고 지정해 주었다. 이제 `kubectl`을 통해 생성해 주도록 하겠다. ```shell $ kubectl apply -f ./efk-stack.yaml ``` `kubectl`을 통해 생성하는 방식은 두가지가 있는데, `apply` 와 `create` 이다. - create 는 [Imperative Management](https://kubernetes.io/docs/tutorials/object-management-kubectl/imperative-object-management-configuration/)로 Kubernetes API에 생성, 삭제 와 같이 어떠한 액션을 하고 싶은지를 알려주는 방식이다. - apply는 [Declarative Management](https://kubernetes.io/docs/tutorials/object-management-kubectl/declarative-object-management-configuration/)로 어떠한 상태를 선언하는 방식으로 위의 예에서 `efk-stack` 네임스페이스를 생성 한 후 다시한번 `apply` 명령어를 실행하면 어떠한 변화도 없을 것이다. `apply` 하고자 하는 상태가 되도록 하는 것이 `apply` 방식이다. 위의 차이로 `create` 보다는 `apply` 를 통해서 생성을 하도록 하겠다. `-f` 는 파일 이름을 지정하는 플래그로 `efk-stack.yaml`에 있는 내용으로 생성을 시켜준다. 생성 후 다시 네임스페이스를 확인해 보면 아래와 같이 `efk-stack`이 생성되었음을 확인할 수 있다. ``` shell $ kubectl get ns NAME STATUS AGE default Active 15m efk-stack Active 4m kube-public Active 15m kube-system Active 15m ``` ## #0 Elasticsearch 배포 Elasticsearch를 배포하기 위해서는 아래와 같은 오브젝트가 필요하다. - Service - PersistentVolumeClaim - StatefulSet - StorageClass ## #1 Elasticsearch Service 배포 서비스는 어플리케이션에 접근하기 위한 진입점으로 노드 밸런싱 역활 등을 담당한다. 서비스의 타입으로는 ClusterIP, NodePort, LoadBalancer 등이 있으나 Elasticsearch를 위한 Service 는 Headless 타입으로 설정한다. Headless 방식으로 설정하는 이유는 로드 밸런싱 역활을 할 필요도 없으며 고정 IP를 가질 필요도 없기 때문이다. Headless Service 설정은 ClusterIP를 None으로 설정하면 된다. ### elasticsearch/service.yaml ``` yaml kind: Service apiVersion: v1 metadata: name: elasticsearch namespace: efk-stack labels: app: elasticsearch spec: selector: app: elasticsearch clusterIP: None ports: - port: 9200 name: rest - port: 9300 name: inter-node ```