Node Agent를 Kubernetes에 올리기: DaemonSet, Helm Chart, 그리고 검증 구조
RCA 플랫폼의 핵심인 Node Agent를 실제 Kubernetes 클러스터에 배포하기 위해 DaemonSet 구조, hostPath mount, Helm Chart, 로컬 smoke test를 어떻게 설계했는지 정리했습니다.
Kubernetes Cluster Infra RCA Platform 개발 기록 3편
지난 글에서는 RCA 플랫폼에 인증과 RBAC, webhook 인증, report drilldown을 붙이면서 고민했던 부분을 정리했습니다.
처음에는 report가 생성되는 것 자체가 중요했지만, 조금만 더 생각해보면 “누가 이 정보를 볼 수 있는가”, “운영자가 어떤 근거까지 따라가 볼 수 있는가”가 훨씬 중요했습니다.
이번 글에서는 그 다음 단계로 넘어가 보려고 합니다.
이제 문제는 이것이었습니다.
Node Agent를 실제 Kubernetes 클러스터에 어떻게 배포할 것인가?
RCA 플랫폼의 핵심은 결국 증거 수집입니다.
Backend가 아무리 잘 만들어져 있어도, 실제 노드에서 의미 있는 evidence를 가져오지 못하면 RCA report의 품질은 좋아질 수 없습니다.
그래서 이번 단계에서는 Node Agent를 Kubernetes 안에 올릴 수 있는 구조로 바꾸고, Helm Chart와 smoke test까지 정리하는 작업을 진행했습니다.
왜 DaemonSet인가
Node Agent는 특정 애플리케이션 Pod 하나를 관찰하는 도구가 아닙니다.
이 프로젝트에서 Agent가 봐야 하는 대상은 Kubernetes 노드 자체입니다.
예를 들면 이런 것들입니다.
- kubelet 상태
- container runtime 상태
- CNI 설정
- DNS 관련 증상
- disk 사용량
- inode 사용량
- kernel log
- systemd 상태
- network/conntrack 상태
이런 정보는 노드마다 다를 수 있습니다.
어떤 노드는 DiskPressure가 발생할 수 있고, 어떤 노드는 MemoryPressure가 발생할 수 있습니다. 또 어떤 노드는 CNI 문제가 있고, 다른 노드는 container runtime 문제가 있을 수도 있습니다.
그래서 Agent는 특정 노드 몇 개에만 떠 있으면 안 됩니다.
가능하면 각 노드에 하나씩 떠 있어야 합니다.
Kubernetes에서는 이런 구조에 가장 잘 맞는 리소스가 DaemonSet입니다.
Kubernetes Node 1
-> node-agent Pod
Kubernetes Node 2
-> node-agent Pod
Kubernetes Node 3
-> node-agent Pod
DaemonSet을 사용하면 클러스터에 노드가 추가될 때 Agent Pod도 자동으로 배포될 수 있습니다.
RCA 플랫폼 입장에서는 이 구조가 자연스럽습니다.
노드 단위 장애를 분석하려면, Agent도 노드 단위로 배포되어야 한다.
Agent가 노드 정보를 읽기 위해 필요한 것
Node Agent가 일반 애플리케이션처럼 동작한다면 단순한 Deployment로도 충분했을 것입니다.
하지만 Agent는 노드의 시스템 정보를 읽어야 합니다.
그래서 일반적인 Pod보다 신경 써야 할 부분이 많습니다.
대표적으로는 hostPath mount가 필요합니다.
/proc
/etc
/var/log
/run
이런 경로들은 컨테이너 내부의 파일이 아니라, 실제 노드의 상태를 보기 위해 필요한 경로입니다.
예를 들어 /var/log를 읽어야 kubelet이나 system log를 확인할 수 있고, /run 쪽을 봐야 container runtime socket 상태를 확인할 수 있습니다.
하지만 여기서 조심해야 할 점이 있습니다.
hostPath mount는 강력합니다.
잘못 사용하면 노드의 민감한 정보가 컨테이너로 너무 많이 노출될 수 있습니다.
그래서 이 프로젝트에서는 처음부터 Agent를 “수정 도구”가 아니라 “읽기 전용 수집 도구”로 생각했습니다.
Agent는 노드를 고치는 도구가 아니라, 노드 상태를 읽어오는 도구여야 한다.
이 원칙은 DaemonSet을 설계할 때도 중요했습니다.
read-only collector로 시작한 이유
Agent가 노드에 올라간다고 해서 바로 자동 복구를 수행하게 만들고 싶지는 않았습니다.
예를 들어 kubelet 재시작, containerd 재시작, CNI 설정 변경 같은 작업은 운영 환경에서 꽤 위험할 수 있습니다.
잘못 실행하면 장애를 해결하는 것이 아니라 더 크게 만들 수도 있습니다.
그래서 처음 Agent의 역할은 명확하게 제한했습니다.
Agent가 하는 일:
노드 상태 읽기
collector 실행
evidence 정리
Backend로 제출
Agent가 하지 않는 일:
노드 직접 수정
서비스 재시작
파일 삭제
CNI 설정 변경
위험 조치 자동 실행
이렇게 범위를 제한하면 구현은 조금 답답해 보일 수 있습니다.
하지만 운영 도구에서는 이 답답함이 오히려 안전장치가 됩니다.
특히 이 프로젝트는 AI 기반 RCA 플랫폼이기 때문에 더 조심해야 한다고 생각했습니다.
LLM이나 rule analyzer가 어떤 조치를 추천할 수는 있지만, 실제 실행은 별도의 정책과 승인 흐름을 거쳐야 합니다.
Agent가 마음대로 노드를 수정하게 만들면 이 원칙이 깨집니다.
Helm Chart가 필요했던 이유
처음에는 Kubernetes manifest를 직접 작성해서 배포할 수도 있습니다.
하지만 프로젝트가 조금만 커져도 단순 YAML 몇 개로 관리하기가 어려워집니다.
예를 들어 Agent를 배포하려면 이런 값들이 환경마다 달라질 수 있습니다.
- Backend URL
- cluster ID
- agent token
- image repository
- image tag
- resource requests/limits
- hostPath mount 여부
- collector 활성화 여부
- node selector
- tolerations
- service account
- RBAC 설정
이 값들을 매번 YAML에서 직접 수정하면 실수가 생기기 쉽습니다.
그래서 Helm Chart로 묶는 방향을 잡았습니다.
values.yaml
-> 환경별 설정
templates/daemonset.yaml
-> Node Agent 배포 구조
templates/serviceaccount.yaml
-> Agent 실행 계정
templates/clusterrole.yaml
-> 필요한 Kubernetes 조회 권한
templates/secret.yaml
-> Agent token 또는 민감 설정
Helm을 쓰면 배포 명령도 훨씬 정리됩니다.
helm install rca-agent ./charts/rca-agent \
--set backend.url=https://example.com \
--set clusterId=my-cluster
운영자가 직접 YAML을 고치는 것보다, values.yaml을 통해 필요한 설정만 바꾸는 편이 더 안전하고 재사용하기 좋습니다.
Agent token을 어떻게 다룰 것인가
Agent가 Backend에 evidence를 제출하려면 인증이 필요합니다.
아무 Pod나 Backend에 evidence를 제출할 수 있으면 안 됩니다.
그래서 Agent 등록 과정에서 node token을 발급하고, Agent는 이 token을 사용해 Backend와 통신하는 구조로 잡았습니다.
흐름은 대략 이렇습니다.
cluster 등록
-> agent install command 생성
-> node-agent 배포
-> node token으로 Backend 인증
-> evidence request polling
-> evidence submit
여기서 중요한 것은 token이 노출되지 않도록 다루는 것입니다.
처음에는 manifest나 install command에 token이 쉽게 드러날 수 있습니다.
하지만 실제 운영을 생각하면 token은 Secret으로 다루는 것이 자연스럽습니다.
문제:
Agent token이 manifest나 command에 그대로 노출될 수 있음
수정 방향:
Kubernetes Secret으로 분리
환경 변수 또는 mounted file로 Agent에 전달
Backend API 접근 시 token 검증
물론 개발 단계에서는 편의를 위해 단순화할 수 있습니다.
하지만 구조를 잡을 때는 나중에 Secret 기반으로 옮길 수 있도록 열어두는 것이 좋다고 봤습니다.
collector 설정을 values.yaml로 분리하기
Agent collector는 환경마다 다르게 켜고 끌 수 있어야 합니다.
예를 들어 어떤 환경에서는 systemd 접근이 어렵고, 어떤 환경에서는 kernel log 접근 권한이 제한될 수 있습니다.
또 어떤 클러스터에서는 containerd를 쓰지만, 다른 클러스터에서는 다른 runtime을 사용할 수도 있습니다.
그래서 collector 설정은 코드에 고정하기보다 values.yaml에서 조정할 수 있는 방향이 좋다고 판단했습니다.
예시는 이런 식입니다.
collectors:
systemd:
enabled: true
kernel:
enabled: true
runtime:
enabled: true
cni:
enabled: true
dns:
enabled: true
disk:
enabled: true
network:
enabled: true
이렇게 해두면 운영 환경에 맞춰 수집 범위를 조절할 수 있습니다.
처음에는 모든 것을 완벽하게 자동 감지하기보다, 운영자가 명시적으로 켜고 끌 수 있는 구조가 더 안전하다고 생각했습니다.
권한은 최소한으로 시작해야 한다
Node Agent는 노드 정보를 읽어야 하기 때문에 어느 정도 권한이 필요합니다.
하지만 그렇다고 처음부터 너무 큰 권한을 주면 안 됩니다.
특히 Kubernetes 권한과 hostPath 권한은 신중하게 봐야 합니다.
Agent에게 필요한 권한은 크게 두 종류로 나뉩니다.
첫 번째는 Kubernetes API를 조회하기 위한 권한입니다.
예를 들면 Node, Pod, Event, Endpoint 같은 정보를 읽기 위한 권한입니다.
두 번째는 노드 파일시스템이나 시스템 정보를 읽기 위한 권한입니다.
이 부분은 hostPath mount나 securityContext와 관련됩니다.
여기서 내가 중요하게 생각한 기준은 이것이었습니다.
필요한 만큼만 열고, 실패는 evidence로 남긴다.
모든 권한을 다 열어서 수집을 성공시키는 것보다, 권한이 부족한 부분은 부족하다고 표시하는 편이 더 안전하다고 봤습니다.
예를 들어 containerd socket에 접근하지 못하면, 그것을 runtime 장애로 단정하지 않고 permission 문제로 분리해야 합니다.
containerd socket 접근 실패
-> permission denied
-> runtime 장애가 아니라 collector 권한 문제로 기록
이 흐름은 앞 글에서 다룬 permission denied 처리와도 이어집니다.
로컬 검증이 필요했던 이유
Helm Chart와 DaemonSet을 만들었다고 해서 끝이 아닙니다.
문제는 실제로 이 구조가 정상 동작하는지 확인하는 것입니다.
특히 이 프로젝트는 Backend, Web Console, Node Agent, DB, migration, API 인증이 서로 연결되어 있습니다.
하나만 실행해서는 전체 흐름을 확인하기 어렵습니다.
그래서 smoke test가 필요했습니다.
smoke test는 아주 깊은 테스트라기보다는, “최소한 핵심 기능이 켜지고 서로 연결되는지”를 빠르게 확인하는 테스트입니다.
예를 들어 이런 것들을 확인합니다.
Backend가 실행되는가
/health/ready가 정상 응답하는가
DB migration이 적용되는가
Web Console이 Backend와 통신하는가
cluster 등록이 가능한가
agent manifest 또는 install command를 받을 수 있는가
RCA report API가 깨지지 않는가
이런 검증이 없으면, 기능을 조금 고친 뒤 전체 흐름이 깨졌는지 놓치기 쉽습니다.
smoke test에서 확인하고 싶었던 흐름
이번 단계에서 확인하고 싶었던 것은 단순히 “서버가 켜진다”가 아니었습니다.
실제로 운영자가 사용할 흐름이 최소한 연결되는지를 보고 싶었습니다.
Backend start
-> readiness check
-> Web Console start
-> cluster create
-> agent install info 조회
-> evidence/report 관련 API 확인
이 정도만 확인해도 많은 문제를 초기에 잡을 수 있습니다.
예를 들어 환경 변수 이름이 바뀌었는데 console이 못 읽고 있다거나, Backend API 경로가 바뀌었는데 프론트에서 예전 경로를 호출하고 있다거나, migration이 누락되어 DB 접근이 실패하는 문제를 빨리 발견할 수 있습니다.
개발하면서 가장 피하고 싶었던 상황은 이것이었습니다.
기능은 추가했는데, 실제로 실행해보면 전체 흐름이 깨져 있는 상황
그래서 로컬 smoke test는 생각보다 중요했습니다.
Codex를 어떻게 활용했는가
이번 단계에서도 전체 방향은 직접 잡았습니다.
Agent는 왜 DaemonSet이어야 하는지, hostPath를 어디까지 열어야 하는지, Helm values를 어떻게 나눌지, 권한을 어디까지 줄지 같은 부분은 직접 판단해야 했습니다.
하지만 Helm template, 반복되는 YAML 구조, smoke test 스크립트, 문서 정리처럼 사람이 직접 다 작성하면 시간이 오래 걸리는 부분은 Codex를 활용했습니다.
예를 들면 이런 작업입니다.
- DaemonSet template 초안 작성
- values.yaml 옵션 정리
- ServiceAccount / RBAC manifest 구성
- Dockerfile과 배포 파일 정리
- smoke test 스크립트 보강
- README 배포 방법 정리
- 반복적인 설정 검증 코드 작성
여기서도 Codex에게 전체 판단을 맡기지는 않았습니다.
Codex는 반복 구현을 빠르게 해주는 도구로 사용했고, Agent가 어떤 권한을 가져야 하는지, 어떤 경로를 mount해야 하는지, 어떤 동작은 금지해야 하는지는 직접 검토했습니다.
역할을 나누면 이렇게 볼 수 있습니다.
사람:
DaemonSet이 필요한 이유 판단
Agent 권한 범위 결정
hostPath 위험성 검토
운영 환경에서의 배포 흐름 설계
최종 구조 검토
Codex:
Helm template 반복 작성
values.yaml 정리
smoke test 스크립트 보강
문서 초안 정리
반복 코드 작성
이 방식이 가장 현실적이었습니다.
사람이 모든 줄을 직접 쓰는 것보다, 사람이 방향과 기준을 잡고 Codex로 반복 구현을 줄이는 방식이 프로젝트 속도와 품질 면에서 더 나았습니다.
이 단계에서 가장 중요했던 점
이번 단계에서 가장 중요하게 본 것은 “배포 가능성”과 “안전성”의 균형이었습니다.
Agent를 실제 클러스터에 올리려면 어느 정도 권한이 필요합니다.
하지만 권한을 너무 많이 주면 RCA 도구 자체가 위험해질 수 있습니다.
그래서 계속 이 기준을 두고 작업했습니다.
- Agent는 노드마다 하나씩 떠야 하므로 DaemonSet으로 배포한다
- Agent는 read-only collector로 시작한다
- hostPath mount는 필요한 경로만 사용한다
- token은 Secret으로 분리할 수 있는 구조를 고려한다
- collector 설정은 values.yaml에서 조절 가능하게 한다
- 권한 부족은 실패가 아니라 evidence로 남긴다
- Helm Chart로 환경별 배포 설정을 분리한다
- smoke test로 Backend, Console, Agent 관련 흐름을 검증한다
- Codex는 반복 구현에 활용하되, 권한과 보안 판단은 사람이 한다
개인적으로 가장 중요했던 것은 2번과 6번이었습니다.
Agent가 노드에 올라간다는 이유만으로 자동 복구 도구가 되어서는 안 됩니다.
그리고 권한이 부족해서 수집하지 못한 정보를 실제 장애로 착각해서도 안 됩니다.
RCA 플랫폼은 무언가를 많이 하는 것보다, 정확히 구분하는 것이 더 중요하다고 생각했습니다.
마무리
이번 단계에서는 Node Agent를 실제 Kubernetes 클러스터에 올릴 수 있는 형태로 정리했습니다.
DaemonSet으로 노드마다 Agent를 배포하고, Helm Chart로 환경별 설정을 분리하고, hostPath와 권한 범위를 신중하게 다루는 방향으로 설계했습니다.
또 Backend, Web Console, Agent 관련 흐름이 깨지지 않도록 smoke test 구조도 함께 정리했습니다.
이 단계부터 프로젝트는 단순한 로컬 API 서버가 아니라, 실제 Kubernetes 환경에 배포할 수 있는 플랫폼 형태로 조금씩 가까워졌습니다.
아직 완성형은 아니지만, 중요한 방향은 잡혔다고 생각합니다.
Agent는 노드를 고치는 도구가 아니라, 노드 상태를 정확히 읽고 evidence를 남기는 도구여야 한다.
이 원칙이 앞으로의 개발에서도 계속 중심이 될 것 같습니다.
다음 글에서는 Web Console을 붙이면서 운영자가 RCA report를 어떻게 확인하고, 어떤 흐름으로 클러스터와 report를 관리할 수 있게 만들었는지 정리해보려고 합니다.