본문 바로가기
프로그래밍/로깅과 트레이싱

[MSA] Elastic Stack을 이용한 중앙 집중형 로깅_실제 운영 환경

by 사바라다 2019. 12. 18.

안녕하세요. 오늘은 Elastic Stack을 이용한 중앙 집중형 로깅_1 포스팅에 이어서 각각의 elastic stack을 한번 이어보도록 하겠습니다.

개론

클라우드 환경에서 중앙 집중형 로깅을 만드는 것은 로컬환경에서 세팅하는 것과 많은 차이를 가지고 있습니다. 우리가 클라우드 상황임에 따라서 고려해야하는 상황이 좀 더 추가되는 것이지요. 고려해야하는 상황과 해결법은 아래와 같습니다.

  1. 서비스는 서버에 독립적이어야 한다.
    • docker를 통한 해결
  2. 서비스는 상황에 따라 유연하게 Scale-out될 수 있어야한다. 새로운 서버에 새로운 서비스가 인스턴스로 올라왔다면 Log Shipper(로그를 중앙으로 전달하는 프로세스)또한 Scale-out 되어야 한다.
    • Auto-Scaling 등을 이용한 해결
    • kubernates 또는 ECS 등으로 서비스가 새로 뜬다면 그 서버에 Log shipper도 같이 뜨게끔 처리
  3. 서비스는 로그파일이 어떻게 처리되는 지 알 필요가 없습니다. (Single Responsible Principle, 단일 책임 원칙)
    • 서비스와 filebeat는 아무런 연관성을 가지지 않도록

이런 3가지를 요소를 MSA에서는 고려하면서 중앙 집중형 로깅을 달성해야합니다. 그렇게 하기 위한 아키텍처를 고려하던 중 저는 아래와 같은 아키텍처를 발견하고 적용하였습니다.

filebeat VS Logstash

지난번 포스팅에서 저는 filebeat와 logstash를 비교했었습니다. 그리고 filebaet는 매우 가볍다라는 장점을 가지고 있는 것을 알 수 있었습니다. 저는 MSA환경에서는 서비스와 함께 logshipper도 빠르게 생성과 소멸이 이루어져야한다고 생각했습니다. 그래서 저는 비교적 가벼운 filebeat로 선택했습니다. 그리고 로그는 서비스에서 파일을 쓸 때 json으로 남기게 만들어 filebeat의 proceccing과 같은 부담을 없앴습니다.

실습

그러면 실습을 진행해보도록 하겠습니다. 순서는 아래와 같이 진행하겠습니다.

  1. filebeat 설정
  2. filebeat docker 배포
  3. spring boot service logback 설정

filebeat 설정

아래 참조 자료에서는 filebeat 6.2.4 버전을 사용했습니다. 하지만 저는 filebeat 7.4.2 버전을 사용하도록 하겠습니다. 6.2.4 버전은 제가 사용하던 7.4.2 버전과 설정이 많이 다르기 때문에 상대적으로 편한 7.4.2를 선택했습니다.

기본 설정에서 변경된 부분은 아래와 같습니다.

filebeat.inputs:

  # Each - is an input. Most options can be set at the input level, so
  # you can use different inputs for various configurations.
  # Below are the input specific configurations.

  - type: log
    json.keys_under_root: true
    json.message_key: log
  # Change to true to enable this input configuration.
    enabled: true

    # Paths that should be crawled and fetched. Glob based paths.
    paths:
      - /usr/share/dockerlogs/data/*-json.log

... [중략] ...

output.elasticsearch:
   # Array of hosts to connect to.
   hosts: ["{elasticsearch가 실행되어 있는 IP}:{PORT}"]
   template:
  • json.keys_under_root : json 형태일 때 elasticsearch로 보내기 전 json 형태의 root를 더해줍니다.
  • json.message_key : 명명된 key의 값의 내용에 대해서 filter 및 멀티라인에 대한 처리를 해주는 옵션입니다.
  • path : path는 docker container 내에서 읽어들일 log 파일이 있는 위치입니다.

좀 더 명확하고 자세한 설명은 공식홈페이지를 참고하시면 됩니다.

filebeat 배포

filebeat를 docker로 image를 만들어 배포해야합니다. dockerfile의 내용은 아래와 같습니다.

FROM docker.elastic.co/beats/filebeat:7.4.2
# Copy our custom configuration file
COPY filebeat.yml /usr/share/filebeat/filebeat.yml
USER root
# Create a directory to map volume with all docker log files
RUN mkdir /usr/share/filebeat/dockerlogs
RUN chown -R root /usr/share/filebeat/
RUN chmod -R go-w /usr/share/filebeat/

간단한 설명은 아래와 같습니다.

  • FROM : container 환경의 기반을 설정
  • COPY : filebeat.yml 파일을 container 내의 /usr/share/filebaet/fileabet.yml으로 copy
  • USER : 명령어를 실행할 user 선택
  • RUN : container에서 실행될 명령어

역시 마찬가지로 docker의 공식홈페이지에서 좀 더 자세한 설명 및 다양한 명령어에 대한 설명을 얻을 수 있습니다.

Dockerfile을 만들었으면 이제 빌드와 실행을 하기 위해서 디렉토리 구성을 아래와 같이 만듭니다.

directroy
total 40
-rw-r--r--  1 kwanghoyeom  staff   322B 12  6 17:04 Dockerfile
-rw-------  1 kwanghoyeom  staff   8.8K 12 10 18:05 filebeat.yml

그리고 아래와 같이 docker file을 빌드 후 run합니다. 그러면 log가 출력되면 정상동작 한 것입니다.

$ docker build -t filebeat .
$ docker run  --name app -v "{log가 남는 local 위치}:/usr/share/dockerlogs/data" app

INFO    instance/beat.go:607    Home path: [/usr/share/filebeat] Config path: [/usr/share/filebeat] Data path: [/usr/share/filebeat/data] Logs path: [/usr/share/filebeat/logs]
INFO    instance/beat.go:615    Beat ID: c0bc9204-bb79-4592-ab4f-8ef579cec61d
INFO    [seccomp]    seccomp/seccomp.go:124    Syscall filter successfully installed
INFO    [beat]    instance/beat.go:903    Beat info    {"system_info": {"beat": {"path": {"config": "/usr/share/filebeat", "data": "/usr/share/filebeat/data", "home": "/usr/share/filebeat", "logs": "/usr/share/filebeat/logs"}, "type": "filebeat", "uuid": "c0bc9204-bb79-4592-ab4f-8ef579cec61d"}}}
...하략...

spring boot service logback 설정

docker file에 대한 설정을 마무리했으니 이제 서비스의 설정을 변경해 보도록 하겠습니다. 서비스는 Spring Boot로 구축할 것이며 먼저 로그를 분석하기 쉽도록 json으로 출력하도록 하겠습니다.

어렵지 않습니다. 다행히 자동으로 변경해주는 오픈소스가 준비되어 있습니다. 바로 logstash-logback-encoder입니다. 의존성을 주입하도록 하겠습니다.

runtime('net.logstash.logback:logstash-logback-encoder:5.1')

그리고 우리는 logback 설정을 변경해야합니다. 조금더 정밀하게 조정하기 위해서 logback-spring.xml 파일을 resource 폴더에 작성하도록 하겠습니다. 내용은 아래와 같습니다.

<?xml version="1.0" encoding="UTF-8"?>
<configuration>

  <property name="LOG_PATH" value="/opt/app/logs"/>
  <property name="FILE_NAME" value="order"/>

  <!-- Logstash JSON 형식으로 파일 로그 생성 -->
  <!-- 5MB 초과, 날짜 변경 시점마다 생성, 생성된지 3일 이상된 파일은 삭제 -->
  <appender name="JSON_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <File>${LOG_PATH}/${FILE_NAME}-json.log</File>
    <encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <FileNamePattern>${LOG_PATH}/${FILE_NAME}_%d{yyyy-MM-dd}.%i.json</FileNamePattern>
      <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
        <maxFileSize>5MB</maxFileSize>
      </timeBasedFileNamingAndTriggeringPolicy>
      <maxHistory>3</maxHistory>
    </rollingPolicy>
  </appender>

  <!-- INFO 레벨 이하 로그를 파일로 출력 -->
  <root level="INFO">
    <appender-ref ref="JSON_FILE"/>
  </root>

</configuration>

주목하셔야 하는부분은 <encoder class="net.logstash.logback.encoder.LogstashEncoder"/> 이부분입니다. encoder를 등록하므로써 쉽게 json형식의 로그로 남길 수 있게 되었습니다. 서비스를 실행하고 로그가 json 형식으로 남는지 확인해보도록 하겠습니다.

{"@timestamp":"2019-12-17T20:31:48.891+09:00","@version":"1","message":"To enable URLs as dynamic configuration sources, define      System property archaius.configurationSource.additionalUrls or make config.properties available on classpath.","logger_name":"     com.netflix.config.sources.URLConfigurationSource","thread_name":"AsyncReporter{org.springframework.cloud.sleuth.zipkin2.sender     .RestTemplateSender@b4d3054a}","level":"INFO","level_value":20000}
{"@timestamp":"2019-12-17T20:31:48.891+09:00","@version":"1","message":"HHH000477: Starting delayed evictData of schema as part      of SessionFactory shut-down'","logger_name":"org.hibernate.tool.schema.internal.SchemaDropperImpl$DelayedDropActionImpl","thread_name":"Thread-13","level":"INFO","level_value":20000}

이렇게 설정하므로써 우리가 그리던 아키텍처를 실제로 구현할 수 있게 되었습니다.

연동 확인

아래는 이렇게 연동하여 kibana에서 쌓인 로그의 모습입니다.

마무리

오늘은 이렇게 msa 환경에서 중앙 집중형 로깅을 구성하는 방법에 대해서 알아보았습니다. cloud환경, docker 환경이기때문에 우리는 좀 더 시스템을 구성할 때 신경을 써야하는 것 같습니다. :)

정말 좋다고 생각하는 기술인데 생각보다 편한 기술만은 아닌것 같습니다. 그래도 이렇게 만들고 적용해보니 뿌듯합니다.

오늘은 여기까지 하고 마치도록 하겠습니다.

다음 포스팅으로 찾아뵙겠습니다.

감사합니다.

참조

https://medium.com/@bcoste/powerful-logging-with-docker-filebeat-and-elasticsearch-8ad021aecd87

docker 공식홈페이지

elasticSearch 공식홈페이지

댓글