PKOS Study #4은 Prometheus, Grafana을 사용하여 k8s 환경 Monitoring, Alert을 진행해보려 한다.
0. 환경 구성
이번 실습은 지난 Study #3의 환경에 그대로 병행해서 진행할 예정이다. 리소스를 지우고 다시 만드는데 시간이 소요되고 kOps설치 시 레이턴시 이슈인지 한 번에 되지 않을 때가 있어 이번엔 이전 환경을 삭제하지 않고 그대로 진행한다.
1. Metric-server 확인
Metric-server : kubelet으로부터 리소스 매트릭을 수집 및 집계하는 클러스터 애드온 구성 요소이다. 실습에 사용 된 yaml에는 Metric-server가 올라오는 낸용이 포함되어 있기 때문에 실습 내용으로 진행하게 되면 Metric-server을 바로 확인 할 수 있다.
node(cp, worker)들의 CPU, Memory 사용량을 확인할 수 있고 각 pod들의 CPU, Memory 점유량도 확인할 수 있다. 나는 지난 실습 환경을 그대로 유지하고 있기 때문에 gitlab, argocd pod들의 내용도 확인할 수 있다.




2. Prometheus를 통한 Metric 수집
Prometheus : Soundcloud에서 제작한 오픈소스 시스템 Monitoring, Alert 툴킷이다.
특징으로는 간단한 구조를 갖추고 있어 수월한 운영을 할 수 있고 강력한 쿼리 기능을 통해 다양한 결과값을 도출할 수 있다. 뒤에 실습할 Grafana와의 조합을 통해 시각화를 할 수 있고 ELK와 같은 로깅 방식이 아니라 시스템으로부터 모니터링 지표를 수집하여 저장하는 시스템이다. 자세한 구조는 아래 그림과 같다.

설치는 아래와 같이 진행한다. 이번엔 실습 때 제공되는 코드에서 인증서 ARN과 Cluster 주소를 한 번에 넣어주는 방식을 사용한다.
예제로 제공 받은 코드는 Grafana도 같이 helm Install을 사용하여 설치를 진행하게 된다. 아래 코드로 설치를 진행하면 Prometheus와 Grafana가 같이 설치되는 점 참고하여 진행한다.
<em># 모니터링</em>
kubectl create ns monitoring
watch kubectl get pod,pvc,svc,ingress -n monitoring
<em># 사용 리전의 인증서 ARN 확인</em>
CERT_ARN=`aws acm list-certificates --query 'CertificateSummaryList[].CertificateArn[]' --output text`
echo "alb.ingress.kubernetes.io/certificate-arn: $CERT_ARN"
<em># 설치</em>
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
<em># 파라미터 파일 생성</em>
cat <<EOT > ~/monitor-values.yaml
alertmanager:
ingress:
enabled: true
ingressClassName: alb
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/group.name: "monitoring"
hosts:
- alertmanager.$KOPS_CLUSTER_NAME
paths:
- /*
grafana:
defaultDashboardsTimezone: Asia/Seoul
adminPassword: prom-operator
ingress:
enabled: true
ingressClassName: alb
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/group.name: "monitoring"
hosts:
- grafana.$KOPS_CLUSTER_NAME
paths:
- /*
prometheus:
ingress:
enabled: true
ingressClassName: alb
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/group.name: "monitoring"
hosts:
- prometheus.$KOPS_CLUSTER_NAME
paths:
- /*
prometheusSpec:
podMonitorSelectorNilUsesHelmValues: false
serviceMonitorSelectorNilUsesHelmValues: false
retention: 5d
retentionSize: "10GiB"
EOT
<em># 배포</em>
helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack --version 45.7.1 \
--set prometheus.prometheusSpec.scrapeInterval='15s' --set prometheus.prometheusSpec.evaluationInterval='15s' \
-f monitor-values.yaml --namespace monitoring

위와 같이 정상적으로 배포 된 것을 확인할 수 있다.
프로메테우스가 각 서비스의 metric 경로에 HTTP GET 방식으로 메트릭 정보를 가져와 TSDB 형식으로 저장을 하게 된다.
<em># 아래 처럼 프로메테우스가 각 서비스의 9100 접속하여 메트릭 정보를 수집</em>
kubectl get node -owide
kubectl get svc,ep -n monitoring kube-prometheus-stack-prometheus-node-exporter
<em># 마스터노드에 lynx 설치</em>
ssh -i ~/.ssh/id_rsa ubuntu@api.$KOPS_CLUSTER_NAME hostname
ssh -i ~/.ssh/id_rsa ubuntu@api.$KOPS_CLUSTER_NAME sudo apt install lynx -y
<em># 노드의 9100번의 /metrics 접속 시 다양한 메트릭 정보를 확인할수 있음 : 마스터 이외에 워커노드도 확인 가능</em>
ssh -i ~/.ssh/id_rsa ubuntu@api.$KOPS_CLUSTER_NAME lynx -dump localhost:9100/metrics

Prometheus 주소(Ingress와 연결 된 Domain 주소)에 접속하면 아래와 같이 접속 화면을 확인할 수 있다.

2. Grafana을 통한 시각화
Grafana는 Grafana Labs에서 개발한 Metric/Log 시각화 Dashboard이다. 다양한 Datasource을 연결해서 시각화하여 제공할 수 있다.
실습 예제로 제공 된 코드에는 Grafana도 Prometheus와 같이 Helm install를 통해 설치하기 때문에 별도 설치하는 내용은 건너뛰고 Grafana에 접근하여 진행하도록 한다. 초기 제공 되는 계정을 사용하여 로그인하고 암호를 변경한다.

암호 변경하고 Data Source을 확인해보면, http://kube-prometheus-stack-prometheus.monitoring:9090/ 가 입력되어 있는 것을 알 수 있다.
kube-system 이외에 상태를 체크하기 위해 테스트용 pod을 배포한 다음 대쉬보드를 통해 확인해 볼 예정이다.
우선 Dashobard을 생성한다. 추천 받은 Dashobard 목록 중 한국어 버전으로 나온 걸 추가해볼 생각이다.
Dashboard->Import을 선택한다.

Import via Grafana.com에 추천 받은 Dashboard의 번호를 입력한다. 여기서는 13770을 입력하고 Load을 클릭한다.

Name과 Data Source을 확인하고 Import을 클릭한다.

몇 번의 클릭만으로 훌륭한 대쉬보드를 가져올 수 있게 됐다. 여기서 불필요한 건 제거하고 필요하다고 생각되는 걸 추가하는 식으로 나만의 Dashboard을 꾸밀 수 있다.
테스트 목적의 Application은 nginx 웹서버를 배포할 예정이다. 배포할 때 참고하는 yaml에는 monitoring 옵션을 활성화해서 바로 Prometheus에서 모니터링할 수 있도록 한다.


위 사진 처럼 Prometheus에 바로 해당 내용을 확인할 수 있다. 예제로 Connections_active을 Graph로 확인해봤다. 메트릭 수집이 정상적으로 되는 것을 확인했으니 이를 Grafana에서 확인해보도록 하겠다.

위에서 Dashboard을 Import할 때와 같은 방식으로 Import ID는 12708을 사용해서 생성한 대쉬보드이다. 요청 수나 Active Connection 수 등을 확인할 수 있다. 앞서 Prometheus에서 확인한 Connections_active와 동일한 모양임을 알 수 있다.
이와 같이 Prometheus에서 수집되는 데이터를 바탕으로 Grafana에서 시각화할 수 있는 것을 확인해보았다.
3. kwatch을 통한 Application 문제 전송
Grafana로 시각화하는 것까지 확인했으니 이제 kwatch을 사용해 Application 충돌 등 이슈를 확인해 Webhook 전송하는 것을 진행하려 한다.
kwatch : Kubernetes(K8s) 클러스터의 모든 변경 사항을 모니터링하고, 실행 중인 앱의 충돌을 실시간으로 감지하고, 채널(Slack, Discord 등)에 즉시 알림을 게시하도록 도와주는 서비스이다. 나는 Study에서 제공 된 webhook이 아닌 개인 스터디 목적으로 생성한 Workspace에 별도 webhook을 생성해서 진행할 예정이다.
우선 간단하게 웹훅을 생성하는 방법을 소개한다.

내가 속해있는 Workspace에서 “+ 앱 추가”을 선택한다.

검색창에 webhook을 검색하고 나온 결과 중 incoming-webhooks에 추가 버튼을 클릭한다.

브라우저로 URL이 연결되면서 위와 같은 페이지를 볼 수 있다. 여기서 Slack에 추가를 선택한다.

Webhook을 초대할 채널명을 선택한다. 여기서는 #스터디 를 선택하였다. 이후 “수신 웹후크 통합 앱 추가”를 선택한다.

Webhook 이름과 아이콘(선택사항)을 변경하고 웹후크URL을 따로 복사해놓고 설정 저장을 클릭한다. 복사한 웹후크 URL은 아래 kwatch 배포 코드에서 webhook 주소에 해당 URL을 입력해주면 된다.

Slack-bot이 정상적으로 추가 된 것을 확인할 수 있다. (위 추가 된 건 테스트 용도로 생성한 거니 무시해도 된다.)
이제 Webhook을 생성했으니 kwatch 배포를 진행한다.
<em># configmap 생성</em>
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: 'https://mywebhookurl'
<em>#title:</em>
<em>#text:</em>
pvcMonitor:
enabled: true
interval: 5
threshold: 70
EOT
kubectl apply -f kwatch-config.yaml
<em># 배포</em>
kubectl apply -f https://raw.githubusercontent.com/abahmed/kwatch/v0.8.3/deploy/deploy.yaml
위 코드에서 webhook URL만 내 웹훅 URL로 변경하고 실행하면 된다.
배포를 하게 되면 내 채널에 kwatch started라는 메시지가 뜨게 된다.

이제 kwatch가 정상적으로 역할을 수행하는지 확인하기 위해서 잘못 된 이미지 정보를 갖고 있는 pod을 배포해보도록 한다.
<em># 터미널1</em>
watch kubectl get pod
<em># 잘못된 이미지 정보의 파드 배포</em>
kubectl apply -f https://raw.githubusercontent.com/junghoon2/kube-books/main/ch05/nginx-error-pod.yml
kubectl get events -w

Error가 나오면서 배포가 완료되지 않는 것을 알 수 있다.

마찬가지로 Slack에서도 위와 같은 경고를 보내준다. Logs까지 상세하게 잡아줘서 대략적으로 어떤 문제인지 확인할 수 있다.
이제 문제가 발생했으니 해결해야 할 차례다. 위에서 사용한 yaml을 불러와 본다.

image: nginx:1.19.19 는 존재하지 않는 이미지이기 때문에 배포가 제대로 되지 않았음을 알 수 있다.

nginx-19 pod의 이미지 정보를 1.19.19에서 1.19로 변경하는 작업을 진행해줬다.

ImagePullBackOff 상태였던 nginx-19가 제대로 Running 상태로 변경된 것을 확인할 수 있었다.
4. Grafana을 통한 경고 전송
kwatch에서 Pod의 문제를 확인하여 트러블슈팅하는 과정을 진행해보았다면 이번엔 Grafana에서 시각화 된 메트릭 중 장애를 확인하여 경고 전송해주는 내용을 진행해볼 예정이다. 스터디에서는 Prometheus Alert Manager을 사용했지만 나는 이번 실습에서 Grafana을 통해 진행해보려고 한다.
경고 전송을 위해 우선 Contact points을 생성한다.

Alerting->Net Contact Point을 선택한다.


Name은 임의로 입력하고 Webhook URL에 kwatch 때 사용했던 Webhook URL을 입력한 다음 Test을 실행한다. 문제없이 Slack 채널에 테스트 메시지가 전송 될 경우 Save Contact Point을 클릭해 설정을 저장한다.
Alert 창구는 만들었으니 이제 경고를 만들어 테스트를 진행해보려 한다. 여기서는 Node가 다운되었을 때 경고를 받는 것을 테스트해볼 예정이다.

위와 같은 조건으로 설정을 진행해보았다.
kube_node_status_condition Metric에서 Condition=Ready, Status=Unknown이 0보다 클 경우 경고를 발생하는 조건이다.
Node 1개에서 kubectl service을 stop 했더니 아래와 같이 경고를 받을 수 있었다.

경고를 받았으니 조치를 취해야 한다. Stop했던 node의 서비스를 다시 시작시켜줬다.

위 메시지가 온 것으로 해결됐음을 알 수 있다.
5. 마무리
모니터링은 서비스 운영에 있어 중요한 지점이나 항상 쉽지 않은 것 같아. Metric-server에서 데이터를 수집하고 Prometheus와 Grafana을 이용해 시각화, 경고를 전송하는 것을 테스트해보았다.
Alert 자체가 수집할 수 있는 Metric의 조건을 거의 모두 활용할 수 있기 때문에 내가 원하는 방식의 경고를 생성할 수 있을 것 같다.
꼭 k8s가 아니더라도 인프라 운영에 있어 Prometheus, Grafana 조합을 활용해봐야겠다.