Kubernetes Cluster Infra RCA Platform
프로젝트 소개
Kubernetes Cluster Infra RCA Platform은 Kubernetes 애플리케이션 장애가 아니라, 클러스터 노드와 Linux 시스템 레벨 장애의 원인을 수집하고 분석하는 RCA 플랫폼입니다.
일반적으로 Kubernetes 장애는 CrashLoopBackOff, OOMKilled, HTTP 5xx, Service endpoint 없음처럼 애플리케이션 레벨 증상으로 먼저 보입니다. 하지만 실제 원인은 그보다 아래 계층인 kubelet, container runtime, CNI/DNS, disk I/O, inode, conntrack, kernel log, systemd, node pressure 문제에서 시작되는 경우가 많습니다.
이 프로젝트는 이러한 문제를 단순히 Pod 단위로만 보지 않고, Node Agent가 수집한 Linux/Kubernetes evidence를 기반으로 원인 후보를 좁히고, Rule-based Analyzer와 선택적 LLM Analyzer, Policy Engine을 통해 RCA report를 생성하는 구조로 설계했습니다.
전체 흐름은 다음과 같습니다.
Alertmanager 또는 수동 수집 요청
-> Node Agent evidence 수집
-> evidence preprocessing
-> Rule-based RCA
-> 선택적 LLM 분석
-> Policy Engine 검증
-> RCA report / incident / export
이 프로젝트의 핵심은 “AI가 알아서 고쳐주는 도구”가 아니라, 장애 원인을 판단할 수 있는 근거를 수집하고, 운영자가 안전하게 판단할 수 있도록 도와주는 플랫폼입니다.
내가 기여한 부분
1. 프로젝트 전체 방향 설계
초기 아이디어 단계에서 프로젝트의 방향을 직접 설계했습니다.
처음에는 Kubernetes 장애를 분석하는 도구로 시작했지만, 단순히 Pod 로그나 애플리케이션 에러를 보는 방식은 차별성이 약하다고 판단했습니다.
그래서 분석 대상을 다음과 같이 정했습니다.
NodeNotReadyDiskPressureMemoryPressurePIDPressureNetworkUnavailable- kubelet 장애
- container runtime 장애
- CNI/DNS 문제
- disk I/O 병목
- inode 고갈
- conntrack 고갈
- kernel/systemd 로그
- NIC link flap
반대로 CrashLoopBackOff, ImagePullBackOff, Pod OOMKilled, HTTP 5xx는 원인이라기보다 노드나 네트워크 장애가 상위 레이어에서 드러난 보조 증상으로 보도록 설계했습니다.
2. RCA 파이프라인 구조 설계
RCA 흐름을 다음과 같은 구조로 나누어 설계했습니다.
수집 요청
-> Agent evidence 수집
-> evidence 전처리
-> Rule-based 분석
-> 선택적 LLM 분석
-> Policy Engine 분류
-> RCA report 생성
특히 LLM이 직접 장애 원인을 단정하거나 클러스터를 수정하지 않도록 했습니다.
LLM은 다음 역할로 제한했습니다.
- evidence 요약
- root cause candidate 설명 보강
- 운영자가 읽기 쉬운 RCA report 문장 생성
- 추가 확인 포인트 제안
반대로 다음 작업은 LLM이 직접 하지 못하도록 설계했습니다.
- 클러스터 직접 수정
- kubelet/containerd 재시작
- 노드 drain/cordon 자동 실행
- 위험 조치 자동 승인
- 운영 데이터 삭제
즉, AI는 보조 분석 도구로 사용하고, 실제 조치 판단은 Policy Engine과 운영자가 하도록 분리했습니다.
3. Node Agent 설계
Node Agent는 Kubernetes 노드마다 배포되어 Linux/Kubernetes evidence를 수집하는 역할로 설계했습니다.
Agent는 DaemonSet으로 배포되는 것을 전제로 했고, 노드의 정보를 읽기 위해 다음과 같은 항목을 수집 대상으로 잡았습니다.
/proc/etc/var/log/run- kubelet 상태
- container runtime 상태
- CNI 설정
- DNS 관련 정보
- disk/inode 상태
- memory/PID pressure
- network/conntrack 상태
- kernel/systemd 로그
중요하게 본 점은 Agent를 read-only collector로 설계한 것입니다.
Agent가 노드를 직접 고치는 구조는 위험하다고 판단했습니다. 따라서 Agent는 노드 상태를 읽고 evidence를 제출하는 역할만 하며, 실제 조치 실행은 수동 runbook 또는 GitOps PR, 승인 흐름을 통해 처리하는 방향으로 설계했습니다.
4. Backend 구조 전환 설계
초기에는 FastAPI 기반 Backend로 시작했습니다.
FastAPI는 초기 MVP를 빠르게 만드는 데 적합했습니다. cluster 등록, evidence request, RCA report 생성, webhook 처리 같은 기능을 빠르게 검증할 수 있었습니다.
하지만 프로젝트가 커지면서 단순 API 서버를 넘어 운영 플랫폼에 가까워졌습니다.
필요한 기능이 늘어났습니다.
- 인증과 권한 관리
- DB migration
- Web Console
- report export
- incident 관리
- audit event
- metric
- scheduling
- LLM provider 연동
- 운영 설정 관리
그래서 FastAPI 기반 Backend를 유지하는 것보다, Spring Boot 3.5.15 + Java 21 기반의 통합 Platform 구조로 전환하는 것이 더 적합하다고 판단했습니다.
전환 후에는 API, 인증, DB, migration, metric, Web Console serving을 하나의 Spring Boot 애플리케이션에서 관리하도록 구조를 바꿨습니다.
5. Web Console 재구성
운영자가 RCA report를 JSON으로만 보는 것은 실무적으로 적합하지 않다고 판단했습니다.
그래서 Web Console을 구성했습니다.
초기 Console은 단순 조회 중심이었지만, 이후 React 19, TypeScript, Vite, Bootstrap 5 기반으로 재구성했습니다.
Web Console에서 중요하게 본 것은 “예쁜 화면”이 아니라, 운영자가 장애 상황에서 빠르게 판단할 수 있는 화면이었습니다.
주요 화면 구성 방향은 다음과 같습니다.
- cluster 목록
- Agent 상태
- evidence request
- RCA report 목록
- RCA report 상세
- incident timeline
- policy classification
- export
- audit event
특히 RCA report 상세에서는 단순 결론보다 어떤 evidence를 근거로 판단했는지를 보여주는 것이 중요하다고 판단했습니다.
6. Codex 활용 방식
이 프로젝트에서는 Codex를 많이 활용했습니다.
다만 Codex를 사용한 범위와 직접 수행한 범위를 구분했습니다.
직접 수행한 부분은 다음과 같습니다.
- 프로젝트 주제 선정
- 문제 정의
- 전체 아키텍처 설계
- FastAPI에서 Spring Boot로 전환 판단
- Node/Linux 계층 중심 RCA 방향 설정
- Agent read-only 원칙 설정
- LLM 역할 제한
- Policy Engine 필요성 정의
- 위험 조치 자동 실행 금지 원칙 설정
- 트러블슈팅 방향 판단
- 최종 코드 구조 검토
- README/블로그/포트폴리오 방향 정리
Codex를 활용한 부분은 다음과 같습니다.
- 반복적인 Controller/Service/Repository 구현
- DTO/model 코드 작성 보조
- React component 초안 작성
- TypeScript type 정리
- Docker/Helm 설정 초안 보강
- Flyway migration 초안 작성
- 테스트 케이스 보강
- README 문장 정리
- 반복적인 에러 처리 코드 작성
즉, 설계와 판단은 직접 수행하고, 반복 구현과 코드 작성 속도 향상에는 Codex를 적극 활용한 프로젝트입니다.
트러블슈팅
1. FastAPI 기반 구조의 확장성 한계
문제 발생
초기 Backend는 FastAPI 기반으로 구성했습니다.
초기 MVP 단계에서는 FastAPI가 적합했습니다.
빠르게 API를 만들 수 있었고, Python 기반 Node Agent와도 자연스럽게 연결할 수 있었습니다.
하지만 기능이 늘어나면서 문제가 생겼습니다.
처음에는 단순히 cluster 등록, evidence request, report 생성 정도만 필요했지만, 이후에는 다음 기능이 필요해졌습니다.
- 인증
- role 기반 권한 제어
- DB migration
- Web Console serving
- metric
- scheduling
- incident 관리
- audit event
- LLM provider 연동
- report export
이 시점부터 프로젝트는 단순 API 서버가 아니라 운영 플랫폼에 가까워졌습니다.
FastAPI Backend와 별도 Web Console, 별도 migration 구조를 계속 유지하면 컴포넌트 간 연결이 복잡해지고, 운영 플랫폼으로 확장하기 어렵다고 판단했습니다.
문제 해결 과정
문제를 단순히 “Python이 좋지 않다”로 보지 않았습니다.
FastAPI 자체의 문제가 아니라, 프로젝트 성격이 바뀐 것이 문제라고 판단했습니다.
그래서 구조를 다음과 같이 바꿨습니다.
기존 구조:
FastAPI Backend
+ SQLAlchemy
+ Alembic
+ 별도 Web Console
+ 별도 API 연동
변경 구조:
Spring Boot Platform
+ Spring Security
+ JDBC
+ Flyway
+ Actuator/Micrometer
+ Spring AI
+ React Web Console
Spring Boot로 통합하면서 API, 인증, DB, migration, metric, Web Console serving을 하나의 Platform 안에서 관리할 수 있게 했습니다.
이전 코드와 비교
이전 구조
backend/
app/
main.py
models.py
store.py
services/
migrations/
alembic.ini
web-console/
Spring Boot Console
이전 구조에서는 Backend와 Console의 책임이 분리되어 있었고, API 서버와 Web Console이 별도 컴포넌트처럼 동작했습니다.
변경 구조
web-console/
pom.xml
src/main/java/...
src/main/resources/db/migration
frontend/
package.json
vite.config.ts
변경 후에는 Spring Boot Platform이 API, 인증, DB, migration, Web Console을 함께 담당하도록 바뀌었습니다.
해당 경험을 통해 알게 된 점
이 경험을 통해 기술 선택은 단순히 언어나 프레임워크 선호로 결정하면 안 된다는 것을 알게 되었습니다.
초기 MVP에는 FastAPI가 적합했지만, 프로젝트가 운영 플랫폼으로 커지면서 Spring Boot가 더 적합해졌습니다.
즉, 좋은 기술 선택은 “항상 좋은 프레임워크”를 고르는 것이 아니라, 현재 프로젝트의 단계와 운영 요구사항에 맞는 구조를 선택하는 것이라는 점을 배웠습니다.
2. Alembic에서 Flyway로 migration 전환
문제 발생
초기 Python Backend에서는 SQLAlchemy와 Alembic을 사용했습니다.
하지만 Platform을 Spring Boot로 통합하면서 DB 접근 방식도 JDBC 기반으로 바뀌었습니다.
이때 기존 Alembic migration 구조를 그대로 가져가기는 어려웠습니다.
문제는 다음과 같았습니다.
기존:
Python Backend
-> SQLAlchemy
-> Alembic
변경:
Spring Boot Platform
-> JDBC
-> Flyway
단순히 새 DB를 만들면 쉬웠겠지만, 이미 Alembic 기반 schema가 존재할 수 있다는 점을 고려해야 했습니다.
운영 플랫폼에서는 기존 데이터를 무시하고 새로 시작하는 방식이 위험할 수 있습니다.
문제 해결 과정
Spring Boot 구조에 맞춰 Flyway를 도입했습니다.
새로운 DB에서는 Flyway가 schema를 직접 생성하도록 했고, 기존 Alembic 기반 DB를 연결하는 경우에는 baseline-on-migrate 개념을 통해 기존 schema를 version 1로 인정하는 방향으로 설계했습니다.
또 PostgreSQL과 MariaDB를 모두 고려했습니다.
JSON 데이터는 DB별 JSON 타입 차이로 인한 호환성 문제를 줄이기 위해 TEXT 기반으로 저장하는 방향을 사용했습니다.
이전 코드와 비교
이전 방식
alembic upgrade head
Python Backend 실행 전 Alembic migration을 실행해야 했습니다.
변경 방식
Flyway migration
-> Spring Boot startup 과정에서 DB schema 관리
Spring Boot Platform 내부에서 migration 흐름을 관리하도록 변경했습니다.
해당 경험을 통해 알게 된 점
DB migration은 단순한 개발 편의 기능이 아니라 운영 안정성과 직접 연결되는 부분이라는 것을 알게 되었습니다.
특히 기술 스택을 바꿀 때는 코드보다 DB schema 승계가 더 조심스럽습니다.
이 경험을 통해 다음을 배웠습니다.
- migration 도구 전환 시 기존 schema 승계를 고려해야 한다
- PostgreSQL/MariaDB처럼 DB가 달라지면 타입 호환성을 신경 써야 한다
- 운영 데이터는 코드보다 더 신중하게 다뤄야 한다
- migration은 자동화하되, 백업과 검증이 전제되어야 한다
3. Agent 수집 실패와 실제 장애를 구분하는 문제
문제 발생
Node Agent는 노드의 Linux/Kubernetes evidence를 수집해야 합니다.
하지만 실제 환경에서는 모든 파일과 socket에 항상 접근할 수 있는 것은 아닙니다.
예를 들어 containerd socket에 접근하려고 할 때 다음 문제가 발생할 수 있습니다.
permission denied
file not found
socket timeout
runtime unavailable
이 문제를 단순히 “containerd 확인 실패”로 처리하면 RCA report가 잘못 나올 수 있습니다.
특히 permission denied를 실제 container runtime 장애로 오해하면 운영자는 잘못된 조치를 할 수 있습니다.
문제 해결 과정
수집 실패를 단순 에러로 버리지 않고, evidence의 일부로 남기는 방향으로 설계했습니다.
예를 들어 containerd socket 접근 실패를 다음처럼 구분했습니다.
containerd_socket_permission_denied: true
runtime_unhealthy: false 또는 unknown
즉, Agent가 정보를 못 읽은 것인지, 실제 runtime에 문제가 있는 것인지 구분하려고 했습니다.
또 특정 collector가 실패해도 Agent 전체가 실패하지 않도록 했습니다.
collector status:
success
partial
failed
이렇게 하면 일부 collector가 실패해도 나머지 evidence는 계속 제출할 수 있습니다.
이전 코드와 비교
피하고 싶었던 방식
raise RuntimeError("collector failed")
이 방식은 하나의 collector 실패가 전체 Agent 실패로 이어질 수 있습니다.
개선 방향
{
"collector": "runtime",
"status": "partial",
"error": "permission denied",
"signals": {
"containerd_socket_permission_denied": true
}
}
실패를 구조화해서 evidence로 남기도록 했습니다.
해당 경험을 통해 알게 된 점
운영 도구에서는 “모른다”는 상태를 정확히 표현하는 것이 중요하다는 것을 알게 되었습니다.
수집 실패를 실제 장애로 단정하면 잘못된 RCA가 생성될 수 있습니다.
이 경험을 통해 다음을 배웠습니다.
- collector 실패와 실제 장애는 구분해야 한다
- permission 문제는 장애가 아니라 수집 환경 문제일 수 있다
- partial evidence도 RCA에 의미가 있다
- 운영 도구는 성공 케이스보다 실패 케이스 설계가 더 중요하다
4. RCA report가 JSON에만 머무르는 문제
문제 발생
초기에는 API를 통해 RCA report를 JSON으로 확인할 수 있었습니다.
개발 중에는 JSON 응답만으로도 충분했습니다.
하지만 운영자가 실제 장애 상황에서 긴 JSON을 읽으며 판단하기는 어렵습니다.
운영자는 빠르게 다음 정보를 확인해야 합니다.
- 어떤 cluster에서 발생했는가
- 어떤 node가 영향을 받았는가
- 원인 후보는 무엇인가
- 어떤 evidence가 근거인가
- 추천 조치는 무엇인가
- 조치 위험도는 어느 정도인가
- report를 export할 수 있는가
단순 JSON 응답만으로는 운영자 관점의 판단 흐름이 부족했습니다.
문제 해결 과정
React 19, TypeScript, Vite, Bootstrap 5 기반 Web Console을 구성했습니다.
Console에서는 report 목록과 상세 화면을 나누고, 상세 화면에서 evidence, root cause candidate, policy classification, incident 정보를 확인할 수 있도록 설계했습니다.
또 Vite 개발 서버에서는 /api, /health 요청을 Spring Boot 8080으로 proxy하고, build 결과는 Spring Boot target에 포함되도록 구성했습니다.
이렇게 하여 개발 중에는 빠르게 프론트엔드를 수정할 수 있고, 배포 시에는 Spring Boot Platform이 Web Console과 API를 같은 origin에서 제공할 수 있도록 했습니다.
이전 코드와 비교
이전 방식
curl /api/rca/reports
-> JSON 확인
개발자 입장에서는 가능하지만, 운영자 입장에서는 불편했습니다.
개선 방식
Web Console
-> report 목록
-> report 상세
-> evidence 확인
-> policy level 확인
-> export
운영자가 판단할 수 있는 화면 중심으로 바꿨습니다.
해당 경험을 통해 알게 된 점
운영 도구에서는 API가 동작하는 것과 사용자가 이해할 수 있는 것은 다르다는 것을 알게 되었습니다.
특히 장애 상황에서는 화면이 복잡하거나 정보가 흩어져 있으면 판단이 느려집니다.
이 경험을 통해 다음을 배웠습니다.
- 운영자는 JSON보다 요약과 근거를 함께 볼 수 있는 화면이 필요하다
- TypeScript는 API 응답 구조 변경을 관리하는 데 도움이 된다
- 같은 origin 구조는 인증, CORS, 배포 문제를 줄이는 데 유리하다
- Web Console은 예쁜 화면보다 판단 가능한 화면이 중요하다
기술 스택
Backend / Platform
| 기술 | 사용 이유 |
|---|---|
| Spring Boot 3.5.15 | API, 인증, DB, migration, metric, Web Console serving을 하나의 Platform으로 통합하기 위해 사용 |
| Java 21 | 장기적인 유지보수성과 최신 Spring Boot 환경에 맞추기 위해 사용 |
| Spring Security | 로그인, 세션, role 기반 API 접근 제어를 구현하기 위해 사용 |
| Spring JDBC | PostgreSQL/MariaDB 중심의 명시적인 DB 접근 구조를 구성하기 위해 사용 |
| Flyway | Spring Boot 구조에 맞춰 DB migration을 관리하기 위해 사용 |
| Spring AI | LLM provider 연동을 Platform 내부에서 관리하기 위해 사용 |
| Actuator / Micrometer | Platform 자체의 health, metric, Prometheus 연동을 제공하기 위해 사용 |
Frontend / Web Console
| 기술 | 사용 이유 |
|---|---|
| React 19 | RCA report, incident, agent 상태 등 동적인 운영 화면을 구성하기 위해 사용 |
| TypeScript | API 응답 구조와 UI 상태를 타입으로 관리해 화면 오류를 줄이기 위해 사용 |
| Vite | React 개발 서버와 빠른 build 환경을 구성하기 위해 사용 |
| Bootstrap 5 | 빠르게 일관된 운영자용 UI를 구성하기 위해 사용 |
| Bootstrap Icons | report, agent, incident, status 표현을 위한 아이콘 구성에 사용 |
Agent / Evidence Collection
| 기술 | 사용 이유 |
|---|---|
| Python 3.10+ | Linux/Kubernetes 환경에서 evidence collector를 빠르게 구현하기 위해 사용 |
| Kubernetes DaemonSet | 각 노드마다 Agent를 배포하기 위해 사용 |
| hostPath mount | 노드의 /proc, /var/log, /run 등 host evidence를 수집하기 위해 사용 |
| Helm Chart | Agent와 Platform 배포 설정을 values.yaml 기반으로 관리하기 위해 사용 |
Database
| 기술 | 사용 이유 |
|---|---|
| PostgreSQL | 운영 환경에서 안정적인 관계형 데이터 저장소로 사용하기 위해 채택 |
| MariaDB | MySQL 계열 환경과의 호환성을 고려하기 위해 지원 |
| H2 | 로컬 개발과 테스트 환경에서 빠르게 실행하기 위해 사용 |
| Flyway | PostgreSQL/MariaDB/H2에서 migration 순서를 관리하기 위해 사용 |
DevOps / Deployment
| 기술 | 사용 이유 |
|---|---|
| Docker | Backend/Platform 실행 환경을 컨테이너로 표준화하기 위해 사용 |
| Docker Compose | 로컬에서 Platform, DB, Web Console 흐름을 쉽게 실행하기 위해 사용 |
| Helm | Kubernetes 환경에서 Agent와 Platform 배포 설정을 관리하기 위해 사용 |
| GitHub | 코드 버전 관리와 커밋 기반 개발 기록 정리에 사용 |
해당 스택을 채택한 이유
이 프로젝트의 스택은 처음부터 완성된 형태로 정한 것이 아니라, 프로젝트가 커지면서 단계적으로 바뀌었습니다.
초기에는 FastAPI가 적합했습니다.
빠르게 MVP를 만들고 RCA pipeline을 검증하기 좋았기 때문입니다.
하지만 프로젝트가 다음 단계로 확장되면서 요구사항이 바뀌었습니다.
- 인증과 권한이 필요해짐
- Web Console이 중요해짐
- DB migration을 안정적으로 관리해야 함
- 운영 metric이 필요해짐
- incident, audit, retention 같은 운영 기능이 필요해짐
- LLM 연동을 Platform 내부에서 관리해야 함
그래서 Spring Boot 3.5.15와 Java 21 기반 Platform으로 전환했습니다.
Frontend는 운영자가 RCA report와 incident를 확인해야 하므로 React 19와 TypeScript를 사용했고, Vite로 개발 편의성을 높였습니다.
DB migration은 Spring Boot 구조에 맞게 Flyway로 변경했습니다.
Node Agent는 Linux/Kubernetes evidence 수집 특성상 Python을 유지했습니다.
즉, 이 프로젝트의 기술 스택은 다음 기준으로 선택했습니다.
- 초기 개발 속도
- 운영 플랫폼으로의 확장성
- 인증/권한/DB/metric 통합 관리
- Kubernetes 배포 가능성
- evidence 수집의 유연성
- AI 기능을 안전하게 보조 역할로 제한할 수 있는 구조
프로젝트를 통해 배운 점
이 프로젝트를 통해 가장 크게 배운 것은, 장애 분석 도구는 단순히 “정답을 말하는 도구”가 아니어야 한다는 점입니다.
운영자가 납득할 수 있는 evidence를 보여주고, 어떤 판단이 어떤 근거에서 나왔는지 설명하고, 위험한 조치는 함부로 실행되지 않도록 막아야 합니다.
또한 기술 스택은 처음 선택한 것을 끝까지 고집하는 것이 아니라, 프로젝트의 성격이 바뀌면 전환할 수 있어야 한다는 것도 배웠습니다.
FastAPI에서 Spring Boot로 전환한 경험을 통해, 초기 MVP와 운영 플랫폼 단계에서 필요한 기술이 다를 수 있다는 점을 알게 되었습니다.
마지막으로 Codex를 적극 활용하면서도, 설계와 판단은 사람이 해야 한다는 점을 느꼈습니다.
Codex는 반복 구현과 코드 작성 속도를 높이는 데 매우 유용했지만, 어떤 기능이 필요한지, 어떤 권한이 위험한지, 어떤 조치를 자동화하면 안 되는지는 직접 판단해야 했습니다.