부제 : 온갖 예외 케이스를 가정하여 사용자 로그의 S3 경로 잡기
1. Fluent Bit에서 S3으로 output 보내기
Fluent Bit 에서 S3 output을 쓰는 건 쉽다.
[OUTPUT]
Name s3
Match *
bucket my-bucket
region us-west-2
s3_key_format /$TAG[2]/$TAG[0]/%Y/%m/%d/$UUID.gz
s3_key_format_tag_delimiters .-
이렇게 s3 output 설정을 넣어주고 butket과 key format을 지정해주면 된다.
나의 경우는 EKS 안의 애플리케이션에서 발생한 사용자 로그를 시스템 로그(var/log/containers/$podname~.log 식으로 저장되는 파드의 로그)와 분리하여 S3 경로를 잡아주려고 했는데,
그러려면 먼저 애플리케이션의 사용자 로그가 저장되는 폴더를 var/log/containers 하위의 특정 폴더에 잡아주어야 한다.
2. Kubernetes 애플리케이션의 Deployment 설정 - 폴더 마운트
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: demo
name: springboot-logger
namespace: test
spec:
selector:
matchLabels:
app: demo
template:
metadata:
labels:
app: demo
spec:
containers:
- image: test-image
name: kubernetes-spring
env:
- name: pod_namespace
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: pod_name
valueFrom:
fieldRef:
fieldPath: metadata.name
volumeMounts:
- name: testmount
mountPath: /userlog
subPath: userlog
imagePullPolicy: Always
ports:
- containerPort: 80
terminationGracePeriodSeconds: 30
volumes:
- name: testmount
hostPath:
path: /var/log/containers
spec.template.spec.containers.volumeMounts 의 mountpath를 애플리케이션의 사용자 로그가 저장될 폴더로 잡아준다.
name은 아래의 spec.template.spec.volumes 의 name과 통일한다.
spec.template.spec.volumes의 path를 /var/log/containers로 잡아주면 워커노드의 /var/log/containers/userlog-위에서 마운트한 경로- 아래에 사용자 로그들이 저장된다.
3. 스프링부트 애플리케이션의 로그가 특정 폴더에 저장되게 하기
<configuration>
<property name="LOG_PATTERN" value="* %-30(%date [%thread]) %-5level %marker|%logger{36}: %msg%n" />
<property name="LOG_BASEDIR" value="userlog"/>
<appender name="appLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_BASEDIR}/test_app+${pod_namespace}+${pod_name}.log</file>
<encoder>
<pattern>${LOG_PATTERN}</pattern>
</encoder>
</appender>
</configuration>
logback에서 RollingFileAppender를 사용하면서, file이 저장되는 directory를 아까 마운트한 폴더인 userlog로 잡아주면
사용자 로그들이 userlog 안에 저장된다.
<file>${LOG_BASEDIR}/test_app+${pod_namespace}+${pod_name.log</file>
${pod_namespace}과 ${pod_name}은 Kubernetes에서 스프링부트 애플리케이션을 만들 때 세팅해준 환경변수로, 로그가 쌓일 때는 환경변수가 대신 들어간다.
+를 넣은 이유는 Fluent Bit의 특성 때문인데, 아래에서 자세히 설명하겠다.
4. Fluent Bit에서 로그 수집 시 TAG 변환하기
[INPUT]
Name tail
Tag kube.*
Path /var/log/containers/*/test*.log
Parser docker
DB /var/log/flb_kube.db
Mem_Buf_Limit 50MB
Skip_Long_Lines On
Refresh_Interval 10
TAG는 Fluent Bit에서 Input 값을 kube.* 처럼 .*을 붙이면 파일의 경로~파일 이름 전체가 된다. ( / 대신에 .로 구분된다.)
위에서 쌓이는 로그의 TAG는 kube.var.log.containers.userlog.test_app+${pod_namespace}+${pod_name}.log 이다.
[FILTER]
Name rewrite_tag
Match kube.*
Rule $key (.*) $TAG[5] false
Emitter_Name emitter
rewrite_tag filter를 사용하여 TAG를 새로 지정해줄 수 있는데, 기존 TAG는 .를 구분자로 인덱스화 할 수 있다.
.으로 TAG를 나누면 TAG[5]는 test_app+${pod_namespace}+${pod_name} 이 된다.
Rule $sample_key (.*) $TAG[5] false 는
로그 안의 key값이 "sample_key" 인 항목의 값이 존재하는 로그에 한해,
기존 TAG를 TAG[5]로 바꿔주고, 기존 TAG가 있던 로그는 삭제한다는 설정이다.
그러면 로그는 test_app+${pod_namespace}+${pod_name} 의 TAG로 쌓인다.
5. TAG를 파싱하여 S3 output 경로 잡기
[OUTPUT]
Name s3
Match test*
bucket test-bucket
region ap-northeast-2
s3_key_format /fluent-bit/eks-$TAG[1]/$TAG[2]/%Y/%m/%d/$TAG[0]_$UUID.txt.gz
s3_key_format_tag_delimiters +
upload_timeout 30m
s3 output 설정에서 새 태그를 또 특정 구분자(delimiter) 로 나눌 수 있는데, +을 사용한 이유는
kubernetes의 podname은 podname-123456 식으로 중간에 - 가 들어가고,
.이나 _ 같은 일반적인 구분자는 개발자들이 사용자 로그의 이름을 지정하면서 들어갈 수도 있는, 내가 통제할 수 있는 영역이 아니기 때문이다.
(만약의 상황에서, 개발자가 test_app 대신 test_nginx_app과 같이 _를 하나 더 써버리면 TAG의 인덱스가 달라지고, 그러면 s3에 저장되는 폴더의 규칙이 어그러진다.)
그래서 +를 사용하였는데, 사실 개발자들이 네이밍을 철저히 한다면 _를 사용해도 된다.
하지만 여기서 복병이 하나 더 있었으니.....
6. 사용자 로그 파일 설정에서의 예외 상황 가정
개발자들이 test_app+${pod_namespace}+${pod_name}.log 형식을 쓰지 않고 test_app.log와 같이 남겨버릴 수도 있다.
이 경우는 그나마 무난한데, 그래도 test_app이 TAG 전체로 인식되어
/fluent-bit/eks-$TAG[1]/$TAG[2]/%Y/%m/%d/$TAG[0]_$UUID.txt.gz 와 같은 output 설정에서도 S3에 들어가긴 하기 때문이다.
(TAG[1]과 TAG[2]를 뒤에 '[1]' 과 '[2]' 같은 string이 추가된 걸로 인식하여,
/fluent-bit/eks-test_app[1]/test_app[2]/%Y/%m/%d/test_app[0]_$UUID.txt.gz 과 같은 형식으로 s3에 저장된다.)
하지만 test_app+${pod_name}.log 같이 ${pod_namespace}를 로그 이름 설정에서 빼먹었거나
test_app++${pod_name}.log 같이 ${pod_namespace}가 들어갈 자리가 빈 경우, (만일의 경우 이런 상황도 있을 수 있으니까...)
<Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message>
에러가 뜨면서 s3에 파일이 저장되지 않는다.
+로 인덱스를 구분해주는데, [1]번째 인덱스는 있어도 [2]번째 인덱스가 없기 때문이다.
이거 때문에 엄청 고생했다....
그리고 좋은 방법은 아닐지라도, 찾은 해결책은
애플리케이션에서 로그 파일을 설정해줄 때
<file>${LOG_BASEDIR}/test_app+ns_${pod_namespace}+pn_${pod_name}.log</file>
와 같이 앞에 ns_ , pn_ 과 같은 문자열을 넣어주는 것이다.
이러면 ${pod_namespace}가 애플리케이션에 따라 빈 값일 경우에도
/fluent-bit/eks-ns_/ps_podname/%Y/%m/%d/test_app_$UUID.txt.gz 과 같은 형태로 로그가 쌓인다.
이상으로 며칠을 삽질한.... 예외상황을 가정한 Fluent Bit에서 S3으로 output path 잡기 글을 마친다.
'인프라,데이터 > Fluentd, Fluent Bit' 카테고리의 다른 글
Fluent Bit의 버퍼링Buffering (번역) (0) | 2022.03.20 |
---|---|
Using Lua filter to find if log matches a pattern, and remove last n characters. (0) | 2022.03.02 |
Remove last n characters using Regex & Fluent Bit (0) | 2022.03.02 |
Fluent Bit Stream Processor 사용하여 데이터 특정 조건에 따라 다른 아웃풋으로 보내기 (0) | 2022.01.26 |
Fluent Bit에서 특정 경로의 로그 파일 받아와 쿠버네티스 메타데이터 추가하기 (0) | 2022.01.25 |