Role: Infra & DevOps — 아키텍처 설계 | 인프라 구축 | CI/CD | Istio 도입
Period: 2025.10 – 2025.12
Team: 7인
Link: [GitHub] [RCA Report]
1. 프로젝트 소개
COME2US는 트래픽 증가 상황을 가정한 MSA 기반 전자상거래 플랫폼입니다.
이 프로젝트에서 저는 단순히 서비스를 클라우드 위에 배포하는 것보다, 서비스가 늘어날수록 반복되는 인프라 구성, 배포 방식, 운영 절차, 장애 대응 방식을 어떻게 더 안정적이고 재현 가능하게 만들 수 있을지에 집중했습니다.
초기에는 Monolith로 빠르게 MVP를 검증했고, 이후 ECS Fargate 기반 MSA로 서비스를 분리했습니다. 그러나 서비스 수가 늘어나면서 서비스 디스커버리, 배포 운영 방식, 공통 정책 관리가 복잡해졌고, 이를 해결하기 위해 최종적으로 EKS 기반 Cloud-Native 환경으로 전환했습니다.
COME2US는 각 단계에서 드러난 운영 한계를 근거로 아키텍처를 전환하며, 인프라 재현성, 배포 자동화, 관측성, 장애 대응 체계를 단계적으로 고도화한 프로젝트입니다.
[이미지 1] Event-Driven MSA Architecture
2. 왜 구조를 전환했는가 — Architecture
COME2US의 아키텍처는 단순히 새로운 기술을 적용하기 위한 것이 아닌 각 단계에서 실제로 드러난 운영 문제를 근거로 다음 단계로 전환되었습니다.
Phase 1. Monolith
초기 구조는 단일 EC2 위에 애플리케이션을 Docker 컨테이너로 배포해 MVP를 빠르게 검증했습니다. 개발 속도는 확보할 수 있었지만, 두 가지 구조적 문제가 분명했습니다.
•
확장성 한계 — 특정 서비스에만 부하가 증가해도 서버 전체를 키워야 하는 수직 확장에 의존
•
장애 전파 — 서비스 간 경계가 없었기 때문에 하나의 서비스 장애가 동일 서버의 다른 기능에도 영향을 줄 수 있는 구조
→ 서비스 독립성과 수평 확장을 위해 MSA + ECS로 전환했습니다.
Phase 2. ECS Fargate 기반 MSA
서비스를 독립 컨테이너로 분리하면서 장애 격리와 서비스 분리는 개선되었지만, 서비스 수가 늘어나면서 새로운 운영 부담이 생겼습니다.
•
ECS Fargate의 서비스 디스커버리 문제
◦
ECS의 내부 IP/외부 IP의 차이로 기반 서비스 디스커버리가 정상 동작하지 않음
◦
일시적으로 hostname을 환경변수로 주입하는 방식으로 대응 → 운영 확장성 떨어짐
•
공통 정책 관리의 한계 — 인증∙인가∙라우팅∙필터링∙트래픽 제어 등의 공통 정책이 Spring Cloud Gateway와 애플리케이션 레벨에 묶임 → 정책 변경이 코드 변경과 재배포로 이어짐
→ 공통 정책을 플랫폼 레이어에서 통합 처리하기 위해 EKS 기반 Cloud-Native 전환
Phase 3. EKS 기반 Cloud-Native
최종적으로 EKS 기반 환경으로 전환하며 Kubernetes, GitOps, Service Mesh를 활용한 운영 구조를 설계함으로써 배포∙정책∙관측을 선언적으로 관리할 수 있는 운영 기반을 구축했습니다.
3. 인프라를 어떻게 재현 가능하게 만들었는가 — Infrastructure as Code
재현성 확보
먼저 해결하고자 했던 문제는 인프라 구성이 사람의 수동 작업에 의존하지 않도록 만드는 것이었습니다. COME2US에서는 AWS 인프라와 Kubernetes 실행 기반을 Terraform으로 구성했습니다. VPC, EKS, RDS, ElastiCache, MSK, IAM 등 주요 인프라 리소스를 코드로 관리해, 동일한 환경을 반복적으로 재구성할 수 있도록 했습니다.
Remote Backend 구성 — S3 + DynamoDB
Terraform state는 로컬 파일에 의존하지 않고 S3 Remote Backend로 관리했습니다. 인프라 상태를 중앙에서 관리함으로써, 로컬 환경 차이로 인한 state 유실이나 불일치 가능성을 줄이고자 했습니다.
또한 DynamoDB Lock Table을 함께 구성해, 동시에 Terraform이 실행될 때 state가 충돌하지 않도록 해 팀원들과 일관된 방식으로 상태를 관리할 수 있도록 구성했습니다.
인프라 레이어와 클러스터 레이어의 경계 분리
또한 Terraform이 관리해야 할 영역과 GitOps가 관리해야 할 영역을 분리했습니다.
•
Terraform: VPC, EKS, IAM, 데이터 계층, 클라우드 인프라 리소스 관리
•
ArgoCD: Kubernetes 내부 애플리케이션, 플랫폼 컴포넌트, Manifest 관리
인프라 레이어와 클러스터 운영 레이어의 변경 주기와 책임 범위가 다르다고 판단해, 이와 같이 책임 경계를 나눴습니다. 인프라는 Terraform으로 재현성을 확보하고, 클러스터 내부 리소스는 GitOps로 선언적 상태 수렴을 유지하는 방향을 선택했습니다.
Packer 기반 Jenkins 환경 재구성
프로젝트 환경에서는 비용 제약으로 인해 AWS 리소스를 자주 Destroy/Recreate해야 했고, 이로 인해 Jenkins 서버와 Docker 환경을 매번 다시 구성해야 하는 문제가 있었습니다. 이 과정은 휴먼 에러와 설정 드리프트로 이어질 수 있었기 때문에, 이를 줄이기 위해 Packer를 사용했습니다.
Packer를 통해 Docker가 사전 구성된 Golden AMI를 만들고, Terraform으로 Jenkins EC2, EBS, ALB, 컨테이너 기동까지 자동화했습니다.
그 결과 terraform apply만으로 CI 환경을 반복적으로 재구성할 수 있는 운영 모델을 만들었습니다.
4. 배포를 어떻게 표준화했는가 — CI/CD & Delivery Automation
아키텍처가 Monolith → ECS → EKS로 고도화되면서, 배포 방식도 단순 자동화에서 운영 효율성과 재현성을 확보하는 방향으로 함께 발전시켰습니다. 서비스 수 증가에 따른 파이프라인 관리 비용을 줄이고, 인프라 변경과 애플리케이션 배포를 더 일관된 방식으로 운영하는 것을 목표로 했습니다.
Phase 1. GitHub Actions — 단일 서비스 CI
Phase 2. Jenkins Shared Library — 공통 CI 표준화 + Terraform 연계 Blue/Green 배포
Phase 3. GitHub Actions(CI) + ArgoCD(CD) — 역할 분리, GitOps 기반 선언적 배포
단일 서비스 CI — GitHub Actions
초기에는 GitHub Actions로 단일 서비스 CI를 구성했지만, MSA 전환 이후 서비스별로 별도 파이프라인이 생기면서 중복 코드와 관리 비용이 증가했습니다.
공통 CI 표준화 — Jenkins Shared Library
마이크로서비스 환경에서 서비스 수 증가에 따라 파이프라인 중복과 관리 포인트가 늘어났습니다.
이를 해결하기 위해 Jenkins Shared Library를 도입했습니다. 빌드∙테스트, 이미지 빌드 및 푸시, 알림 흐름을 공통화해 Jenkinsfile 중복을 줄이고, 공통 CI 로직을 표준화했습니다.
Gradle 빌드 캐시와 Docker BuildKit 레이어 캐시를 적용해 반복 빌드 효율도 함께 개선했습니다. 그 결과 Docker Build 시간이 약 95% 단축되는 효과를 얻을 수 있었습니다.
•
Shared Library를 활용해 서비스별 Jenkinsfile 중복을 줄이고 공통 배포 로직을 표준화
•
BuildKit, Gradle Cache 적용을 통해 빌드 시간을 약 95% 단축
[이미지 2] Docker Layer Cache 적용 전/후: 1분 3초 → 3.2초 단축
CI 빌드
→ Warm-up 서비스 생성
→ Health Check
→ ALB Target Group 전환
→ 이전 환경 정리
Plain Text
복사
[이미지 3] CI/CD 파이프라인 흐름도
CI/CD 역할 분리 — GitHub Actions + ArgoCD
이후 Jenkins 자체가 별도 운영 대상이 되면서 CI와 CD의 책임을 다시 분리했습니다.
•
GitHub Actions: 이미지 빌드 및 테스트, ECR Push
•
ArgoCD: Git 상태 기준 Kubernetes 리소스 선언적 동기화
•
External Secrets + SSM Parameter Store: 애플리케이션 시크릿을 Git 저장소와 분리하여 관리
배포 파이프라인에서 서버 운영 부담을 줄이고, 인프라와 애플리케이션 변경을 GitHub를 단일 지점으로 설계했습니다.
COME2US에서는 Istio를 도입해 인증∙인가∙라우팅 정책을 애플리케이션 코드에서 분리했습니다.
기존에는 공통 정책 변경이 서비스 코드 수정과 재배포로 이어졌지만, Istio 도입 이후 Manifest PR과 ArgoCD Sync 흐름으로 정책을 반영할 수 있게 되었습니다.
5. 운영 환경을 어떻게 안정화했는가 — Runtime Operations
단순히 워크로드를 실행하는 것이 아닌, 플랫폼 컴포넌트 안정성, 애플리케이션 확장성, 비용 효율을 고려한 운영 구조를 설계했습니다.
노드 그룹 분리
플랫폼 컴포넌트와 애플리케이션 워크로드가 서로 영향을 주지 않도록 노드 그룹을 분리했습니다.
노드 그룹 | 운영 방식 | 목적 |
Infra Node Group | On-Demand | Karpenter · ArgoCD · Istio · 모니터링 등 핵심 운영 컴포넌트 안정성 확보 |
App Baseline Node Group | On-Demand | 일반 트래픽 구간의 기본 애플리케이션 처리 용량 보장 |
Dynamic Node | Karpenter + Spot | 트래픽 증가 시 비용 효율적인 확장 처리 |
•
핵심 운영 컴포넌트 안정성 확보:
infra 노드 그룹에는 Taint를 적용해 핵심 운영 컴포넌트가 애플리케이션 부하에 밀려 Pending 상태가 되는 상황을 방지했습니다.
•
Karpenter + Spot 기반 확장:
애플리케이션 워크로드는 평상시에는 On-Demand 기반 노드에서 안정적으로 운영하고, 트래픽 증가 구간에서는 Karpenter가 Spot 노드를 동적으로 프로비저닝해 확장하도록 구성했습니다.
이 구조를 통해 기본 안정성을 유지하며 빠른 프로비저닝과 약 70% 비용 절감 효과를 얻을 수 있었습니다.
HPA + Karpenter Race Condition — minReplicas 버퍼 전략
HPA는 메트릭 감지 즉시 Pod 생성을 요청하지만 Karpenter는 노드 프로비저닝에 약 60초가 소요됩니다. 이 시간 차이로 인해 트래픽이 급격히 증가할 경우, 노드가 Ready되기 전 Pod Pending이 누적되는 문제가 발생할 수 있다고 판단했습니다.
이를 완화하기 위해 핵심 도메인에는 더 높은 minReplicas를 설정하고, 조회성 서비스에는 상대적으로 낮은 minReplicas를 적용했습니다.
•
주문∙결제: 높은 minReplicas로 기본 처리 여유 확보
•
조회성 서비스: 낮은 minReplicas로 비용 효율 유지
Spot Interruption 대응
SQS + EventBridge로 Spot Interruption을 감지해 선제적으로 대체 노드를 프로비저닝하는 구조를 갖춰 Spot 사용으로 발생할 수 있는 장애를 대비했습니다.
6. 운영 상태를 어떻게 보이게 만들었는가 — Observability
Service Mesh 도입으로 서비스 간 호출 흐름 추적이 필요해졌습니다. COME2US에서는 Prometheus, Grafana, Loki, Tempo를 활용해 메트릭∙로그∙트레이스를 통합하는 관측성 체계를 구성했습니다.
구분 | 구성 |
Metric | Prometheus → Grafana |
Log | Fluent Bit → Loki → Grafana |
Trace | OTel → Tempo → Grafana |
Infra Metric | CloudWatch |
•
CPU, Memory, Disk I/O 등 시스템 메트릭
•
HTTP 응답 시간 및 상태값
•
서비스별 요청 빈도와 에러율
•
서비스 간 호출 경로와 지연 시간
운영 이슈를 분석할 때 지표와 로그를 기준으로 원인을 좁히는 기반으로써 관측성 체계를 활용했습니다. 특히 Jenkins 장애 분석 과정에서는 CloudWatch의 EBS 지표를 통해 스토리지 I/O 병목이 원인임을 확인할 수 있었습니다.
7. 장애를 어떻게 구조적으로 개선했는가 — RCA
Issue. Jenkins Node Hang 장애 — EBS gp2 병목 장애
CI 환경에서 Docker 이미지 빌드 중 노드 전체가 Hang 상태로 전환되는 문제가 발생했습니다. 일반적인 CPU와 Memory의 리소스 부족을 의심했지만 정상범위였습니다. 따라서 다른 병목 가능성을 확인하기 위해 CloudWatch 지표를 확인했습니다.
CloudWatch 지표를 통해 다음 패턴을 확인했습니다.
[이미지 4] BurstBalance 고갈
[이미지 5] VolumeQueueLength 급증
[이미지 6] VolumeTotalReadTime 급증
•
BurstBalance 고갈
•
VolumeTotalReadTime 급증
•
VolumeQueueLength 급증
Jenkins 서버는 단일 Root EBS(gp2)에서 OS I/O와 Docker I/O가 경합하는 구조였습니다. 따라서 Docker 이미지 빌드 과정에서 I/O가 집중되면서 gp2의 크레딧 기반 IOPS 구조가 한계에 도달했고, 그 결과 EBS I/O Throttling이 발생한 것으로 판단했습니다.
해결은 두 방향으로 진행했습니다.
1.
Root/Data 볼륨 분리
OS 레이어와 Docker 빌드 워크로드의 I/O 경합을 분리했습니다.
2.
gp2 → gp3 전환
크레딧 기반 가변 IOPS에서 고정 IOPS 구조로 전환했습니다.
Jenkins Node의 Node Hang 현상이 해소되었고, 지표도 안정화되었습니다.
이 경험을 바탕으로 EKS 환경에서 gp3를 Default StorageClass로 설정함으로써 Prometheus, Loki, Tempo와 같이 지속적인 쓰기 I/O가 발생하는 컴포넌트에서 유사한 문제가 반복되지 않도록 했습니다.
[이미지 7] QueueLength 안정화 (피크 후 안정화)
[이미지 8] TotalReadTime 안정화 (피크 후 안정화)
8. 어떤 결과를 만들었는가
COME2US에서의 DevOps 관점 결과는 다음과 같습니다.
영역 | 변화 |
인프라 재현성 | 수동 설정 반복 → Terraform 기반 재구성 |
CI 표준화 | 서비스별 Jenkinsfile 중복 → Shared Library 기반 공통화 |
배포 방식 | 개별 배포 중심 → GitHub Actions + ArgoCD 기반 GitOps |
배포 안정성 | Terraform 연계 Blue/Green 배포로 무중단 전환 |
빌드 효율 | Docker Build 시간 1분 3초 → 3.2초 |
운영 가시성 | 메트릭·로그·트레이스 통합 관측성 체계 구성 |
장애 대응 | CloudWatch 기반 EBS I/O 병목 RCA 및 gp3 전환 |
런타임 운영 | Karpenter + Spot 기반 확장 구조 설계 |
정책 변경 | 서비스 재배포 중심 → Manifest PR 기반 반영 |
9. 프로젝트 이후 보완 Lab
COME2US에서 일정상 완성하지 못했던 Canary 배포와 Circuit Breaker 시나리오를 프로젝트 종료 후 k3d 로컬 환경에서 별도로 구현·검증했습니다.
Istio 1.29.0과 httpbin을 사용해 DestinationRule 기반 Circuit Breaker, stable/canary subset, VirtualService 기반 weight 라우팅을 구성했고, 수동 Canary 전환과 Envoy sidecar의 비정상 엔드포인트 차단 동작을 확인했습니다.
이를 통해 COME2US에서 설계로 남아 있던 트래픽 관리 시나리오를 실제 동작 수준까지 보완했습니다.







