Cloud-Native Platform · DevOps Portfolio

Notiflex Platform

B2B 알림 SaaS의 쿠버네티스 운영 환경을 빈 클러스터부터 구축했습니다. GitOps 배포 파이프라인 · 3축 관측 가능성 · 무중단 배포 전략 · 이벤트 기반 아키텍처를 하나의 플랫폼으로 통합한 엔드투엔드 인프라 프로젝트입니다.

Role
DevOps Engineer · 1인
Period
2026.07 · 약 3주
Platform
GKE Standard · GCP
8
구축 단계
(기반→고도화)
15+
통합 오픈소스
도구
3
배포 전략
진화
16
아키텍처
결정 기록
84
Git 커밋
(전 이력 추적)
4
GitOps 앱
전부 Synced
01

프로젝트 개요

Notiflex는 B2B 알림 SaaS 플랫폼입니다. 이 프로젝트는 "코드는 돌아가지만 운영은 없는" 상태에서 출발해, 배포 자동화 · 관측 가능성 · 무중단 배포 · 확장성 · 이벤트 처리를 갖춘 프로덕션급 운영 환경으로 끌어올리는 전 과정을 다룹니다.

모든 변경은 Git을 단일 진실 소스로 삼아 선언적으로 관리되며, 클러스터 상태는 ArgoCD가 자동으로 Git과 일치시킵니다. 애플리케이션은 외부 프레임워크 없는 Go 표준 라이브러리로 작성해 scratch 컨테이너로 빌드했고, 인프라는 비용 효율을 위해 Spot VM 2노드라는 극단적 제약 위에서 전체 스택을 운영했습니다 — 이 제약이 곧 리소스 최적화와 장애 대응 역량을 검증하는 무대가 되었습니다.

02

기술 스택

각 도구는 대안과의 비교(탐색→비교→결정)를 거쳐 선택했고, 그 근거는 16개의 ADR로 저장소에 남겼습니다.

Cloud / Infra

GKE Standard (Zonal)Spot VM Artifact RegistryCloud Build멀티 노드풀

GitOps / CI-CD

ArgoCDArgo Rollouts GitHub ActionsApp of Apps

Observability

PrometheusGrafanaLoki Fluent BitAlertmanagerTempo OpenTelemetry

Data / Messaging

ValkeyApache KafkaStrimzi (KRaft)

Security

Secret ManagerWorkload IdentitySecrets Store CSI

Traffic / App

Gateway APIHTTPRoute Go (stdlib)scratch image
03

시스템 아키텍처

외부 요청은 Gateway API로 진입해 Canary Rollout으로 라우팅되고, 캐시·메시징·시크릿·트레이싱 백엔드와 연결됩니다.

런타임 요청 경로
Ingress
Gateway API
gke-l7-regional · 외부 IP
HTTPRoute
/ → notiflex-api
HealthCheckPolicy
/health : 8080
Service
notiflex-api
stable (active)
notiflex-api-preview
canary
Workload
Rollout · notiflex-api
Argo Rollouts · Canary · api-pool · ×2
Rollout · enterprise
2번째 테넌트 · api-pool
Backend
Valkey
INCR 카운터 공유
Kafka
notifications topic ×3
Secret Manager
CSI + Workload Identity
Tempo
OTLP gRPC :4317
GitOps 배포 파이프라인
git
코드 push
app/ 변경
CI
GitHub Actions
빌드 · push
매니페스트 태그 갱신
registry
Artifact Registry
sha-xxxxxxx 태그
CD
ArgoCD
감지 · auto-sync
selfHeal
rollout
Argo Rollouts
20 → 50 → 80 → 100%
04

배포 전략의 진화

단순 롤링 업데이트에서 시작해, 안전성과 리소스 효율을 저울질하며 두 번의 전략 전환을 거쳤습니다. 모든 전환은 Git 커밋만으로 롤백 가능합니다.

Phase · 초기

Rolling Update

v0.1.x
  • ArgoCD 기본 배포
  • Pod 하나씩 순차 교체
  • 문제 발견이 배포 이후
Phase · 무중단

Blue / Green

v0.2.0
  • Argo Rollouts 도입
  • active / preview 분리
  • 30초 검증 후 auto-promote
Phase · 점진

Canary

v0.4.1 ~
  • 트래픽 20→50→80→100%
  • 단계별 30초 pause
  • 리소스 2배 불필요 · 조기 발견
05

구축 여정

빈 클러스터에서 고도화된 이벤트 기반 플랫폼까지, 7개 단계로 점진적으로 쌓아 올렸습니다.

Phase 1 · 기반

클러스터 · 컨테이너 · 첫 배포

GKE Standard(Spot) 클러스터를 생성하고 Go API를 scratch 이미지로 빌드해 첫 배포. Gateway API를 사전 활성화하고 kubectl 컨텍스트 안전 규칙을 확립.

GKEArtifact RegistryGo / scratch
Phase 2 · GitOps CI/CD

배포를 Git으로 옮기다

ArgoCD로 GitOps를 구축하고 GitHub Actions CI를 연결. 코드 push → 이미지 빌드 → 매니페스트 자동 갱신 → ArgoCD 자동 배포의 엔드투엔드 파이프라인을 완성하고 롤백까지 검증.

ArgoCDGitHub ActionsselfHeal
Phase 3 · 관측 가능성

메트릭 · 로그 · 알림

kube-prometheus-stack, Loki+Fluent Bit를 설치하고 Notiflex 전용 대시보드를 구성. PrometheusRule 알림을 실제 firing→resolved까지 유발해 검증.

PrometheusGrafanaLokiAlertmanager
Phase 4 · 무중단 배포

외부 트래픽 · Rollouts

Gateway API로 외부 진입점을 열고, Deployment를 Argo Rollouts로 전환해 Blue/Green 무중단 배포를 도입. 아키텍처 결정을 ADR로 기록하기 시작.

Gateway APIArgo RolloutsADR
Phase 5 · 엔터프라이즈 기반

상태 공유 · 시크릿 · Canary

Valkey로 Pod 간 상태를 공유하고, 시크릿을 Secret Manager + Workload Identity + CSI로 전환(SA 키 파일 제거). 배포 전략을 Canary로 진화시킴.

ValkeySecret ManagerWorkload IdentityCanary
Phase 6 · 규모 확장

노드 분리 · App of Apps · 멀티테넌시

역할별 노드풀로 워크로드를 격리하고, App of Apps로 여러 Application을 선언적으로 통합. Namespace 기반 멀티테넌시로 2번째 테넌트를 추가.

멀티 노드풀App of Apps멀티테넌시
Phase 7 · 고도화

이벤트 기반 · 분산 트레이싱 · 배치

Kafka(Strimzi, KRaft)로 비동기 처리를 도입하고, OpenTelemetry+Tempo로 요청의 전체 여정을 추적. CronJob으로 헬스체크를 자동화해 관측 3축(메트릭·로그·트레이스)을 완성.

KafkaTempoOpenTelemetryCronJob
06

핵심 구현

01 GitOps 배포 파이프라인

Git을 유일한 진실 소스로 삼아, 코드 push 한 번으로 빌드부터 Canary 배포까지 사람 개입 없이 진행됩니다. selfHeal이 클러스터의 수동 변경을 자동으로 되돌립니다.

트리거app/ 변경 시 GitHub Actions
이미지git SHA 기반 태그 (추적성)
동기화ArgoCD auto-sync + selfHeal

02 관측 가능성 3축

메트릭·로그·트레이스를 Grafana 하나로 통합했습니다. /id 요청 하나가 Valkey·Kafka 자식 span으로 분해되어 어디서 느린지 눈으로 보입니다.

메트릭Prometheus · ServiceMonitor
로그Loki · Fluent Bit DaemonSet
트레이스Tempo · OTLP gRPC

03 Keyless 시크릿 관리

비밀번호를 Git이나 K8s Secret 평문이 아니라 GCP Secret Manager에 두고, Workload Identity로 SA 키 파일 없이 Pod에 파일로 마운트합니다. 두 테넌트가 동일 GSA를 안전하게 재사용합니다.

저장소GCP Secret Manager
인증Workload Identity (keyless)
전달Secrets Store CSI 파일 마운트

04 이벤트 기반 비동기

동기 처리로 몰리면 느려지던 요청을 Kafka로 분리했습니다. API가 Producer, 별도 Consumer가 메시지를 소비 — Consumer가 죽어도 디스크에 남은 메시지는 유실되지 않습니다.

브로커Kafka 4.1 · KRaft · 단일 브로커
운영Strimzi Operator · CRD 선언
토픽notifications · 3 partitions
07

문제 해결 경험

가장 많이 배운 지점들입니다. 특히 "에러 없이 조용히 실패하는" 유형의 문제를, 종료 코드·재시작 카운트·스키마 검증 같은 근거로 추적해 해결했습니다.

리소스 고갈로 인한 연쇄 장애

Critical · 연쇄
상황
2노드 e2-medium 환경에서 공유 캐시 Valkey가 14시간 넘게 Pending. 그 여파로 이를 의존하는 API가 275회 CrashLoopBackOff.
원인
Canary·노드풀 갱신으로 노드 여유가 줄어든 상태에서 Valkey(50m 요청)가 스케줄 실패 → 의존 앱까지 도미노.
해결
CPU 요청을 10m으로 축소(helm-values로 재현성 확보), StatefulSet을 scale 0→1로 강제 재기동해 새 리비전 적용.
Lesson공유 백엔드의 스케줄 실패는 단일 앱이 아니라 전체 도메인을 무너뜨린다. 리소스 예산은 "최악의 동시 배포 시점"을 기준으로 잡는다.

조용한 실패 — Tempo OOMKilled

Warning · 은폐
상황
트레이싱은 "설치 완료" 상태였지만 Tempo가 30분 주기로 OOMKilled. 앱은 죽지 않아 겉보기엔 정상이었음.
원인
메모리 한도 256Mi가 트레이스 블록 flush 시점에 부족. 앱의 트레이스 export만 조용히 실패(connection refused)했고 사용자 경로엔 노출 안 됨.
해결
한도 512Mi로 상향, 종료 코드(137)·재시작 카운트로 원인 확정, 신규 트레이스 저장까지 검증.
Lesson"설치됨 ≠ 동작함". 관측 스택 자체도 관측 대상이다 — 배포 후 재시작 카운트와 exit code를 반드시 본다.

조용한 실패 — Kafka 파티션 누락

Warning · 데이터 손실
상황
Producer는 정상인데 Consumer 로그에 메시지가 하나도 안 찍힘. 에러조차 없음.
원인
Consumer가 파티션 0만 구독. Producer는 Key 없이 발행 → 기본 파티셔너가 3개 파티션에 랜덤 분산.
해결
consumer.Partitions()로 전체 파티션을 조회해 각각 구독하도록 수정.
Lesson분산 시스템의 기본 동작(파티셔닝)을 이해하지 못하면 "에러 없는 데이터 손실"을 만난다.

CRD 스키마의 함정 — Strimzi

Info · 스키마
상황
Kafka Pod에 nodeSelector를 지정했는데 적용되지 않고 ArgoCD가 계속 OutOfSync.
원인
Strimzi CRD의 template.pod에는 nodeSelector 필드가 없음 → API 서버가 조용히 제거. 노드 배치는 affinity만 지원.
해결
kubectl explain으로 스키마 확인 후 nodeAffinity로 교체.
LessonOperator CRD는 K8s 표준과 필드가 다르다. "당연히 있겠지"를 스키마로 검증하는 습관.

제약 속 최적화 — 2노드 전체 스택

Win · 사전 대응
상황
e2-medium 2노드에 ArgoCD·관측 스택·Valkey·CSI를 모두 올려 CPU 95%+. 신규 컴포넌트 추가 시 Pending 위험.
해결
Secret Manager CSI(노드당 120m) 활성화 전에 Prometheus/Grafana/Alertmanager를 100m→5m, Loki/Fluent Bit를 10m→1m로 선제 축소.
결과
CSI 전환·Workload Identity 활성화가 모두 Pending·재시도 없이 한 번에 성공.
Lesson리소스 예산을 사후 대응이 아니라 사전 확보로 다루면 연쇄 장애를 예방한다.
08

아키텍처 의사결정 기록

"무엇을 왜 선택했고 무엇을 안 썼는가"를 16개의 ADR로 저장소에 누적했습니다 — 다른 사람도, 미래의 나도 같은 근거를 추적할 수 있습니다.

ID결정대신 안 쓴 것영역
ADR-001GitOps 도구로 ArgoCDFlux · Jenkins X · SpinnakerGitOps/CI
ADR-002CI로 GitHub ActionsCloud Build · GitLab CI · JenkinsGitOps/CI
ADR-003메트릭에 Prometheus + GrafanaDatadog · CloudWatch관측
ADR-004로그에 Loki + Fluent BitELK Stack관측
ADR-005알림에 PrometheusRule + AlertmanagerGrafana Alerting관측
ADR-006외부 트래픽에 Gateway APIIngress NGINX · Istio트래픽
ADR-007무중단 배포에 Argo Rollouts (B/G)Flagger · Istio 카나리배포
ADR-008캐시에 ValkeyRedis(SSPL) · Memcached데이터
ADR-009시크릿에 Secret Manager CSI + WISealed Secrets · SA 키보안
ADR-010배포 전략을 Canary로 전환Blue/Green 유지배포
ADR-011워크로드 배치에 nodeSelector + 노드풀taint · nodeAffinity 단독확장
ADR-012다중 앱에 App of AppsApplicationSet · 수동확장
ADR-013멀티테넌시에 Namespace 분리vCluster · 클러스터 분리확장
ADR-014메시징에 Kafka (Strimzi)RabbitMQ · NATS고도화
ADR-015트레이싱에 Grafana TempoJaeger · Zipkin고도화
ADR-016배치에 K8s CronJob외부 cron · Argo Workflows고도화
09

프로젝트 방법론 · GitAIOps

전 과정을 Git 중심 + AI 페어링 + 운영 자동화가 결합된 GitAIOps 워크플로로 수행했습니다. 모든 도구 선택은 "탐색 → 비교 → 결정" 3단계를 거치고, 위험 작업은 가드레일로 통제하며, 결정은 ADR로, 진행은 JOURNEY 기록으로 남겨 재현 가능하고 감사 가능한 방식으로 진행했습니다.

Git

단일 진실 소스

모든 변경은 커밋으로만. 클러스터는 Git을 자동 추종하고, 롤백은 revert 하나로.

AI

가드레일 페어링

반복·위험 작업을 AI와 페어링하되, 실행 전 상태 확인과 승인 규칙으로 통제.

Ops

3층 문서 체계

CLAUDE.md(규칙) · claude-context(현재 그림) · ADR(결정 누적)로 컨텍스트 분리.

10

검증된 역량

GitOps · CD 파이프라인 설계

ArgoCD App of Apps와 Argo Rollouts로 선언적·자가치유 배포를 엔드투엔드로 구축.

관측 가능성 엔지니어링

메트릭·로그·트레이스 3축을 Grafana로 통합하고, 알림을 실제 상황으로 검증.

장애 대응 · 근본 원인 분석

연쇄 Pending, OOMKilled, 조용한 데이터 손실을 exit code·스키마·리소스로 추적·해결.

클라우드 보안 · IAM

Workload Identity 기반 keyless 인증과 Secret Manager로 SA 키 파일을 제거.

리소스 최적화 · 비용 효율

Spot 2노드 제약 위에서 관측·데이터·메시징 전체 스택을 사전 예산 관리로 운영.

확장성 · 멀티테넌시

역할별 노드풀 분리와 Namespace 테넌시로 워크로드를 격리하고 2번째 테넌트를 추가.