[AWES 3기] 4주차 스터디 내용 정리

ajufresh·2025년 3월 1일
0

0. 실습 환경 배포

[1] CloudFormation 스택 배포 한줄 실행

# YAML 파일 다운로드
curl -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/K8S/myeks-4week.yaml

# 변수 지정
CLUSTER_NAME=myeks
SSHKEYNAME=aews
MYACCESSKEY=~
MYSECRETKEY=~
WorkerNodeInstanceType=<워커 노드 인스턴스 타입> # 워커노드 인스턴스 타입 변경 가능

# CloudFormation 스택 배포
aws cloudformation deploy --template-file myeks-4week.yaml --stack-name $CLUSTER_NAME --parameter-overrides KeyName=$SSHKEYNAME SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32  MyIamUserAccessKeyID=$MYACCESSKEY MyIamUserSecretAccessKey=$MYSECRETKEY ClusterBaseName=$CLUSTER_NAME WorkerNodeInstanceType=$WorkerNodeInstanceType --region ap-northeast-2

# CloudFormation 스택 배포 완료 후 작업용 EC2 IP 출력
aws cloudformation describe-stacks --stack-name myeks --query 'Stacks[*].Outputs[0].OutputValue' --output text 

해당 CloudFormation 스택에는 배포된 운영서버를 통해서 EKS를 배포하는 것까지 포함되어 있다. 스택이 만들어진다고 끝나는게 아니라 운영 서버에서 EKS를 배포하기 때문에 아래 과정을 통해 모니터링하며 EKS가 생성되길 기다리면 된다.

[2] 배포 과정 살펴보기

# 운영서버 EC2 SSH 접속
ssh -i ~/.ssh/aews.pem ec2-user@$(aws cloudformation describe-stacks --stack-name myeks --query 'Stacks[*].Outputs[0].OutputValue' --output text)
-------------------------------------------------
#
whoami
pwd

# cloud-init 실행 과정 로그 확인
tail -f /var/log/cloud-init-output.log

# eks 설정 파일 확인
cat myeks.yaml

# cloud-init 정상 완료 후 eksctl 실행 과정 로그 확인
tail -f /root/create-eks.log

#
exit
-------------------------------------------------

/root/create-eks.log에 EKS가 생성되는 로그를 확인할 수 있다. 20분정도 걸리니 기다렸다 EKS 설치 확인을 하면 된다.

[3] AWS EKS 설치 확인
아래 커맨드를 입력하여 잘 나오는지 확인한다.

# 클러스터 설치 확인
eksctl get cluster
eksctl get nodegroup --cluster $CLUSTER_NAME
eksctl get addon --cluster $CLUSTER_NAME
eksctl get iamserviceaccount --cluster $CLUSTER_NAME

# kubeconfig 생성
aws sts get-caller-identity --query Arn
aws eks update-kubeconfig --name myeks --user-alias < 출력된 자격증명 사용자>

# 기본 구성 정보 확인
kubectl cluster-info

kubectl get node
kubectl get node --label-columns=node.kubernetes.io/instance-type,eks.amazonaws.com/capacityType,topology.kubernetes.io/zone


kubectl get pod -A

저번 주에 했던 것처럼 자신의 집 IP를 SG에 추가해서 노드 서버에 접근할 수 있도록 테스트한다.

[4] kube-ops-view(Ingress), AWS LoadBalancer Controller, ExternalDNS, gp3 storageclass 설치

# kube-ops-view
helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set service.main.type=ClusterIP  --set env.TZ="Asia/Seoul" --namespace kube-system

# gp3 스토리지 클래스 생성
cat <<EOF | kubectl apply -f -
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: gp3
  annotations:
    storageclass.kubernetes.io/is-default-class: "true"
allowVolumeExpansion: true
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer
parameters:
  type: gp3
  allowAutoIOPSPerGBIncrease: 'true'
  encrypted: 'true'
  fsType: xfs # 기본값이 ext4
EOF
kubectl get sc

# ExternalDNS
curl -s https://raw.githubusercontent.com/gasida/PKOS/main/aews/externaldns.yaml | MyDomain=$MyDomain MyDnzHostedZoneId=$MyDnzHostedZoneId envsubst | kubectl apply -f -

# AWS LoadBalancerController
helm repo add eks https://aws.github.io/eks-charts
helm install aws-load-balancer-controller eks/aws-load-balancer-controller -n kube-system --set clusterName=$CLUSTER_NAME \
  --set serviceAccount.create=false --set serviceAccount.name=aws-load-balancer-controller

# kubeopsview 용 Ingress 설정 : group 설정으로 1대의 ALB를 여러개의 ingress 에서 공용 사용
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    alb.ingress.kubernetes.io/certificate-arn: $CERT_ARN
    alb.ingress.kubernetes.io/group.name: study
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}, {"HTTP":80}]'
    alb.ingress.kubernetes.io/load-balancer-name: $CLUSTER_NAME-ingress-alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/ssl-redirect: "443"
    alb.ingress.kubernetes.io/success-codes: 200-399
    alb.ingress.kubernetes.io/target-type: ip
  labels:
    app.kubernetes.io/name: kubeopsview
  name: kubeopsview
  namespace: kube-system
spec:
  ingressClassName: alb
  rules:
  - host: kubeopsview.$MyDomain
    http:
      paths:
      - backend:
          service:
            name: kube-ops-view
            port:
              number: 8080
        path: /
        pathType: Prefix
EOF

kube-ops-view, external-dns, loadbalancer-controller, storage를 추가한다.

# 설치된 파드 정보 확인
kubectl get pods -n kube-system

# service, ep, ingress 확인
kubectl get ingress,svc,ep -n kube-system

# Kube Ops View 접속 정보 확인 
echo -e "Kube Ops View URL = https://kubeopsview.$MyDomain/#scale=1.5"
open "https://kubeopsview.$MyDomain/#scale=1.5" # macOS

[[옵션] Bookinfo 애플리케이션 배포]

# 모니터링
watch -d 'kubectl get pod -o wide;echo;kubectl get svc'

# Bookinfo 애플리케이션 배포
kubectl apply -f https://raw.githubusercontent.com/istio/istio/refs/heads/master/samples/bookinfo/platform/kube/bookinfo.yaml

# 확인
kubectl get all,sa

# product 웹 접속 확인
kubectl exec "$(kubectl get pod -l app=ratings -o jsonpath='{.items[0].metadata.name}')" -c ratings -- curl -sS productpage:9080/productpage | grep -o "<title>.*</title>"

# 로그
kubectl stern -l app=productpage
혹은
kubectl log -l app=productpage -f

#
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    alb.ingress.kubernetes.io/certificate-arn: $CERT_ARN
    alb.ingress.kubernetes.io/group.name: study-new
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}, {"HTTP":80}]'
    alb.ingress.kubernetes.io/load-balancer-name: $CLUSTER_NAME-ingress-alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/ssl-redirect: "443"
    alb.ingress.kubernetes.io/success-codes: 200-399
    alb.ingress.kubernetes.io/target-type: ip
  labels:
    app.kubernetes.io/name: bookinfo
  name: bookinfo
spec:
  ingressClassName: alb
  rules:
  - host: bookinfo.$MyDomain
    http:
      paths:
      - backend:
          service:
            name: productpage
            port:
              number: 9080
        path: /
        pathType: Prefix
EOF
kubectl get ingress

# bookinfo 접속 정보 확인 
echo -e "bookinfo URL = https://bookinfo.$MyDomain/productpage"
open "https://bookinfo.$MyDomain/productpage" # macOS

# 반복 접속 실행
curl -s -k https://bookinfo.$MyDomain/productpage | grep -o "<title>.*</title>"
while true; do curl -s -k https://bookinfo.$MyDomain/productpage | grep -o "<title>.*</title>" ; echo "--------------" ; sleep 1; done
for i in {1..100};  do curl -s -k https://bookinfo.$MyDomain/productpage | grep -o "<title>.*</title>" ; done

트래픽을 생성하기 위해 위 코드를 실행하면 된다.

[Monitoring vs Observability(o11y)]
모니터링은 사전에 정의된 기준을 기반으로 현재 시스템의 상태를 감시에 중점하는 것이고, 관측 가능성은 수집된 다양한 데이터를 활용하여 예측되지 않은 문제까지 분석하는 것이다.
그래서 모니터링을 위한 지표로 CPU 사용량, 메모리 사용량, 응답 시간, 오류율 등 특정 지표를 지속적으로 측정하고, 관측 가능성은 로그(이벤트 기록), 메트릭(수치 데이터), 트레이스(요청 흐름 추적), 그리고 일부 경우 이벤트가 포함된다.

[SLI, SLO, SLA]

  • SLI (Service Level Indicator, 서비스 수준 지표)
    • SLI는 서비스의 성능이나 신뢰성을 측정하는 구체적인 지표이다. 예를 들어, 웹 서버의 응답 시간이나 데이터베이스의 가용성과 같은 특정 측정 가능한 값이 될 수 있습니다.
    • 예시: "HTTP 요청의 95%가 100ms 이내에 응답한다"
  • SLO (Service Level Objective, 서비스 수준 목표)
    • SLO는 SLI에 기반하여 설정된 목표 수준이다. 이는 조직이 자신의 서비스를 얼마나 잘 제공하고자 하는지를 정량적으로 나타내는 목표치입니다.
    • 예시: 위 SLI 예시에 따라 설정할 수 있는 SLO는 "월간 기준으로 HTTP 요청의 95%가 100ms 이내에 처리되어야 한다"
  • SLA (Service Level Agreement, 서비스 수준 계약)
    • SLA는 서비스 제공자와 고객 간의 공식적인 계약으로, SLO가 법적으로 보장되는 약속이다.
    • 예시: 클라우드 서비스 제공자가 고객에게 제공하는 SLA는 "월간 가용성이 99.9% 미만일 경우, 서비스 이용료의 10%를 환불한다"는 조건을 포함할 수 있다.

1. EKS Console

EKS Console에 보이는 정보들은 어떻게 가져오는 걸까? => 쿠버네티스 API를 통해서 리소스 및 정보를 확인한다.

Control Plane에서 발생하는 이벤트를 EKS 설정에 따라 로그를 남길 수 있다.

  • Kubernetes API server component logs (api) – kube-apiserver-<nnn...>
  • Audit (audit) – kube-apiserver-audit-<nnn...>
  • Authenticator (authenticator) – authenticator-<nnn...>
  • Controller manager (controllerManager) – kube-controller-manager-<nnn...>
  • Scheduler (scheduler) – kube-scheduler-<nnn...>

기본은 로그가 Off 상태이다.

# 모든 로깅 활성화
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

# 로그 tail 확인 : aws logs tail help
aws logs tail /aws/eks/$CLUSTER_NAME/cluster | more

# 신규 로그를 바로 출력
aws logs tail /aws/eks/$CLUSTER_NAME/cluster --follow

# 필터 패턴
aws logs tail /aws/eks/$CLUSTER_NAME/cluster --filter-pattern <필터 패턴>

# 로그 스트림이름
aws logs tail /aws/eks/$CLUSTER_NAME/cluster --log-stream-name-prefix <로그 스트림 prefix> --follow
aws logs tail /aws/eks/$CLUSTER_NAME/cluster --log-stream-name-prefix kube-apiserver --follow
aws logs tail /aws/eks/$CLUSTER_NAME/cluster --log-stream-name-prefix kube-apiserver-audit --follow
aws logs tail /aws/eks/$CLUSTER_NAME/cluster --log-stream-name-prefix kube-scheduler --follow
aws logs tail /aws/eks/$CLUSTER_NAME/cluster --log-stream-name-prefix authenticator --follow
aws logs tail /aws/eks/$CLUSTER_NAME/cluster --log-stream-name-prefix kube-controller-manager --follow
aws logs tail /aws/eks/$CLUSTER_NAME/cluster --log-stream-name-prefix cloud-controller-manager --follow
kubectl scale deployment -n kube-system coredns --replicas=1
kubectl scale deployment -n kube-system coredns --replicas=2

# 시간 지정: 1초(s) 1분(m) 1시간(h) 하루(d) 한주(w)
aws logs tail /aws/eks/$CLUSTER_NAME/cluster --since 1h30m

# 짧게 출력
aws logs tail /aws/eks/$CLUSTER_NAME/cluster --since 1h30m --format short


필터 패턴, 스트림 지정, 시간, 포맷 등 다양하게 지정이 가능하다.

[Cloudwatch Log Insight]
Cloudwatch의 log insight 기능을 사용하면 원하는대로 필터링해서 로그를 확인할 수도 있다.

# EC2 Instance가 NodeNotReady 상태인 로그 검색
fields @timestamp, @message
| filter @message like /NodeNotReady/
| sort @timestamp desc

# kube-apiserver-audit 로그에서 userAgent 정렬해서 아래 4개 필드 정보 검색
fields userAgent, requestURI, @timestamp, @message
| filter @logStream ~= "kube-apiserver-audit"
| stats count(userAgent) as count by userAgent
| sort count desc

#
fields @timestamp, @message
| filter @logStream ~= "kube-scheduler"
| sort @timestamp desc

#
fields @timestamp, @message
| filter @logStream ~= "authenticator"
| sort @timestamp desc

#
fields @timestamp, @message
| filter @logStream ~= "kube-controller-manager"
| sort @timestamp desc

다 확인한 후에는 로깅을 끈다.

# EKS Control Plane 로깅(CloudWatch Logs) 비활성화
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


2. Logging in EKS

[컨테이너(파드) 로깅]

  • NGINX 웹서버 배포 with Ingress(ALB)
# NGINX 웹서버 배포
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update

# 도메인, 인증서 확인
echo $MyDomain $CERT_ARN

# 파라미터 파일 생성
cat <<EOT > nginx-values.yaml
service:
  type: NodePort
  
networkPolicy:
  enabled: false
  
resourcesPreset: "nano"

ingress:
  enabled: true
  ingressClassName: alb
  hostname: nginx.$MyDomain
  pathType: Prefix
  path: /
  annotations: 
    alb.ingress.kubernetes.io/certificate-arn: $CERT_ARN
    alb.ingress.kubernetes.io/group.name: study-new
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}, {"HTTP":80}]'
    alb.ingress.kubernetes.io/load-balancer-name: $CLUSTER_NAME-ingress-alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/ssl-redirect: "443"
    alb.ingress.kubernetes.io/success-codes: 200-399
    alb.ingress.kubernetes.io/target-type: ip
EOT
cat nginx-values.yaml

# 배포
helm install nginx bitnami/nginx --version 19.0.0 -f nginx-values.yaml


# 확인
kubectl get ingress,deploy,svc,ep nginx
kubectl describe deploy nginx # Resource - Limits/Requests 확인
kubectl get targetgroupbindings # ALB TG 확인

# 접속 주소 확인 및 접속
echo -e "Nginx WebServer URL = https://nginx.$MyDomain"
curl -s https://nginx.$MyDomain
kubectl stern deploy/nginx
혹은
kubectl logs deploy/nginx -f


Ingress 추가도 잘되었다.

# 반복 접속
while true; do curl -s https://nginx.$MyDomain | grep title; date; sleep 1; done
while true; do curl -s https://nginx.$MyDomain -I | head -n 1; date; sleep 1; done

로그를 확인할 예정이라 터미널을 하나 띄워두고 반복 접속을 한다.

[컨테이너(파드) 로깅]
어플리케이션의 핵심 로그는 /dev/stdout으로 추가하도록 하고, 에러의 경우에는 /dev/stderr로 추가하도록 한다.

RUN ln -sf **/dev/stdout** **/opt/bitnami/nginx/logs/access.log**
RUN ln -sf **/dev/stderr** **/opt/bitnami/nginx/logs/error.log**

# forward request and error logs to docker log collector
RUN ln -sf /dev/stdout /var/log/nginx/access.log \
 && ln -sf /dev/stderr /var/log/nginx/error.log

3. Container Insights metrics in Amazon CloudWatch & Fluent Bit (Logs)

  • CCI CloudWatch Container Observability
    • 노드에 CW Agent 파드와 Fluent Bit 파드가 데몬셋으로 배치되어 Metrics 와 Logs 수집하는 것

  1. Application(파드), 2. Host(워커 노드), 3. Data Plane 로그를 CloudWatch Agent를 통해 수집하여 Fluent Bit 같은 시스템을 사용해 전달하게 된다. => 종합한 후 CloudWatch Logs에서 확인이 가능하다.

[CloudWatch Container observability 설치]

# IRSA 설정
eksctl create iamserviceaccount \
  --name cloudwatch-agent \
  --namespace amazon-cloudwatch --cluster $CLUSTER_NAME \
  --role-name $CLUSTER_NAME-cloudwatch-agent-role \
  --attach-policy-arn arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy \
  --role-only \
  --approve

# addon 배포
aws eks create-addon --addon-name amazon-cloudwatch-observability --cluster-name myeks --service-account-role-arn arn:aws:iam::545009851510:role/myeks-cloudwatch-agent-role

# addon 확인
aws eks list-addons --cluster-name myeks --output table

amazon-cloudwatch-observability 설치가 된 것을 확인할 수 있다.


# 설치 확인
kubectl get crd | grep -i cloudwatch
kubectl get-all -n amazon-cloudwatch
kubectl get ds,pod,cm,sa,amazoncloudwatchagent -n amazon-cloudwatch
kubectl describe clusterrole cloudwatch-agent-role amazon-cloudwatch-observability-manager-role    # 클러스터롤 확인
kubectl describe clusterrolebindings cloudwatch-agent-role-binding amazon-cloudwatch-observability-manager-rolebinding  # 클러스터롤 바인딩 확인
kubectl -n amazon-cloudwatch logs -l app.kubernetes.io/component=amazon-cloudwatch-agent -f # 파드 로그 확인
kubectl -n amazon-cloudwatch logs -l k8s-app=fluent-bit -f    # 파드 로그 확인

# cloudwatch-agent 설정 확인
kubectl describe cm cloudwatch-agent -n amazon-cloudwatch
kubectl get cm cloudwatch-agent -n amazon-cloudwatch -o jsonpath="{.data.cwagentconfig\.json}" | jq
{
  "agent": {
    "region": "ap-northeast-2"
  },
  "logs": {
    "metrics_collected": {
      "application_signals": {
        "hosted_in": "myeks"
      },
      "kubernetes": {
        "cluster_name": "myeks",
        "enhanced_container_insights": true
      }
    }
  },
  "traces": {
    "traces_collected": {
      "application_signals": {}
    }
  }
}

Fluent Bit 컨테이너를 데몬셋으로 동작시키고, 3가지 종류의 로그CloudWatch Logs 에 전송한다.
1. /aws/containerinsights/*Cluster_Name*/application : 로그 소스(All log files in /var/log/containers), 각 컨테이너/파드 로그
2. /aws/containerinsights/*Cluster_Name*/host : 로그 소스(Logs from /var/log/dmesg/var/log/secure, and /var/log/messages), 노드(호스트) 로그
3. /aws/containerinsights/*Cluster_Name*/dataplane : 로그 소스(/var/log/journal for kubelet.servicekubeproxy.service, and docker.service), 쿠버네티스 데이터플레인 로그

kubectl describe -n amazon-cloudwatch ds cloudwatch-agent

# Fluent Bit 로그 INPUT/FILTER/OUTPUT 설정 확인
kubectl describe cm fluent-bit-config -n amazon-cloudwatch

# Fluent Bit 파드가 수집하는 방법
kubectl describe -n amazon-cloudwatch ds fluent-bit

위에서 설명한대로 /var/log/~ 하위 파일들 로그를 수집해서 전송한다.

CloudWatch에 접속하면 application, dataplane, performance 로그 그룹이 생긴 것을 확인할 수 있다.

[[운영 서버 EC2] 로그 확인 : nginx 웹서버]

# 부하 발생
curl -s https://nginx.$MyDomain
yum install -y httpd
ab -c 500 -n 30000 https://nginx.$MyDomain/

# 파드 직접 로그 모니터링
kubectl stern deploy/nginx

# Application log errors by container name : 컨테이너 이름별 애플리케이션 로그 오류
# 로그 그룹 선택 : /aws/containerinsights/<CLUSTER_NAME>/application
stats count() as error_count by kubernetes.container_name 
| filter stream="stderr" 
| sort error_count desc

# All Kubelet errors/warning logs for for a given EKS worker node
# 로그 그룹 선택 : /aws/containerinsights/<CLUSTER_NAME>/dataplane
fields @timestamp, @message, ec2_instance_id
| filter  message =~ /.*(E|W)[0-9]{4}.*/ and ec2_instance_id="<YOUR INSTANCE ID>"
| sort @timestamp desc

# Kubelet errors/warning count per EKS worker node in the cluster
# 로그 그룹 선택 : /aws/containerinsights/<CLUSTER_NAME>/dataplane
fields @timestamp, @message, ec2_instance_id
| filter   message =~ /.*(E|W)[0-9]{4}.*/
| stats count(*) as error_count by ec2_instance_id

# performance 로그 그룹
# 로그 그룹 선택 : /aws/containerinsights/<CLUSTER_NAME>/performance
# 노드별 평균 CPU 사용률
STATS avg(node_cpu_utilization) as avg_node_cpu_utilization by NodeName
| SORT avg_node_cpu_utilization DESC

# 파드별 재시작(restart) 카운트
STATS avg(number_of_container_restarts) as avg_number_of_container_restarts by PodName
| SORT avg_number_of_container_restarts DESC

# 요청된 Pod와 실행 중인 Pod 간 비교
fields @timestamp, @message 
| sort @timestamp desc 
| filter Type="Pod" 
| stats min(pod_number_of_containers) as requested, min(pod_number_of_running_containers) as running, ceil(avg(pod_number_of_containers-pod_number_of_running_containers)) as pods_missing by kubernetes.pod_name 
| sort pods_missing desc

# 클러스터 노드 실패 횟수
stats avg(cluster_failed_node_count) as CountOfNodeFailures 
| filter Type="Cluster" 
| sort @timestamp desc

# 파드별 CPU 사용량
stats pct(container_cpu_usage_total, 50) as CPUPercMedian by kubernetes.container_name 
| filter Type="Container"
| sort CPUPercMedian desc

[메트릭 확인]

Add-on을 설치했기 때문에 AWS 콘솔에서 Container Insights 확인이 가능하다.

aws eks delete-addon --cluster-name $CLUSTER_NAME --addon-name amazon-cloudwatch-observability
  • 로그 그룹도 같이 삭제하기!

4. Metrics-server & kwatch & botkube

  • kubelet으로부터 수집한 리소스 메트릭을 수집 및 집계하는 클러스터 Add-on 구성 요소
  • cAdvisor : kubelet에 포함된 컨테이너 메트릭을 수집, 집계, 노출하는 데몬
    => AWS Add-on에 깔려있기 때문에 별도로 설치하지 않아도 된다.

# 메트릭 서버 확인 : 메트릭은 15초 간격으로 cAdvisor를 통하여 가져옴
kubectl get pod -n kube-system -l app.kubernetes.io/name=metrics-server
kubectl api-resources | grep metrics
kubectl get apiservices |egrep '(AVAILABLE|metrics)'

# 노드 메트릭 확인
kubectl top node

# 파드 메트릭 확인
kubectl top pod -A
kubectl top pod -n kube-system --sort-by='cpu'
kubectl top pod -n kube-system --sort-by='memory'

[kwatch]
Kubernetes(K8s) 클러스터의 모든 변경 사항을 모니터링하고, 실행 중인 앱의 충돌을 실시간으로 감지하고, Slack, Discord 같이 원하는 채널에 알림을 즉시 게시하는 기능을 가진다.

# 닉네임
NICK=sunyoung

# configmap 생성
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Namespace
metadata:
  name: kwatch
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: kwatch
  namespace: kwatch
data:
  config.yaml: |
    alert:
      slack:
        webhook: ~
        title: $NICK-eks
    pvcMonitor:
      enabled: true
      interval: 5
      threshold: 70
EOF

# 배포
kubectl apply -f https://raw.githubusercontent.com/abahmed/kwatch/v0.8.5/deploy/deploy.yaml

배포한 이후에는 알림이 온다. 파드를 잘못 배포한 뒤 알림이 오는지 확인한다.

# 터미널1
watch kubectl get pod

# 잘못된 이미지 정보의 파드 배포
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: nginx-19
spec:
  containers:
  - name: nginx-pod
    image: nginx:1.19.19  # 존재하지 않는 이미지 버전
EOF
**kubectl get events -w**

# 이미지 업데이트 방안2 : set 사용 - iamge 등 일부 리소스 값을 변경 가능!
kubectl set 
kubectl set image pod nginx-19 nginx-pod=nginx:1.19

# 삭제
**kubectl delete pod nginx-19**

# (옵션) 노드1번 강제 재부팅 해보기
**ssh $N1 sudo reboot**

잘못된 파드를 배포하자마자 Slack 메세지가 오는 것을 확인할 수 있다.

kubectl delete -f https://raw.githubusercontent.com/abahmed/kwatch/v0.8.5/deploy/deploy.yaml

5. 프로메테우스-스택

프로메테우스, 그라파나를 포함하고 있으며 사용하기 편한 오퍼레이터 기능들을 제공해준다.

  • 제공해주는 기능
    • Metric 이름과 Key/Value 쌍으로 식별되는 시계열 데이터(=TSDB, 시계열 데이터베이스)를 사용하는 다차원 데이터 모델
    • 이러한 다차원성을 활용하는 유연한 쿼리 언어인 PromQL
    • 분산 저장소에 의존하지 않음; 단일 서버 노드가 독립적으로 작동 - 공식 문서
    • 시계열 데이터 수집은 HTTP를 통한 풀(pull) 모델로 이루어짐
    • 다양한 형태의 그래프 및 대시보드 지원
  • 구성 요소
    • 시계열 데이터를 수집(scrape)하고 저장하는 메인 Prometheus 서버
    • 애플리케이션 코드를 계측하기 위한 클라이언트 라이브러리
    • short-lived jobs을 지원하기 위한 푸시 게이트웨이
    • HAProxy, StatsD, Graphite 등과 같은 서비스를 위한 exporters
    • alert를 처리하기 위한 얼럿매니저(alertmanager)

[스택 설치]



# 모니터링
watch kubectl get pod,pvc,svc,ingress -n monitoring

# repo 추가
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts

# 파라미터 파일 생성
cat <<EOT > monitor-values.yaml
prometheus:
  prometheusSpec:
    scrapeInterval: "15s"
    evaluationInterval: "15s"
    podMonitorSelectorNilUsesHelmValues: false
    serviceMonitorSelectorNilUsesHelmValues: false
    retention: 5d
    retentionSize: "10GiB"
    storageSpec:
      volumeClaimTemplate:
        spec:
          storageClassName: gp3
          accessModes: ["ReadWriteOnce"]
          resources:
            requests:
              storage: 30Gi

  ingress:
    enabled: true
    ingressClassName: alb
    hosts: 
      - prometheus.$MyDomain
    paths: 
      - /*
    annotations:
      alb.ingress.kubernetes.io/scheme: internet-facing
      alb.ingress.kubernetes.io/target-type: ip
      alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}, {"HTTP":80}]'
      alb.ingress.kubernetes.io/certificate-arn: $CERT_ARN
      alb.ingress.kubernetes.io/success-codes: 200-399
      alb.ingress.kubernetes.io/load-balancer-name: myeks-ingress-alb
      alb.ingress.kubernetes.io/group.name: study-new
      alb.ingress.kubernetes.io/ssl-redirect: '443'

grafana:
  defaultDashboardsTimezone: Asia/Seoul
  adminPassword: prom-operator

  ingress:
    enabled: true
    ingressClassName: alb
    hosts: 
      - grafana.$MyDomain
    paths: 
      - /*
    annotations:
      alb.ingress.kubernetes.io/scheme: internet-facing
      alb.ingress.kubernetes.io/target-type: ip
      alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}, {"HTTP":80}]'
      alb.ingress.kubernetes.io/certificate-arn: $CERT_ARN
      alb.ingress.kubernetes.io/success-codes: 200-399
      alb.ingress.kubernetes.io/load-balancer-name: myeks-ingress-alb
      alb.ingress.kubernetes.io/group.name: study-new
      alb.ingress.kubernetes.io/ssl-redirect: '443'

  persistence:
    enabled: true
    type: sts
    storageClassName: "gp3"
    accessModes:
      - ReadWriteOnce
    size: 20Gi

alertmanager:
  enabled: false
defaultRules:
  create: false
kubeControllerManager:
  enabled: false
kubeEtcd:
  enabled: false
kubeScheduler:
  enabled: false
prometheus-windows-exporter:
  prometheus:
    monitor:
      enabled: false
EOT
cat monitor-values.yaml

# 배포
helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack --version 69.3.1 \
-f monitor-values.yaml --create-namespace --namespace monitoring

설치가 완료되면 모니터링을 해본다.



# 확인
## alertmanager-0 : 사전에 정의한 정책 기반(예: 노드 다운, 파드 Pending 등)으로 시스템 경고 메시지를 생성 후 경보 채널(슬랙 등)로 전송
## grafana-0 : 프로메테우스는 메트릭 정보를 저장하는 용도로 사용하며, 그라파나로 시각화 처리
## prometheus-0 : 모니터링 대상이 되는 파드는 ‘exporter’라는 별도의 사이드카 형식의 파드에서 모니터링 메트릭을 노출, pull 방식으로 가져와 내부의 시계열 데이터베이스에 저장
## node-exporter : 노드익스포터는 물리 노드에 대한 자원 사용량(네트워크, 스토리지 등 전체) 정보를 메트릭 형태로 변경하여 노출
## operator : 시스템 경고 메시지 정책(prometheus rule), 애플리케이션 모니터링 대상 추가 등의 작업을 편리하게 할수 있게 CRD 지원
## kube-state-metrics : 쿠버네티스의 클러스터의 상태(kube-state)를 메트릭으로 변환하는 파드
helm list -n monitoring
kubectl get sts,ds,deploy,pod,svc,ep,ingress,pvc,pv -n monitoring
kubectl get-all -n monitoring
kubectl get prometheus,servicemonitors -n monitoring
kubectl get crd | grep monitoring
kubectl df-pv

# 프로메테우스 버전 확인
echo -e "https://prometheus.$MyDomain/api/v1/status/buildinfo"
open https://prometheus.$MyDomain/api/v1/status/buildinfo # macOS
kubectl exec -it sts/prometheus-kube-prometheus-stack-prometheus -n monitoring -c prometheus -- prometheus --version
...

# 프로메테우스 웹 접속
echo -e "https://prometheus.$MyDomain"
open "https://prometheus.$MyDomain" # macOS

# 그라파나 웹 접속
echo -e "https://grafana.$MyDomain"
open "https://grafana.$MyDomain" # macOS

  1. 쿼리(Query) : 프로메테우스 자체 검색 언어 PromQL을 이용하여 메트릭 정보를 조회가 가능하다.
  2. 경고(Alerts) : 사전에 정의한 시스템 경고 정책(Prometheus Rules)에 대한 상황 전파 역할을 한다.
  3. 상태(Status) : 경고 메시지 정책(Rules), 모니터링 대상(Targets) 등 다양한 프로메테우스 설정 내역 확인이 가능하다.

둘 다 잘 나온다!

[[Amazon EKS] AWS CNI Metrics 수집을 위한 사전 설정]

# PodMonitor 배포
cat <<EOF | kubectl create -f -
apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
  name: aws-cni-metrics
  namespace: kube-system
spec:
  jobLabel: k8s-app
  namespaceSelector:
    matchNames:
    - kube-system
  podMetricsEndpoints:
  - interval: 30s
    path: /metrics
    port: metrics
  selector:
    matchLabels:
      k8s-app: aws-node
EOF

# PodMonitor 확인
kubectl get podmonitor -n kube-system
kubectl get podmonitor -n kube-system aws-cni-metrics -o yaml | kubectl neat
          
# metrics url 접속 확인
curl -s $N1:61678/metrics | grep '^awscni'
awscni_add_ip_req_count 10
awscni_assigned_ip_addresses 8
awscni_assigned_ip_per_cidr{cidr="192.168.1.117/32"} 1
awscni_assigned_ip_per_cidr{cidr="192.168.1.131/32"} 1
awscni_assigned_ip_per_cidr{cidr="192.168.1.184/32"} 1
awscni_assigned_ip_per_cidr{cidr="192.168.1.210/32"} 0
awscni_assigned_ip_per_cidr{cidr="192.168.1.243/32"} 1
awscni_assigned_ip_per_cidr{cidr="192.168.1.247/32"} 1
awscni_assigned_ip_per_cidr{cidr="192.168.1.38/32"} 1
...

podMonitor가 추가된 것을 볼 수 있다.

[프로메테우스 기본 사용]

모니터링 대상 서비스는 일반적으로 자체 웹 서버의 /metrics 엔드포인트 경로에 다양한 메트릭 정보를 노출하고, 프로메테우스는 해당 경로로 요청을 보내 메트릭 정보를 가져온다. 그 뒤 TSDB 형식으로 저장한다.

# 아래 처럼 프로메테우스가 각 서비스의 포트 접속하여 메트릭 정보를 수집
kubectl get node -owide
kubectl get svc,ep -n monitoring kube-prometheus-stack-prometheus-node-exporter

# (노드 익스포터 경우) 노드의 9100번 포트의 /metrics 접속 시 다양한 메트릭 정보를 확인할수 있음 : 마스터 이외에 워커노드도 확인 가능
ssh ec2-user@$N1 curl -s localhost:9100/metrics

# ingress 확인
kubectl get ingress -n monitoring kube-prometheus-stack-prometheus
kubectl describe ingress -n monitoring kube-prometheus-stack-prometheus

[서비스 모니터 동작]

  • Prometheus Operator의 커스텀 리소스로, 어떤 서비스의 엔드포인트를 모니터링할지 정의한다.
  • ServiceMonitor 객체는 Kubernetes 서비스를 선택하고 해당 서비스의 엔드포인트에서 메트릭을 스크랩(수집)하는 방법을 정의
  • 목적:
    • 메트릭을 노출하는 애플리케이션/서비스의 모니터링 설정을 자동화
    • 라벨 셀렉터를 통해 모니터링할 서비스를 선언적으로 지정
  • 작동 방식:
    • 서비스의 라벨을 기반으로 모니터링 대상을 동적으로 발견.
    • 프로메테우스 구성을 자동으로 업데이트하여 새로운 대상 엔드포인트를 추가

[메트릭을 그래프(Graph)로 조회]

node_cpu_seconds_total
node_cpu_seconds_total{mode="idle"}
(node_cpu_seconds_total{mode="idle"}[1m])
# 노드 메트릭
node 입력 후 자동 출력되는 메트릭 확인 후 선택
node_boot_time_seconds

# kube 메트릭
kube 입력 후 자동 출력되는 메트릭 확인 후 선택

현재는 15초 간격으로 쌓이고 있다.

[쿼리 : node-exporter , kube-state-metrics , kube-proxy]

node-exporter는 호스트 시스템(서버 또는 노드)의 하드웨어 및 OS 수준 메트릭을 수집하여 Prometheus가 스크랩할 수 있는 형식으로 노출하는 도구이다.

  • 수집되는 주요 메트릭
    • 노드 수준 메트릭
      • node_cpu_seconds_total: CPU 코어별 사용 시간
      • node_memory_MemFree_bytes: 사용 가능한 메모리
      • node_filesystem_avail_bytes: 파일시스템 가용 공간
      • node_network_receive_bytes_total: 네트워크 수신 바이트
  • 운영체제 정보
    • node_os_info: OS 버전, 커널 정보
    • node_time_seconds: 시스템 시간
  • 하드웨어 메트릭
    • node_thermal_zone_temp: 온도 센서 정보
    • node_disk_io_time_seconds_total: 디스크 I/O 시간

# Table 아래 쿼리 입력 후 Execute 클릭 -> Graph 확인
## 출력되는 메트릭 정보는 node-exporter 를 통해서 노드에서 수집된 정보
node_memory_Active_bytes

# 특정 노드(인스턴스) 필터링 : 아래 IP는 출력되는 자신의 인스턴스 PrivateIP 입력 후 Execute 클릭 -> Graph 확인
node_memory_Active_bytes{instance="192.168.1.105:9100"}

[kube-state-metrics]
k8s api 통해 k8s 오브젝트 정보를 수집하는 역할을 한다.

# replicas's number
kube_deployment_status_replicas
kube_deployment_status_replicas_available
kube_deployment_status_replicas_available{deployment="coredns"}

# scale out
kubectl scale deployment -n kube-system coredns --replicas 3

# 확인
kube_deployment_status_replicas_available{deployment="coredns"}

# scale in
kubectl scale deployment -n kube-system coredns --replicas 1

replica 수 조정에 따라 그래프가 실시간으로 바뀌는 것을 확인할 수 있다.

[kube-proxy]

kubeproxy_sync_proxy_rules_iptables_total
kubeproxy_sync_proxy_rules_iptables_total{table="filter"}
kubeproxy_sync_proxy_rules_iptables_total{table="nat"}
kubeproxy_sync_proxy_rules_iptables_total{table="nat", instance="192.168.1.188:10249"}

[PromQL]

  • 메트릭 종류
  1. Gauge : 특정 시점의 값을 표현하기 위해서 사용하는 메트릭 타입, CPU 온도나 메모리 사용량에 대한 현재 시점 값
  2. Counter : 누적된 값을 표현하기 위해 사용하는 메트릭 타입, 증가 시 구간 별로 변화(추세) 확인, 계속 증가 → 함수 등으로 활용
  3. Summary : 구간 내에 있는 메트릭 값의 빈도, 중앙값 등 통계적 메트릭
  4. Histogram : 사전에 미리 정의한 구간 내에 있는 메트릭 값의 빈도를 측정 → 함수로 측정 포맷을 변경
node_memory_Active_bytes
node_memory_Active_bytes{instance="192.168.1.188:9100"}
node_memory_Active_bytes{instance!="192.168.1.188:9100"}

# 정규표현식
node_memory_Active_bytes{instance=~"192.168.+"}
node_memory_Active_bytes{instance=~"192.168.1.+"}

# 다수 대상
node_memory_Active_bytes{instance=~"192.168.1.188:9100|192.168.2.170:9100"}
node_memory_Active_bytes{instance!~"192.168.1.188:9100|192.168.2.170:9100"}

# 여러 조건 AND
kube_deployment_status_replicas_available{namespace="kube-system"}
kube_deployment_status_replicas_available{namespace="kube-system", deployment="coredns"}
  • Binary Operators 이진 연산자
    • 산술 이진 연산자 : + - / ^
    • 비교 이진 연산자 : = = ! = > < > = < =
    • 논리/집합 이진 연산자 : and 교집합 , or 합집합 , unless 차집합
# 산술 이진 연산자 : + - * / * ^
node_memory_Active_bytes
node_memory_Active_bytes/1024
node_memory_Active_bytes/1024/1024

# 비교 이진 연산자 : = =  ! = > < > = < =
nginx_http_requests_total
nginx_http_requests_total > 100
nginx_http_requests_total > 10000

# 논리/집합 이진 연산자 : and 교집합 , or 합집합 , unless 차집합
kube_pod_status_ready
kube_pod_container_resource_requests

kube_pod_status_ready == 1
kube_pod_container_resource_requests > 1

kube_pod_status_ready == 1 or kube_pod_container_resource_requests > 1
kube_pod_status_ready == 1 and kube_pod_container_resource_requests > 1

6. 그라파나 Grafana

시계열 데이터를 시각화 해주는 도구 (메트릭, 로그, 트레이스 등)

# 그라파나 버전 확인
kubectl exec -it -n monitoring sts/kube-prometheus-stack-grafana -- grafana cli --version
grafana cli version 11.5.1

# ingress 확인
kubectl get ingress -n monitoring kube-prometheus-stack-grafana
kubectl describe ingress -n monitoring kube-prometheus-stack-grafana

# ingress 도메인으로 웹 접속 : 기본 계정 - admin / prom-operator
echo -e "Grafana Web URL = https://grafana.$MyDomain"


이미 Prometheus가 Data sources로 추가가 되어 있다.

# 서비스 주소 확인
kubectl get svc,ep -n monitoring kube-prometheus-stack-prometheus
NAME                                       TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
service/kube-prometheus-stack-prometheus   ClusterIP   10.100.143.5   <none>        9090/TCP   21m

NAME                                         ENDPOINTS           AGE
endpoints/kube-prometheus-stack-prometheus   192.168.2.93:9090   21m

[대시보드 사용]

- [**Kubernetes / Views / Global**] Dashboard → New → Import → **15757** 력입력 후 Load ⇒ 데이터소스(Prometheus 선택) 후 **Import** 클릭

하나하나를 패널이라고 하고, edit를 누르면 어떤 쿼리로 가져온 그래프인지 알 수 있다.

- [**1 Kubernetes All-in-one Cluster Monitoring KR**] Dashboard → New → Import → **17900** 입력 후 Load ⇒ 데이터소스(Prometheus 선택) 후 **Import** 클릭

노드 정보가 나오지 않으니 아래와 같이 수정

sum by (node) (irate(node_cpu_seconds_total{mode!~"guest.*|idle|iowait", node="$node"}[5m]))
node_cpu_seconds_total
node_cpu_seconds_total{mode!~"guest.*|idle|iowait"}
avg(node_cpu_seconds_total{mode!~"guest.*|idle|iowait"}) by (node)
avg(node_cpu_seconds_total{mode!~"guest.*|idle|iowait"}) by (instance)

# 수정
sum by (instance) (irate(node_cpu_seconds_total{mode!~"guest.*|idle|iowait", instance="$instance"}[5m]))
# 수정 : 메모리 점유율
(node_memory_MemTotal_bytes{instance="$instance"}-node_memory_MemAvailable_bytes{instance="$instance"})/node_memory_MemTotal_bytes{instance="$instance"}

# 수정 : 디스크 사용률
sum(node_filesystem_size_bytes{instance="$instance"} - node_filesystem_avail_bytes{instance="$instance"}) by (instance) / sum(node_filesystem_size_bytes{instance="$instance"}) by (instance)

필터가 제대로 걸리지 않아서 namespace 경우 -> kube_pod_info로 수정

7. 그라파나 얼럿 Alert

Alert 상태에 맞춰 전파하는 기능이 있다.

  • Integration : 슬랙
  • Webhook URL
  • Optional Slack settings → Username : 메시지 구분을 위해서 각자 자신의 닉네임 입력

while true; do curl -s https://nginx.$MyDomain -I | head -n 1; date; done

9. OpenTelemetry(OTel)

  • 추적 , 메트릭 , 로그 와 같은 원격 측정 데이터를 생성하고 관리하도록 설계된 Observability 프레임워크 및 툴킷
  • 공급업체나 도구에 구애받지 않는다 => 따라서 오픈소스 도구는 물론, 상용 제품을 포함한 광범위한 Observability 백엔드와 함께 사용할 수 있다.
  • 원격 측정의 생성, 수집, 관리 및 내보내기에 중점을 둡니다. OpenTelemetry의 주요 목표는 언어, 인프라 또는 런타임 환경에 관계없이 애플리케이션이나 시스템을 쉽게 계측할 수 있다는 것. 원격 측정의 저장 및 시각화는 의도적으로 다른 도구에 맡겨집니다.
  • Distributed Tracing
    클라우드 네이티브 애플리케이션의 요청이 여러 애플리케이션을 거치며 처리되기 때문에, 서비스 간 경계를 넘는 추적이 필수이다.

요청마다 상관관계 ID(Correlation ID) 를 생성하여 이벤트 로그 및 서비스 간 전달
이를 통해 분산된 애플리케이션 전반에서 특정 트랜잭션과 관련된 로그를 추적하여 오류 파악, 성능 점검을 진행한다.

  • Trace : 하나의 요청 또는 트랜잭션과 관련된 모든 활동 (Trace ID 를 통해 고유 식별, 여러 개의 Span 으로 구성)
  • Span : 요청 처리의 각 단계를 나타냄 (시작 및 종료 타임스탬프 포함)

결과적으로는 Trace ID + Span ID로 고유 식별한다.

10. 실습 환경 삭제

# eksctl delete cluster --name $CLUSTER_NAME && aws cloudformation delete-stack --stack-name $CLUSTER_NAME
nohup sh -c "eksctl delete cluster --name $CLUSTER_NAME && aws cloudformation delete-stack --stack-name $CLUSTER_NAME" > /root/delete.log 2>&1 &

# (옵션) 삭제 과정 확인
tail -f delete.log
profile
공블로그

0개의 댓글

관련 채용 정보