일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 |
- headless service
- EKS 클러스터
- SAA 합격 후기
- kubernetes 동작 원리
- terraform
- 로드밸런서 컨트롤러
- 딥레이서 보상함수
- Kubernets on Jenkins
- Prometheus install
- 딥레이서
- helm
- github action 사용법
- Solution Architecture
- 솔데스크
- blue-green
- Firelens
- 그라파나 대시보드
- 깃허브 액션
- jenkins
- AWS 딥레이서
- 그라파나 시각화
- grafana on kubernetes
- LoadBalancer Controller
- Aurora cluster
- livenessPorbe
- 쿠버네티스 컴포넌트
- 쿠버네티스
- Kubernetes
- 메탈LB
- EFS CSI Driver
mingming
Apache Tomcat & ELK Stack 본문
Apache Tomcat 위에 동작하는 Spring으로 구성된 애플리케이션의 로그를 수집하고 모니터링하기 위해 ELK 스택을 사용했습니다. 위 아키텍쳐에 대한 간단한 동작 설명은 다음과 같습니다.
- 먼저 사이드카 컨테이너로 동작하는 filebeat 컨테이너가 Apache Tomcat의 로그를 수집하여 Logstash로 전송합니다.
- Logstash는 해당 로그를 규칙에 맞게 필터링하여 Elasticsearch로 전송합니다.
- Elasticsearch는 로그를 적재하고 Kibana를 통해 적재된 로그를 시각화 하여 확인할 수 있습니다.
Version
Kubernetes: 1.27
Elasticsearch: 8.5.1
Logstash: 8.5.1
Kibana: 8.5.1
Filebeat: 8.11.3
Filebeat
Kubernetes위에 파드로 동작하는 애플리케이션의 로그를 수집하기 위해 Filebeat를 이용했습니다. 로그를 어떻게 원하는 부분만 수집할지 고민하였고, 결과적으 Filebeat를 사이드카 컨테이너로 추가하여 로그수집 하였습니다.
다음은 실제 프로젝트에 사용한 Deployment manifest file 입니다.
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: sessac-app-deployment
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: sessac-app
template:
metadata:
labels:
app: sessac-app
spec:
containers:
- name: sessac-app
image: <image>
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
volumeMounts:
- name: logs-volume
mountPath: /usr/local/tomcat/logs
resources:
requests:
cpu: 250m
memory: 500Mi
- name: filebeat
image: docker.elastic.co/beats/filebeat:8.11.3
volumeMounts:
- name: filebeat-config
mountPath: /usr/share/filebeat/filebeat.yml
subPath: filebeat.yml
- name: logs-volume
mountPath: /usr/local/tomcat/logs
resources:
requests:
cpu: 100m
memory: 100Mi
volumes:
- name: filebeat-config
configMap:
name: filebeat-config
- name: logs-volume
persistentVolumeClaim:
claimName: logs-pvc
filebeat config 파일을 configmap으로 생성하여 볼륨으로 마운트시켜 사용했습니다. 또한 로그 볼륨을 추가로 생성하여 두 컨테이너가 같은 볼륨을 사용하도록 설정했습니다. 다음은 filebeat config 파일입니다.
filebeat.config.modules:
path: ${path.config}/modules.d/*.yml
reload.enabled: true
filebeat.inputs:
- type: filestream
id: logstream_access
enabled: true
paths:
- /usr/local/tomcat/logs/localhost_access_log*
fields:
log: access_log
- type: filestream
id: logstream_catalina
enabled: true
paths:
- /usr/local/tomcat/logs/catalina*.log
fields:
log: catalina_log
output.logstash:
hosts: ["logstash-logstash-0.logstash-logstash-headless.elk.svc.cluster.local:5044"]
filebeat.config.modules: filebeat 모듈 구성을 지정합니다.
- path: 파일시스템 경로 ${path.config}/modules.d/*.yml에 있는 모든 .yml 파일을 로드합니다.
- reload.enabled: 설정 파일이 변경되었을 때 Filebeat가 자동으로 다시 로드할지 여부를 나타냅니다.
filebeat.inputs: filebeat에 의해 수집되는 로그 입력을 정의합니다
- type: 입력 유형을 나타냅니다. 여기서는 로그 파일을 수집하기 위해 'log'로 설정되어 있습니다.
- id: 입력의 고유 식별자입니다.
- paths: 수집할 로그 파일의 경로를 지정합니다.
- fields: 출력에 추가 정보를 추가하기 위해 지정할 수 있는 선택적 필드입니다.일반 구성에서 중복 필드가 선언되면 해당 필드의 값이 여기에 선언된 값으로 덮어씁니다.
output.logstash: filebeat의 input을 어디로 보낼지 지정합니다.
Logstash
Elastic에서 제공하는 helm chart를 이용해 배포하였습니다. values.yaml 파일을 수정해 커스텀하여 배포했습니다. 8.5.1 버전의 Elasticsearch와 연동하기 위해서 ssl/tls 인증이 필요하고 Elasticsearch의 인증정보를 마운트해 사용했습니다. 다음은 수정한 values 파일 일부입니다.
secretMounts:
- name: elasticsearch-certs
secretName: elasticsearch-master-certs
path: /usr/share/logstash/certs
resources:
requests:
cpu: "100m"
memory: "536Mi"
limits:
cpu: "1000m"
memory: "1536Mi"
extraInitContainers:
- name: cert
image: busybox
command: ['sh','-c','mkdir -p /usr/share/logstash/certs']
securityContext:
runAsUser: 0
logstash.yml
xpack 모니터링 설정 및 elasticsearch 인증정보입니다.
xpack.monitoring.enabled: true
xpack.monitoring.elasticsearch.username: elastic
xpack.monitoring.elasticsearch.password: sessacteam
xpack.monitoring.elasticsearch.hosts: ["https://elasticsearch-master:9200"]
xpack.monitoring.elasticsearch.ssl.certificate_authority: "/usr/share/logstash/certs/ca.crt"
xpack.monitoring.elasticsearch.ssl.verification_mode: none
xpack.monitoring.elasticsearch.sniffing: true
http.host: "0.0.0.0"
logstash.conf
input {
beats {
port => 5044
}
}
filter {
if [fields][log] == "access_log" {
grok {
match => {
"message" => '%{IPORHOST:clientip} %{HTTPDUSER:ident} %{HTTPDUSER:auth} \[%{HTTPDATE:timestamp}\] "(?:%{WORD:verb} %{NOTSPACE:request}(?: HTTP/%{NUMBER:httpversion})?|%{DATA:rawrequest})" (?:-|%{NUMBER:response}) (?:-|%{NUMBER:bytes})'
}
}
date {
match => ["timestamp", "dd/MMM/yyyy:HH:mm:ss Z"]
}
}
if [fields][log] == "catalina_log" {
grok {
match => {
"message" => "%{MONTHDAY:day}-%{WORD:month}-%{YEAR:year} %{TIME:time} %{DATA:level} \[%{DATA:request}\] %{JAVACLASS:class} %{JAVALOGMESSAGE:logmessage}"
}
}
mutate {
add_field => {
"timestamp_combined" => "%{day}-%{month}-%{year} %{time}"
}
}
date {
match => ["timestamp_combined", "dd-MMM-yyyy HH:mm:ss.SSS"]
target => "@timestamp"
}
mutate {
remove_field => ["timestamp_combined"]
}
}
}
output {
if [fields][log] == "access_log"{
elasticsearch {
hosts => ["https://elasticsearch-master:9200"]
cacert => "/usr/share/logstash/certs/ca.crt"
user => "elastic"
password => "sessacteam"
index => "acc_log-%{+yyyy.MM.dd}"
}
}
if [fields][log] == "catalina_log" {
elasticsearch {
hosts => ["https://elasticsearch-master:9200"]
cacert => "/usr/share/logstash/certs/ca.crt"
user => "elastic"
password => "sessacteam"
index => "catlina_log-%{+yyyy.MM.dd}"
}
}
}
input: logstash의 input을 지정합니다. filebeat를 input으로 사용합니다.
filter: 로그 데이터를 수집하여 처리하고, 이를 분석하고 가공하여 Elasticsearch, Kafka, 또는 다른 저장소로 보내기 전에 데이터를 변환하는 역할을 합니다.
- filebeat로부터 전달받은 로그의 fileds 값으 로그를 식별합니다.
- Grok : Grok 필터는 일반 텍스트로 되어 있는 로그 메시지를 구조화된 데이터로 파싱할 때 사용됩니다. 이를 위해 정규 표현식을 기반으로 패턴을 지정하여 로그 메시지를 필드로 분리합니다.
- Date: Date 필터는 로그 메시지에서 날짜 및 시간 정보를 파싱하여 Logstash 이벤트에 타임스탬프를 추가합니다. 이를 통해 Elasticsearch 등에서 로그 데이터를 시간 기준으로 정렬하고 분석할 수 있습니다.
- Mutate: Mutate 필터는 이벤트에 대한 필드 추가, 삭제, 수정 등의 작업을 수행합니다. 예를 들어, 필드 이름 변경, 필드 유형 변경, 필드 값의 trim 등을 수행할 수 있습니다.
output: Logstash에서 처리한 로그를 보낼 곳을 지정합니다. 필터링된 각각의 로그를 index를 지정하여 Elasticsearch로 전송합니다.
Elasticsearch
Elasticsearch 또한 Elastic사에서 제공하는 helm chart를 이용했습니다. 기본적으로 클러스터 구성을 제공하지만 현재 상황에선 컴퓨팅 리소스를 많이 잡아먹기에 단일 노드로 구성했습니다. Elasticsearch는 검색기능을 지원하는 데이터베이스로 스토리지가 필요합니다. StatefulSet 오브젝트로 배포되며 PVC를 통해 동적으로 스토리지를 할당받도록 구성했습니다.
다음은 수정한 values 파일의 일부입니다.
volumeClaimTemplate:
accessModes: ["ReadWriteOnce"]
storageClassName: sessac-sc
resources:
requests:
storage: 30Gi
Elasticsearch 접속 테스트
Elasticsearch는 Restful API를 이용하여 HTTPS 요청을 보낼 수 있습니다. 우선 접근하기 위해 인증정보가 필요합니다.
helm 차트를 통해 배포하게되면 다음과 같은 두개의 secret 오브젝트가 생성됩니다.
- elasticsearch-master-certs : ssl/tls 암호화를 위한 인증서 정보를 저장하고 있는 secret 오브젝트 입니다. 과거 elasticsearch 7 버전에선 http로 통신이 가능했으나 최신버전에선 https 통신을 권장하며 기본적으로 ssl 인증서가 생성되는 것 같습니다.
- elasticsearch-master-credentials : elasticsearch에대한 인증정보를 저장하고 있는 secret 오브젝트입니다. password와 username 정보를 담고 있습니다.
Secret 오브젝트는 base64로 인코딩 된 값을 저장하고있기 때문에 다음 명령어를 통해 디코딩하여 로그인 정보를 확인할 수 있습니다.
kubectl get secret elasticsearch-master-credentials -o jsonpath='{.data.username}' | base64 -d
kubectl get secret elasticsearch-master-credentials -o jsonpath='{.data.password}' | base64 -d
앞서 언급한 것 처럼 ssl/tls 암호화가 되어 있기 때문에 https 접속하기위해 공개키가 필요합니다 접속에 필요한 공개키를 elasticsearch-master-certs에서 가져올 수 있습니다. 다음 명령어를 통해 공개키를 ca.crt 라는 이름으로 저장할 수 있습니다.
kubectl get secret elasticsearch-master-certs -o jsonpath='{.data.ca\.crt}' | base64 -d > ca.crt
다음 명령을 이용해 현재 Elasticsearch cluster 상태를 확인할 수 있습니다.
curl https://10.44.0.6:9200 --cacert ca.crt -k -u elastic
{
"name" : "elasticsearch-master-0",
"cluster_name" : "elasticsearch",
"cluster_uuid" : "uAEPMB30TbSo9ALpgV5Dhw",
"version" : {
"number" : "8.5.1",
"build_flavor" : "default",
"build_type" : "docker",
"build_hash" : "c1310c45fc534583afe2c1c03046491efba2bba2",
"build_date" : "2022-11-09T21:02:20.169855900Z",
"build_snapshot" : false,
"lucene_version" : "9.4.1",
"minimum_wire_compatibility_version" : "7.17.0",
"minimum_index_compatibility_version" : "7.0.0"
},
"tagline" : "You Know, for Search"
}
Kibana
kibana또한 Elastic에서 제공하는 helm chart를 이용해 배포했습니다. 관리의 편의를 위해 base url을 /kibana로 커스텀하였습니다. 다음은 설치에 사용한 values 파일 일부입니다.
kibanaConfig:
kibana.yml: |
server.name: kibana
server.basePath: "/kibana"
server.rewriteBasePath: true
server.host: "0.0.0.0"
server.shutdownTimeout: "5s"
elasticsearch.hosts: [ "http://elasticsearch:9200" ]
monitoring.ui.container.elasticsearch.enabled: true
data view
인덱스 패턴은 Elasticsearch에 저장된 데이터를 Kibana에서 data view를 만들 때 사용하는 패턴입니다. 인덱스 패턴을 데이터가 저장되어 있는 인덱스를 지정하여 데이터를 탐색하고 시각화하는데 사용합니다.
access_log data view
인덱스 패턴에 acc_log* 을 입력해 Elasticsearch에 저장된 모든 access_log 인덱스가 포함될 수 있도록 합니다. 타임스탬프 필드를 선택하면 대시보드에서 시각화를 할 때 타임스탬프를 기반으로 시계열로 나타낼 수 있습니다.
catalina_log data view
인덱스 패턴에 catalina_log* 을 입력해 Elasticsearch에 저장된 모든 catalina_log 인덱스가 포함될 수 있도록 합니다.
KQL 쿼리를 이용한 데이터 검색
KQL(Kibana Query Language)은 데이터 필터링을 위한 간단한 텍스트 기반 쿼리 언어입니다. KQL을 사용하여 필드 값이 존재하거나 지정된 값과 일치하거나 지정된 범위 내에 있는 데이터를 검색할 수 있습니다.
예를 들어, 로그레벨이 WARNING인 데이터만 검색하려면 level.keyword : “WARNING” 으로 검색할 수 있습니다.
'ELK' 카테고리의 다른 글
ElasticSearch - REST API (1) | 2023.12.09 |
---|---|
ElaticSearch - cluster 설정 및 role (0) | 2023.12.09 |
ELK on Kubernetes - Kibana (0) | 2023.12.07 |
ELK on Kubernetes - Logstash (4) | 2023.12.07 |
ELK on Kubernetes - ElasticSearch (2) | 2023.12.06 |