클러스터 모니터링 구축하기 (Loki, Prometheus, Grafana)

온프레미스 K8s 클러스터 구축하기 - 5 (작성 중)
이민석's avatar
Mar 14, 2025
클러스터 모니터링 구축하기 (Loki, Prometheus, Grafana)

💡

모든 글은 가급적 미괄식으로 작성되어, 독자도 같은 상황을 겪게 설계했습니다.

🚩

직전에 로그 시스템 배포하기[1]를 진행하면서
여러 로그(log)를 조회하는 LogQL을 사용하는 간단한 실습을 진행했습니다.

A. 실습환경

A.1. kube-state-metrics 이해하기

기본적으로 up{job=”대상”}을 사용하면 많은 요소들을 확인할 수 있습니다.
하지만 모든 쿠버네티스 컴포넌트를 지원하지 않으며 etcd, kube-proxy, kube-scheduler의 상태는 확인할 방법이 없습니다.

이런 경우 kube-state-metrics [A.1.1]를 사용하여 집계할 수 있습니다.
이 친구는 기본적으로 활성화 되어 있으므로 그대로 사용할 수 있습니다.

A.2. kubernetes-event-exporter 배포하기

쿠버네티스 이벤트(event)는 기본적으로 1시간(60분)만 저장됩니다.
resmoio/kubernetes-event-exporter [A.2.1]을 사용하면
이벤트를 관찰하면서 수신기(receiver)로 이를 전달할 수 있습니다.

mkdir -p ~/kubernetes-event-exporter
cd ~/kubernetes-event-exporter

cat <<EOF > values.yaml
config:
  logLevel: debug
  logFormat: json
  metricsNamePrefix: event_exporter_

  route:
    routes:
      - match:
          - receiver: "loki"

  receivers:
    - name: "loki"
      loki:
        streamLabels:
          job: kubernetes-event
        url: http://loki-gateway.kube-monitor.svc.cluster.local/loki/api/v1/push
EOF
helm uninstall kubernetes-event-exporter --namespace kube-monitor

helm upgrade --install kubernetes-event-exporter oci://registry-1.docker.io/bitnamicharts/kubernetes-event-exporter \
  --values ~/kubernetes-event-exporter/values.yaml \
  --namespace kube-monitor \
  --create-namespace

A.3. kube-bench 배포하기

쿠버네티스는 CIS Kubernetes Benchmark [A.3.1]를 통해
쿠버네티스 환경의 보안 구성을 평가하고 강화하기 위한 지침을 따를 수 있습니다.

이때 aquasecurity/kube-bench [A.3.2]를 사용하면
CLI를 통해서 손쉽게 CIS Benchmark와 유사한 형태의 작업을 진행할 수 있습니다.

  1. 컨트롤 플레인 [A.3.3]공식 문서 [A.3.4]

  2. 워커 노드 [A.3.5]공식 문서 [A.3.6]

  3. 헬프 [A.3.7]

아래의 요구사항을 충족하는 네이티브 워크로드 리소스는 없습니다.

  1. 파드가 모든 노드에서 최소/최대 1개가 실행되어야 한다.

  2. 파드가 일정 간격으로 실행되어야 하며 컨테이너는 재실행되면 안된다.

⚠️

DaemonSet은 1번 문제를 해결할 수 있으나 2는 지원하지 않는다. CronJob은 2번 문제를 해결할 수 있으나 1은 지원하지 않는다.

단순하게는 오퍼레이터(Operator)와 커스텀 리소스 정의(CRD)가 필요해보입니다.
하지만 아래 옵션을 사용하면 적은 노력으로도 유사하게 구현이 가능해 보입니다.

⚠️

두 방식 모두 파드가 일정 간격으로 실행되도록 할 수 있습니다. 하지만 모든 파드가 "매주 금요일에 실행되어야 한다"등을 충족할 수 없습니다.

방식

장점

terminationGracePeriodSeconds

  1. 손쉽게 재실행 보장 가능

단점

  1. Cron 처럼 균일하게 실행되지 않음

  2. 작업용 컨테이너가 계속 실행되고 있음

  3. 종료 시간의 계산은 Kubelet의 HookHandler [A.3.9]가 진행하여, 지속적인 부하가 발생

preStop

장점

  1. 손쉽게 재실행 보장 가능

단점

  1. Cron 처럼 균일하게 실행되지 않음

  2. 작업용 컨테이너가 계속 실행되고 있음

  3. 일시 정지를 위한 별도의 exec 컨테이너가 실행됨

따라서 sleep, crontab을 활용하여 정확한 시점에 실행되도록 보완할 수 있습니다.

⚠️

단, 이 방법은 위의 두 방법에 비해서 더 많은 CPU, MEM을 차지하게 됩니다. 또한 alpine linux 자체가 보안 취약점이 될 가능성도 다분히 존재합니다. 또한 폐쇄망 환경에서도 적합하지 않는 구현 방식으로 보입니다.

# ...
        - name: cron-killer
          image: alpine:latest
          env:
            - name: SLEEP_SECONDS
              value: "1209600"  # 14일을 초로 표현
            - name: CRON_SCHEDULE
              value: "0 0 * * 5"  # 매주 금요일 자정
          command: ["/bin/sh", "-c"]
          args: # ...
# ...

정리하면 다음과 같이 세 가지 구현 방식이 존재하는 것으로 보입니다.

  1. DaemonSet [A.3.10]

  2. DaemonSet + terminationGracePeriodSeconds option [A.3.11]

  3. DaemonSet + preStop option [A.3.12]

  4. DaemonSet + Sidecar Contaienr (sleep, crontab) [A.3.13]

해당 방식은 아래의 PoC Terminator [A.3.14]를 통해서 검증할 수 있습니다.

⚠️

이 방법 또한 물리적인 한계, 단점, 위험성이 존재합니다. 따라서 장기간 정확하게 운영해야하는 워크로드라면 오퍼레이터가 필요합니다.

kubectl create ns kube-bench

kubectl apply \
  -f  https://raw.githubusercontent.com/unchaptered/onpremise-kubernetes/refs/heads/main/kube-bench/00-08-daemonset-poc.yaml \
  --namespace kube-bench

kubectl get ds,pod -n kube-bench

이 방식은 추후에 DaemonCronJob 오퍼레이터를 직접 만들거나
비슷한게 구현된 오픈소스를 찾는 것으로 해결하면 좋을 것 같습니다.

A.4. kube-bench 출력 형태 조정하기

A.3. kube-bench 배포하기 [A.4.1]은 아래와 같은 출력문을 보여줍니다.

[INFO] 4 Worker Node Security Configuration
[INFO] 4.1 Worker Node Configuration Files
[FAIL] 4.1.1 Ensure that the kubelet service file permissions are set to 600 or more restrictive (Automated)
...

== Remediations node ==
4.1.1 Run the below command (based on the file location on your system) on the each worker node.
For example, chmod 600 /lib/systemd/system/kubelet.service
...

이 정보를 적절한 매트릭(metric), 로그(log)으로 저장하기 위해서
정보를 파싱하고 Prometheus Push Gateway, Loki Gateway로 전송합니다.

kubectl apply \
  -f https://raw.githubusercontent.com/unchaptered/onpremise-kubernetes/10392f4f82f4c7162758387d19cec385f5864a4e/kube-bench/00-09-cronjob-kube-bench-debugger.yaml \
  --namespace kube-bench

kubectl exec kube-bench-debugger -it -- /bin/sh

/opt/kube-bench # kube-bench run --targets=node --json > kube-bench-node.log
/opt/kube-bench # cat kube-bench-node.log | jq -r '.Totals'

예를 들어, 벤치마킹에 대한 주요 매트릭(metric)을 아래와 같이 조회할 수 있습니다.

설명

JQ

INFO 수량

cat kube-bench-node.log | jq -r '.Totals.total_info'

PASS 수량

cat kube-bench-node.log | jq -r '.Totals.total_pass'

WARN 수량

cat kube-bench-node.log | jq -r '.Totals.total_warn'

FAIL 수량

cat kube-bench-node.log | jq -r '.Totals.total_fail'

커스텀 매트릭에 대한 라벨링 설계를 다음과 같이 진행했습니다.

종류

라벨

예시

타입

kube_bench_type

total_info, total_pass, total_warn, total_fail

타켓

kube_bench_target

master, node

문법

kube_bench_syntax

kube-bench run --targets=master

노드 이름

kube_bench_node_name

cp-k8s, wk1-k8s, wk2-k8s, …

그리고 이 정보를 Prometheus Push Gateway로 전송할 수 있습니다.

# [RESULT]
RS_INFO=$(cat kube-bench-node.log | jq -r '.Totals.total_info')
RS_PASS=$(cat kube-bench-node.log | jq -r '.Totals.total_pass')
RS_WARN=$(cat kube-bench-node.log | jq -r '.Totals.total_warn')
RS_FAIL=$(cat kube-bench-node.log | jq -r '.Totals.total_fail')

# [LABELS]
LB_TARGET="master"
LB_SYNTAX="kube-bench run --targets=master"
LB_NODE=${NODE_NAME}

METRIC_JOB_NAME="kube-bench"
PUSHGATEWAY_URL="http://prometheus-prometheus-pushgateway.kube-monitor.svc.cluster.local"

cat <<EOF | curl --data-binary @- ${PUSHGATEWAY_URL}/metrics/job/${METRIC_JOB_NAME}

kube_bench_info{
  kube_bench_type="total_info",
  kube_bench_target=${LB_TARGET},
  kube_bench_syntax=${LB_SYNTAX},
  kube_bench_node_name=${LB_NODE}
} ${RS_INFO}

kube_bench_info{
  kube_bench_type="total_pass",
  kube_bench_target=${LB_TARGET},
  kube_bench_syntax=${LB_SYNTAX},
  kube_bench_node_name=${LB_NODE}
} ${RS_PASS}

kube_bench_info{
  kube_bench_type="total_warn",
  kube_bench_target=${LB_TARGET},
  kube_bench_syntax=${LB_SYNTAX},
  kube_bench_node_name=${LB_NODE}
} ${RS_WARN}

kube_bench_info{
  kube_bench_type="total_fail",
  kube_bench_target=${LB_TARGET},
  kube_bench_syntax=${LB_SYNTAX},
  kube_bench_node_name=${LB_NODE}
} ${RS_FAIL}
EOF

이후, 벤치마킹에 대한 주요 로그(log)를 아래와 같이 조회할 수 있습니다.

설명

JQ

CIS ID

cat kube-bench-node.log | jq '.Controls[].id'

CIS 버전

cat kube-bench-node.log | jq '.Controls[].version'

CIS 설명

cat kube-bench-node.log | jq '.Controls[].text'

타켓 종류

cat kube-bench-node.log | jq '.Controls[].node_type'

타켓 버전

cat kube-bench-node.log | jq '.Controls[].detected_version'

타켓 테스트

cat kube-bench-node.log | jq '.Controls[].tests'

이후 커스텀 로그에 대한 라벨링을 아래와 같이 정의 했습니다.

레벨

종류

라벨

예시

global

CIS ID

kube_bench_gb_cis_id

4

global

CIS 버전

kube_bench_gb_cis_version

cis-1.10

global

CIS 설명

kube_bench_gb_cis_description

Worker Node Security Configuration

global

타켓 종류

kube_bench_gb_tg_type

node

global

타켓 버전

kube_bench_gb_tg_version

1.31

major

메이저 ID

kube_bench_gb_tg_test_major_section

4.3

major

설명

kube_bench_gb_tg_test_major_description

kubelet, kube-proxy, …

major

종류

kube_bench_gb_tg_test_major_type

““

major

성공

kube_bench_gb_tg_test_major_pass

1

major

실패

kube_bench_gb_tg_test_major_fail

0

major

경고

kube_bench_gb_tg_test_major_warn

3

major

정보

kube_bench_gb_tg_test_major_info

0

minor

마이너 ID

kube_bench_gb_tg_test_minor_section

4.3.1, 4.3.2, …

minor

설명

kube_bench_gb_tg_test_minor_description

Ensure that the kube-proxy metrics service is bound to localhost (Automated)

minor

감사 명령

kube_bench_gb_tg_test_minor_audit

/bin/ps -fC kube-proxy

minor

감사 변수

kube_bench_gb_tg_test_minor_audit_env

minor

감사 파일

kube_bench_gb_tg_test_minor_audit_conf

minor

테스트 유형

kube_bench_gb_tg_test_minor_type

minor

조치 사항

kube_bench_gb_tg_test_minor_remediation

minor

테스트 추가 정보

kube_bench_gb_tg_test_minor_test_info

minor

테스트 결과 상태

kube_bench_gb_tg_test_minor_status

PASS, WARN, INFO

minor

테스트 수집 정보

kube_bench_gb_tg_test_minor_actual_value

minor

테스트가 점수에 포함되는지 여부

kube_bench_gb_tg_test_minor_scroed

minor

복합 검사 여부

kube_bench_gb_tg_test_minor_is_multiple

minor

테스트를 통과하기 위해서 예상되는 결과

kube_bench_gb_tg_test_minor_expected_result

그리고 이 정보를 Loki Gateway로 전송할 수 있습니다.

LOKI_GATEWAY_URL=""

cat <<EOF | curl -H "Content-Type: application/json" --data-binary @- $LOKI_GATEWAY_URL/loki/api/v1/push
{
  "streams": [
    {
      "stream": {}, # label
      "values": [[""ddd"], ["dddd"]]  
    }
    {
      "stream": {}, # label
      "values": [[""ddd"], ["dddd"]]  
    
  ]
}
EOF

최종적으로 아래의 DaemonSet을 이용해서 벤치마킹 로그/매트릭을 수집합니다.

  1. 컨트롤 플레인 [A.4.2]

  2. 워커 노드 [A.4.3]

kubectl apply -f ~.

B. 클러스터 모니터링 구축하기

클러스터 모니터링을 위해서 어떤 PromQL을 쓸 수 있을까요?

정확히는 클러스터의 어떤 부분을 모니터링하고 장애를 감지할 수 있을까요?

그리고 이러한 함수, 지표들이 시스템의 안정성, 확장성을 보장할까요?

이번 실습을 통해서 이런 의문에 대한 답을 찾아가는 시간이 되면 좋을 것 같습니다.

💡

DataDog의 Real Time Kubernetes Monitoring을 참고하여 구성했습니다. - 출처 : https://www.datadoghq.com/dg/monitor/kubernetes-monitoring-benefits

B.1. 쿠버네티스 컴포넌트 상태 (Prometheus, Stat)

B.2. 쿠버네티스 컴포넌트 연결성 상태 (Prometheus, NodeGraph)

B.3. 쿠버네티스 컴포넌트 로그 (Loki, Logs)

쿠버네티스의 컴포넌트 [B.3.1]는 그 구성에 따라 다양한 방법*으로 배포됩니다.

💡

[다양한 방법*] systemctl, static-pod, workload 등의 다양한 방법으로 배포가 됩니다. 이 방법에 따라서 로그의 저장 위치나 조회 방법이 약간씩 달라집니다.

아래와 같이 kubernetes-components 로그를 조회할 수 있습니다.

B.4. 쿠버네티스 이벤트 로그 (Loki, Logs)

쿠버네티스 로그는 기본적으로 1시간(60분)만 저장됩니다.
A.2. kubernetes-event-exporter 배포하기 [B.2.1]로 수집한 쿼리를 질의합니다.
현재는 시각화를 위해서 모든 로그를 저장하고 보여주도록 설정되어 있습니다.

쿼리

설명

{ job=”kubernetes-event” }

출력

{ job=”kubernetes-event” }
| json

json 형태로 출력

{ job=”kubernetes-event” }
| json
| line_format "[{{.type}}] {{.reason}}: {{.message}} (Pod: {{.involvedObject_name}})"

json 형태로 출력 후
line_format으로 포맷팅 변경

아래와 같이 kubernetes-event-exporter 로그를 조회할 수 있습니다.

B.5. 쿠버네티스 컴포넌트 보안취약점 상태 (Prometheus, Stat)

A.3. kube-bench 배포하기 [B.5.1] ~ A.4. kube-bench 출력 형태 조정하기 [B.5.2]를 진행했습니다. 이 과정을 통해 수집한 로그/매트릭을 시각화하는 법을 학습합니다.

kube-bench → Prometheus, Loki

B.6. 쿠버네티스 컴포넌트 보안취약점 로그 (Loki, Logs)

kube-bench → Prometheus, Loki

B.7. 쿠버네티스 오브젝트 보안취약점 상태 (Promehteus, Stat)

trivy, kyverno → Prometheus

B.8. 쿠버네티스 오브젝트 보안취약점 로그 (Loki, Logs)

trivy, kyverno → Loki

B.9. 쿠버네티스 컨테이너 보안취약점 상태 (Prometheus, Stat)

trivy → Loki

B.10. 쿠버네티스 컨테이너 보안취약점 로그 (Loki, Logs)

trivy → Loki

B.11. 쿠버네티스 컨테이너 런타임 상태 (Prometheus, Stat)

falco

B.12. 쿠버네티스 컨테이너 런타임 로그 (Loki, Logs)

falco

C. 시나리오 테스트

노드 모니터링 구축하기 (Prometheus, Grafana) - C. 스트레스 테스트 [C.1]에서는
시스템 스트레스에 대한 가설, 검증 및 수정의 방식으로 진행했습니다.

C.1. [테스트] 컴포넌트 연결성 시나리오

특정한 컴포넌트가 마비되었을때, 연결성 대시보드에서 어떻게 표시되는지
해당 표시 사항에 대한 조치사항이 직관적인지…

C.2. [테스트] 컴포넌트 보안취약점 시나리오

컴포넌트에 어떤 보안 취약점이 있는지…
컴포넌트에 보안 취약점의 증가/감소 추적이 되는지

C.3. [테스트] 오브젝트 보안취약점 시나리오

오브젝트에 어떤 보안 취약점이 있는지…
오브젝트에 보안 취약점의 증가/감소 추적이 되는지

C.4. [테스트] 컨테이너 보안취약점 시나리오

컨테이너에 어떤 보안 취약점이 있는지…
컨테이너에 보안 취약점의 증가/감소 추적이 되는지

C.5. [테스트] 컨테이너 런타임 시나리오

컨테이너 런타임에 보안 강화를 어떻게 할 수 있는지
컨테이너 런타임의 잘못된 시스템 호출을 어떻게 감지할 수 있는지

Share article

Unchaptered