RCA 플랫폼에 인증과 드릴다운을 붙이면서 배운 것

RCA report가 만들어지기 시작하자, 이제 중요한 문제는 “누가 이 정보를 볼 수 있는가”와 “운영자가 어떤 근거까지 따라가 볼 수 있는가”였습니다. 인증, RBAC, webhook 인증, report drilldown, 그리고 실제 노드 증거 수집 과정에서 고민했던 내용을 정리했습니다.

RCA 플랫폼에 인증과 드릴다운을 붙이면서 배운 것
Photo by Growtika / Unsplash

Kubernetes Cluster Infra RCA Platform 개발 기록 2편

지난 글에서는 이 프로젝트를 왜 시작했는지, 그리고 어떤 흐름으로 RCA 파이프라인을 만들기 시작했는지 정리했습니다.

처음 목표는 단순했습니다.

장애 알림이 들어오면, Backend가 evidence request를 만들고, Node Agent가 증거를 수집한 뒤, RCA report까지 이어지는 흐름을 먼저 만드는 것이었습니다.

Alertmanager webhook
  -> evidence request
  -> Node Agent evidence
  -> RCA report
  -> policy decision

이 흐름이 어느 정도 잡히고 나니, 다음으로 바로 보이는 문제가 있었습니다.

“이 정보를 아무나 보면 안 되지 않나?”

RCA report에는 생각보다 민감한 정보가 많이 들어갈 수 있습니다. 노드 이름, 클러스터 정보, kubelet 상태, 런타임 상태, 네트워크 문제, 시스템 로그 요약, 장애 원인 후보까지 포함될 수 있기 때문입니다.

그래서 두 번째 단계에서는 단순히 기능을 더 붙이는 것보다, 인증, 권한, webhook 보호, report drilldown을 중심으로 다듬기 시작했습니다.


RCA 플랫폼에서 보안이 중요한 이유

처음에는 RCA report가 만들어지는 것 자체가 중요했습니다.

하지만 report가 만들어진다는 것은, 누군가가 그 정보를 조회할 수 있다는 뜻이기도 합니다.

운영 환경에서 RCA report는 단순한 로그 모음이 아닙니다.

  • 어떤 클러스터가 등록되어 있는지
  • 어떤 노드에서 문제가 발생했는지
  • kubelet이나 containerd 상태가 어떤지
  • 네트워크나 CNI 쪽에 어떤 이상이 있는지
  • 어떤 조치가 필요하다고 판단했는지
  • 자동 조치가 가능한지, 승인 후 조치가 필요한지

위와 같은 정보들이 들어있기 때문에 위험하다고 생각을 했습니다.

즉, 이 플랫폼을 통해 클러스터 내부 상태가 외부로 드러날 수도 있습니다.

그래서 이 단계에서 중요하게 본 것은 기능보다 접근 제어였습니다.

RCA report는 운영자를 돕는 정보이지만, 동시에 보호해야 할 정보이기도 하다.

이 생각을 기준으로 인증과 권한 구조를 붙이기 시작했습니다.


처음에는 admin token으로 시작했다

초기 단계에서는 가장 단순한 방식으로 관리자 토큰을 사용했습니다.

Backend API에 접근할 때 X-Admin-Token 같은 값을 확인하는 방식입니다.

빠르게 MVP를 만들 때는 이런 방식이 나쁘지 않습니다.

복잡한 로그인 시스템 없이도 최소한의 보호 장치를 둘 수 있고, API가 아무나 열람되는 상황은 막을 수 있기 때문입니다.

하지만 이 방식에는 한계가 분명했습니다.

문제:
admin token 하나로 모든 사용자가 같은 권한을 가짐

결과:
누가 조회만 해야 하는지,
누가 cluster를 등록할 수 있는지,
누가 위험한 조치를 승인할 수 있는지 구분하기 어려움

RCA 플랫폼은 단순 조회 도구가 아닙니다.

나중에는 report 조회, cluster 등록, evidence 요청, webhook 관리, 조치 승인 같은 기능이 나뉘어야 합니다.

그래서 단순 admin token만으로는 부족하다고 판단했습니다.


로그인 세션과 RBAC 추가

다음 단계에서는 로그인 세션과 RBAC를 추가했습니다.

RBAC는 Role-Based Access Control, 즉 역할 기반 접근 제어입니다.

이 프로젝트에서는 역할을 크게 세 가지로 나눠 생각했습니다.

admin
operator
viewer

각 역할의 느낌은 이렇게 잡았습니다.

  • admin: 클러스터 등록, 삭제, 설정 변경 같은 관리 작업 가능
  • operator: report 확인, evidence 요청, 운영 판단 가능
  • viewer: report 조회 중심의 읽기 권한

이렇게 나눈 이유는 실제 운영 환경을 생각했기 때문입니다.

모든 사람이 같은 권한을 가지면 편해 보이지만, 장애 대응 도구에서는 오히려 위험합니다.

예를 들어 report만 봐야 하는 사람이 cluster 삭제나 위험 조치 승인까지 할 수 있으면 안 됩니다.

그래서 기능을 만들 때마다 “이 API는 어떤 역할까지 허용해야 하지?”를 같이 고민하기 시작했습니다.

GET /api/rca/reports
  -> viewer 이상 가능

POST /api/clusters
  -> admin 또는 operator 필요

DELETE /api/clusters
  -> admin만 가능

위험 조치 승인
  -> 최소 operator 이상 필요

이런 식으로 API마다 접근 권한을 분리하는 방향으로 잡았습니다.


webhook도 보호해야 했다

Alertmanager webhook은 외부에서 Backend로 들어오는 입구입니다.

처음에는 “알림을 받으면 evidence request를 만든다”는 흐름 자체가 중요했습니다.

하지만 조금만 생각해보면 webhook도 반드시 보호해야 합니다.

만약 webhook이 아무 인증 없이 열려 있다면, 누군가가 가짜 alert를 보낼 수 있습니다.

그러면 이런 문제가 생길 수 있습니다.

가짜 alert 전송
  -> evidence request 생성
  -> Agent가 불필요한 수집 수행
  -> RCA report 생성
  -> 운영자가 잘못된 장애로 오인

심하면 반복적인 가짜 alert로 시스템을 시끄럽게 만들 수도 있습니다.

그래서 webhook 요청에도 인증을 붙였습니다.

핵심은 “Alertmanager에서 온 요청인지 확인할 수 있어야 한다”는 점이었습니다.

문제:
webhook endpoint가 열려 있으면 가짜 alert가 들어올 수 있음

수정:
webhook secret 또는 인증 헤더를 확인하고,
유효한 요청만 evidence request로 연결

이 부분은 실제 운영을 생각하면 아주 기본적인 부분이지만, MVP 단계에서는 놓치기 쉽습니다.

이번 단계에서 이 부분을 빨리 보강한 것은 잘한 선택이라고 생각합니다.


report drilldown이 필요했던 이유

처음 RCA report는 요약 중심이었습니다.

어떤 원인 후보가 있고, 어떤 조치가 필요할 수 있는지를 보여주는 구조였습니다.

하지만 운영자가 실제로 장애를 판단하려면 요약만으로는 부족합니다.

예를 들어 report에 이렇게 나왔다고 가정해보겠습니다.

Possible root cause:
container runtime degradation

이 문장만 보면 운영자는 바로 이런 질문을 하게 됩니다.

  • 어떤 노드에서 발생했는가?
  • containerd socket 접근이 실패했는가?
  • 실제로 containerd가 죽은 것인가?
  • 단순 permission 문제인가?
  • kubelet log에는 어떤 내용이 있었는가?
  • CNI나 DNS 증상도 같이 있었는가?

즉, report는 결론만 보여주면 안 됩니다.

운영자가 결론까지 따라갈 수 있는 근거를 보여줘야 합니다.

그래서 report drilldown을 추가했습니다.


drilldown에서 보여주고 싶었던 것

drilldown의 목적은 “더 많은 정보를 보여주기”가 아니었습니다.

중요한 것은 RCA 판단에 영향을 준 근거를 따라갈 수 있게 하는 것이었습니다.

그래서 report 화면에서 이런 정보를 더 자세히 볼 수 있는 방향을 잡았습니다.

  • evidence request 정보
  • 수집 대상 cluster와 node
  • collector별 수집 결과
  • 주요 metric
  • 감지된 signal
  • root cause candidate
  • recommendation
  • policy classification
  • raw evidence 요약

이 구조가 있어야 운영자가 report를 보고 납득할 수 있습니다.

단순히 “AI가 이렇게 말했습니다”가 아니라,

이런 evidence가 있었고,
이런 signal이 감지됐고,
이런 rule 또는 analyzer가 판단했고,
그래서 이런 원인 후보와 조치가 나왔다

이 흐름을 보여주는 것이 중요했습니다.


실제 노드 증거 수집으로 넘어가면서 보인 문제

초기에는 fake evidence로 전체 흐름을 검증했습니다.

하지만 결국 이 프로젝트의 핵심은 실제 노드에서 증거를 수집하는 것입니다.

그래서 Kubernetes evidence collector와 node collector를 붙이면서, 실제 환경에서 생길 수 있는 문제들을 생각하기 시작했습니다.

가장 먼저 고려한 것은 권한 문제였습니다.

Node Agent가 노드의 정보를 읽으려면 여러 시스템 경로에 접근해야 합니다.

예를 들어 다음과 같은 것들입니다.

  • /proc
  • /var/log
  • /run/containerd/containerd.sock
  • kubelet 관련 log
  • CNI 설정 파일
  • systemd 상태
  • kernel log

그런데 운영 환경에서는 이 모든 것에 항상 접근할 수 있다고 보장할 수 없습니다.

권한이 부족할 수도 있고, 파일 경로가 다를 수도 있고, 컨테이너 안에서 hostPath mount가 제대로 안 되어 있을 수도 있습니다.

그래서 collector는 실패를 잘 다뤄야 했습니다.


permission denied를 장애로 착각하면 안 된다

이 단계에서 특히 중요하게 본 것은 permission 문제였습니다.

예를 들어 containerd socket에 접근하다가 permission denied가 발생했다고 해보겠습니다.

이걸 잘못 처리하면 “containerd가 죽었다”거나 “runtime 장애가 발생했다”고 오진할 수 있습니다.

하지만 실제로는 containerd가 정상일 수도 있습니다.

문제는 Agent가 socket에 접근할 권한이 없었던 것일 수 있습니다.

잘못된 판단:
containerd socket 접근 실패
  -> container runtime 장애로 판단

더 나은 판단:
containerd socket 접근 실패
  -> permission denied 여부 확인
  -> runtime 장애와 수집 권한 문제를 분리

이 차이는 중요합니다.

운영자가 report를 봤을 때 “containerd 장애”라고 나오면 실제 조치를 고민하게 됩니다.
하지만 원인이 단순 권한 문제라면 조치 방향이 완전히 달라집니다.

그래서 수집 실패를 단순 실패로 뭉개지 않고, 가능한 한 구체적인 signal로 남기는 방향을 잡았습니다.

containerd_socket_permission_denied: true
runtime_unhealthy: false 또는 unknown

이렇게 분리해두면 RCA report가 훨씬 조심스럽게 나올 수 있습니다.


collector 실패도 evidence다

이 프로젝트를 만들면서 계속 중요하게 본 생각이 있습니다.

수집 실패도 evidence다.

보통은 collector가 실패하면 에러로 처리하고 끝내기 쉽습니다.

하지만 RCA 관점에서는 “무엇을 수집하지 못했는가”도 중요한 정보입니다.

예를 들어 다음과 같은 차이가 있습니다.

case 1:
containerd socket에 접근했지만 응답이 없음
  -> runtime 문제 가능성

case 2:
containerd socket에 접근하려 했지만 permission denied
  -> Agent 권한 또는 mount 설정 문제 가능성

case 3:
containerd socket 경로 자체가 없음
  -> runtime 종류가 다르거나 경로 설정 문제 가능성

세 경우 모두 “containerd 확인 실패”로 뭉개면 RCA 품질이 떨어집니다.

그래서 collector는 실패를 최대한 구조화해서 남기는 쪽이 더 낫다고 봤습니다.


Codex를 어떻게 활용했는가

이 단계에서도 전체 방향은 직접 잡았습니다.

특히 보안 모델, 권한 분리, webhook 보호, collector 실패를 evidence로 남기는 방향은 직접 고민해야 하는 부분이었습니다.

다만 사람이 직접 모든 반복 구현을 끝까지 다 하는 데에는 한계가 있었습니다.

그래서 설계 방향을 잡고, 사람이 검토할 수 있는 수준까지 구조를 만든 뒤, 반복적인 구현과 테스트 확장은 Codex를 활용했습니다.

예를 들면 이런 부분입니다.

  • 로그인 세션 관련 반복 코드 보강
  • API별 권한 체크 패턴 정리
  • webhook 인증 처리 테스트 확장
  • report drilldown 응답 구조 정리
  • collector permission case 테스트 추가
  • 에러 응답 형식 정리
  • README와 개발 문서 정리

여기서 중요하게 생각한 것은 Codex에게 판단을 맡기는 것이 아니었습니다.

Codex는 구현 속도를 높이는 도구로 사용하고, 어떤 구조가 안전한지, 어떤 정보가 민감한지, 어떤 자동화가 위험한지는 직접 판단하려고 했습니다.

즉, 역할을 이렇게 나눴습니다.

사람:
설계 방향, 보안 기준, 운영 위험 판단, 최종 검토

Codex:
반복 구현, 테스트 확장, 문서 초안, 코드 패턴 보강

이 방식이 프로젝트에는 꽤 잘 맞았습니다.


이 단계에서 가장 중요했던 점

이번 단계에서 가장 중요하게 고려한 것은 “보여주면 안 되는 정보”와 “보여줘야 하는 근거”를 동시에 생각하는 것이었습니다.

RCA 플랫폼은 운영자에게 많은 정보를 보여줘야 합니다.

하지만 동시에 그 정보가 아무에게나 노출되면 안 됩니다.

그래서 다음 기준을 계속 두고 개발했습니다.

  1. RCA report는 민감한 운영 정보로 본다
  2. 사용자 역할에 따라 접근 가능한 API를 나눈다
  3. webhook은 외부 입력이므로 반드시 인증한다
  4. report는 결론뿐 아니라 근거까지 따라갈 수 있어야 한다
  5. collector 실패는 단순 에러가 아니라 분석 가능한 evidence로 남긴다
  6. permission 문제와 실제 장애를 구분한다
  7. Codex는 구현 보조로 쓰되, 운영 위험 판단은 사람이 한다

개인적으로는 6번이 특히 중요했습니다.

권한 부족 때문에 수집에 실패한 것을 실제 장애로 오해하면, 운영자는 엉뚱한 조치를 할 수 있습니다.

RCA 플랫폼은 그럴듯한 말을 하는 것보다, 모르는 것은 모른다고 표시하고, 실패한 것은 실패한 이유를 분리해서 보여주는 것이 더 중요하다고 생각했습니다.


마무리

두 번째 단계에서는 단순히 기능을 늘리기보다, RCA 플랫폼이 운영 도구로 쓰이기 위해 필요한 기본기를 붙였습니다.

인증을 붙이고, 역할을 나누고, webhook을 보호하고, report를 drilldown할 수 있게 만들고, 실제 노드 증거 수집에서 생길 수 있는 permission 문제를 별도 signal로 다루기 시작했습니다.

이 단계에서 프로젝트의 성격이 조금 더 분명해졌습니다.

이 프로젝트는 단순히 장애 원인을 “추측”하는 도구가 아닙니다.

운영자가 근거를 따라가 볼 수 있게 하고, 위험한 조치를 함부로 실행하지 않게 하며, 수집 실패까지도 분석 가능한 정보로 남기려는 도구입니다.

다음 글에서는 Agent를 실제 Kubernetes 클러스터에 배포하기 위한 Helm chart, DaemonSet 구조, 그리고 로컬 검증과 smoke test를 어떻게 구성했는지 정리해보려고 합니다.