AWS Cloud School 13기 112일차
2026-06-12
Today
🔒 dev 시크릿 분리 — Secrets Manager 마이그레이션 (완료)
- ECS task def
dev-app의 평문 민감정보 10개(DB_PASSWORD·JWT_SECRET·PORTONE×3·KAKAO×2·KMA·FCM) → Secrets Manager farmily/dev/app로 이동
- task def
dev-app:4에서 secrets[] valueFrom 참조 주입으로 전환 (비민감 11개는 environment 유지)
- execution role에 인라인
farmily-dev-app-secrets-read(GetSecretValue, 시크릿 ARN 한정) 추가 → 최소권한
- 🔧 트러블슈팅 — 1차 배포 실패
ResourceInitializationError: did not contain json key DB_PASSWORD. 원인=dev tfvars에 DB값 없어(prod tfvars에만 존재) 시크릿 생성 시 키 누락. 해결=값 출처를 현재 떠 있던 dev-app:3 task def env(실배포 평문값)로 잡아 보강 → 재배포
- ✅ 무중단 전환 — 기존
dev-app:3 생존(Circuit Breaker + minHealthy 100%). 시크릿 보강 11:12 → 재배포 11:16 → steady state 11:20:29 (KST)
- [결정] 시크릿 권한·리소스 = 콘솔 관리(Terraform 미관리) — 속도 우선. role 재생성·DR 시 수동 복구 필요 → 설계서에 DR 복구 3단계 + 코드화 TODO 명시. prod도 동일 콘솔 방식
- 📝 설계서 [[Farmily-Secrets-Manager-마이그레이션]] 신규 + 블로그 [[Farmily-보안-1편-Secrets-Manager-마이그레이션]](새 "보안" 시리즈) + [[Farmily-IAM-역할-설계서]] §3.3 추가·§2.2 정정
📊 k6 한계점 부하시험 실행 — 전면 붕괴(78% 에러)로 병목 규명 ⭐ (노션 [[k6 실험결과 보고서]])
- 실측: 2회 런 모두 FAIL. app_errors 79.72% / 77.96%, p95 = 60초(클라이언트 타임아웃 천장), VU 2000인데 throughput 33~35 req/s뿐, dropped_iterations ~2.1만(초당 ~108개 부하를 못 받아냄). "성공" 요청조차 p95 56~57초
- 세 엔드포인트(health 27%·public-profile 19%·public-calendar 18% 성공) 동시 실패 → 특정 API가 아니라 시스템 전체(공유 자원) 병목
- 🎯 근본 원인 진단(인프라 관점): HikariCP 풀 = 10/태스크인데 RDS(prod-rds, db.t3.small)
max_connections≈225 → DB는 여유, 앱이 스스로 10에서 막음. ★ CPU 기반 오토스케일이 풀 병목에 "장님" — 11번째 요청부터 스레드가 풀 대기(blocked)라 CPU를 안 써서 70% 미발동 → 스케일아웃 안 뜸 → Max 8여도 1태스크 × 10연결에 갇힘
- 연결 예산(AWS 실측): 풀10 × 8태스크 = 80 ≪ RDS 225 → 풀 매우 보수적. 안전선 풀 ≤ ~25/태스크 → 권장 풀 20. 읽기복제본(+225 예산) 미사용
- 개선 방향(우선순위): ①[앱] 공개 read 85%(profile·calendar) Read Replica 분산 ②[앱] HikariCP 10→20 ③[인프라] CPU 외 스케일 신호
ALBRequestCountPerTarget TargetTracking ④[인프라] 필요시 RDS 수직확장(t3.medium, max_conn~450). ※ 셋이 맞물려야(태스크↑ × 풀↑ = 총 연결↑) 스케일아웃이 의미
- ⚠️ 방법론 자기비판: "1차 베이스라인"인데 VU 2000 → 정상 기준선·degradation 곡선·breaking point를 못 얻음(즉시 붕괴만 관측). v2 스크립트 약점(sleep 페이싱·단일 hot row 캐시히트·더미0)으로 서버 자극 부족(과거 p95 22ms). 60s 타임아웃이 클라이언트(노트북·홈네트워크) 병목일 가능성도 → 서버지표 교차확인 필요
- 관측 대시보드 v2(Golden Signals) 보강: ALB 5xx 4종 구분(
ELB_5XX=504/Target_5XX=앱500/TargetConnectionError=포화/Rejected)·Target Health·RDS Connections 우측축·ECS CPU 70%선. AMG=서버지표만 → k6(클라이언트 dropped/VU/p95)와 병행 필수
- v3 재테스트 설계(작성됨): open model(
ramping-arrival-rate, sleep 제거)·실데이터(더미 54만 분산)·부하 사다리(baseline 200rps → stress 200~4000 계단 → spike 5000), 같은 리전 EC2 실행(홈네트워크 금지), stress 각 단 5분+(오토스케일 반응 4~5분). 대상 dev 권장(prod 오염 회피, 단 dev는 단일AZ·소형이라 한계점 상이 → 환경별 대시보드)
Notes
시크릿 작업 결정·함정
ResourceInitializationError: did not contain json key X = 시크릿 JSON에 그 키 자체가 없을 때(오타·tfvars 누락). 값 출처를 실배포 중인 task def env로 잡아 복원
- 콘솔 관리(state 밖) 트레이드오프: 평상시
apply는 인라인 정책 안 건드리나 role 재생성·DR 시 수동 복구 필요 → DR 절차 문서화로 보완
k6 설계 결정·전제
- ⚠️ 환경 안전 — to-failure는 본질상 시스템을 무너뜨리는 시험 → prod 직접은 위험. dev/부하전용에서 한계 측정, prod는 야간 knee 70%까지만 캘리브레이션 권고
- ⚠️ 앱 지표 갭 — Farmily 백엔드에
micrometer-registry-prometheus 미탑재(actuator health/info만) → HikariCP pending·JVM thread 패널은 micrometer 도입 후속. 이번엔 CloudWatch(RDS connections)로 커넥션 병목을 간접 관찰
- 콘서트 티켓팅 프로젝트의 k6+Grafana 방법론(엔드포인트별 p50/p95/max 표 + JVM Thread 패널)을 Farmily에 재사용
Learned
ECS 시크릿 주입 — Execution Role vs Task Role
- 시크릿(
valueFrom) 주입은 Execution Role 담당 — 컨테이너 기동 전 ECS 에이전트가 Secrets Manager에서 값을 fetch해 환경변수로 주입하기 때문
- Task Role은 앱이 런타임에 AWS API(S3·Bedrock 등) 호출할 때 쓰는 권한 → 시크릿 주입과 무관
- 그래서
GetSecretValue 권한은 task role이 아니라 execution role에 붙인다 (IAM 설계서 §2.2 개념 정정)
- task def
secrets[](ARN 참조) ↔ environment[](평문): 민감정보는 secrets[]로, 비민감은 environment로 분리
k6 — open model(도착률) vs closed model(VU)
- closed(VU 기반): 서버가 느려지면 다음 요청을 늦게 보내 부하가 저절로 줄어든다 → 진짜 포화를 가린다
- open(
ramping-arrival-rate, 도착률 고정): 서버가 느려져도 목표 RPS를 계속 밀어붙인다 → breakpoint를 드러낸다
- 한계점 시험엔 반드시 open model.
preAllocatedVUs/maxVUs로 충분한 VU 풀 확보
knee point / breakpoint
- 부하를 점증하면 어느 지점까지 latency가 평탄하다가, 임계 RPS를 넘으면 p95/p99 급등 + 성공 throughput 정체 → 그 직전이 안전 운영 한계(knee)
thresholds + abortOnFail + delayAbortEval: SLO(예 p99<1500ms, error<1%)가 깨지면 시험을 자동 중단 → 그 RPS가 한계점
병목은 옮겨다닌다 (Little's Law 관점)
- 한 자원의 병목을 풀면 병목이 사라지는 게 아니라 다음 자원으로 이동한다
- 예: ECS CPU 병목을 오토스케일링으로 풀면 → 태스크 수 × 커넥션 풀이 RDS 커넥션 천장을 새 병목으로 만든다
- 그래서 한계점 시험의 핵심 산출물은 "몇 RPS에서 터지나"가 아니라 "무엇이 먼저 터지나(병목 식별)"
CPU 오토스케일의 맹점 — 풀 병목엔 "장님"
- 커넥션 풀이 마르면 11번째+ 요청 스레드는 풀을 기다리며 blocked 상태 → CPU를 거의 안 씀
- 그래서 CPU 사용률이 안 올라 "CPU 70%" 오토스케일이 미발동 → 서비스는 죽는데 스케일아웃은 손 놓음(Max 8여도 1태스크에 갇힘)
- 교훈: 풀/IO 병목 시스템은 CPU 외 신호(
ALBRequestCountPerTarget·TargetResponseTime)로 스케일해야 함
AMG(서버지표)와 k6(클라이언트지표)는 반드시 병행
- 백엔드가 클라이언트 타임아웃되면 ALB
TargetResponseTime엔 안 잡히고 504로만 잡힘 → AMG만 보면 "응답시간 정상"처럼 착시
- k6의
dropped_iterations·VU·p95와 교차해야 진실이 보임
- 진단 순서(이번 붕괴): 처리량 정체+에러폭증 → ALB 504/ConnectionError → ECS CPU 낮은데 죽음(=CPU병목 아님) → Task 1 정체(오토스케일 미발동) → RDS Connections 10 천장+CPU 낮음(=풀 범인 확정)
Prompt(회고)
잘된 것
- 시크릿 마이그레이션을 무중단으로 끝냈다. 기존 task def 리비전이 살아있는 동안 새 리비전을 올려 Circuit Breaker + minHealthy 100%로 전환 → 실사용 영향 0. 배포 실패(키 누락)도 실측 타임라인으로 근본원인을 추적해 깔끔히 복구
- k6 시나리오를 "단추 누르기"가 아니라 병목 진단 서사로 설계했다. 팀이 FIS·알림 실험에서 이미 얻은 "병목은 커넥션 풀" 통찰을 한계점 시험의 클라이맥스로 끌어와, 포트폴리오로 쓸 수 있는 이야기 구조를 잡음
배운 것 / 다음
- 시크릿 주입 주체(Execution Role)와 권한 주체(Task Role)를 헷갈리지 않게 됐다 — 면접에서 바로 설명 가능한 포인트
- ⏭️ prod Secrets Manager 적용(콘솔: 인라인정책→시크릿 생성→task def secrets화→무중단 배포), k6 시나리오 실행 환경(dev/부하전용) 결정, micrometer 도입 검토(앱 지표)
팀플
- 소통이 중요하다 진짜 너무 팀원이 맞고 싶어하는 담당을 맞기고 믿고 소통없이 진행했다. 진짜 한 파트가 진행이 안되니 우리도 진행이 되지 않는 부분이 생겨버렸다. 분명 오전까지는 없었던 ECS Cluster가 어느새 생겼고 그 담당자는 조퇴를 해버렸다. 말도 없이 이런일이 생기면 안되는데 다시 한 번 이 소통이 얼마나 중요한가를 느끼게 되었고 아무도 그분한테 소통이 안된다고 말씀하시기 어려워 하시는거 같아 그냥 내가 단도직입적으로 말씀을 드렸다. 내 프로젝트가 더 중요하고 모두가 프로젝트가 잘 되길 원하니깐 총대메고 말씀을 드렸다. 참 이 인간관계에서의 소통은 중요한거 같다.