Project 2026.10.19 10 min read

온프레미스 RCA Platform 구축기 ESXi부터 승인 배포까지

2026년 10월, ESXi 두 대와 공유 NFS 위에 가상화 환경을 구성하고 Terraform, Ansible, Jenkins, Kubernetes를 차례로 연결했다. 배포 대상으로는 기존에 개발하던 RCA Platform을 사용했다. 인프라 상태를 수집하고 장애 분석과 보고서 생성 흐름을 다루는 애플리케이션이어서, 직접 만든 인프라에

가상머신을 만들고 Kubernetes에 애플리케이션을 띄우는 것까지는 각각의 실습으로 해볼 수 있었다. 이번에는 그 사이를 연결해보고 싶었다. 코드를 바꾸면 테스트와 이미지 빌드가 실행되고, 검토한 이미지를 배포한 뒤 문제가 생겼을 때 이전 이미지로 돌아갈 수 있는 환경이 목표였다.

2026년 10월, ESXi 두 대와 공유 NFS 위에 가상화 환경을 구성하고 Terraform, Ansible, Jenkins, Kubernetes를 차례로 연결했다. 배포 대상으로는 기존에 개발하던 RCA Platform을 사용했다. 인프라 상태를 수집하고 장애 분석과 보고서 생성 흐름을 다루는 애플리케이션이어서, 직접 만든 인프라에 올려 연동을 확인하기에 적합했다.

최종적으로 소스 변경 감지부터 CI, 사설 Registry 게시, GitOps PR 생성, 사람의 검토와 병합, Argo CD 수동 배포까지 연결했다. 그 과정에서 가장 오래 붙잡았던 문제는 설치 명령보다 구성 요소 사이의 경계에서 나왔다. 인증서를 신뢰하는 curl과 이미지를 받지 못하는 containerd의 결과가 달랐다. 배포가 끝나 Pod 두 개가 Ready여도 원하는 노드 분산은 이루어지지 않았다.

이 글에서는 전체 구성을 소개하고, 이어지는 글에서 각 문제를 어떻게 좁혀갔는지 정리한다. 작업 결과의 기준 시점은 2026년 10월 19일 새벽이다.

가상화와 Kubernetes를 연결한 구성

기반은 ESXi 호스트 두 대와 vCenter, Ubuntu NFS 서버다. 관리망은 기존 환경을 사용했고, vMotion과 NFS에는 별도의 직결 경로를 구성했다. 기존 스위치 구간의 속도 제약을 고려한 선택이었다.

자동화 서버에는 Terraform과 Ansible, Jenkins Controller를 두었다. 별도 CI VM에서 Jenkins Agent가 테스트와 이미지 빌드를 실행하며, 같은 VM의 다른 OS 계정이 사설 Registry를 운영한다. 빌드 계정과 Registry 서비스 계정은 나눴지만, VM과 디스크의 장애 영역까지 분리한 구성은 아니다.

Kubernetes는 Control Plane 한 대와 Worker 두 대로 구성했다. 네트워크는 Calico, 외부 진입은 MetalLB와 Traefik, 영속 볼륨은 NFS CSI를 사용했다. 모니터링은 Prometheus와 Grafana를 연결했다.

계층 실제 사용한 구성 맡긴 역할
가상화 ESXi 2대와 vCenter VM 실행과 관리
공유 스토리지 Ubuntu NFS ESXi 데이터스토어와 Kubernetes 영속 볼륨
VM 및 OS 자동화 Terraform과 Ansible VM 생성, 노드 환경 구성
CI Jenkins와 rootless Podman 테스트, 이미지 빌드와 검증
이미지 저장소 Distribution HTTPS와 인증을 적용한 사설 Registry
애플리케이션 실행 Kubernetes와 Calico Platform, PostgreSQL, 관찰용 Agent 실행
배포 관리 GitOps 저장소와 Argo CD 검토한 변경을 수동 Sync로 적용
ESXi 두 호스트를 관리하는 Infra-Cluster 요약
Infra-Cluster의 HA 보호 상태와 vMotion 이동 기록.
관리·Kubernetes·CI VM을 함께 보여주는 클러스터 인벤토리
클러스터 VM 9개와 관리·Kubernetes·CI 역할 구성.

처음 학습할 때 검토했던 구성과 최종 선택은 달랐다. Harbor와 GitLab도 검토했지만 실제 Registry는 Distribution으로 정했다. 초기 문서의 Proxmox, HAProxy, Keepalived 구성도 이번 최종 구현 목록에 포함하지 않았다. 구축을 진행하면서 선택이 바뀐 만큼, 이 글의 구성은 실제로 연결해 확인한 환경을 기준으로 한다.

Terraform의 완료와 서버 준비의 완료를 나눴다

Terraform에서는 vCenter API로 데이터센터, 클러스터, 데이터스토어, 네트워크, 템플릿을 조회한 뒤 VM을 복제했다. 첫 Clone은 정상적으로 완료됐지만, 그것만으로 사용할 서버가 준비된 것은 아니었다. 게스트의 IP, cloud-init 상태, 사용자 계정과 SSH 접근은 별도로 확인해야 했다.

Ansible에서는 SSH로 대상에 연결한 뒤 기본 환경과 Kubernetes 노드 구성을 관리했다. VM을 만드는 Terraform과 VM 안의 상태를 구성하는 Ansible의 역할을 나누니, 문제가 발생했을 때 어느 단계부터 확인해야 하는지 더 분명해졌다.

스토리지에서도 비슷한 구분이 필요했다. VM에 가상 디스크 40GiB를 할당해도 root 파일시스템이 같은 크기로 잡히지는 않았다. 실제로 작은 root LV가 빌드와 Kubernetes 운영의 제약이 됐다. 이후에는 가상 디스크 크기와 함께 파티션, PV, VG, LV, 파일시스템 크기를 따로 확인했다.

테스트부터 배포 승인까지 연결했다

최종 흐름은 다음과 같다.

GitHub 소스
  → Jenkins SCM polling
  → Java · Frontend · Python 테스트
  → 통합 JAR과 rootless OCI 이미지 빌드
  → 런타임 검사 · Trivy 스캔 · SBOM 생성
  → 사설 Registry 게시와 digest 검증
  → GitOps 저장소에 Platform 이미지 변경 PR 생성
  → 사람의 검토와 병합
  → 승인한 Git revision을 Argo CD로 수동 Sync
  → 실행 이미지 · 노드 분산 · HTTPS · 데이터 · 모니터링 확인

최종 main 빌드에서는 Java 395개, Frontend 24개, Python 219개로 총 638개 테스트가 통과했다. 실패, 오류, 건너뛴 테스트는 0개였다. 브라우저 테스트와 실제 외부 LLM 호출은 이 결과에 포함하지 않았다.

이미지를 게시한 뒤에는 소스 commit과 OCI revision, Registry manifest와 config, 실제 Pod의 imageID를 연결해 확인했다. 테스트한 소스에서 나온 이미지가 실제 배포 대상과 이어지는지 확인하기 위한 과정이었다.

Jenkins가 자동으로 처리하는 범위는 이미지 게시와 GitOps PR 생성까지다. PR 병합과 운영 Sync는 직접 검토하고 승인했다. Argo CD의 Auto Sync, Prune, Self-Heal도 끈 상태로 검증했다.

최종 main 빌드의 테스트 638개 통과 결과
main build #1의 테스트 638개 통과, 실패·건너뜀 0개.
완료된 Jenkins Pipeline 단계
main build #1의 Pipeline 단계 완료.

Ready 두 개 뒤에 남아 있던 배포 문제

첫 신규 이미지 배포는 Argo CD에서 성공했고, 새 Pod 두 개도 Ready였다. HTTPS와 API, DB 접근도 정상이었다. 하지만 두 Pod가 모두 같은 Worker에 올라갔다.

원인은 롤링 업데이트 중 기존 revision과 신규 revision의 Pod를 함께 세는 topology 분산 조건이었다. 기존 Pod가 한쪽 Worker에 남아 있는 동안 신규 Pod 두 개가 다른 쪽에 배치될 수 있었다. 이후 pod-template-hash를 기준으로 revision별 분산을 계산하도록 수정했다.

수정 후에는 Worker마다 Platform Pod가 한 개씩 배치되는 것을 확인했다. 이어 이전 애플리케이션 이미지로 복귀하고 동일한 신규 이미지를 다시 적용했다. 최종 상태는 Healthy/Synced였고, 두 Pod 모두 Ready이며 재시작 횟수는 0이었다.

이미지 전환 중에는 기존 보고서 두 개의 해시와 Flyway 메타데이터, PVC 상태도 확인했다. 이 시험은 애플리케이션 이미지 복귀와 재적용을 확인한 것이며, DB 백업을 실제 복원하는 시험은 별도 과제로 남겼다.

Argo CD의 현재 건강·Sync·수동 정책 요약
Healthy·Synced, Auto Sync 비활성, 1 Warning.
Argo CD의 Synced 리소스 수와 애플리케이션 트리 일부
Argo CD 관리 리소스 Synced 8개와 연결 관계.

RCA Platform의 알림 처리 흐름도 확인했다

인프라에 애플리케이션을 올린 뒤에는 모니터링 연동도 확인했다. 제한된 합성 신호로 Prometheus 알림을 발생시키고, Alertmanager를 거쳐 RCA Platform에 전달했다. 관찰용 Agent가 증거를 수집한 뒤 분석과 보고서, incident 생성으로 이어지고, 원래 신호를 해제하면 incident가 resolved로 바뀌는 흐름을 확인했다.

이때 실제 Worker 장애를 만들지는 않았다. Agent도 기존 safe canary를 유지하며 관찰 범위에서 사용했다. 새 Agent 이미지는 CI에서 검증하고 Registry에 게시했지만 운영 Agent를 교체하지는 않았다.

인프라와 알림 연동을 한 번에 확인할 수 있었지만, 자동으로 노드를 수정하거나 장애를 복구하는 단계까지 구현한 것은 아니다. 실제 장애를 어떤 조건에서 감지하고 누구에게 알릴지, 어떤 변경을 자동으로 허용할지는 후속 운영 설계가 필요하다.

다음에 보완할 부분

현재 구성에는 단일 NFS, 단일 Control Plane, 단일 PostgreSQL이 남아 있다. Platform Pod를 두 Worker에 분산했어도 전체 서비스의 장애 지점이 모두 사라지지는 않는다.

DB 백업은 암호화 파일 생성, 인증된 복호화, pg_restore-list 확인까지 진행했다. 다음 단계는 별도 환경에서 실제 복원을 실행해 데이터와 애플리케이션 동작을 확인하는 것이다. 운영 실패 알림 경로, 자격증명과 백업 키 인수도 정리할 필요가 있다.

이번 구축에서는 설계 선택과 진행 판단을 내가 맡고, 명령 검토와 문제 분석에 ChatGPT를, 일부 변경과 검증 자동화에 Codex를 사용했다. 실행 결과를 확인하고 배포 범위를 결정하는 과정까지 함께 기록하려고 했다.

연재는 이 소개 글과 본편 22편으로 구성했다. 1–6편은 가상화와 물리 네트워크, 스토리지와 원격 접속을 다룬다. 7–12편에서는 Terraform과 Ansible, Kubernetes의 네트워크와 영속 볼륨을 연결한다. 13편은 Prometheus·Grafana·Alertmanager 관측을, 14–20편은 Jenkins와 Registry, RCA 연동과 GitOps를 다룬다. 21–23편에서는 실제 롤아웃 수정, 이미지 복귀와 데이터 확인, 남은 운영 과제를 정리한다.

설치가 끝난 화면보다 어떤 관측으로 판단을 바꿨는지 남기는 것이 이 프로젝트를 다시 이해하는 데 도움이 됐다.

SEARCH JOURNAL

기록 검색

글 제목, 요약, 태그에서 찾습니다.

검색을 준비하고 있어요.

검색어를 입력해 주세요.

    Ctrl/⌘ K 검색 Esc 닫기 ↑ ↓ 결과 이동