[AEWS] #4์ฃผ์ฐจ EKS ๋กœ๊น… & AWS Cloud Watch ์‹ค์Šต (1)

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

 

์‹ค์Šต ์™„๋ฃŒ ํ›„ ์• ๋“œ์˜จ๊ณผ ๋กœ๊ทธ ๊ทธ๋ฃน์„ ์‚ญ์ œ!