AEWS) EKS Observability

CloudNet@팀에서 진행하는 AWS EKS Workshop 실습 스터디 참가글입니다.

AWS EKS Workshop을 기반으로, AWS EKS 배포 및 실습하고, 내용을 공유합니다.


1. Observability란?

일반적으로, Observability란 외부 출력으로 나타난 지식으로만 기반하여, 복잡한 시스템의 내부 상태나 상황을 이해할 수 있는 정도를 말합니다. 시스템의 관찰 가능성이 높을수록, 추가 테스트나 코딩 없이도 식별된 성능 문제에서 근본 원인까지 더 빠르고 정확하게 탐색할 수 있습니다.

In general, observability is the extent to which you can understand the internal state or condition of a complex system based only on knowledge of its external outputs. The more observable a system, the more quickly and accurately you can navigate from an identified performance problem to its root cause, without additional testing or coding.

What is observability?, IBM


Observability란, 다수의 서비스가 생성하는 Log/Metric/Trace 등의 지표들을 통합하여, 시스템에 대한 가시성(Visibility)이 확보된 상태를 의미한다. 통합적 지표들을 통해, 서비스의 가동상태 확인 및 문제 상황 파악과 추적할 수 있는 시스템 구축을 의미한다.

시스템의 작동 상태를 확인하고, 문제사항을 파악하기 위한 도구로는 일반적으로 Monitoring이 언급된다. 다른 단어에는 달라진 이유가 있어야 한다. Monitoring 대비 Observability의 차이점은 무엇일까? 무엇이 추가되었고, 무엇이 개선되었는가?

Observability VS Monitoring


모니터링과 옵저빌리티, 특정 대상의 상태를 파악하고 장애를 추적 한다는 점에서 일견 두 용어간에는 큰 차이가 없게 느껴진다.

두 용어간의 차이를 이해하려면 민첩하며 복잡해진 IT 조직의 변화부터 시작해야 할것이다.

기존의 모놀리식 패턴의 어플리케이션이 개발되고, 운영되던 환경에서는 모니터링 시나리오를 상정하고 운영되었다. CPU/Mem/Stroage/Network 등 장애가 생길만한 지점이 미리 정의되어있고, 해당 부분의 데이터를 수집하고 경보를 보내는 알람 시스템이 사용되었다.

그러나 클라우드 네이티브 환경, 또는 MSA 패턴의 개발, 또는 DevOps 및 CI/CD의 자동화, 또는 컨테이너의 도입 등, 이들 중 무엇이 원인이였던 간에, IT조직은 서로 긴밀한 관계를 갖는 수많은 서비스들을 배포하게 되었고, 세분화된 많은 서비스에 개별 모니터링 대시보드를 붙이는 것으로는 문제 파악이 쉽지 않게 되었다.

복잡한 서비스 구조로 인해서 장애의 원인 파악이 쉽지 않게 된 예시는 TechWorld with Nana 채널의 How Prometheus Monitoring works 영상 중 Specific Use Cases 부분이 제일 인상적이였다.

가령 모종의 이유로, 데이터베이스가 구축된 서버의 Memory가 부족해서 OOM이 발생했다고 가정해보자. 해당 데이터베이스는 유저의 인증정보가 담겨있었기 때문에 연계된 인증 backend 모듈 또한 장애가 생겼고, 인증모듈이 작동되지 않기 때문에 프론트에서는 AuthN 에러가 발생하고, 사용자는 로그인이 되지 않는다고 리포트를 할것이다.

이 상황에서, 문제의 근본 원인을 파악하기 위해서는 역순으로 하나씩 확인을 해가야할것이다.

  • 왜 로그인에 실패 하였는가?
    • Front<>Back 간의 통신은 정상적으로 수행되고 있는가?
    • Backend 인증 모듈은 정상적으로 작동하고 있는가?
    • Backend 인증 모듈에서 어떤 에러 로그가 발생하고있는가?
    • Backend는 Dababase의 정보에 접근이 가능한가?
    • Database의 접근이 되지 않는 이유는? 작동상태가 어떠한가?
    • Host 머신의 Memory는 왜 부족해졌는가?

로그인 장애가 났다고 해서, 에러 페이지만 / 인증 서비스만 / 데이터베이스의 작동 현황만 살펴보아서는 문제를 해결할 수 없을 것 이다. 각 서비스 마다의 Log/Metric/Trace를 종합적으로 살펴보아야하고, 일부라도 누락되어있으면 다음 단계로 나아갈 수 없을 것이다.

이번 챕터에서는, 바로 이러한 클러스터의 상태의 종합적인 가시성을 확보할 수 있는 여러 도구들을 구축해보고 실습할것이다.

실습 환경 배포

  • Amazon EKS 원클릭 배포 ver3!
# YAML 파일 다운로드
curl -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/K8S/eks-oneclick3.yaml

# CloudFormation 스택 배포
aws cloudformation deploy --template-file eks-oneclick3.yaml --stack-name myeks --parameter-overrides KeyName=nasirk17 SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32  MyIamUserAccessKeyID=(개인 액세스키) MyIamUserSecretAccessKey=(개인 시크릿키) ClusterBaseName=myeks --region ap-northeast-2

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

# 작업용 EC2 SSH 접속
ssh -i ~/.ssh/nasirk17.pem ec2-user@$(aws cloudformation describe-stacks --stack-name myeks --query 'Stacks[*].Outputs[0].OutputValue' --output text)
  • 이후 추가적으로, AWS LB/ExternalDNS/EBS(gp3)/EFS, kube-ops-view등을 구성함

2. EKS Console

AWS 관리 콘솔의 메뉴 중, EKS 콘솔에 접근해서 Kubernetes API 리소스들을 확인해 볼 수 있다.

터미널의 명령줄 CLI를 사용하여도 동일한 리소스 조회 값을 볼 수 있지만, 명령어에 익숙하지 않다면 / 일일히 명령어를 치기 번거롭다면 Web UI를 통해 어떤 리소스들이 있는지, 상태는 어떤지 확인 할 수 있다.

해당 화면을 보기 위해서는 eks:AccessKubernetesApi 등과 같은 적절한 IAM 권한이 부여되어있어야한다.

EKS Console 화면

3. Logging in EKS

EKS에서 로깅을 해야할 주요 주체는 ControlPlane / Node / Application가 있다.

  • ControlPlane
    • API/Audit/Authenticator/ControllerManager/Scheduler
    • 어떤 사용자가, 어떤 API를 호출했고, 어떤 변화가 있었는지
  • Application
    • 컨테이너화된 어플리케이션의 작동 로그 수집
  • Node
    • Container/Pod의 로그는 호스트머신의 /var/log/containers 또는 /var/log/pods에 저장되어 있음

ControlPlane Logging

쿠버네티스의 감사로그(Audit Log)는 별도의 Policy 리소스를 생성하여 수집을 설정할 수 있다.

그러나 EKS의 경우, EKS의 컨트롤 플레인은 AWS의 영역이기 때문에 CP 로깅의 설정을 켜고/끄기만 가능하고, 정책의 내용의 커스텀은 불가능하다

실습!

  • ControlPlane 로깅 활성화
# 모든 로깅 활성화
aws eks update-cluster-config --region $AWS_DEFAULT_REGION --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-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

CP로깅 활성화후, CloudWatch의 로그 스트림에서 실제 로그들의 조회가 가능하다. 그러나 원본로그를 직접 살펴보는것은 쉽지않다.

CloudWatch Logs Insights를 사용하면 쿼리식으로 간단히 로그 데이터를 검색하고 분석할 수 있다.

Log Insights 실습

  • Log Insights URL
  • https://ap-northeast-2.console.aws.amazon.com/cloudwatch/home?region=ap-northeast-2#logsV2:logs-insights

  • 쿼리 예시
# 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
  • CLI를 통해서도 로그 인사이트 사용 가능
# CloudWatch Log Insight Query
aws logs get-query-results --query-id $(aws logs start-query \
--log-group-name '/aws/eks/myeks/cluster' \
--start-time `date -d "-1 hours" +%s` \
--end-time `date +%s` \
--query-string 'fields @timestamp, @message | filter @logStream ~= "kube-scheduler" | sort @timestamp desc' \
| jq --raw-output '.queryId')
Log Insights 실습
터미널에서 Log Insight 실행

Container Logging

컨테이너 환경의 로그는 표준 출력 stdout과 표준 에러 stderr로 보내는 것이 권고된다

해당 권고에 따라 작성된 컨테이너 애플리케이션의 로그는 해당 파드 안으로 접속하지 않아도 사용자는 외부에서 kubectl logs 명령어로 애플리케이션 종류에 상관없이,
애플리케이션마다 로그 파일 위치에 상관없이, 단일 명령어로 조회 가능.

Test용 NGINX 웹서버 배포 및 로그 확인

  • Helm을 사용한 NGINX 웹서버 배포
# NGINX 웹서버 배포
helm repo add bitnami https://charts.bitnami.com/bitnami

# 사용 리전의 인증서 ARN 확인
CERT_ARN=$(aws acm list-certificates --query 'CertificateSummaryList[].CertificateArn[]' --output text)
echo $CERT_ARN

# 도메인 확인
echo $MyDomain

# 파라미터 파일 생성
cat <<EOT > nginx-values.yaml
service:
    type: NodePort

ingress:
  enabled: true
  ingressClassName: alb
  hostname: nginx.$MyDomain
  path: /*
  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: $CLUSTER_NAME-ingress-alb
    alb.ingress.kubernetes.io/group.name: study
    alb.ingress.kubernetes.io/ssl-redirect: '443'
EOT
cat nginx-values.yaml | yh

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

# 확인
kubectl get ingress,deploy,svc,ep nginx
kubectl get targetgroupbindings # ALB TG 확인

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

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

# (참고) 삭제 시
helm uninstall nginx

Test NGINX 접속 로그 확인

kubectl logs deploy/nginx -f 명령어로 logs를 확인할 수 있는 이유는 해당 컨테이너(NGINX)빌드 시, nginx의 access/error 로그를 stdout과 stderr로 보내도록 설정이 되어있기 때문이다.

  • 컨테이너 로그 파일 위치 확인
[root@myeks-bastion-EC2 ~]# kubectl exec -it deploy/nginx -- ls -l /opt/bitnami/nginx/logs/
total 0
lrwxrwxrwx 1 root root 11 Apr 24 10:13 access.log -> /dev/stdout
lrwxrwxrwx 1 root root 11 Apr 24 10:13 error.log -> /dev/stderr
  • Bitnami NGINX Dockerfile
...
RUN ln -sf /dev/stdout /opt/bitnami/nginx/logs/access.log
RUN ln -sf /dev/stderr /opt/bitnami/nginx/logs/error.log
...

Node Logging

컨테이너가 실제 가동되는 워커노드에서는, Application/Host/Dataplane 등 다양한 로그가 저장된다.

그러나 워커노드의 수가 많아질수록, 개별 워커노드에 접근해서 해당 로그를 확인하기가 쉽지않다. 따라서 다음 실습에서, FluentBit을 사용하여 메트릭과 로그를 수집하고, CloudWatch Container Insight를 통해 조회하는 실습을 수행할 것이다.

Node의 로그 확인

  • application 로그 소스(All log files in /var/log/containers → 심볼릭 링크 /var/log/pods/<컨테이너>, 각 컨테이너/파드 로그
# 로그 위치 확인
#ssh ec2-user@$N1 sudo tree /var/log/containers
#ssh ec2-user@$N1 sudo ls -al /var/log/containers
for node in $N1 $N2 $N3; do echo ">>>>> $node <<<<<"; ssh ec2-user@$node sudo tree /var/log/containers; echo; done
for node in $N1 $N2 $N3; do echo ">>>>> $node <<<<<"; ssh ec2-user@$node sudo ls -al /var/log/containers; echo; done

# 개별 파드 로그 확인 : 아래 각자 디렉터리 경로는 다름
ssh ec2-user@$N3 sudo tail -f /var/log/pods/default_nginx-685c67bc9-ctlh7_7aa3b6aa-5257-42b2-96bd-b9452a12677c/nginx/0.log

  • host 로그 소스(Logs from /var/log/dmesg, /var/log/secure, and /var/log/messages), 노드(호스트) 로그
# 로그 위치 확인
#ssh ec2-user@$N1 sudo tree /var/log/ -L 1
#ssh ec2-user@$N1 sudo ls -la /var/log/
for node in $N1 $N2 $N3; do echo ">>>>> $node <<<<<"; ssh ec2-user@$node sudo tree /var/log/ -L 1; echo; done
for node in $N1 $N2 $N3; do echo ">>>>> $node <<<<<"; ssh ec2-user@$node sudo ls -la /var/log/; echo; done

# 호스트 로그 확인
#ssh ec2-user@$N1 sudo tail /var/log/dmesg
#ssh ec2-user@$N1 sudo tail /var/log/secure
#ssh ec2-user@$N1 sudo tail /var/log/messages
for log in dmesg secure messages; do echo ">>>>> Node1: /var/log/$log <<<<<"; ssh ec2-user@$N1 sudo tail /var/log/$log; echo; done
for log in dmesg secure messages; do echo ">>>>> Node2: /var/log/$log <<<<<"; ssh ec2-user@$N2 sudo tail /var/log/$log; echo; done
for log in dmesg secure messages; do echo ">>>>> Node3: /var/log/$log <<<<<"; ssh ec2-user@$N3 sudo tail /var/log/$log; echo; done
  • dataplane 로그 소스(/var/log/journal for kubelet.service, kubeproxy.service, and docker.service), 쿠버네티스 데이터플레인 로그
# 로그 위치 확인
#ssh ec2-user@$N1 sudo tree /var/log/journal -L 1
#ssh ec2-user@$N1 sudo ls -la /var/log/journal
for node in $N1 $N2 $N3; do echo ">>>>> $node <<<<<"; ssh ec2-user@$node sudo tree /var/log/journal -L 1; echo; done

# 저널 로그 확인 - 링크
ssh ec2-user@$N3 sudo journalctl -x -n 200
ssh ec2-user@$N3 sudo journalctl -f

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

CloudWatch Container Insight : 노드에 CW Agent 파드와 Fluent Bit 파드가 데몬셋으로 배치되어 Metrics 와 Logs 수집

CloudWatch Container Insight Architecutre

  • [수집]플루언트비트 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), 쿠버네티스 데이터플레인 로그
  • [저장] : CloudWatch Logs 에 로그를 저장, 로그 그룹 별 로그 보존 기간 설정 가능
  • [시각화] : CloudWatch 의 Logs Insights 를 사용하여 대상 로그를 분석하고, CloudWatch 의 대시보드로 시각화한다

CloudWatch Container Insight 설치

  • 설치 과정
# 설치
FluentBitHttpServer='On'
FluentBitHttpPort='2020'
FluentBitReadFromHead='Off'
FluentBitReadFromTail='On'
curl -s https://raw.githubusercontent.com/aws-samples/amazon-cloudwatch-container-insights/latest/k8s-deployment-manifest-templates/deployment-mode/daemonset/container-insights-monitoring/quickstart/cwagent-fluent-bit-quickstart.yaml | sed 's/{{cluster_name}}/'${CLUSTER_NAME}'/;s/{{region_name}}/'${AWS_DEFAULT_REGION}'/;s/{{http_server_toggle}}/"'${FluentBitHttpServer}'"/;s/{{http_server_port}}/"'${FluentBitHttpPort}'"/;s/{{read_from_head}}/"'${FluentBitReadFromHead}'"/;s/{{read_from_tail}}/"'${FluentBitReadFromTail}'"/' | kubectl apply -f -

# 설치 확인
kubectl get-all -n amazon-cloudwatch
kubectl get ds,pod,cm,sa -n amazon-cloudwatch
kubectl describe clusterrole cloudwatch-agent-role fluent-bit-role                          # 클러스터롤 확인
kubectl describe clusterrolebindings cloudwatch-agent-role-binding fluent-bit-role-binding  # 클러스터롤 바인딩 확인
kubectl -n amazon-cloudwatch logs -l name=cloudwatch-agent -f # 파드 로그 확인
kubectl -n amazon-cloudwatch logs -l k8s-app=fluent-bit -f    # 파드 로그 확인
for node in $N1 $N2 $N3; do echo ">>>>> $node <<<<<"; ssh ec2-user@$node sudo ss -tnlp | grep fluent-bit; echo; done


# cloudwatch-agent 설정 확인
kubectl describe cm cwagentconfig -n amazon-cloudwatch
{
  "agent": {
    "region": "ap-northeast-2"
  },
  "logs": {
    "metrics_collected": {
      "kubernetes": {
        "cluster_name": "myeks",
        "metrics_collection_interval": 60
      }
    },
    "force_flush_interval": 5
  }
}

# Fluent Bit Cluster Info 확인
kubectl get cm -n amazon-cloudwatch fluent-bit-cluster-info -o yaml | yh
apiVersion: v1
data:
  cluster.name: myeks
  http.port: "2020"
  http.server: "On"
  logs.region: ap-northeast-2
  read.head: "Off"
  read.tail: "On"
kind: ConfigMap
...

# Fluent Bit 로그 INPUT/FILTER/OUTPUT 설정 확인
## 설정 부분 구성 : application-log.conf , dataplane-log.conf , fluent-bit.conf , host-log.conf , parsers.conf
kubectl describe cm fluent-bit-config -n amazon-cloudwatch
...
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*
...

[FILTER]
    Name                kubernetes
    Match               application.*
    Kube_URL            https://kubernetes.default.svc:443
...

[OUTPUT]
    Name                cloudwatch_logs
    Match               application.*
    region              ${AWS_REGION}
    log_group_name      /aws/containerinsights/${CLUSTER_NAME}/application
...

# (참고) 삭제
curl -s https://raw.githubusercontent.com/aws-samples/amazon-cloudwatch-container-insights/latest/k8s-deployment-manifest-templates/deployment-mode/daemonset/container-insights-monitoring/quickstart/cwagent-fluent-bit-quickstart.yaml | sed 's/{{cluster_name}}/'${CLUSTER_NAME}'/;s/{{region_name}}/'${AWS_DEFAULT_REGION}'/;s/{{http_server_toggle}}/"'${FluentBitHttpServer}'"/;s/{{http_server_port}}/"'${FluentBitHttpPort}'"/;s/{{read_from_head}}/"'${FluentBitReadFromHead}'"/;s/{{read_from_tail}}/"'${FluentBitReadFromTail}'"/' | kubectl delete -f 

  • Logging 확인: Cloudwatch > 로그 그룹

유용한 Log Insights Query 예시

  • Logs Insight
# 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
  • Metric 확인: Cloudwatch > 인사이트> Container Insights

5. Metrics-server & kwatch & botkube

Metrics-server

Metrics-server는 kubelet으로부터 수집한 리소스 메트릭을 수집 및 집계하는 클러스터 애드온 구성 요소이다.

resource-metrics-pipeline, k8s doc

Metrics는 kubelet을 통해 수집한 metric 정보를 API서버로 노출하여 HPA/VPA와 같은 오토스케일링기능을 사용할 수 있도록하고, kubectl top과 같은 명령어를 사용할 수 있게한다.

Metrics-server 실습

# 배포
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

# 메트릭 서버 확인 : 메트릭은 15초 간격으로 cAdvisor를 통하여 가져옴
kubectl get pod -n kube-system -l k8s-app=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'
Metrics를 통한 CPU/Mem 수집

kwatch

kwatch는 Kubernetes(K8s) 클러스터의 모든 변경 사항을 모니터링하고, 실행 중인 앱의 충돌을 실시간으로 감지하며, 채널(Slack, Discord 등)에 알림을 즉시 게시할 수 있도록 도와줍니다.

kwatch helps you monitor all changes in your Kubernetes(K8s) cluster, detects crashes in your running apps in realtime, and publishes notifications to your channels (Slack, Discord, etc.) instantly

kwatch Github

kwatch를 통해서 클러스터 내부의 변경/이상 감지시, 별도의 채널을 통해 알림을 받을 수 있다.

사전에 생성해둔 별도 슬랙 채널의 Webhook을 사용하여 실습을 진행할 것이다.

kwatch 배포

  • 배포
# configmap 생성
cat <<EOT > ~/kwatch-config.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: kwatch
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: kwatch
  namespace: kwatch
data:
  config.yaml: |
    alert:
      slack:
        webhook: '개인 Webhook'
        title: $NICK-EKS
        #text:
    pvcMonitor:
      enabled: true
      interval: 5
      threshold: 70
EOT
kubectl apply -f kwatch-config.yaml

# 배포
kubectl apply -f https://raw.githubusercontent.com/abahmed/kwatch/v0.8.3/deploy/deploy.yaml
  • 배포 확인

잘못된 이미지의 파드 배포 및 수정

  • 에러 이미지 배포
# 터미널1
watch kubectl get pod

# 잘못된 이미지 정보의 파드 배포
kubectl apply -f https://raw.githubusercontent.com/junghoon2/kube-books/main/ch05/nginx-error-pod.yml
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
  • kwatch를 통한 알람 수신

botkube

개요(Overview)

Botkube는 Kubernetes 클러스터를 모니터링하고, 중요한 deployments를 디버깅하고, Kubernetes 리소스에 대한 검사를 실행하여 표준 관행에 대한 권장 사항을 제공하는 데 도움이 됩니다. Slack, Discord 또는 Mattermost와 같은 여러 커뮤니케이션 플랫폼과 통합됩니다.

또한 애플리케이션이나 클러스터를 디버깅하는 데 도움이 되는 Botkube를 통해 K8s 클러스터에서 kubectl 명령을 실행할 수도 있습니다.

Botkube helps you monitor your Kubernetes cluster, debug critical deployments and gives recommendations for standard practices by running checks on the Kubernetes resources. It integrates with multiple communication platforms, such as SlackDiscord, or Mattermost.

You can also execute kubectl commands on K8s cluster via Botkube which helps debugging an application or cluster.

botkube Github
Botkube Architecture

kwatch와 비슷하게 k8s클러스터의 이벤트 알림을 슬랙으로 전송해주는 도구이다.

kwatch와의 차이점은, 단순 알람 수신외에, 수신된 알람에 대해서 interactive한 대응이 가능하다는 점이 주요 차이점인듯 하다.

botkube 설치과정

  • 필요한 토큰 발급 후 환경변수로 저장
# Slack App 생성 후 Bot token 확인
export SLACK_API_BOT_TOKEN="{botToken, xoxb-}"

# 웹소켓 연결을 위한 App-Level Token 생성
export SLACK_API_APP_TOKEN="${appToken,xapp-}"
  • k8s 클러스터에 botkube backend 배포
# repo 추가
helm repo add botkube https://charts.botkube.io
helm repo update

# 변수 지정
export ALLOW_KUBECTL=true
export ALLOW_HELM=true
export SLACK_CHANNEL_NAME= botkube-chan

#
cat <<EOT > botkube-values.yaml
actions:
  'describe-created-resource': # kubectl describe
    enabled: true
  'show-logs-on-error': # kubectl logs
    enabled: true

executors:
  k8s-default-tools:
    botkube/helm:
      enabled: true
    botkube/kubectl:
      enabled: true
EOT

# 설치
helm install --version v1.0.0 botkube --namespace botkube --create-namespace \
--set communications.default-group.socketSlack.enabled=true \
--set communications.default-group.socketSlack.channels.default.name=${SLACK_CHANNEL_NAME} \
--set communications.default-group.socketSlack.appToken=${SLACK_API_APP_TOKEN} \
--set communications.default-group.socketSlack.botToken=${SLACK_API_BOT_TOKEN} \
--set settings.clusterName=${CLUSTER_NAME} \
--set 'executors.k8s-default-tools.botkube/kubectl.enabled'=${ALLOW_KUBECTL} \
--set 'executors.k8s-default-tools.botkube/helm.enabled'=${ALLOW_HELM} \
-f botkube-values.yaml botkube/botkube

botkube 사용

  • Slack 채널 메세지창에 kubectl 명령어 사용 가능
# 연결 상태, notifications 상태 확인
@Botkube ping
@Botkube status notifications

# 파드 정보 조회
@Botkube k get pod
@Botkube kc get pod --namespace kube-system
@Botkube kubectl get pod --namespace kube-system -o wide

# Actionable notifications
@Botkube kubectl

잘못된 이미지 파드 배포 및 확인


# 터미널1
watch kubectl get pod

# 잘못된 이미지 정보의 파드 배포
kubectl apply -f https://raw.githubusercontent.com/junghoon2/kube-books/main/ch05/nginx-error-pod.yml
kubectl get events -w
@Botkube k get pod

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

# 삭제
kubectl delete pod nginx-19
  • Botkube 배포 및 기본 명령어 활용
  • 잘못된 이미지 배포 및 확인
  • 알람 하단의 run command를 눌러, 상세정보 확인 가능(describe,get,top,logs)

기본설정으로는 클러스터내부의 환경 조회만 가능하도록 설정되어있으나, 별도 설정(권한부여)를 하면 슬랙을 통한 디버깅도 가능할 듯 함

기본설정 Botkube의 clusterrole, get/watch/list 허용 <> create/delete 불가
create/delete가 불가능

6. Kubecost

kubectl-cost는 kubectl의 API를 통해, 과거 비용 및 예측된 미래 비용과 같은 쿠버네티스 비용 정보에 대한 간편한 CLI 액세스를 제공하는 kubectl 플러그인이다. 이를 통해 개발자, 개발 운영자 및 기타 사용자는 모든 쿠버네티스 워크로드의 비용과 효율성을 신속하게 확인할 수 있다.

kubectl-cost is a kubectl plugin that provides easy CLI access to Kubernetes cost information, like historical cost and predicted future cost, via Kubecost’s APIs. It allows developers, devops, and others to quickly determine the cost & efficiency of any Kubernetes workload.

kubectl-cost Github

kubecost는 opencost를 기반으로 구축되었으며 AWS에서 적극 지원, 쿠버네티스 리소스별 비용 분류 가시화 제공.

  • Pricing – 링크 : Free(메트릭 15일 보존, Business(메트릭 30일 보존, …), Enterprise(.)
  • Amazon EKS cost monitoring with Kubecost architecture링크
kubecost on EKS Architecture

kubecost 설치

# 
cat <<EOT > cost-values.yaml
global:
  grafana:
    enabled: true
    proxy: false

priority:
  enabled: false
networkPolicy:
  enabled: false
podSecurityPolicy:
  enabled: false

persistentVolume:
    storageClass: "gp3"

prometheus:
  kube-state-metrics:
    disabled: false
  nodeExporter:
    enabled: true

reporting:
  productAnalytics: true
EOT

# kubecost chart 에 프로메테우스가 포함되어 있으니, 기존 프로메테우스-스택은 삭제하자 : node-export 포트 충돌 발생
helm uninstall -n monitoring kube-prometheus-stack

# 배포
kubectl create ns kubecost
helm install kubecost oci://public.ecr.aws/kubecost/cost-analyzer --version 1.103.2 --namespace kubecost -f cost-values.yaml

# 배포 확인
kubectl get-all -n kubecost
kubectl get all -n kubecost

# kubecost-cost-analyzer 파드 IP변수 지정 및 접속 확인
CAIP=$(kubectl get pod -n kubecost -l app=cost-analyzer -o jsonpath={.items[0].status.podIP})
curl -s $CAIP:9090

# 외부에서 bastion EC2 접속하여 특정 파드 접속 방법 : socat(SOcket CAT) 활용 - 링크
yum -y install socat
socat TCP-LISTEN:80,fork TCP:$CAIP:9090
웹 브라우저에서 bastion EC2 IP로 접속

대시보드를 간단히 살펴보았을때, 주로 볼 영역은 Monitor인듯하다

  • Monitor – Allocations
  • Namespace 별로 사용량과 금액 지표 환산
    • Network Traffic Cost에 대한 의문, 문서 찾아보니 container_network_transmit_bytes_total 지표 기반으로 산출하는듯 한데, AWS 측면에서 AZ내 통신비용없음 AZ간 통신비용존재 등도 구분이 가능한지?
    • Efficiency 기능이 재밌었다. pod/deploy 생성시 request 한 자원대비 실제 어느정도 사용되고있는지 %로 보여주는 지표. 적정량의 사용량으로 조정하기에 좋은 지표인듯 하다.
  • Monitor – Assets
  • 클러스터내의 리소스 뿐만아니라, 클러스터 외부 리소스(Volume,LB, 등)의 비용


7. Grafana Alert w/Image (진행중)

이번주차 실습중 Prometheus와 Grafana 부분은 지난 PKOS의 진행내용과 유사한 부분이 많아, 도전과제의 ‘그래프 이미지가 포함된 알람을 슬랙으로 전달하기’ 부분을 시도해보았습니다. 하지만 결과적으로 아직 성공은 못했습니다. 그래도 중간과정을 간단히 정리해 보았습니다.

Helm을 사용한 Grafana Image Renderer 설치

그라파나 대시보드의 panel을 png image로 변환하기 위해서는 grafana image renderer를 구성해야합니다.(Set up image rendering) 그러나 설치 안내 문서(Grafana Image Renderer Installation instructions)에는 Grafana CLI 또는 docker를 사용한 설치만 안내되어있어, 쿠버네티스 환경에 적합하지 않다고 생각했습니다.

그러나 Helm Grafana chart에 values에 imagerender가 구성되어 있기 때문에, 해당 부분을 enable하면 k8s 클러스터 상에 이미지 렌더러 설치가 가능합니다.

kube-prometheus stack에 Grafana Image Renderer 배포

  • 기존 monitor-values.yaml에 Image Renderer enable 구문 추가
  • https://github.com/grafana/helm-charts/issues/1556
grafana:
  defaultDashboardsTimezone: Asia/Seoul
  adminPassword: prom-operator

  imageRenderer:
    enabled: true
    networkPolicy:
      limitIngress: false
  grafana.ini:
    unified_alerting.screenshots:
      capture: true

  ingress:
    enabled: true
    ingressClassName: alb
    hosts: 
      - grafana.devxmonitor.click
    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: arn:aws:acm:ap-northeast-2:596152156334:certificate/749846b6-b26e-4108-9e2e-1642c3c9f60c
      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
      alb.ingress.kubernetes.io/ssl-redirect: '443'
  • 배포 확인
Image Renderer pod 배포 확인
  • Panel 클릭 > share시 Direct link rendered image 사용 가능
생성된 png이미지

여기까지 이미지 렌더링에는 금방 성공해두었으니, 알람 메세지에 첨부는 간단할것이라고 생각했으나, 실제 알람 메세지에 이미지가 첨부되지 않습니다..

중간 과정은 생략하고, 추후 작업방향을 정리해두면,
incoming-webhook을 사용해 웹훅방식으로 전송받고 있었는데, 깃헙 이슈 살펴보니 웹훅방식으로 알람을 전달받을시에는 S3와 같이 외부 저장소를 경유해야 이미지 첨부된 알람이 가능하고, 아니면 적절한 권한(chat:write and files:write)을 부여받은 슬랙 앱을 사용해야 되는듯 합니다.

Leave a Reply