Spring log를 ELK를 구축하여 실시간 모니터링
왜 ELK를 선택했을까 ?
ELK stack
로그 수집(logstash), 검색(elasticsearch), 데이터 시각화(kibana)를 통합한 서비스
장점
- 오픈소스 보편적으로 사용되며 레퍼런스가 많다.
- 모니터링 시스템까지 갖출 수 있다.
- logstash 외에 다른 tool을 사용하여 데이터 수집 기능을 대체할 수 있다.
(순수 데이터 수집만을 목적으로 경량화된 모듈 beats를 제공한다) - 수집된 데이터(로그) 검색 기능
- Kibana를 연결하여 실시간으로 로그를 분석하고 시각화할 수 있다.
단점
- 설정이 복잡하다.
- 완전히 실시간으로 동작하지는 않는다.
- 관리할 로그의 규모에 비해 투자되는 리소스가 많아보인다.(인적, 시간적 리소스)
- elasticsearch와 함께 설치되는 X-Pack 플러그인을 무료로 사용할 수 있었는데, ver6.3을 기점으로 라이센스가 변경됨에 따라 Alert을 비롯한 대부분의 쓸만한 기능은 라이센스 비용을 지불해야 한다.
결론: 설정이나 시간적 리소스가 많이투여되지만 오픈소스로 무료로 사용할 수 있다. 그래서 ELK를 구축하여 실시간으로 kibana를 통해 모니터링을 할 계획이다. 우리 서비스는 24시간 365일 돌아가는 서비스이기 때문에 실시간로그처리를 해놔야 추후에 병목지점을 파악하는데 있어서 매우유용하다.
ELK구성도
1. 테스트에 사용하는 Spring Boot 앱은 Spring Boot의 src/resources/logback.xml 라는 파일이 있으며 이 파일에 로그가 기록된다.
2. logback.xml에서 logstash주소를 입력하여 TCP통신을 통해 모아둔 log들을 elasticsearch에 저장될 수 있는 형태로 전처리작업들을 수행하여 elasticsearch에 저장한다.(나는 logback에서 json형태로 보냈다. )
3.elasticsearch는 logstash에서 보낸 데이터를 인덱싱해 저장한다.
4.kibana를 통해 elasticsearch에서 보낸 데이터를 검색하거나 시각화 시킨다.
이 테스트에서 서로 다른 버전의 filebeat, logstash, elasticsearch, kibana를 사용함으로써 발생하는 문제 해결을 위해 docker container를 이용했으며(버전 맞추기가 쉽다) 서로간의 통신 문제를 없애기 위해 동일 네트워크를 사용했다.
logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%-5level %d{HH:mm:ss.SSS} [%thread] %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<appender name="stash" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<destination>localhost:5000</destination>
<!-- encoder is required -->
<encoder class="net.logstash.logback.encoder.LogstashEncoder" />
</appender>
<root level="INFO">
<appender-ref ref="console"/>
<appender-ref ref="stash"/>
</root>
</configuration>
- <configuration>: 이는 Logback 구성 파일의 루트 요소입니다.
- 콘솔 앱렌더(<appender name="console" class="ch.qos.logback.core.ConsoleAppender">):
- "console"이라는 이름의 앱렌더를 정의하여 로그 메시지를 콘솔에 출력합니다.
- PatternLayoutEncoder는 로그 메시지의 형식을 지정합니다. 여기에서는 로그 레벨, 타임스탬프, 스레드 이름, 로거 이름 및 실제 로그 메시지를 포함하는 패턴을 사용합니다.
- Logstash TCP 소켓 앱렌더(<appender name="stash" class="net.logstash.logback.appender.LogstashTcpSocketAppender">):
- "stash"라는 이름의 앱렌더를 정의하여 Logstash로 로그 메시지를 TCP 소켓을 통해 전송합니다.
- <destination> 요소는 Logstash가 실행 중인 대상 주소 및 포트를 지정합니다(localhost:5000 이 경우).
- LogstashEncoder는 Logstash가 이해할 수 있는 형식으로 로그 이벤트를 인코딩하는 데 사용됩니다.
- 루트 로거(<root level="INFO">):
- 최소 로그 레벨이 INFO로 설정된 루트 로거를 구성합니다.
- 루트 로거에는 "console" 및 "stash"라는 두 개의 앱렌더가 포함되어 있습니다. 이는 INFO 레벨 이상의 로그 메시지가 콘솔과 Logstash 양쪽에 전송됨을 의미합니다.
- filebeat가 수집한 로그데이터의 처리를 위해 logstash로 데이터를 보낸다.
logstash.xml
---
## 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
logstash.conf
input {
tcp {
port => 5000
codec => json_lines
}
}
## Add your filters / logstash plugins configuration here
output {
elasticsearch {
hosts => "elasticsearch:9200"
index => "springboot-elk"
user => "elastic"
password => "changeme"
}
}
- input 블록은 데이터를 수신하는 부분을 정의합니다.
- tcp는 Logstash가 TCP 프로토콜을 사용하여 데이터를 수신한다는 것을 나타냅니다.
- port는 Logstash가 수신 대기할 포트 번호를 나타냅니다. 여기서는 5000번 포트를 사용합니다.
- codec은 데이터의 인코딩을 정의합니다. 여기서는 JSON Lines 형식으로 인코딩된 데이터를 사용하고 있습니다.
- output 블록은 처리된 데이터를 최종 목적지로 전송하는 부분을 정의합니다.
- 여기서는 Elasticsearch로 데이터를 전송하도록 설정되어 있습니다.
- hosts는 Elasticsearch 클러스터의 호스트 및 포트를 나타냅니다. 여기서는 "elasticsearch:9200"을 사용하며, 이는 Elasticsearch가 로컬에서 9200번 포트에서 실행 중이라고 가정합니다.
- index는 Elasticsearch에 저장될 데이터의 인덱스 이름을 나타냅니다. 여기서는 "springboot-elk"로 설정되어 있습니다.
- user 및 password는 Elasticsearch 클러스터에 연결할 때 사용되는 인증 정보입니다.
elasticsearch.xml
---
## Default Elasticsearch configuration from Elasticsearch base image.
## https://github.com/elastic/elasticsearch/blob/master/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/setup-xpack.html
# X-Pack은 보안, 알림, 모니터링, 보고, 그래프 기능을 설치하기 편리한 단일 패키지로 번들 구성한 Elastic Stack 확장 프로그램
# 30일 동안 x-pack 모든 기능 사용 가능
xpack.license.self_generated.type: trial
# 6.8 버전부터 시큐리티 기능 제공, 활성화 시 request에 id,pw를 파라미터로 담아서 보내주어야 한다.
xpack.security.enabled: true
xpack.monitoring.collection.enabled: true
- cluster.name: "docker-cluster":
- Elasticsearch 클러스터의 이름을 "docker-cluster"로 설정합니다.
- network.host: 0.0.0.0:
- Elasticsearch가 어떤 네트워크 인터페이스에서 들어오는 연결을 수락할지를 나타냅니다. 여기서는 "0.0.0.0"으로 설정되어 있어 모든 네트워크 인터페이스에서의 접근을 허용합니다.
- X-Pack 설정 (xpack 섹션):
- xpack은 Elasticsearch의 보안, 알림, 모니터링, 보고 및 그래프 기능을 통합한 확장 프로그램 패키지입니다.
- xpack.license.self_generated.type: trial:
- X-Pack의 모든 기능을 30일 동안 사용 가능한 시험 라이선스를 설정합니다.
- xpack.security.enabled: true:
- Elasticsearch에 보안 기능을 활성화합니다. 이로써 Elasticsearch에 접근하려면 사용자 인증이 필요하게 됩니다.
- xpack.monitoring.collection.enabled: true:
- Elasticsearch 모니터링을 위한 데이터 수집을 활성화합니다. 이를 통해 Elasticsearch 클러스터의 성능 및 상태를 모니터링할 수 있습니다.
kibana.xml
## Default Kibana configuration from Kibana base image.
## https://github.com/elastic/kibana/blob/master/src/dev/build/tasks/os_packages/docker_generator/templates/kibana_yml.template.js
#
server.name: kibana
server.host: 0.0.0.0
elasticsearch.hosts: [ "http://elasticsearch:9200" ]
monitoring.ui.container.elasticsearch.enabled: true
## X-Pack security credentials
#
elasticsearch.username: elastic
elasticsearch.password: changeme
- server.name: kibana:
- Kibana 서버의 이름을 "kibana"로 설정합니다.
- server.host: 0.0.0.0:
- Kibana가 수신하는 호스트를 나타냅니다. 여기서는 "0.0.0.0"으로 설정되어 있어 모든 네트워크 인터페이스에서의 접근을 허용합니다.
- elasticsearch.hosts: [ "http://elasticsearch:9200" ]:
- Kibana가 연결할 Elasticsearch 클러스터의 호스트를 지정합니다. 여기서는 "http://elasticsearch:9200"으로 설정되어 있어, Elasticsearch가 동일한 네트워크에서 9200번 포트에서 실행 중이라고 가정합니다.
- monitoring.ui.container.elasticsearch.enabled: true:
- Kibana에서 Elasticsearch 모니터링을 위한 UI를 활성화합니다. Kibana에서 Elasticsearch 클러스터의 상태 및 성능에 대한 정보를 시각적으로 확인할 수 있게 됩니다.
- X-Pack 보안 자격 증명 (elasticsearch.username 및 elasticsearch.password):
- elasticsearch.username: elastic:
- Kibana가 Elasticsearch에 연결할 때 사용할 사용자 이름을 "elastic"으로 설정합니다.
- elasticsearch.password: changeme:
- 해당 사용자의 비밀번호를 "changeme"로 설정합니다. 이러한 자격 증명을 사용하여 Elasticsearch에 대한 보안 인증을 수행합니다.
- elasticsearch.username: elastic:
docker-compose.yml
version: '3.2'
services:
elasticsearch:
container_name: elasticsearch
build:
context: elasticsearch/ # elasticsearch build 경로
args:
ELK_VERSION: $ELK_VERSION
volumes:
- type: bind
source: ./elasticsearch/config/elasticsearch.yml
target: /usr/share/elasticsearch/config/elasticsearch.yml
read_only: true
- type: volume
source: elasticsearch
target: /usr/share/elasticsearch/data
ports:
- "9200:9200"
- "9300:9300"
environment:
ES_JAVA_OPTS: "-Xmx256m -Xms256m"
ELASTIC_PASSWORD: changeme
# 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
logstash:
container_name: logstash
build:
context: logstash/
args:
ELK_VERSION: $ELK_VERSION
volumes:
- type: bind
source: ./logstash/config/logstash.yml
target: /usr/share/logstash/config/logstash.yml
read_only: true
- type: bind
source: ./logstash/pipeline
target: /usr/share/logstash/pipeline
read_only: true
ports:
- "5000:5000/tcp"
- "5000:5000/udp"
- "9600:9600"
environment:
LS_JAVA_OPTS: "-Xmx256m -Xms256m"
networks:
- elk
depends_on:
- elasticsearch
kibana:
container_name: kibana
build:
context: kibana/
args:
ELK_VERSION: $ELK_VERSION
volumes:
- type: bind
source: ./kibana/config/kibana.yml
target: /usr/share/kibana/config/kibana.yml
read_only: true
ports:
- "5601:5601"
networks:
- elk
depends_on:
- elasticsearch
networks:
elk:
driver: bridge
volumes:
elasticsearch:
이제 docker-compose up 명령어를 통해 전부 실행시켜준다.
kibana에접속한다 localhost:5601
ID,PASSWORD는 자신이 지정한거 입력해서 들어가면된다.
이제 여기에서 index patterns을 지정해줘야한다. 아까전에 logstash.conf 파일안에서 index를 지정해주었다.
input {
tcp {
port => 5000
codec => json_lines
}
}
## Add your filters / logstash plugins configuration here
output {
elasticsearch {
hosts => "elasticsearch:9200"
index => "springboot-elk"
user => "elastic"
password => "changeme"
}
}
이 index값이 이제 키값이 되는 것이다. logback.xml에 있던 로그들이 springboot-elk라는 키값으로 구분되어 해당 로그가 찍히게 되는 것이다.
이제 끝이다 . kibana를 통해 실시간 로그를 필터처리하고 실시간으로 모니터링이 가능해졌다. 이로써 24시간 365일 돌아가는 서비스에서 어느 지점에서 병목이 생겼는지, 어느 로그가 많이 발생했는지 처리가 가능해진다.
위에 그래프는 자신이 custom해서 더욱 보기쉬운형태로도 변형이 가능하다.
실시간로그처리를 하긴했지만 , 데이터가 점점 쌓일 것이다. 그럼 volume에도 데이터가 쌓일 것이고 그러게되면 디스크의 용량은 점점 차게될 것이고, 서비스에도 영향을 끼칠것 같다. 이걸 이제 처리하는 방법을 다음에 알아봐야겠다.
번외
For mac users
logstash image를 다운받으면 5000포트가 사용중이라 설치가 안되는 버그가 발생했는데 , 다음 블로그를 참고하면 좋습니다ㅎㅎ
https://algoroot.tistory.com/44
참고블로그
https://philosophyanddevelope.tistory.com/12
https://investment-engineer.tistory.com/5