Collecting and Analysis for syslog via ELK 8.17.x
- 1 개요
- 2 Set up ELK 8.17.x as a container
- 3 Basic single mode
- 3.1 ./.env
- 3.2 ./elasticsearch/Dockerfile
- 3.3 ./kibana/Dockerfile
- 3.4 ./logstash/Dockerfile
- 3.5 ./docker-compose.yml
- 3.5.1 setup 서비스
- 3.5.2 logstash 서비스
- 3.5.3 es 서비스
- 3.5.4 kibana 서비스
- 3.5.5 공유 볼륨 및 네트워크
- 4 Basic multi-nodes mode
- 5 Set vm.max_map_count to at least 262144
- 6 ES index mapping
- 7 Setting Syslog data collect
- 8 See via Kibana Discover Menu
개요
시스템 로그와 App 로그를 rsyslog로 통합하여 수집(logstash) → 저장(elasticsearch).
Elasticsearch의 text analysis 기능을 사용하여 형태소 기반 토큰 분리 즉, 역색인화(Inverted Indexing).
역색인을 활용한 log message 분석 기법.
등 [비정형] 로그 text를 색인화하여 저장하고, 이를 문제 발견 및 해결을 위해 root cause을 분석할 수 있도록 활용하는 방안을 도출하여 보는 것을 목적으로 한다.
Set up ELK 8.17.x as a container
Elasticsearch 8이상부터는 보안 설정이 기본적으로 활성화되어, 각종 인증서, 암호화 키 등을 발급하고, 알맞게 셋팅되어야 한다.
그런데, 공식 가이드를 따라하다보면 군데군데 구멍이 뚫려있어 각 config 옵션이 영향을 미치는 범위 등을 이해하지 않고는, 처음 세팅할때 어려움을 겪었다. (물론 Docker compose로 편하게 실행할때의 얘기이다.)
해서, 위 [Ref.] github의 내용을 기반으로 아래와 같이 single mode와 multi mode 두 개의 과정을 편하게 세팅하여 사용해 볼 수 있도록 하였다.
Multi-node는 단순한 형태로만 제시하며, 개요에서 밝힌 과정은 편의상 Single node에서만 진행할 것 이다.
Basic single mode
./.env
# Password for the 'elastic' user (at least 6 characters)
ELASTIC_PASSWORD=changeme
# Password for the 'kibana_system' user (at least 6 characters)
KIBANA_PASSWORD=changeme
# Version of Elastic products
STACK_VERSION=8.17.3
# Set the cluster name
CLUSTER_NAME=docker-cluster
# Set to 'basic' or 'trial' to automatically start the 30-day trial
LICENSE=basic
#LICENSE=trial
# Port to expose Elasticsearch HTTP API to the host
ES_PORT=9200
#ES_PORT=127.0.0.1:9200
# Port to expose Kibana to the host
KIBANA_PORT=5601
#KIBANA_PORT=80
LOGSTASH_PORT=5045
LOGSTASH_SYSLOG_PORT=5140
LOGSTASH_MONITORING_PORT=9601
# Increase or decrease based on the available host memory (in bytes)
# MEM_LIMIT=1073741824 # 1GiB
MEM_LIMIT=2147483648 # 2GiB
# MEM_LIMIT=6442450944 # 6GiB
# Project namespace (defaults to the current folder name if not set)
COMPOSE_PROJECT_NAME=elk-single
./elasticsearch/Dockerfile
ARG ELK_VERSION
# https://www.docker.elastic.co/
FROM docker.elastic.co/elasticsearch/elasticsearch:${ELK_VERSION}
# Add your elasticsearch plugins setup here
RUN elasticsearch-plugin install analysis-nori
./kibana/Dockerfile
ARG ELK_VERSION
# https://www.docker.elastic.co/
FROM docker.elastic.co/kibana/kibana:${ELK_VERSION}
# Add your kibana plugins setup here
# Example: RUN kibana-plugin install <name|url>
./logstash/Dockerfile
ARG ELK_VERSION
# https://www.docker.elastic.co/
FROM docker.elastic.co/logstash/logstash:${ELK_VERSION}
# Add your logstash plugins setup here
# Example: RUN logstash-plugin install logstash-filter-json
# RUN logstash-plugin install --version 1.3.1 logstash-input-snmp
# RUN logstash-plugin install logstash-filter-mutate
RUN logstash-plugin install logstash-filter-ruby
RUN logstash-plugin install logstash-filter-grok
RUN logstash-plugin install logstash-output-elasticsearch
# RUN logstash-plugin install logstash-integration-rabbitmq
# RUN logstash-plugin install logstash-output-influxdb
./docker-compose.yml
setup 서비스
ELASTIC_PASSWORD
와KIBANA_PASSWORD
환경 변수가 설정되어 있는지를 확인하며, 누락 시 오류를 출력하고 종료한다.config/certs/ca.zip
및certs.zip
파일이 존재하지 않을 경우,elasticsearch-certutil
명령어를 통해 CA 및 인증서를 생성하고 압축을 해제한다.생성된 인증서의 소유자를
root:root
로 설정하며, 디렉터리 권한은755
, 파일 권한은644
로 지정한다.Elasticsearch가 기동되고
"missing authentication credentials"
메시지를 반환할 때까지 대기하며, 이후 Kibana 시스템 계정의 비밀번호를 설정한다.헬스체크는
config/certs/es/es.crt
파일의 존재 여부를 기준으로 수행한다.
logstash 서비스
setup
서비스가 헬시 상태가 된 이후에 기동되도록 설정되어 있다.인증서 디렉터리와 Logstash 파이프라인 디렉터리를 컨테이너 내부에 마운트한다.
Beats(5044), 모니터링 API(9600), Syslog(5140) 포트를 외부에 노출한다.
사실 여기서는 Beats를 사용할 계획이 없기때문에 필요는 없으나, 일단 노출하기로 한다.
X-Pack 모니터링 기능과 Elasticsearch 연동을 위해 인증 정보 및 CA 경로를 환경 변수로 설정한다.
컨테이너는 고정 IP
192.168.16.3
을 사용하며, 9600 포트에 대한 HTTP 200 응답을 통해 헬스체크를 수행한다.
es 서비스
setup
및logstash
서비스가 모두 헬시 상태일 때 기동되도록 구성되어 있다.인증서 및 데이터 디렉터리를 컨테이너에 마운트하며, Elasticsearch 포트(9200)를 외부에 노출한다.
보안 기능(X-Pack Security) 및 HTTP/Transport 계층의 SSL 설정을 활성화하며, 인증서 및 키 파일 경로를 환경 변수로 지정한다.
클러스터는 단일 노드로 구성되며, 라이선스 유형 및 메모리 자동 설정 기능을 활성화한다.
고정 IP는
192.168.16.4
이며,syslog
드라이버를 통해tcp://10.20.2.216:514
로 로그를 전송한다.10.20.2.216:514
는 로컬호스트의 IP:rsyslog(tcp) port 이다.note Time Zone이 다른 경우에 대비하여 로컬호스트의 rsyslog를 거쳐서 UTC로 통일한 후, logstash로 전송한다. → Collecting and Analysis for syslog via ELK 8.17.x | rsyslog configuration: 참고
헬스체크는 HTTPS 요청 결과
"missing authentication credentials"
문자열의 존재 여부를 기준으로 판단한다.
kibana 서비스
es
서비스가 헬시 상태가 된 이후 기동되도록 설정되어 있다.인증서 및 Kibana 데이터 디렉터리를 마운트하며, Kibana 포트(5601)를 외부에 개방한다.
Elasticsearch와의 연동을 위해 호스트, 사용자 이름, 비밀번호, CA 경로를 환경 변수로 지정한다.
고정 IP는
192.168.16.5
이며, 로그는syslog
드라이버를 통해tcp://10.20.2.216:514
로 전송된다.10.20.2.216:514
는 로컬호스트의 IP:rsyslog(tcp) port 이다.note 아래에 다시 언급하겠으나, Time Zone이 다른 경우에 대비하여 로컬호스트의 rsyslog를 거쳐서 UTC로 통일한 후, logstash로 전송한다. → Collecting and Analysis for syslog via ELK 8.17.x | rsyslog configuration: 참고
헬스체크는 5601 포트로의 HTTP 요청에 대해 302 리다이렉트 응답 여부를 확인하여 수행된다.
공유 볼륨 및 네트워크
certs
,esdata
,kibanadata
,logstash_pipeline
볼륨은 로컬 드라이버를 사용하여 정의되어 있다.네트워크는 브리지 모드를 사용하며, 서브넷은
192.168.16.0/24
, 게이트웨이는192.168.16.1
로 지정되어 있어 각 서비스에 고정 IP를 할당할 수 있도록 구성되어 있다.
참고: IP 대역을 반드시 지정하고 각 서비스에 고정 할당할 필요는 없으나, 여기서는 만일을 위해 지정하여 사용한다.
services:
setup:
image: elk/elasticsearch:${STACK_VERSION}
build:
context: elasticsearch/
args:
ELK_VERSION: ${STACK_VERSION}
volumes:
- certs:/usr/share/elasticsearch/config/certs
user: "0"
command: >
bash -c '
if [ x${ELASTIC_PASSWORD} == x ]; then
echo "Set the ELASTIC_PASSWORD environment variable in the .env file";
exit 1;
elif [ x${KIBANA_PASSWORD} == x ]; then
echo "Set the KIBANA_PASSWORD environment variable in the .env file";
exit 1;
fi;
if [ ! -f config/certs/ca.zip ]; then
echo "Creating CA";
bin/elasticsearch-certutil ca --silent --pem -out config/certs/ca.zip;
unzip config/certs/ca.zip -d config/certs;
fi;
if [ ! -f config/certs/certs.zip ]; then
echo "Creating certs";
echo -ne \
"instances:\n"\
" - name: es\n"\
" dns:\n"\
" - es\n"\
" - localhost\n"\
" ip:\n"\
" - 127.0.0.1\n"\
> config/certs/instances.yml;
bin/elasticsearch-certutil cert --silent --pem -out config/certs/certs.zip --in config/certs/instances.yml --ca-cert config/certs/ca/ca.crt --ca-key config/certs/ca/ca.key;
unzip config/certs/certs.zip -d config/certs;
fi;
echo "Setting file permissions"
chown -R root:root config/certs;
find . -type d -exec chmod 755 \{\} \;;
find . -type f -exec chmod 644 \{\} \;;
echo "Waiting for Elasticsearch availability";
until curl -s --cacert config/certs/ca/ca.crt https://es:9200 | grep -q "missing authentication credentials"; do sleep 30; done;
echo "Setting kibana_system password";
until curl -s -X POST --cacert config/certs/ca/ca.crt -u "elastic:${ELASTIC_PASSWORD}" -H "Content-Type: application/json" https://es:9200/_security/user/kibana_system/_password -d "{\"password\":\"${KIBANA_PASSWORD}\"}" | grep -q "^{}"; do sleep 10; done;
echo "All done!";
'
healthcheck:
test: ["CMD-SHELL", "[ -f config/certs/es/es.crt ]"]
interval: 1s
timeout: 5s
retries: 120
logstash:
depends_on:
setup:
condition: service_healthy
image: elk/logstash:${STACK_VERSION}
build:
context: logstash/
args:
ELK_VERSION: ${STACK_VERSION}
volumes:
- certs:/usr/share/logstash/config/certs
- logstash_pipeline:/usr/share/logstash/pipeline
ports:
- ${LOGSTASH_PORT}:5044 # Beats, Logstash Forwarder, etc.
- ${LOGSTASH_MONITORING_PORT}:9600 # Logstash Monitoring API
- ${LOGSTASH_SYSLOG_PORT}:5140
environment:
xpack.monitoring.elasticsearch.hosts: "https://es:9200"
xpack.monitoring.elasticsearch.username: elastic
xpack.monitoring.elasticsearch.password: changeme
xpack.monitoring.elasticsearch.ssl.certificate_authority: config/certs/ca/ca.crt
ELASTICSEARCH_HOSTS: "https://es:9200"
ELASTICSEARCH_USERNAME: "elastic"
ELASTICSEARCH_PASSWORD: ${ELASTIC_PASSWORD}
ELASTICSEARCH_SSL_CERTIFICATEAUTHORITIES: config/certs/ca/ca.crt
mem_limit: ${MEM_LIMIT}
networks:
default:
ipv4_address: 192.168.16.3
healthcheck:
test:
[
"CMD-SHELL",
"curl -s -I http://localhost:9600 | grep -q 'HTTP/1.1 200 OK'",
]
interval: 10s
timeout: 10s
retries: 120
es:
depends_on:
logstash:
condition: service_healthy
setup:
condition: service_healthy
image: elk/elasticsearch:${STACK_VERSION}
build:
context: elasticsearch/
args:
ELK_VERSION: ${STACK_VERSION}
volumes:
- certs:/usr/share/elasticsearch/config/certs
- esdata:/usr/share/elasticsearch/data
ports:
- ${ES_PORT}:9200
environment:
# logger.org.elasticsearch.discovery: DEBUG
node.name: es
discovery.type: single-node
cluster.name: ${CLUSTER_NAME}
ELASTIC_PASSWORD: ${ELASTIC_PASSWORD}
bootstrap.memory_lock: "true"
xpack.security.enabled: "true"
xpack.security.http.ssl.enabled: "true"
xpack.security.http.ssl.key: certs/es/es.key
xpack.security.http.ssl.certificate: certs/es/es.crt
xpack.security.http.ssl.certificate_authorities: certs/ca/ca.crt
xpack.security.transport.ssl.enabled: "true"
xpack.security.transport.ssl.key: certs/es/es.key
xpack.security.transport.ssl.certificate: certs/es/es.crt
xpack.security.transport.ssl.certificate_authorities: certs/ca/ca.crt
xpack.security.transport.ssl.verification_mode: certificate
xpack.license.self_generated.type: ${LICENSE}
xpack.ml.use_auto_machine_memory_percent: "true"
mem_limit: ${MEM_LIMIT}
networks:
default:
ipv4_address: 192.168.16.4
ulimits:
memlock:
soft: -1
hard: -1
logging:
driver: syslog
options:
syslog-address: "tcp://10.20.2.216:514"
tag: "ai01 elasticsearch"
syslog-facility: "local0"
healthcheck:
test:
[
"CMD-SHELL",
"curl -s --cacert config/certs/ca/ca.crt https://localhost:9200 | grep -q 'missing authentication credentials'",
]
interval: 10s
timeout: 10s
retries: 120
kibana:
depends_on:
es:
condition: service_healthy
image: elk/kibana:${STACK_VERSION}
build:
context: kibana/
args:
ELK_VERSION: ${STACK_VERSION}
volumes:
- certs:/usr/share/kibana/config/certs
- kibanadata:/usr/share/kibana/data
ports:
- ${KIBANA_PORT}:5601
environment:
# LOGGING_ROOT_LEVEL: debug
SERVERNAME: kibana
ELASTICSEARCH_HOSTS: https://es:9200
ELASTICSEARCH_USERNAME: kibana_system
ELASTICSEARCH_PASSWORD: ${KIBANA_PASSWORD}
ELASTICSEARCH_SSL_CERTIFICATEAUTHORITIES: config/certs/ca/ca.crt
mem_limit: ${MEM_LIMIT}
networks:
default:
ipv4_address: 192.168.16.5
logging:
driver: syslog
options:
syslog-address: "tcp://10.20.2.216:514"
tag: "ai01 kibana"
syslog-facility: "local1"
healthcheck:
test:
[
"CMD-SHELL",
"curl -s -I http://localhost:5601 | grep -q 'HTTP/1.1 302 Found'",
]
interval: 10s
timeout: 10s
retries: 120
volumes:
certs:
driver: local
esdata:
driver: local
kibanadata:
driver: local
logstash_pipeline:
driver: local
networks:
default:
driver: bridge
ipam:
config:
- subnet: 192.168.16.0/24
gateway: 192.168.16.1
Basic multi-nodes mode
Set vm.max_map_count
to at least 262144
The vm.max_map_count
kernel setting must be set to at least 262144
for production use.
To view the current value for the vm.max_map_count
setting, run:
$ sudo sysctl -a|grep vm.max_map_count
vm.max_map_count = 65536
To apply the setting on a live system, run:
$ sudo vim /etc/sysctl.d/00-vm.conf
vm.max_map_count=262144
$ sudo sysctl --system
ES index mapping
아래는 기본 제공되는 영어 형태소 분석기를 사용한 syslog 인덱스 매핑이다.
Kibana의 “Dev tools”에서 적용하도록 한다.
PUT _index_template/syslog-tpl
{
"index_patterns": [
"syslog-*"
],
"template": {
"mappings": {
"properties": {
"event": {
"properties": {
"original": {
"type": "text",
"analyzer": "english"
}
}
},
"host": {
"properties": {
"hostname": {
"type": "keyword"
},
"ip": {
"type": "ip"
}
}
},
"log": {
"properties": {
"syslog": {
"properties": {
"facility": {
"properties": {
"code": {
"type": "long"
}
}
},
"priority": {
"type": "long"
},
"severity": {
"properties": {
"code": {
"type": "long"
}
}
}
}
}
}
},
"message": {
"type": "text",
"analyzer": "english",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"process": {
"properties": {
"name": {
"type": "keyword"
},
"pid": {
"type": "long"
}
}
},
"service": {
"properties": {
"type": {
"type": "keyword"
}
}
},
"type": {
"type": "keyword"
}
}
}
}
}
Setting Syslog data collect
logstash configuration for syslog:
Docker system 로그 중에는 아래와 같은 로그가 자주 발생한다."run-docker-runtime\x2drunc-moby-77ab16d03d5107e7be0b1050a97781f7f1d57311a1e6e74be9768b7f8d6e8256-runc.apZ7Tn.mount: Deactivated successfully."
여기서 \x2d
는 ‘-'의 ASCII 값인데 '-’으로 변환되지 않고 전달된다.
이는 형태소 분석기에서 오류가 발생하기 때문에, 아래와 같이 logstash의 filter로 문자로 변환하도록 하는 내용이 포함되어 있음에 주의한다.
/var/lib/docker/volumes/elk-single_logstash_pipeline/_data$ sudo cat logstash.rb
input {
syslog {
port => 5140
type => "syslog"
use_labels => false
syslog_field => "message"
}
}
filter {
ruby {
code => '
if event.get("message")
decoded = event.get("message").gsub(/\\x([0-9A-Fa-f]{2})/) { [$1].pack("H*") }
event.set("message", decoded)
end
'
}
}
output {
# stdout for debugging
stdout {
codec => rubydebug
}
elasticsearch {
hosts => ["${ELASTICSEARCH_HOSTS}"]
user => "${ELASTICSEARCH_USERNAME}"
password => "${ELASTICSEARCH_PASSWORD}"
cacert => "${ELASTICSEARCH_SSL_CERTIFICATEAUTHORITIES}"
ssl_certificate_verification => false
index => "syslog-%{+YYYY.MM.dd}"
}
}
And then restart logstash container.
rsyslog configuration:
UTC로 통일한 후, logstash로 전송
/etc/rsyslog.d$ sudo cat 51-logstash.conf
template(name="UtcSyslogFormat" type="string" string="<%PRI%>%timereported:::date-rfc3339% %hostname% %syslogtag%%msg%\n")
*.* @@localhost:5140;UtcSyslogFormat
$ sudo systemctl restart rsyslog
You can see the logs via Kibana dashboard
See via Kibana Discover Menu
아래와 같이 출력되려면 몇 가지 선행 작업이 필요하나, 이는 kibana 매뉴얼이나 다른 가이드를 참고하길 바란다.
다음은 Log Message에 대해 어떤 방식으로 tokenize 되었는지, 이를 활용하여 어떤 작업이 가능한지에 대해 살펴보겠다.