Cloudnet AWES 4주차 스터디를 진행하며 정리한 글입니다.
이번 포스팅에서는 EKS 환경의 로깅에 대해서 포스팅하도록 하겠습니다.
EKS 컨트롤 플레인 로그
EKS에서 컨트롤 플레인에 대한 정보는 사용자에게 추상화되어있어서 알기 어렵습니다. EKS에서 컨트롤 플레인에 대한 로깅은 기본적으로 비활성화되어있는데, 이를 AWS Cloudwatch logs로 전송하여 Kubernetes API 서버 및 컨트롤 플레인 컴포넌트들의 로그를 활성화하도록 하겠습니다.

EKS에서 5가지 컨트롤 플레인에서 대한 로그를 제공합니다.
- api : Kubernetes API 서버의 요청 및 응답 로그
- audit : Kubenetes 감사(Audit) 로그
- authenticator : IAM 인증 관련 로그 (aws-iam-authenticator
- controllerManager : Kubernetes 컨트롤러 매니저 관련 로그
- scheduler : Kubernetes 스케줄러 관련 로그
기본적으론는 모두 꺼져있어 아래 작업처럼 수동으로 활성화 시켜줘야하며, 컨트롤 플레인 로깅을 활성화하면 CloudWatch Logs 사용량에 따라 요금이 발생할 수 있으므로, 필요 없는 로그는 비활성화하는 것이 좋습니다.
++ Amazone Cloudwatch란?
AWS 리소스 및 AWS에서 실행되는 어플리케이션을 실시간 모니터링이 가능하도록 지원하는 솔루션입니다.
Cloudwatch를 사용하여 리소스 및 어플리케이션에 대해 측정할 수 있느 변수인 지표를 수집하고 추적할 수 있습니다.
https://docs.aws.amazon.com/ko_kr/AmazonCloudWatch/latest/monitoring/WhatIsCloudWatch.html
Amazon CloudWatch란 무엇인가요? - Amazon CloudWatch
이 페이지에 작업이 필요하다는 점을 알려 주셔서 감사합니다. 실망시켜 드려 죄송합니다. 잠깐 시간을 내어 설명서를 향상시킬 수 있는 방법에 대해 말씀해 주십시오.
docs.aws.amazon.com
기존 EKS 클러스터에서 모든 로깅을 활성화하겠습니다.
❯ aws eks update-cluster-config --region ap-northeast-2 --name $CLUSTER_NAME \
--logging '{"clusterLogging":[{"types":["api","audit","authenticator","controllerManager","scheduler"],"enabled":true}]}'
{
"update": {
"id": "e749911e-7774-3b90-a3ed-d4b151c8ddcb",
"status": "InProgress",
"type": "LoggingUpdate",
"params": [
{
"type": "ClusterLogging",
"value": "{\"clusterLogging\":[{\"types\":[\"api\",\"audit\",\"authenticator\",\"controllerManager\",\"scheduler\"],\"enabled\":true}]}"
}
],
"createdAt": "2025-03-02T00:47:59.197000+09:00",
"errors": []
}
}

로깅이 활성화되면 CloudWatch에서 해당 로그를 확인할 수 있습니다.
++) 로그 그룹이란?
AWS Cloudwatch Logs에서 로그 스트림을 저장하고 관리하는 단위입니다.
EKS에서 컨트롤 플레인 로깅을 활성화하면 로그 그룹이 자동으로 생성됩니다.

로그 그룹 (Log Group) # 여러 개의 로그 스트림 포함
├── 로그 스트림 (Log Stream) # 시간 순으로 저장된 로그 이벤트들의 모임
│ ├── 로그 이벤트 (Log Event) # 실제 개별적인 메시지 포함
│ ├── 로그 이벤트 (Log Event)
│ ├── ...
│
├── 로그 스트림 (Log Stream)
│ ├── 로그 이벤트 (Log Event)
│ ├── ...
│
├── 로그 스트림 (Log Stream)
│ ├── 로그 이벤트 (Log Event)
│ ├── ...
❯ aws logs describe-log-groups | jq
{
"logGroups": [
{
"logGroupName": "/aws/eks/eks-console-automode/cluster",
"creationTime": 1738998815962,
"metricFilterCount": 0,
"arn": "arn:aws:logs:ap-northeast-2:xxx:log-group:/aws/eks/eks-console-automode/cluster:*",
"storedBytes": 16658665,
"logGroupClass": "STANDARD",
"logGroupArn": "arn:aws:logs:ap-northeast-2:xxx:log-group:/aws/eks/eks-console-automode/cluster"
},
{
"logGroupName": "/aws/eks/myeks/cluster",
"creationTime": 1740844090723,
"metricFilterCount": 0,
"arn": "arn:aws:logs:ap-northeast-2:xxx:log-group:/aws/eks/myeks/cluster:*",
"storedBytes": 0,
"logGroupClass": "STANDARD",
"logGroupArn": "arn:aws:logs:ap-northeast-2:xxx:log-group:/aws/eks/myeks/cluster"
}
]
}
로그를 확인해보겠습니다.
# follow 옵션을 주면 실시간 로그를 바로 출력할 수 있습니다.
❯ aws logs tail /aws/eks/$CLUSTER_NAME/cluster --follow
2025-03-01T15:48:11.801000+00:00 kube-apiserver-audit-bf7fa45ae808536e5c93358852add1a1 {"kind":"Event","apiVersion":"audit.k8s.io/v1","level":"Metadata","auditID":"1e533d1a-22ec-4ea6-be25-e8d61729c167","stage":"ResponseComplete","requestURI":"/apis/coordination.k8s.io/v1/namespaces/kube-system/leases/eks-coredns-autoscaler","verb":"get","user":{"username":"eks:coredns-autoscaler","groups":["system:authenticated"]},"sourceIPs":["10.0.180.168"],"userAgent":"controller/v0.0.0 (linux/amd64) kubernetes/$Format/leader-election","objectRef":{"resource":"leases","namespace":"kube-system","name":"eks-coredns-autoscaler","apiGroup":"coordination.k8s.io","apiVersion":"v1"},"responseStatus":{"metadata":{},"code":200},"requestReceivedTimestamp":"2025-03-01T09:34:31.582714Z","stageTimestamp":"2025-03-01T09:34:31.587464Z","annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":"RBAC: allowed by RoleBinding \"eks:coredns-autoscaler/kube-system\" of Role \"eks:coredns-autoscaler\" to User \"eks:coredns-autoscaler\""}}
2025-03-01T15:48:11.801000+00:00 kube-apiserver-audit-bf7fa45ae808536e5c93358852add1a1 {"kind":"Event","apiVersion":"audit.k8s.io/v1","level":"Metadata","auditID":"99780557-8244-477c-9ed4-373eb3c94726","stage":"ResponseComplete","requestURI":"/apis/coordination.k8s.io/v1/namespaces/kube-system/leases/kube-controller-manager?timeout=5s","verb":"get","user":{"username":"system:kube-controller-manager","groups":["system:authenticated"]},"sourceIPs":["10.0.180.168"],"userAgent":"kube-controller-manager/v1.31.6 (linux/amd64) kubernetes/7555883/leader-election","objectRef":{"resource":"leases","namespace":"kube-system","name":"kube-controller-manager","apiGroup":"coordination.k8s.io","apiVersion":"v1"},"responseStatus":{"metadata":{},"code":200},"requestReceivedTimestamp":"2025-03-01T09:34:31.794155Z","stageTimestamp":"2025-03-01T09:34:31.798149Z","annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":"RBAC: allowed by ClusterRoleBinding \"system:kube-controller-manager\" of ClusterRole \"system:kube-controller-manager\" to User \"system:kube-controller-manager\""}}
...
EKS 콘솔에서 api 서버 로그 보기를 클릭하니 Cloudwatch 콘솔로 접속되었습니다.

왼쪽 탭에서 Logs Insights라는 로그를 대화식으로 검색하고 분석할 수 있는 기능을 활용하여 쿼리를 사용하여 로그를 확인해보겠습니다.


로깅을 종료하겠습니다.
❯ eksctl utils update-cluster-logging --cluster $CLUSTER_NAME --region ap-northeast-2 --disable-types all --approve
❯ aws logs delete-log-group --log-group-name /aws/eks/$CLUSTER_NAME/cluster
EKS 데이터 플레인 로그
EKS에서 컨트롤 플레인 로깅은 직접 수집해야합니다.
데이터 플레인 로그 수집 방법에는 여러 가지가 있지만, 일반적으로 Fluent Bit, Fluentd, Opentelemetry Collector, Promtail이 있습니다.
(AWS 공식문서에는 Fluent Bit를 활용한 로깅에 대해 소개하였습니다.)
Fluent Bit를 DaemonSet로 설정하여 CloudWatch Logs에 로그 전송 - Amazon CloudWatch
Container Insights에 이미 FluentD가 구성되어 있고 FluentD DaemonSet가 예상대로 실행되지 않는 경우(containerd 런타임을 사용하는 경우 발생할 수 있음), Fluent Bit를 설치하기 전에 Fluent Bit를 제거해야 Fluent
docs.aws.amazon.com
AWS에서 제공하는 방식으로 Fluent Bit를 이용하여 EKS 파드의 로그를 수집해보도록 하겠습니다.
이때, Fluent Bit는 컨테이너를 데몬셋으로 동작시키고, 데이터 플레인의 3가지 로그를 CloudWatch Logs에 전송합니다.

- 각 컨테이너/파드 로그 (/var/log/containers),
- 노드(호스트) 로그 (/var/log/dmesg, /var/log/secure, /var/log/messages)
- 쿠버네티스 데이터플레인 로그 (/var/log/journal <- kubelet.service, kubeproxy.service, docker.service)
이 때 로그는 Fluent Bit만으로도 충분한데 왜 Cloudwatch agent까지 설치할까요?
바로, Fluent Bit만 사용하면 Cloud Watch Logs에서 원본 로그만 저장할 수 있지만, Cloudwatch Container insights를 활성화하면, 메트릭과 로그를 통합하여 분석이 가능하기 때문입니다.
Cloudwatch에 EKS 클러스터 대시보드가 생성되어 클러스터, 노드, 파드의 CPU, Memory 등 메트릭 값이 시각화되고, 로그와 메트릭을 통합하여 분석할 수 있습니다.
++ Amazone Cloudwatch Container Insight
컨테이너형 어플리케이션 및 마이크로 서비스에 대한 모니터링, 트러블 슈팅 및 알람을 위한 관리형 솔루션입니다.
설정을 하지 않았을 때는 다음과 같이 보입니다.

Fluent Bit이 로그를 수집하는 방법
Fluent Bit은 노드에 모두 하나씩 뜨는 Daemonset으로 배포됩니다.
이때 Kubernetes의 로그 파일을 직접 읽어와서 수집하는 방식으로, tail이나 systemd 플러그인을 사용하여 로그를 가져옵니다.
1) 파드 로그 수집 (tail 플러그인)
- /var/log/containers/*.log 경로에서 컨테이너 표준 출력 로그 수집
- Kubernetes 메타데이터(파드명, 네임스페이스 등) 추가 후 전송
2) 노드 로그 수집 (systemd 플러그인)
- /var/log/journal/ 경로에서 kubelet, docker, containerd 서비스 로그 수집
3) 로그 필터링 및 전송 (kubernetes 필터, cloudwatch_logs 출력 플러그인)
- 필터 적용 후 CloudWatch 등으로 전송
일부 Fluent Bit 설정에서는 docker.sock을 마운트하여 컨테이너 정보를 수집하는데, docker.sock은 호스트의 Docker 데몬과 직접 통신할 수 있는 Unix 소켓으로 만약 악의적인 사용자가 Fluent Bit 컨테이너에서 docker 명령어를 실행할 수 있다면, 호스트 파일 시스템을 마운트하고 Root 권한을 획득 가능하여 컨테이너 탈출(Container Escape)이 가능해지며, 노드 전체가 공격에 노출될 위험이 있습니다.
우선, Fluent Bit만 설치해본 후, 로그를 확인한 뒤 CloudWatch agent를 설치하여 CloudWatch Container Insights까지 비교하며 확인해보겠습니다. 위의 공식문서를 따라 실습해보겠습니다.
❯ kubectl apply -f https://raw.githubusercontent.com/aws-samples/amazon-cloudwatch-container-insights/latest/k8s-deployment-manifest-templates/deployment-mode/daemonset/container-insights-monitoring/cloudwatch-namespace.yaml
namespace/amazon-cloudwatch created
❯ ClusterName=myeks
❯ RegionName=ap-northeast-2
❯ FluentBitHttpPort='2020'
FluentBitReadFromHead='Off'
[[ ${FluentBitReadFromHead} = 'On' ]] && FluentBitReadFromTail='Off'|| FluentBitReadFromTail='On'
[[ -z ${FluentBitHttpPort} ]] && FluentBitHttpServer='Off' || FluentBitHttpServer='On'
kubectl create configmap fluent-bit-cluster-info \
--from-literal=cluster.name=${ClusterName} \
--from-literal=http.server=${FluentBitHttpServer} \
--from-literal=http.port=${FluentBitHttpPort} \
--from-literal=read.head=${FluentBitReadFromHead} \
--from-literal=read.tail=${FluentBitReadFromTail} \
--from-literal=logs.region=${RegionName} -n amazon-cloudwatch
❯ kubectl apply -f https://raw.githubusercontent.com/aws-samples/amazon-cloudwatch-container-insights/latest/k8s-deployment-manifest-templates/deployment-mode/daemonset/container-insights-monitoring/fluent-bit/fluent-bit.yaml
serviceaccount/fluent-bit created
clusterrole.rbac.authorization.k8s.io/fluent-bit-role created
clusterrolebinding.rbac.authorization.k8s.io/fluent-bit-role-binding created
configmap/fluent-bit-config created
daemonset.apps/fluent-bit created
# fluent-bit 설정파일 일부
apiVersion: v1
kind: ConfigMap
metadata:
name: fluent-bit-config
namespace: amazon-cloudwatch
labels:
k8s-app: fluent-bit
data:
fluent-bit.conf: |
[SERVICE]
Flush 5
Grace 30
Log_Level error
Daemon off
Parsers_File parsers.conf
HTTP_Server ${HTTP_SERVER}
HTTP_Listen 0.0.0.0
HTTP_Port ${HTTP_PORT}
storage.path /var/fluent-bit/state/flb-storage/
storage.sync normal
storage.checksum off
storage.backlog.mem_limit 5M
@INCLUDE application-log.conf
@INCLUDE dataplane-log.conf
@INCLUDE host-log.conf
application-log.conf: |
[INPUT]
Name tail
Tag application.*
Exclude_Path /var/log/containers/cloudwatch-agent*, /var/log/containers/fluent-bit*, /var/log/containers/aws-node*, /var/log/containers/kube-proxy*, /var/log/containers/fluentd*
Path /var/log/containers/*.log
multiline.parser docker, cri
DB /var/fluent-bit/state/flb_container.db
Mem_Buf_Limit 50MB
Skip_Long_Lines On
Refresh_Interval 10
Rotate_Wait 30
storage.type filesystem
Read_from_Head ${READ_FROM_HEAD}
[INPUT]
Name tail
Tag application.*
Path /var/log/containers/fluent-bit*
multiline.parser docker, cri
DB /var/fluent-bit/state/flb_log.db
Mem_Buf_Limit 5MB
Skip_Long_Lines On
Refresh_Interval 10
Read_from_Head ${READ_FROM_HEAD}
앗차차 권한이 없어서 다음 에러가 발생합니다.
# Fluent Bit 에러 로그
CreateLogStream API responded with error='AccessDeniedException'
Failed to create log stream
Failed to send events
# CloudWatch로 로그 전송 권한이 포함된 IAM Role 생성
❯ eksctl create iamserviceaccount \
--name fluent-bit \
--namespace amazon-cloudwatch \
--cluster $CLUSTER_NAME \
--role-name $CLUSTER_NAME-fluent-bit-role \
--attach-policy-arn arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy \
--role-only \
--approve
❯ kubectl annotate serviceaccount fluent-bit \
-n amazon-cloudwatch \
eks.amazonaws.com/role-arn=arn:aws:iam::941377114730:role/$CLUSTER_NAME-fluent-bit-role \
--overwrite
serviceaccount/fluent-bit annotated
# fluent-bit daemonset 재시작
야호! 로그 그룹이 생성되었습니다. 하지만 여전히 Container Insights에는 아무 내용이 없네요.



이제 그럼 배포했던 Fluent Bit말고 cloudwatch agent를 설치하여 통합적으로 관측해보도록 하겠습니다.
해당 포스팅은 다음 포스팅에 작성하며, 메트릭과 통합하여 관측하는 방법에 대해 설명하도록 하겠습니다.
'스터디 > AEWS' 카테고리의 다른 글
| [AEWS] 4주차 Kubernetes 이상징후에 알람 받기 (kwatch, botkube) (1) | 2025.03.02 |
|---|---|
| [AEWS] 4주차 CloudWatch를 활용한 EKS 모니터링 - Container Insights 활용하기 (0) | 2025.03.02 |
| [AEWS] 3주차 EKS에서 CSI Driver를 통한 AWS 스토리지 사용하기 (EBS, EFS) (0) | 2025.02.23 |
| [AEWS] 3주차 쿠버네티스 스토리지 이해하기 (emptyDir, hostPath, PV, PVC, storageClass, CSI) (0) | 2025.02.22 |
| [AEWS] 2주차 EKS VPC CNI를 통한 파드 간 통신 (작성중) (0) | 2025.02.16 |