25๋ ๋ AWS EKS Hands-on Study ์คํฐ๋ ์ ๋ฆฌ ๋ด์ฉ์ ๋๋ค.
EKS ๋ก๊น
aws eks update-cluster-config --region ap-northeast-2 --name $CLUSTER_NAME \
--logging '{"clusterLogging":[{"types":["api","audit","authenticator","controllerManager","scheduler"],"enabled":true}]}'
aws logs describe-log-groups | jq
EKS์ ๋ก๊น ์ ํ์ฑํํ๊ณ ์์ฑ๋ ๋ก๊ทธ ๊ทธ๋ฃน์ ํ์ธํ๋ค.
๋ก๊ทธ ์ธ์ฌ์ดํธ
๋ก๊ทธ ์ธ์ฌ์ดํธ๋ฅผ ํตํด ํํฐ๋ง์ ํด์ ๋ณด๋ฉด kube-apiserver-audit ๋ก๊ทธ๊ฐ ์ค์บ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
Nginx ์ปจํ ์ด๋ ๋ฐฐํฌ
ํ ์คํธ๋ฅผ ์ํ ํ๋ ๋ฐฐํฌ
kubectl get ingress,deploy,svc,ep nginx
NAME CLASS HOSTS ADDRESS PORTS AGE
ingress.networking.k8s.io/nginx alb nginx.$MyDomain myeks-ingress-alb-1318062263.ap-northeast-2.elb.amazonaws.com 80 2m31s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/nginx 1/1 1 1 2m31s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/nginx NodePort 10.100.186.235 <none> 80:32219/TCP,443:31660/TCP 2m31s
NAME ENDPOINTS AGE
endpoints/nginx 192.168.3.115:8443,192.168.3.115:8080 2m31s
kubectl describe deploy nginx
Name: nginx
Namespace: default
CreationTimestamp: Thu, 27 Feb 2025 22:10:33 +0900
Labels: app.kubernetes.io/instance=nginx
app.kubernetes.io/managed-by=Helm
app.kubernetes.io/name=nginx
app.kubernetes.io/version=1.27.4
helm.sh/chart=nginx-19.0.0
Annotations: deployment.kubernetes.io/revision: 1
meta.helm.sh/release-name: nginx
meta.helm.sh/release-namespace: default
Selector: app.kubernetes.io/instance=nginx,app.kubernetes.io/name=nginx
Replicas: 1 desired | 1 updated | 1 total | 1 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 25% max unavailable, 25% max surge
Pod Template:
Labels: app.kubernetes.io/instance=nginx
app.kubernetes.io/managed-by=Helm
app.kubernetes.io/name=nginx
app.kubernetes.io/version=1.27.4
helm.sh/chart=nginx-19.0.0
Service Account: nginx
Init Containers:
preserve-logs-symlinks:
Image: docker.io/bitnami/nginx:1.27.4-debian-12-r1
Port: <none>
Host Port: <none>
SeccompProfile: RuntimeDefault
Command:
/bin/bash
Args:
-ec
#!/bin/bash
. /opt/bitnami/scripts/libfs.sh
# We copy the logs folder because it has symlinks to stdout and stderr
if ! is_dir_empty /opt/bitnami/nginx/logs; then
cp -r /opt/bitnami/nginx/logs /emptydir/app-logs-dir
fi
Limits:
cpu: 150m
ephemeral-storage: 2Gi
memory: 192Mi
Requests:
cpu: 100m
ephemeral-storage: 50Mi
memory: 128Mi
Environment: <none>
Mounts:
/emptydir from empty-dir (rw)
Containers:
nginx:
Image: docker.io/bitnami/nginx:1.27.4-debian-12-r1
Ports: 8080/TCP, 8443/TCP
Host Ports: 0/TCP, 0/TCP
SeccompProfile: RuntimeDefault
Limits:
cpu: 150m
ephemeral-storage: 2Gi
memory: 192Mi
Requests:
cpu: 100m
ephemeral-storage: 50Mi
memory: 128Mi
Liveness: tcp-socket :http delay=30s timeout=5s period=10s #success=1 #failure=6
Readiness: http-get http://:http/ delay=5s timeout=3s period=5s #success=1 #failure=3
Environment:
BITNAMI_DEBUG: false
NGINX_HTTP_PORT_NUMBER: 8080
NGINX_HTTPS_PORT_NUMBER: 8443
Mounts:
/certs from certificate (rw)
/opt/bitnami/nginx/conf from empty-dir (rw,path="app-conf-dir")
/opt/bitnami/nginx/logs from empty-dir (rw,path="app-logs-dir")
/opt/bitnami/nginx/tmp from empty-dir (rw,path="app-tmp-dir")
/tmp from empty-dir (rw,path="tmp-dir")
Volumes:
empty-dir:
Type: EmptyDir (a temporary directory that shares a pod's lifetime)
Medium:
SizeLimit: <unset>
certificate:
Type: Secret (a volume populated by a Secret)
SecretName: nginx-tls
Optional: false
Node-Selectors: <none>
Tolerations: <none>
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
OldReplicaSets: <none>
NewReplicaSet: nginx-7c94c9bdcb (1/1 replicas created)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 2m39s deployment-controller Scaled up replica set nginx-7c94c9bdcb to 1
nginx ํ๋๋ฅผ ๋ฐฐํฌํ ํ ์ฑ๊ณต์ ์ผ๋ก ์คํ ์ค์์ ํ์ธํ ์ ์๋ค.
์ฌ๊ธฐ์ ์ด๋ฒคํธ๋ฅผ ๋ณด๋ฉด ScalingReplicaSet์ด๋ผ๊ณ ๋จ๋๋ฐ ์ด๋ ReplicaSet์ ํฌ๊ธฐ๊ฐ ์กฐ์ ๋์์์ ์๋ฏธํ๋ค.
curl -s https://nginx.$MyDomain
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
์ ์์ ์ผ๋ก ๋ฐฐํฌ๋์๋์ง ํ์ธํ๋ค.
kubectl stern deploy/nginx
+ nginx-7c94c9bdcb-44hzp › nginx
+ nginx-7c94c9bdcb-44hzp › preserve-logs-symlinks
nginx-7c94c9bdcb-44hzp nginx nginx 13:10:43.31 INFO ==>
nginx-7c94c9bdcb-44hzp nginx nginx 13:10:43.31 INFO ==> Welcome to the Bitnami nginx container
nginx-7c94c9bdcb-44hzp nginx nginx 13:10:43.39 INFO ==> Subscribe to project updates by watching https://github.com/bitnami/containers
nginx-7c94c9bdcb-44hzp nginx nginx 13:10:43.39 INFO ==> Did you know there are enterprise versions of the Bitnami catalog? For enhanced secure software supply chain features, unlimited pulls from Docker, LTS support, or application customization, see Bitnami Premium or Tanzu Application Catalog. See https://www.arrow.com/globalecs/na/vendors/bitnami/ for more information.
nginx-7c94c9bdcb-44hzp nginx nginx 13:10:43.40 INFO ==>
nginx-7c94c9bdcb-44hzp nginx nginx 13:10:43.40 INFO ==> ** Starting NGINX setup **
nginx-7c94c9bdcb-44hzp nginx nginx 13:10:43.49 INFO ==> Validating settings in NGINX_* env vars
nginx-7c94c9bdcb-44hzp nginx nginx 13:10:43.59 INFO ==> No custom scripts in /docker-entrypoint-initdb.d
nginx-7c94c9bdcb-44hzp nginx nginx 13:10:43.60 INFO ==> Initializing NGINX
nginx-7c94c9bdcb-44hzp nginx realpath: /bitnami/nginx/conf/vhosts: No such file or directory
nginx-7c94c9bdcb-44hzp nginx
nginx-7c94c9bdcb-44hzp nginx nginx 13:10:43.79 INFO ==> ** NGINX setup finished! **
nginx-7c94c9bdcb-44hzp nginx nginx 13:10:43.81 INFO ==> ** Starting NGINX **
nginx-7c94c9bdcb-44hzp nginx 192.168.3.32 - - [27/Feb/2025:13:10:49 +0000] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.31+" "-"
nginx-7c94c9bdcb-44hzp nginx 192.168.3.32 - - [27/Feb/2025:13:10:54 +0000] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.31+" "-"
nginx-7c94c9bdcb-44hzp nginx 192.168.3.32 - - [27/Feb/2025:13:10:59 +0000] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.31+" "-"
nginx-7c94c9bdcb-44hzp nginx 192.168.3.250 - - [27/Feb/2025:13:11:01 +0000] "GET / HTTP/1.1" 200 409 "-" "ELB-HealthChecker/2.0" "-"
nginx-7c94c9bdcb-44hzp nginx 192.168.1.4 - - [27/Feb/2025:13:11:01 +0000] "GET / HTTP/1.1" 200 409 "-" "ELB-HealthChecker/2.0" "-"
nginx-7c94c9bdcb-44hzp nginx 192.168.2.80 - - [27/Feb/2025:13:11:01 +0000] "GET / HTTP/1.1" 200 409 "-" "ELB-HealthChecker/2.0" "-"
nginx-7c94c9bdcb-44hzp nginx 192.168.3.32 - - [27/Feb/2025:13:11:04 +0000] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.31+" "-"
nginx-7c94c9bdcb-44hzp nginx 192.168.3.32 - - [27/Feb/2025:13:11:09 +0000] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.31+" "-"
nginx-7c94c9bdcb-44hzp nginx 192.168.3.32 - - [27/Feb/2025:13:11:14 +0000] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.31+" "-"
nginx-7c94c9bdcb-44hzp nginx 192.168.3.250 - - [27/Feb/2025:13:11:16 +0000] "GET / HTTP/1.1" 200 409 "-" "ELB-HealthChecker/2.0" "-"
...
nginx์ ์ํ ๋ชจ๋ ํ๋์ ๋ก๊ทธ๋ฅผ ์ค์๊ฐ์ผ๋ก ์ถ๋ ฅํ ์ ์๋ค.
kubectl exec -it deploy/nginx -- ls -l /opt/bitnami/nginx/logs/
Defaulted container "nginx" out of: nginx, preserve-logs-symlinks (init)
total 0
lrwxrwxrwx. 1 1001 1001 11 Feb 27 13:10 access.log -> /dev/stdout
lrwxrwxrwx. 1 1001 1001 11 Feb 27 13:10 error.log -> /dev/stderr
์ก์ธ์ค, ์๋ฌ ๋ก๊ทธ๊ฐ ์ ์ฅ๋๋ ์์น๋ ํ์ธ์ด ๊ฐ๋ฅํ๋ค.
์ ์ฅ๋๋ ์์น๊ฐ ์ ๊ทผ ๋ก๊ทธ๋ stdout, ์๋ฌ ๋ก๊ทธ๋ stderr๋ก ์ ์ก ๋๋๋ฐ ์ปจํ ์ด๋ ํ๊ฒฝ์์ ๋ฐฐํฌ๋์ด ๋ณ๋ ํ์ผ๋ก ๋ก๊ทธ๋ฅผ ๋จ๊ตฌ์ง ์๊ธฐ ๋๋ฌธ์ด๋ค. ๋ฐ๋ผ์ ํ์ค ์ถ๋ ฅ, ํ์ค ์๋ฌ๋ฅผ ํ์ฉํ์ฌ ๋ก๊ทธ๋ฅผ ๊ธฐ๋กํ๋ ๊ฒ์ด๋ค
Amazon CloudWatch & Fluent Bit
Fluent Bit์ ๋ก๊ทธ ๋ฐ ๋ฉํธ๋ฆญ ๋ฐ์ดํฐ๋ฅผ ์์ง, ์ฒ๋ฆฌ, ํํฐ๋ง ๋ฐ ์ ์กํ๋ ๊ฒฝ๋ ๋ก๊ทธ ํ๋ก์ธ์๋ก ์ฟ ๋ฒ๋คํฐ์ค, Docker ๋ฑ ์ฌ๋ฌ ํ๊ฒฝ์์ ๋ก๊ทธ ๋ฐ์ดํฐ๋ฅผ ํจ์จ์ ์ผ๋ก ์์งํ๊ณ ์ ์กํ๋ ์ญํ ์ ์ํํ๋ค.
์ ์ฅ๋ ๋ก๊ทธ๋ aws ํด๋ผ์ฐ๋ ์์น๋ฅผ ํตํด ๋์๋ณด๋์์ ํ์ธ์ด ๊ฐ๋ฅํ๋ค.
export ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text)
aws eks create-addon --addon-name amazon-cloudwatch-observability --cluster-name myeks --service-account-role-arn arn:aws:iam::$ACCOUNT_ID:role/myeks-cloudwatch-agent-role
fluent bit ํ๋๊ฐ ๋ก๊ทธ๋ฅผ ์ ์ฅํ๋ ๋ฐฉ๋ฒ
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*
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}
[INPUT]
Name tail
Tag application.*
Path /var/log/containers/cloudwatch-agent*
multiline.parser docker, cri
DB /var/fluent-bit/state/flb_cwagent.db
Mem_Buf_Limit 5MB
Skip_Long_Lines On
Refresh_Interval 10
Read_from_Head ${READ_FROM_HEAD}
[FILTER]
Name aws
Match application.*
az false
ec2_instance_id false
Enable_Entity true
[FILTER]
Name kubernetes
Match application.*
Kube_URL https://kubernetes.default.svc:443
Kube_Tag_Prefix application.var.log.containers.
Merge_Log On
Merge_Log_Key log_processed
K8S-Logging.Parser On
K8S-Logging.Exclude Off
Labels Off
Annotations Off
Use_Kubelet On
Kubelet_Port 10250
Buffer_Size 0
Use_Pod_Association On
[OUTPUT]
Name cloudwatch_logs
Match application.*
region ${AWS_REGION}
log_group_name /aws/containerinsights/${CLUSTER_NAME}/application
log_stream_prefix ${HOST_NAME}-
auto_create_group true
extra_user_agent container-insights
add_entity true
fluent bit๋ ๋ก๊ทธ๋ฅผ ์์ง(INPUT)ํ๊ณ ํํฐ๋ง(FILTER)ํด์ aws๋ก ์ ์ก(OUTPUT)ํ๋ ๊ตฌ์กฐ๋ก ๋ก๊ทธ๋ฅผ ์ฒ๋ฆฌํ๋๋ฐ,
CloudWatch ์์ด์ ํธ๋ ์ฟ ๋ฒ๋คํฐ์ค ๋ ธ๋์ ๋ก๊ทธ(/var/lib/docker, /run/containerd/containerd.sock)์ ์์คํ ๋ฉํธ๋ฆญ(/sys, /dev/disk/)์ ์์งํ์ฌ AWS CloudWatch๋ก ์ ์กํ๋ ๊ฒ์ ์ ์ ์๋ค.
kubectl describe -n amazon-cloudwatch ds cloudwatch-agent
Volumes:
...
rootfs:
Type: HostPath (bare host directory volume)
Path: /
HostPathType:
HostPath๊ฐ ๋ฃจํธ ๋๋ ํฐ๋ฆฌ ์ ์ฒด๋ฅผ ๋ง์ดํธํ ๊ฒ์ ํ์ธํ ์ ์๋๋ฐ, ์ด๋ ํ๋๊ฐ ๋ ธ๋์ ๋ชจ๋ ํ์ผ์ ์ฝ๊ฑฐ๋ ๋ณ๊ฒฝํ ์ ์๋ ๊ถํ์ ๊ฐ์ง ์ ์๊ฒ ๋์ด ์ํํ ์ค์ ์ด๋ผ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
containerinsights ๊ฒฝ๋ก ๋ฐ์ผ๋ก fluent bit์์ ๋ณด๋ธ ๋ก๊ทธ๋ค์ด ์๊ธด๊ฒ์ ํ์ธํ ์ ์๋ค.
- application : ์ปจํ ์ด๋์ ํ๋ ๋ก๊ทธ
- dataplane : ์ฟ ๋ฒ๋คํฐ์ค ๋ฐ์ดํฐํ๋ ์ธ ๋ก๊ทธ
- performance : ์ฑ๋ฅ ๋ก๊ทธ ์ด๋ฒคํธ
๋ถํ ํ ์คํธ
ab -c 500 -n 30000 https://nginx.$MyDomain/
nginx ์ปจํ ์ด๋์์ ๋ถํ๋ฅผ ๋ฐ์ํ๋ฉด ์ ํ๋ฆฌ์ผ์ด์ ๋จ ๋ก๊ทธ๊ฐ 200์ผ๋ก ์๋ต๊ฐ์ด ์ ๋๋ก ๋ฐ์ํ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
๋ก๊ทธ ์ธ์ฌ์ดํธ์์ ์ ํ๋ฆฌ์ผ์ด์ ํ์์ ์ปจํ ์ด๋์์ ๋ฐ์ํ ์๋ฌ ๋ก๊ทธ๋ฅผ ํ์ธํ ์ ์๋ค.
๋์๋ณด๋
์ค์ต ์๋ฃ ํ ์ญ์
aws eks delete-addon --cluster-name $CLUSTER_NAME --addon-name amazon-cloudwatch-observability
์ค์ต ์๋ฃ ํ ์ ๋์จ๊ณผ ๋ก๊ทธ ๊ทธ๋ฃน์ ์ญ์ !