
💡
모든 글은 가급적 미괄식으로 작성되어, 독자도 같은 상황을 겪게 설계했습니다.
🚩
직전에 로그 시스템 배포하기[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개가 실행되어야 한다.
파드가 일정 간격으로 실행되어야 하며 컨테이너는 재실행되면 안된다.
⚠️
DaemonSet은 1번 문제를 해결할 수 있으나 2는 지원하지 않는다. CronJob은 2번 문제를 해결할 수 있으나 1은 지원하지 않는다.
단순하게는 오퍼레이터(Operator)와 커스텀 리소스 정의(CRD)가 필요해보입니다.
하지만 아래 옵션을 사용하면 적은 노력으로도 유사하게 구현이 가능해 보입니다.
⚠️
두 방식 모두 파드가 일정 간격으로 실행되도록 할 수 있습니다. 하지만 모든 파드가 "매주 금요일에 실행되어야 한다"등을 충족할 수 없습니다.
방식 | 장점 |
---|---|
terminationGracePeriodSeconds |
|
단점 | |
| |
preStop | 장점 |
| |
단점 | |
|
따라서 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: # ...
# ...
정리하면 다음과 같이 세 가지 구현 방식이 존재하는 것으로 보입니다.
해당 방식은 아래의 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을 이용해서 벤치마킹 로그/매트릭을 수집합니다.
컨트롤 플레인 [A.4.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 형태로 출력 |
{ job=”kubernetes-event” } | json 형태로 출력 후 |
아래와 같이 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. [테스트] 컨테이너 런타임 시나리오
컨테이너 런타임에 보안 강화를 어떻게 할 수 있는지
컨테이너 런타임의 잘못된 시스템 호출을 어떻게 감지할 수 있는지