可観測性 — 三つ(あるいは四つ)のシグナル
한국어 원문으로 표시합니다.
한 줄 요약
모니터링은 "내가 미리 정해 둔 질문에 답하는 것"이고, 관측 가능성은 "미리 생각 못 한 질문에도 답할 수 있는 성질" 입니다. 시그널을 나누는 이유는 각각 답할 수 있는 질문이 다르기 때문입니다.
왜 이게 필요했나
단일 서버 시절엔 로그 파일 하나면 됐습니다. 마이크로서비스에서는 요청 하나가 서비스 열 개를 지나가고, 그중 어디가 느린지 로그만으로는 알 수 없습니다. 게다가 파드는 죽고 새로 뜨므로 로그를 남긴 주체가 이미 사라진 뒤에 조사를 시작하게 됩니다.
그래서 "장애가 나면 서버에 들어가서 본다"는 방식이 성립하지 않습니다. 애초에 들어갈 서버가 없거나 이미 교체됐으니까요. 데이터를 밖으로 내보내 두어야 합니다.
어떻게 동작하나
세 개의 기둥, 그리고 네 번째
| 시그널 | 답하는 질문 | 비용 | 대표 도구 |
|---|---|---|---|
| 메트릭(Metrics) | 지금 얼마나 나쁜가? 언제부터? | 싸다 (집계된 수치) | Prometheus |
| 로그(Logs) | 그때 정확히 무슨 일이 있었나? | 중간~비쌈 | Loki, Elasticsearch |
| 트레이스(Traces) | 이 요청이 어디서 시간을 썼나? | 비쌈 (보통 샘플링) | Jaeger, Tempo |
| 프로파일(Profiles) | 이 프로세스의 CPU/메모리를 누가 먹나? | 비쌈 | Pyroscope |
조사 순서도 대개 이 순입니다. 메트릭으로 이상을 감지하고 범위를 좁히고, 트레이스로 어느 구간인지 찾고, 로그로 그 구간의 사연을 읽습니다. 로그부터 뒤지면 건초더미에서 바늘을 찾게 됩니다.
쿠버네티스에서의 수집 경로
- 메트릭: 각 컴포넌트와 앱이
/metrics를 노출 → Prometheus 가 주기적으로 pull - 로그: 컨테이너의 stdout/stderr → 노드 디스크의 파일 → DaemonSet 수집기가 읽어 전송
- 트레이스: 앱이 계측되어 OTLP 로 push → 컬렉터 → 백엔드
여기서 KCNA 가 짚는 포인트 하나. 컨테이너 로그는 stdout/stderr 로 보내야 합니다. 앱이 자기 파일에 쓰면 파드가 죽을 때 같이 사라지고 수집기도 보지 못합니다. "로그는 이벤트 스트림"이라는 12-factor 항목이 바로 이 얘기입니다.
OpenTelemetry 가 푸는 문제
예전에는 백엔드마다 SDK 가 달랐습니다. Jaeger 를 쓰다 다른 걸로 바꾸면 애플리케이션 코드를 다시 고쳐야 했죠. OTel 은 계측 API/SDK 와 전송 프로토콜(OTLP)을 표준화해서 이 종속을 끊었습니다. 계측은 한 번, 백엔드는 컬렉터 설정으로 바꿉니다.
쿠버네티스가 원래부터 주는 관측 재료
- 이벤트 —
kubectl describe pod아래쪽. 스케줄링 실패, 이미지 pull 실패, 프로브 실패 등 컨트롤 플레인의 판단 근거가 여기 남습니다. 로그를 뒤지기 전에 볼 곳입니다. - 상태(status) —
.status.conditions와.status.phase. - metrics-server —
kubectl top이 쓰는 최소한의 리소스 사용량. 장기 보관은 안 합니다.
신뢰성 용어
- SLI — 실제로 측정하는 지표 (예: 성공한 요청 비율)
- SLO — 그 지표의 목표 (예: 30일간 99.9%)
- SLA — 그 목표를 못 지켰을 때의 계약상 배상. 기술이 아니라 계약 개념
- 에러 버짓 — SLO 가 99.9% 면 0.1% 만큼은 깨져도 되는 예산. 이 예산이 남으면 배포를 더 빠르게, 바닥나면 안정화에 집중하는 식으로 의사결정 기준이 됩니다
현장에서 만나는 모습
저자의 홈랩에서 관측 가능성이 극적으로 드러난 사례가 둘 있습니다.
첫째, Cilium 의 Hubble 입니다. 사이드카를 하나도 주입하지 않고 eBPF 로 커널에서 직접 관측하는데, 실제 출력이 이렇습니다.
07:50:38.064: ebpf-demo/client:47918 -> ebpf-demo/api:80 http-request FORWARDED (HTTP/1.1 GET http://api/)
07:50:38.223: ebpf-demo/client:47932 -> ebpf-demo/api:80 http-request DROPPED (HTTP/1.1 POST http://api/)
07:50:38.223: ebpf-demo/client:47932 <- ebpf-demo/api:80 http-response FORWARDED (HTTP/1.1 403 0ms POST)
메서드·경로·응답 코드·지연 시간이 전부 보이고, 정책에 의한 DROPPED 가 명시됩니다.
"왜 막혔는가"를 추측이 아니라 기록으로 알 수 있다는 게 핵심입니다.
같은 클러스터의 nginx 는 정책 적용 전 GET 200 / POST 200 이었다가 적용 후
GET 200 / POST 403 으로 갈렸고, 그 변화가 그대로 흐름 로그에 남았습니다.
둘째, 앞서 나온 KubeVirt 사례입니다. 컴포넌트 상태는 전부 AllComponentsReady 인데 VM 은 안 떴습니다.
상태 필드는 "내가 켜졌다"고 말할 뿐 "내가 하는 일이 실제로 된다"고 말하지 않습니다.
그래서 저자는 클러스터 검증을 상태 조회가 아니라 실제 동작 시나리오 14 개(노드 Ready,
컨트롤 플레인 구성요소, CNI, CoreDNS, 워커 스케줄링, 파드 간 통신, 서비스 DNS + HTTP 200)로 짜서
통과 14 / 실패 0 을 확인했습니다. 관측 가능성의 마지막 형태는 합성 점검(synthetic check) 입니다.
다음 퀴즈에서 확인할 것
이 코스의 마지막 퀴즈로 CNCF 생태계와 관측 가능성을 함께 점검합니다. 그다음은 KCSA — 지금까지 배운 구조를 공격자의 눈으로 다시 보는 코스입니다.