AWS EKS 로그 관리...다들 하고 계시죠? - AWS EKS에서 효과적인 로그 관리 Fluent Bit으로 시작하기

Written by Minhyeok Cha
예전에 공부하려고 작성한 노트들을 정리하다 EKS 관련된 글을 발견했습니다. 완전 주니어때 정리한 거라 제가 이해하고 작성한 건지도 모르겠더라고요. 그래서 다시 복습도 할 겸, 블로그 소재로 삼을 겸 하나 들고 왔습니다.
간단한 아키텍처로 먼저 소개하겠습니다.

nginx이미지는 그냥 로그 테스트용으로 넣었습니다. 사실 이게 없으면 아키텍처가 너무 심심해 보여서 억지로 끼워 넣은 거라고 봐도 무방합니다.
이제 Fluent-bit이라는 새 친구를 통해 로그 가져다가 OpenSearch로 보내는 과정을 살펴보려고 합니다.
Fluent-bit이란?
본격적으로 시작하기 전에 Fluent-bit가 무엇을 하는 친구인지 알아보겠습니다.
Fluentd는 사실 많이 들었던 EFK (Elasticsearch, Fluentd, Kibana)에서 F를 담당하는 친구입니다. 이 친구는 EFK의 하나인 만큼 많은 곳에서 신뢰를 얻는 오픈소스 로그 수집기입니다.
그리고 오늘 사용할 Fluent Bit는 최대한 가볍게 사용하기 위해 만들어졌습니다.
Fluentd와 Fluent-bit의 차이점
Fluentd | Fluent Bit | |
리소스 사용 | 많은 리소스를 사용합니다. (일반적으로 수십 MB의 메모리) | 매우 경량화되어 있어 메모리 사용량이 적습니다.(보통 수 MB) |
기능성 | 플러그인과 확장 기능을 제공하여 복잡한 로그 처리 시나리오를 지원합니다. | 기본적인 로그 수집 및 전송 기능에 초점을 맞춥니다. |
구성 복잡성 | 더 복잡한 구성이 가능하며, 고급 기능을 위해서는 더 많은 설정이 필요할 수 있습니다. | 비교적 간단한 구성으로 시작할 수 있습니다. |
💡 Fluent Bit이 더 가볍고 사용이 용이하다고 합니다.
데모 시작
1. EKS 클러스터 생성, Opensearch 도메인 생성
💡 이 부분(EKS, 도메인 생성)은 주요 내용이 아니라 패스하겠습니다.
2. OIDC 생성
# eksctl로 만드는 방법
eksctl utils associate-iam-oidc-provider \
--cluster eks \
--approve

IAM 역할에 연결할 정책 생성
fluent-bit-policy
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"es:ESHttp*"
],
"Resource": "arn:aws:es:${AWS_REGION}:${ACCOUNT_ID}:domain/${ES_DOMAIN_NAME}",
"Effect": "Allow"
}
]
}
위에서 만든 OIDC와 정책을 가지고 iamserviceaccount 생성
kubectl create namespace log
eksctl create iamserviceaccount \
--name fluent-bit \
--namespace log \
--cluster eks-logging \
--attach-policy-arn "arn:aws:iam::${ACCOUNT_ID}:policy/fluent-bit-policy" \
--approve \
--override-existing-serviceaccounts
💡 콘솔로 가서 생성된 역할 및 적용된 정책, Opensearch 도메인 등이 잘 적용되었는지 확인합니다.
3. OpenSearch 액세스 권한 부여
사전에 생성된 혹은 기존에 있는 오픈서치 ID와 PW를 변수로 넣고 다음 명령어를 사용합니다.
curl -sS -u " ${ES_DOMAIN_USER} : ${ES_DOMAIN_PASSWORD} " \
-X PATCH \ "https://{opensearchendpoint}/_opendistro/_security/api/rolesmapping/all_access?pretty" \
-H 'Content-Type: application/json' \
-d '[
{
"op": "add", "path": "/backend_roles", "value": ["'iamsa:role:arn'"]
}
]'
4. Fluent Bit 설치
Fluent Bit은 쿠버네티스 기준으로 config 설정과 데몬셋을 배포해 주면, 완료입니다.
먼저 cluster role을 만들고 service account와 바인딩 작업을 해주는 파일을 하나 작성합니다.
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: fluent-bit-read
rules:
- apiGroups: [""]
resources:
- namespaces
- pods
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: fluent-bit-read
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: fluent-bit-read
subjects:
- kind: ServiceAccount
name: fluent-bit
namespace: log
ConfigMap입니다.
---
apiVersion: v1
kind: ConfigMap
metadata:
name: fluent-bit-config
namespace: logging
labels:
k8s-app: fluent-bit
data:
fluent-bit.conf: |
[SERVICE]
Flush 1
Log_Level info
Daemon off
Parsers_File parsers.conf
HTTP_Server On
HTTP_Listen 0.0.0.0
HTTP_Port 2020
@INCLUDE input-kubernetes.conf
@INCLUDE filter-kubernetes.conf
@INCLUDE output-elasticsearch.conf
input-kubernetes.conf: |
[INPUT]
Name tail
Tag kube.*
Path /var/log/nginx/access.log
Parser docker
DB /var/log/flb_kube.db
Mem_Buf_Limit 50MB
Skip_Long_Lines On
Refresh_Interval 10
filter-kubernetes.conf: |
[FILTER]
Name kubernetes
Match kube.*
Kube_URL https://kubernetes.default.svc:443
Kube_CA_File /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
Kube_Token_File /var/run/secrets/kubernetes.io/serviceaccount/token
Kube_Tag_Prefix kube.var.log.containers.
Merge_Log On
Merge_Log_Key log_processed
K8S-Logging.Parser On
K8S-Logging.Exclude Off
output-elasticsearch.conf: |
output-opensearch.conf: |
[OUTPUT]
Name es
Match *
Host "오픈서치 도메인"
Port 443
TLS On
AWS_Auth On
AWS_Region ap-northeast-2
Index "오픈서치에 들어갈 인덱스"
Replace_Dots On
Suppress_Type_Name On
제가 config 부분에서 애를 먹었는데, output에서 "Suppress_Type_Name"을 켜야 합니다. 처음에 설치 후 로그를 쏴보니 Fluent Bit에서 OpenSearch로 보낼 수 없다는 오류만 나와서 역할 바인딩 이슈의 문제인 줄 알았습니다.
삽질하여 찾아본 결과, Fluent Bit의 설정이 최신 OpenSearch 버전과 호환되지 않아 그런 거더라고요. 따라서 위와 같이 configmap 설정을 하여 로그 전송에 성공하였습니다.
parser.conf 파일도 공식 링크에 잘 작성되어 있으며, 저희는 테스트를 nginx로 진행할 예정이라 다음과 같이 사용했습니다.
parsers.conf: |
[PARSER]
Name nginx
Format regex
Regex ^(?<remote>[^ ]*) (?<host>[^ ]*) (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^\"]*?)(?: +\S*)?)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")
Time_Key time
Time_Format %d/%b/%Y:%H:%M:%S %z
[PARSER]
Name json
Format json
Time_Key time
Time_Format %d/%b/%Y:%H:%M:%S %z
[PARSER]
Name docker
Format json
Time_Key time
Time_Format %Y-%m-%dT%H:%M:%S.%L
Time_Keep On
Deamonset 입니다.
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluent-bit
namespace: log
labels:
k8s-app: fluent-bit-logging
version: v1
kubernetes.io/cluster-service: "true"
spec:
selector:
matchLabels:
k8s-app: fluent-bit-logging
template:
metadata:
labels:
k8s-app: fluent-bit-logging
version: v1
kubernetes.io/cluster-service: "true"
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "2020"
prometheus.io/path: /api/v1/metrics/prometheus
spec:
containers:
- name: fluent-bit
image: amazon/aws-for-fluent-bit:2.5.0
imagePullPolicy: Always
ports:
- containerPort: 2020
volumeMounts:
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
- name: fluent-bit-config
mountPath: /fluent-bit/etc/
terminationGracePeriodSeconds: 10
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
- name: fluent-bit-config
configMap:
name: fluent-bit-config
serviceAccountName: fluent-bit
남은 부분은 제가 저번에 작성한 데이터 파이프라인 구축 중 logstash와 비슷한 맥락의 파일입니다.
정확한 설명 참고는 공식 문서를 읽어보면 좋을 것 같습니다.
5. 테스트 이미지 배포 및 Fluent Bit 로그 확인
# 데모 nginx pod 만들기
kubectl run nginx --image=nginx -n default
# nginx에 10개의 HTTP GET 요청을 하는 루프
for i in {1..10}; do
kubectl exec -it nginx -n default -- curl -s http://{파드 IP 조회해서 기입} | grep -q . && echo "Request $i: Success" || echo "Request $i: Failure"
done
# nginx pod 로그 확인
kubectl logs nginx -n default
# fluent bit 로그 적재 및 전달 확인
kubectl logs (fluent bit pod name) -n log
Opensearch 도메인 접속 및 로그 확인
Opensearch에서는 패턴을 만들어야 대시보드에서 로그를 간편하게 볼 수 있습니다.



💡 nginx에 쏜 로그가 전달된 것을 확인할 수 있습니다.
마무리
Fluent Bit이 컨테이너에서 주로 쓰는 로그 수집기로 유명해 AWS OpenSearch와 EKS 클러스터를 통합하는 데모 세션을 EKS 복습 겸 진행해 보았습니다.
EKS는 manifest 파일로 운용되는 느낌이 강해서 다시 보니 yaml 파일만 잔뜩 있어 읽기가 귀찮네요.
그래서 더 쉬운 방법이 없나 찾아봤는데, 요즘엔 이런 것도 있더라고요.
이 글을 보시는 분들은 helm 사용법을 찾아보시면 좋을 것 같습니다.
그리고 개인적으로 궁금했던 Logstach가 컨테이너에 못 쓰는 이유를 찾아봤습니다. 사이즈가 무거운 것도 있지만 핵심 기능 중 하나인 영구 큐 같은 기능이 스테이트리스 컨테이너 환경에서 사용이 힘들다고 하네요.
이번에 컨테이너 로그 구축을 하며 느낀 점은, 뭐든 하기 전에 공식 문서를 먼저 읽는 게 좋다는 것입니다. 파일 하나하나 읽어가면서 오랜만에 삽질도 하고 눈도 아팠네요. 여러분들은 이점을 참고하셔서 눈 아프지 마시길 바랍니다.
Comments