[ELK 스택 적용기]

박상준·2024년 3월 24일
0

ELK

목록 보기
1/1

왜 ELK 를 적용해야하나?

  • 일단 실무적으로 ELK 의 경우 로그 수집, 처리, 분석 및 시각화를 위하여 사용한다.
  • 일단 오픈소스이기도하고,
    • 사실 단일 서버인 경우 분산 시스템보다 이점이 조금 줄어드는건 사실..
  • 하지만,

중앙 집중식 로그 관리 가능

  • 문제점
    • 단일 서버 환경에서도 여러 어플과 서비스가 실행될 수 있다.
    • 각각은 자체 로그를 생성하는데, 해당 로그를 중앙 집중식으로 수집하고 관리가 가능함.

실시간성 로그 분석

  • 로그 데이터를 거의 실시간으로 분석가능.
  • Kibana 를 통해 dashboard 구성을 하는 경우, 중요한 로그 이벤트를 실시간으로 모니터링하고 시각적으로 파악가능.

ELK 에 굳이 카프카를 연결해야하나?

  • 단일 스프링 부트 어플리케이션 + Redis 환경에서 발생하는 로그의 양이 관리가 가능한 수준이라면, 카프카 없이 직접 ELK 스택으로 로그를 전송하는 게 더 간단할 수 있음..
  • 개인적으로 카프카 까지는 단일 어플리케이션에선 굳이굳이이다.

적용기

  1. ELK 스택 docker-compose 세팅

    https://github.com/deviantony/docker-elk

    • 위 링크 참고하여 clone 후 설정값 변경하여 사용함

    Docker를 사용해 ELK를 구축하기

    • 요 게시물의 ELK 8 버전 세팅을 참고하였습니다.

    • elk/elasticsearch/config/elasticsearch.yml
      ---
      ## Default Elasticsearch configuration from Elasticsearch base image.
      ## https://github.com/elastic/elasticsearch/blob/main/distribution/docker/src/docker/config/elasticsearch.yml
      #
      cluster.name: "docker-cluster"
      network.host: 0.0.0.0
      
      ## X-Pack settings
      ## see https://www.elastic.co/guide/en/elasticsearch/reference/current/security-settings.html
      #
      xpack.license.self_generated.type: trial
      xpack.security.enabled: true
      • cluster.name
        • 클러스터 이름
        • 클러스터 내의 모든 노드가 서로 인식하기 위해 설정하는 것임
      • network.host
        • es 서비스가 바인딩할 네트워크 호스트임
        • 0.0.0.0 의 경우 모든 네트워크 인터페이스에 접근 가능
        • Docker 컨테이너 내에서 실행하는 경우 외부에서 es 에 접근이 가능하도록 하려면 이런식으로 설정
          • 운영에서는 별도의 IP 를 정해놓고 사용해야할 것 같다.
      • X-Pack settings
        • xpack.license.self_generated.type
          • es 확장 기능을 제공하는 플러그인임
          • 보안, 모니터링, 알림 보고 기능 등을 포함
          • trial 인 경우 시험용으로 X-Pack 기능을 일정 기간 동안 사용 가능하다.
        • xpack.security.enabled
          • X-Pack 보안 기능을 활성화 할지 여부를 정함.
          • 사용자 인증, 역할 기반 접근 제어 등의 보안 기능 사용가
    • elk/kibana/config/kibana.yml
      ---
      ## Default Kibana configuration from Kibana base image.
      ## https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/templates/kibana_yml.template.ts
      #
      server.name: kibana
      server.host: 0.0.0.0
      elasticsearch.hosts: [ http://elasticsearch:9200 ]
      
      monitoring.ui.container.elasticsearch.enabled: true
      monitoring.ui.container.logstash.enabled: true
      
      ## X-Pack security credentials
      #
      elasticsearch.username: elastic
      elasticsearch.password: ${KIBANA_SYSTEM_PASSWORD}
      • 원래 Fleet 라고 ES agent 들을 중앙 관리해줄 수 있는 기능도 있었는데, 삭제했다. 단일 서버이기에 필요가 없음
      • elasticsearch.hosts
        • kibana 가 연결할 es 클러스터 주소 목록임.
        • 단일 주소로 일단 설정
      • monitoring.ui.container.elasticsearch.enable
        • es 컨테이너 모니터링을 활성화함.
      • monitoring.ui.container.logstash.enable
        • 로그스태시 컨테이너의 모니터링을 활성화함
  • elk/logstash/config/logstash.yml
    ## Default Logstash configuration from Logstash base image.
    ## https://github.com/elastic/logstash/blob/master/docker/data/logstash/config/logstash-full.yml
    #
    http.host: "0.0.0.0"
    xpack.monitoring.elasticsearch.hosts: [ "http://elasticsearch:9200" ]
    
    ## X-Pack security credentials
    #
    xpack.monitoring.enabled: true
    xpack.monitoring.elasticsearch.username: elastic
    xpack.monitoring.elasticsearch.password: ${MONITORING_INTERNAL_PASSWORD}
    • 로그스태시의 모니터링을 on
    • 사용자 설정및 비밀번호 설정
  • elk/logstash/pipeline/logstash.conf
    input {
    	tcp {
    		port => 50000
    	}
    }
    
    ## Add your filters / logstash plugins configuration here
    
    output {
    	elasticsearch {
    		hosts => "elasticsearch:9200"
    		user => "elastic"
    		password => "${LOGSTASH_INTERNAL_PASSWORD}"
    	}
    }
    • 소스 input 에서 데이터를 수집, 변환(필터링) 후 es 같은 저장소로 전송하는 역할을 함.
      • input 섹션, filter 섹션, output 섹션으로 나뉘어서 설정되며,
      • 지금은 filter 는 설정하지 않았다.
      • 일단 input 의 경우
        • tcp 입력 플러그인이 사용되며, 포트 50000에서 들어오는 데이터를 수신하도록 설정되어 있다.
        • 주석 처리한 beats 부분은 일단 여기선 사용하지 않을 것이기에, 제외했다.
      • filter 의 경우
        • 수집된 데이터에 대한 변환 || 필터링 작업을 정의한다.
        • 해당 예제에서는 구체적인 필터 설정이 제공되지 않는다.
        • 사용자가 필요에 따라 필터를 추가할 수 있도록 되어 있다.
          • 데이터를 파싱, 필드 추가/삭제

          • 조건에 따라 데이터 변형 등의

            행위가 수행가능하다.

      • output 섹션
        • 처리된 데이터를 전송할 목적지를 정의
        • es 가 목적지
  elasticsearch:
    build:
      context: elk/elasticsearch/
      args:
        ELASTIC_VERSION: ${ELASTIC_VERSION}
    volumes:
      - ./elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml:ro,Z
      - elasticsearch:/usr/share/elasticsearch/data:Z
    ports:
      - "9200:9200"
      - "9300:9300"
    environment:
      node.name: elasticsearch
      ES_JAVA_OPTS: -Xms256m -Xmx256m
      # Bootstrap password.
      # Used to initialize the keystore during the initial startup of
      # Elasticsearch. Ignored on subsequent runs.
      ELASTIC_PASSWORD: ${ELASTIC_PASSWORD:-}
      # Use single node discovery in order to disable production mode and avoid bootstrap checks.
      # see: https://www.elastic.co/guide/en/elasticsearch/reference/current/bootstrap-checks.html
      discovery.type: single-node
    networks:
      - elk
    restart: unless-stopped

  logstash:
    build:
      context: elk/logstash/
      args:
        ELASTIC_VERSION: ${ELASTIC_VERSION}
    volumes:
      - ./logstash/config/logstash.yml:/usr/share/logstash/config/logstash.yml:ro,Z
      - ./logstash/pipeline:/usr/share/logstash/pipeline:ro,Z
    ports:
      - "5044:5044"
      - "50000:50000/tcp"
      - "50000:50000/udp"
      - "9600:9600"
    environment:
      LS_JAVA_OPTS: -Xms256m -Xmx256m
      LOGSTASH_INTERNAL_PASSWORD: ${LOGSTASH_INTERNAL_PASSWORD:-}
    networks:
      - elk
    depends_on:
      - elasticsearch
    restart: unless-stopped

  kibana:
    build:
      context: elk/kibana/
      args:
        ELASTIC_VERSION: ${ELASTIC_VERSION}
    volumes:
      - ./kibana/config/kibana.yml:/usr/share/kibana/config/kibana.yml:ro,Z
    ports:
      - "5601:5601"
    environment:
      KIBANA_SYSTEM_PASSWORD: ${KIBANA_SYSTEM_PASSWORD:-}
    networks:
      - elk
    depends_on:
      - elasticsearch
    restart: unless-stopped

kibana 와 logstach 에서 오류 발생

Error: [config validation of [elasticsearch].username]: value of "elastic" is forbidden. This is a superuser account that cannot write to system indices that Kibana needs to function. Use a service account token instead. Learn more: https://www.elastic.co/guide/en/elasticsearch/reference/8.0/service-accounts.html
2024-03-23T14:02:10.401730675Z     at ensureValidConfiguration (/usr/share/kibana/node_modules/@kbn/core-config-server-internal/src/ensure_valid_configuration.js:23:11)
ERROR: Failed to load settings file from "path.settings". Aborting... path.setting=/usr/share/logstash/config, exception=LogStash::ConfigurationError, message=>Cannot evaluate `${MONITORING_INTERNAL_PASSWORD}`. Replacement variable `MONITORING_INTERNAL_PASSWORD` is not defined in a Logstash secret store or as an Environment entry and there is no default value given.
2024-03-23T13:55:18.008385436Z [FATAL] 2024-03-23 13:55:18.003 [main] Logstash - Logstash stopped processing because of an error: (SystemExit) exit

인증 실패 ..

  • kibana 를 띄우려고 해보았으나/.

    • 서버 인증이 실패했다

      [2024-03-23T14:37:39.103+00:00][ERROR][elasticsearch-service] Unable to retrieve version information from Elasticsearch nodes. security_exception
      2024-03-23T14:37:39.103509881Z 	Root causes:
      2024-03-23T14:37:39.103513398Z 		security_exception: unable to authenticate user [kibana_system] for REST request [/_nodes?filter_path=nodes.*.version%2Cnodes.*.http.publish_address%2Cnodes.*.ip]
      2024-03-23T14:37:39.393682723Z [2024-03-23T14:37:39.393+00:00][INFO ][plugins.screenshotting.chromium] Browser executable: /usr/share/kibana/node_modules/@kbn/screenshotting-plugin/chromium/headless_shell-linux_x64/headless_shell
      • 에러 메시지상으로는
        Kibana 가 es 노드로부터 버전 정보를 검색해보려고 했지만,,, 실패했다는 의미이다. + 인증 실패

      • 해당 이슈는 elk 스택에 내가 설정한 유저이름을 아직 만들어주고, 권한을 주지 않았기에 발생하는 문제로서

        security_exception: unable to authenticate user [kibana_system] · Issue #863 · deviantony/docker-elk

        • docker compose up -d setup 명령어를 사용하여 setup 을 수행해야한다

          • 일단 docker-elk 프로젝트에서 setup 디렉터리를 내 프로젝트로 옮기고
          • setup 관련 명령어를 docker-compose 에 이식해야한다.
          • intellij 에서 단순히 docker 배포를 수행하는 경우 setup 은 별도로 수행되지 않으니, 별도로 setup 을 수행해줘야한다.
              setup:
                profiles:
                  - setup
                build:
                  context: elk/setup/
                  args:
                    ELASTIC_VERSION: ${ELASTIC_VERSION}
                init: true
                volumes:
                  - ./elk/setup/entrypoint.sh:/entrypoint.sh:ro,Z
                  - ./elk/setup/lib.sh:/lib.sh:ro,Z
                  - ./elk/setup/roles:/roles:ro,Z
                environment:
                  ELASTIC_PASSWORD: ${ELASTIC_PASSWORD:-}
                  LOGSTASH_INTERNAL_PASSWORD: ${LOGSTASH_INTERNAL_PASSWORD:-}
                  KIBANA_SYSTEM_PASSWORD: ${KIBANA_SYSTEM_PASSWORD:-}
                  METRICBEAT_INTERNAL_PASSWORD: ${METRICBEAT_INTERNAL_PASSWORD:-}
                  FILEBEAT_INTERNAL_PASSWORD: ${FILEBEAT_INTERNAL_PASSWORD:-}
                  HEARTBEAT_INTERNAL_PASSWORD: ${HEARTBEAT_INTERNAL_PASSWORD:-}
                  MONITORING_INTERNAL_PASSWORD: ${MONITORING_INTERNAL_PASSWORD:-}
                  BEATS_SYSTEM_PASSWORD: ${BEATS_SYSTEM_PASSWORD:-}
                networks:
                  - elk
                depends_on:
                  - elasticsearch
            • 가볍게 키워드들에 대해 설명한다면
              • 일단, ELK 스택을 구성하는 각 컴포넌트의 초기 설정과 스크립트 환경변수를 정의해놓은 모음집이다.
              • profile 의 경우 Docker compose 3.4 이상에서 지원하는 프로필 기능인데, setup 으로 특정 프로필에 속한 서비스만 시작할 수 있다.
              • build 의 경우 Docker 이미지를 빌드할 때 사용되는 설정이다.
                • context 는 어떤 디렉터리를 이미지 빌드의 컨텍스트로 사용할 건지에 대한 내용이다. 단순히 디렉터리 위치만 기입하면된다.
                • args 는 빌드시 사용된 인자 - 환경 변숭디ㅏ.
              • init
                • true 로 설정한 경우 컨테이너에 있는 모든 설정을 초기화하고 새로 주입한다.

es 인덱스 추가

  • 일단 es 는 시간에 따라 변하는 데이터를 저장하고 검색하는 데 최적화된 시계열 데이터베이스 이다.
    • 시계열을 다루는 경우 @timestamp 이라는 필드를 사용한다.
    • 해당 필드는 데이터가 언제 생성되었는지를 나타내는 시간 정보를 담고 있는데, 해당 정보를 통해 데이터를 시간 순서대로 정렬하거나 특정 시간 범위의 데이터를 쉽게 찾을 수 있음
  • es 에서의 인덱스 는 비슷한 특성을 가진 데이터들의 집합이다.
    • 예시로 , 하나의 인덱스에는 한 달 동안의 로그 데이터가 저장될 수 있다. 인덱스명을 지정하는 것은 이 데이터를 어떤식으로 구분할 지 결정하는 중요한 과제..
    • 파티셔닝 으로 대용량의 데이터를 효율적으로 관리하기 위하여 데이터를 분할하는 기법인데, 인덱스명에 날짜를 인덱스이름-yyyy-mm-dd ← 이런 식으로 지정한다면, 파티셔닝의 한 예시로서, 이를 통해 특정 기간의 데이터만 빠르게 조회할 수 있음.
       output {
      	 	elasticsearch {
      	 		hosts => "elasticsearch:9200"
      	 		user => "elastic"
      	 		password => "${LOGSTASH_INTERNAL_PASSWORD}"
      	 		index => "date-index-%{+YYYY.MM.dd}"
      	 	}
       }

여기서 크나큰 문제점이 발생했다.

  • ELK 스택의 경우 서비스에 대한 자원의 소모가 너무 심하다.
  • 단순한 로그 수집만해도 램을 벌써 2기가씩 먹기 시작하는 것이다.
  • 이정도면 사이드 프로젝트가 아니라 거의 실제 프로덕션급의 서버가 필요할 것 같았다.

다행히.. 프리티어 레벨에서 aws elastic service 를 무료로 월 750 정도 지원한다고 한다. 그걸 사용하면 될 것같다.

실행해보자

docker-compose up -d setup 
docker-compose up 
  • 명령어를 수행하고 주소입력

  • Username 은 elastic password 는 별도로 설정한 패스워드 기입하면됨.
  • kibana 진입시 이런 화면이 뜬다.

그렇다면 Spring boot 의 로그를 logstash 가 수집하도록 설정해보자

  • gradle 에서
        implementation 'net.logstash.logback:logstash-logback-encoder:6.6'
    • Logback 을 사용하는 것이 일반적임
    • Logback 은 SLF4J 의 구현체 중 하나인데, Spring Boot 와 잘 통합되고, Logstash 와의 통신을 위하여 logstash-logback-encoder 를 사용할 수 있다.
    • 해당 방식은 JSON 형식으로 로그를 구성하고, Logstash 로 전송하 수 있게 도움을 주는 라이브러리 이다.
  • Logback 설정
    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
    
        <appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
            <destination>localhost:5044</destination> <!-- Logstash 서버 주소와 포트 -->
            <encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
        </appender>
    
        <root level="INFO">
            <appender-ref ref="LOGSTASH"/>
        </root>
    
    </configuration>
    • 로그를 전송하여 위하여 Logstash 의 TCP 입력 플러그인을 사용해 로그를 수신할 것이기에.. 호스트와 포트를 지정한다.
    • 로그 레벨을 info 레벨로 잡았다.

정작 intellij 콘솔에 출력되는 건 없어져버렸다..

  • 이는 로그를 logstash 에만 전송하도록 logback 을 설정했기 때문이다.
  • 본래 설정이 있지만, logback 설정파일을 건드리면서 overriding 된 속성까지 덮혀버린거같다.

콘솔까지 기존 Spring Boot ANSI 콘솔 색상 출력

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- 콘솔 Appender 설정 -->
    <include resource="org/springframework/boot/logging/logback/base.xml"/>

    <appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
        <destination>localhost:5044</destination> <!-- Logstash 서버 주소와 포트 -->
        <encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
    </appender>

    <!-- Root Logger 설정 -->
    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="LOGSTASH"/>
    </root>
</configuration>
  • 기본 Spring Boot 속성을 포함하면 콘솔에 정상적으로 출력됨.

다시 logstash 의 수집된 로그를 es 로 쏴주는 부분을 수정하자

  • elk/logstash/pipeline/logstash.conf 수정
input {
    tcp {
        port => 5044
        codec => json_lines
    }
}

 ## Add your filters / logstash plugins configuration here

 output {
    elasticsearch {
        hosts => "http://localhost:9200"
        index => "logstash-spring-boot-%{+YYYY.MM.dd}"
    }
 }
  • index 의 경우 저게 더 알아보기 편한 것 같아서 이름 변경했음.

이제 Kibana 에서 날짜별 인덱싱된 로그를 찾아보자

  • 원래 Kibana 에서는 stackManagement 에

    • 분명히 index pattern 이라는 메뉴가 있어야 하는데 안보였다

      • 찾아보니… Create a data view | Kibana Guide [8.12] | Elastic 로 변경이 된 듯하다.

      • data view 를 설정하고, 거기에서 index 를 설정해주면 알맞게 데이터를 시각화 해주는 듯하다.

        그런데 data view 에 들어가니.. es 쪽으로 로그데이터가 정상적으로 전달되지 않는 문제점이 있었다.

      • 알고 보니 이는 xpack 이라는 라이센스가 필요한 플러그인 설정때문 이였다.

      • 별도 라이센스가 업기에 logstash..yml 에서 xpack 관련한 부분을 전부 제거하였다.

        ---
        ## Default Logstash configuration from Logstash base image.
        ## https://github.com/elastic/logstash/blob/main/docker/data/logstash/config/logstash-full.yml
        #
        http.host: 0.0.0.0
        
        node.name: logstash
      • 가 되었다

      • 인덱스가 정상적으로 es 에 올라왔기에, matching sources 에 제대로 오늘 날짜로 잡히는 모습

      • 정상적으로 잡힌다.

      • 대시보드로 이동한다면

        • 시각화로 보인다.
        • INFO 데이터는 굳이 필요할 까 싶은데, 나중에 ERROR 만 가져오는 별도의 설정을 추가할 예정이다.
        • 그리고 만약 검색을 하려는 경우 kibana에서 별도로 제공하는
          • KQL ( Kibana Query Language ) 를 사용하여 필터링해야한다.
            • 설명서에 잘 나와 있으니 보고 진행하면 될 것 같다.
            • 뭐 로거네임이 hikari 관련된 부분을 검색하고 싶다라고 하면, logger_name.keyword: hikari 요런식으로 KQL 로 검색하면 될 것같다.

profile
이전 블로그 : https://oth3410.tistory.com/

0개의 댓글

관련 채용 정보