본문으로 건너뛰기

CA · 확장 후 — 클라우드 형상

갱신일 2026-08-12 · 기준: 확장 개요 · 현재 판 CA 이 문서가 갖는 것: 무엇 위에서 돌리는가(ECS), 오디오가 어떤 경로로 S3와 GPU로 가는가, Fargate에서 관측을 어떻게 하는가, AWS를 어떻게 굴리는가, 무엇이 날아가면 무엇으로 되돌리는가, 얼마가 드는가. 여기 없는 것: 화면(IA) · 모듈(AA) · 경계 프로토콜(SA) · 저장소 소유권(DA) · 그래프 스키마(온톨로지).

⚠︎ 이건 계획이지 현황이 아니다. 지금 서 있는 것은 현재 판 CA가 갖는다. 인스턴스 타입·금액·임계값은 전부 추정이다.

1. 지금과 무엇이 다른가

지금1만10만
실행 기반EC2 + docker compose (손으로)ECS Fargate (ARM)그대로
앞단EC2 안의 nginxALBALB
servert4g.small ×1Fargate 2vCPU/4GB ×2~4×4~10
ait4g.micro ×1Fargate 1vCPU/2GB ×2×4~8
speech없다ECS on EC2 · GPU Spot ×0..2×0..8
mcp없다Fargate 0.5vCPU/1GB ×2×2~4
DBdb.t4g.micro Single-AZdb.m7g.large Multi-AZ+ 읽기 복제본
벡터같은 RDS 안 pgvector그대로전용 인스턴스
Neo4j없다EC2 m7g.large ×1m7g.xlarge ×1
오디오저장 안 함S3 · 7일 수명그대로
브로커없음 (JVM 내부)관리형 RabbitMQ이중화
인메모리SQS그대로
아웃바운드public subnet 직결NAT ×1 + VPC endpointNAT ×2
시크릿EC2의 .env 파일Secrets Manager → 태스크 정의그대로
배포SSM + compose 교체ECS 롤링 · 태스크 정의 리비전으로 롤백그대로
관측EC2에 Alloy사이드카 + OTLP push그대로
백업RDS 자동 백업만PITR + 재생성 리허설+ 스냅샷 타 리전 복사
IaC없다필수필수

2. 실행 기반 — Fargate를 기본, GPU만 예외

왜 Fargate 인가

지금 제일 큰 운영 부채는 인스턴스 타입이 아니라 "EC2 안의 Git·Docker·Nginx·Certbot은 코드 밖" 이라는 사실이다. 서비스가 다섯이 되면 그 수작업이 다섯 배가 된다.

Fargate로 사라지는 것
AMI 패치·재부팅태스크를 새로 띄우면 끝
SSM 접속해서 compose 교체태스크 정의 리비전
Nginx·CertbotALB + ACM
디스크 관리ephemeral storage
.env 파일과 KEY=null 문자열 함정Secrets Manager 주입 (§15)

Graviton(ARM)을 쓴다. 지금 이미 ARM 이미지를 빌드하고 있어서(t4g) 그대로 간다. Fargate ARM은 x86 대비 약 20% 싸다. 대가로 포기하는 것이 하나 있다 — Fargate Spot은 ARM에서 못 쓸 가능성이 높다(§18). 상시 부하라 Spot 이득이 작아 문제로 보지 않는다.

왜 EKS가 아닌가

서비스 다섯에 Kubernetes는 과하다. 컨트롤 플레인 비용에 더해 배워야 할 것이 제품과 무관하게 늘어난다. ECS는 태스크 정의·서비스·capacity provider 세 개념이 전부다. 이 규모에서 k8s가 주는 것은 대부분 우리가 안 쓰는 것이다.

GPU는 Fargate에 없다

Fargate는 GPU를 지원하지 않는다. speech만 다른 기반이 필요하다.

후보판단
ECS on EC2 + GPU capacity provider채택. 같은 ECS 안이라 태스크 정의·배포·관측이 나머지와 같은 방식
AWS Batch큐·재시도·scale-to-zero가 내장이지만 우리는 이미 SQS로 파이프라인을 돌린다. 큐가 둘이 된다
SageMaker Async InferenceS3 in/out·scale-to-zero가 딱 맞지만 비싸고 잠긴다

ECS 하나로 통일하는 게 이득이다. GPU 노드는 capacity provider로 min=0 ASG를 붙이고 SQS 큐 깊이로 스케일한다.

3. 1만 명 형상

짚어야 할 것 넷

① ALB idle timeout을 올린다. 기본 60초는 STOMP·SSE를 끊는다. 오디오 스트림은 회의 내내 산다. 상한 4000초.

② 배포 draining이 오디오 스트림을 끊는다. ECS가 태스크를 내릴 때 열린 WebSocket이 같이 죽는다. 어떻게 푸는지는 §13의 S5 타임라인이 갖는다.

③ S3는 gateway endpoint로 붙는다 — 무료다. speech가 오디오를 내려받는 트래픽이 NAT를 타면 GB 마다 돈을 낸다. 나머지 endpoint는 §5에서 고른다.

④ Soniox는 인터넷이라 못 뺀다. egress의 본체가 여기다(§16).

4. 태스크 정의 — 무엇을 얼마나 주나

전부 추정이다. 실측 없이 정한 값이므로 2주 돌려보고 다시 잡는다.

서비스태스크 CPU/메모리앱 컨테이너 한도사이드카포트헬스체크min%/max%
server2 vCPU / 4 GB1.6 vCPU / 3.2 GBADOT 0.25/512MB · FireLens 0.1/128MB8080 앱 · 8081 actuatorALB → :8081/actuator/health · interval 10s · healthy 2 · unhealthy 3100 / 200
ai1 vCPU / 2 GB0.65 vCPU / 1.4 GB같음8000:8000/health (경로 확인 §18)100 / 200
mcp0.5 vCPU / 1 GB0.4 vCPU / 0.6 GBADOT만8080:8080/health100 / 200
speechg5.xlarge 1대 = 태스크 1개
3.5 vCPU / 14 GB / GPU 1
ADOT만없다 (SQS 폴링)컨테이너 healthCheck — 모델 로드 완료 파일해당 없음

사이드카에서 실제로 걸리는 것 넷

함정어떻게
JVM이 태스크 전체를 자기 것으로 안다컨테이너별 memory hard limit을 명시하고 -XX:MaxRAMPercentage=70. 안 걸면 사이드카가 먹을 640MB를 JVM이 힙으로 잡고 태스크 OOM으로 죽는다
관측이 앱을 죽인다ADOT·FireLens는 essential=false. 관측 때문에 회의를 끊지 않는다. 대신 OTLP 전송 실패를 알람으로 본다(§12)
앱이 사이드카보다 먼저 뜬다dependsOn: [{otel, START}, {log-router, START}]. FireLens는 ECS가 마지막에 종료하므로 종료 로그는 남는다
토큰 갱신이 프로세스 재시작을 요구한다현재 판의 GRAFANA_CLOUD_OTLP_TOKEN 함정이 사이드카에서도 그대로다. 크레덴셜이 collector 한 곳으로 모이는 것이 이득이고, 갱신은 태스크 교체로만 한다

서비스 설정

항목
deploymentCircuitBreakerenable + rollback헬스체크가 계속 실패하면 자동으로 이전 리비전. 현재 판 server에 없는 롤백을 여기서 얻는다
minimumHealthyPercent100server·ai는 연결을 들고 있다. 용량이 절대 줄면 안 된다
maximumPercent200새 태스크가 먼저 뜨고 옛 태스크가 늦게 내려간다(§12)
stopTimeout120초 (Fargate 상한)45분 회의를 기다릴 수 없다 — 그래서 §13이 다른 장치를 쓴다
ALB 알고리즘least_outstanding_requestsround robin 이면 오래 사는 STOMP 세션이 한 태스크에 몰린다
ephemeralStorage기본 20GB (speech는 EBS)speech만 45분 오디오를 내려받는다
speech 스케일Application Auto Scaling · 큐 backlog per taskRunTask 를 안 쓴다. 재시도·배포·관측이 서비스 쪽이 낫고 멱등은 이미 큐가 준다

5. 네트워크 — 서브넷·엔드포인트·NAT

서브넷무엇이 산다나가는 길
public 2a/2bALB · NAT (2a 에만 1개)IGW
private-app 2a/2bserver·ai·mcp의 Fargate ENI · speech GPU EC2NAT · VPC endpoint
private-data 2a/2bRDS · Neo4j · Amazon MQ없다

지금과 제일 크게 달라지는 것은 "앱이 public subnet에 있지 않다"이다. 현재 판은 NAT를 안 만들려고 EC2를 public에 뒀다. 태스크가 열 개가 되면 그 선택은 퍼블릭 IP를 열 개 여는 것과 같아진다.

endpoint를 다 만들면 NAT보다 비싸진다

interface endpoint는 AZ 당 시간 과금이다. 데이터 요금만 보고 만들면 손해다.

대상종류만드나
S3gateway무료. speech가 회의마다 86MB를 내려받는다(Opus 전환 후 11MB · §8). NAT로 태우면 GB 마다 낸다
ECR api · dkrinterface태스크가 뜰 때마다 이미지 pull. GPU 이미지는 모델 가중치까지 들어 크다
Secrets Managerinterface태스크 기동마다 호출. 크레덴셜이 NAT를 안 타는 편이 낫다
SQSinterface파이프라인 메시지가 전부 여기 흐른다
STS · KMSinterface🔸태스크 role·SSE-KMS. 호출량이 작아 NAT로 보내도 된다. 6개를 2AZ로 깔면 월 $100을 넘어 §16의 endpoint 줄을 깬다
CloudWatch LogsinterfaceFireLens가 Loki로 보낸다. 현재 판이 CloudWatch Logs를 뺀 결정을 유지한다

1만 구간은 NAT를 1개만 둔다. 2개면 월 $43이 그대로 두 배가 된다. 대가는 둘이다 — 그 AZ가 죽으면 다른 AZ 태스크의 아웃바운드까지 끊긴다, 그리고 cross-AZ 데이터 요금 $0.01/GB가 붙는다. 10만 구간에서 2개로 올린다.

NAT를 못 피하는 트래픽

목적지
SonioxAWS 밖egress의 본체. 회의당 시간당 230MB
OpenAIAWS 밖작다
Grafana CloudAWS 밖로그가 크다. 태스크가 늘수록 는다
LangfuseAWS 밖작다
HuggingFace런타임에 안 나간다. 모델을 이미지에 굽는다(§6)
apt · pip · npm빌드 타임. 런타임 태스크에 없다

6. speech — GPU를 두면서 파산하지 않는 방법

이 제품의 GPU는 하루 대부분 놀고 정각마다 몰린다. 상시로 두면 가동률이 10%도 안 나온다.

규칙
평소 0대SQS 큐 깊이로 capacity provider 스케일
Spot 기본배치 잡이고 재시도가 안전하다. 온디맨드의 30% 수준
Spot 중단 = 재시도AA §3이 단계를 멱등으로 잡아둔 이유가 이것이다
모델 가중치는 이미지에 굽는다런타임에 받으면 HuggingFace 장애가 우리 장애가 된다. 이미지가 커지는 대신 재현된다
managed termination protection = ON안 켜면 capacity provider가 잡을 처리 중인 인스턴스를 scale-in으로 죽인다
scale-in은 15분 유예큐가 비었다고 바로 내리면 다음 버스트가 콜드스타트를 다시 문다
조직당 인플라이트 상한큐 하나를 전 조직이 공유한다. 한 조직이 정각에 300건을 끝내면 다른 조직 회의가 그 뒤에 줄 선다. server가 SQS 진입 전에 게이트한다 — 조직당 동시 ② 잡 5(추정). ASG max 는 총량 상한이지 공정성 장치가 아니다
초과분은 지터를 키운다상한에 걸린 잡은 버리지 않고 0~180초 지터를 조직 인플라이트에 비례해 늘려 넣는다. 대기가 그 조직 안에서만 길어진다
정각 평탄화가 먼저아래

큐를 조직별로 샤딩하지 않는다. 공정성은 진입 게이트로 풀고 큐는 하나로 둔다. 큐가 늘면 스케일 지표(ApproximateNumberOfMessagesVisible)와 §7의 90초 알람 지연이 큐마다 따로 붙어서 콜드스타트 판단이 큐 수만큼 갈라진다. 조직 하나가 큐 하나를 오래 비워 두면 그 큐만 계속 늦게 뜬다.

평탄화가 콜드스타트까지 흡수한다

min=0 의 대가는 콜드스타트다 — EC2 부팅 + GPU 이미지 pull로 3~5분. 잡 하나마다 이걸 물면 파이프라인이 못 쓴다.

정각 버스트를 지연 + 지터로 펴면 두 가지가 동시에 해결된다. 인스턴스가 한 번 뜨면 뒤이어 들어오는 잡을 계속 처리하므로 콜드스타트를 버스트 전체가 한 번만 문다. 필요한 동시 인스턴스 수도 같이 떨어진다.

콜드스타트 3~5분은 IA §5의 "누가 말했는지 정리 중"이 흡수한다. 사용자에게 보이는 지연의 상한을 먼저 정하고 거기서 역산해야 한다.

7. 정각 버스트를 인프라 시계로 따라간다 — S4

10:45에 12건이 동시 종료한다(등록 1만 피크). 시각은 전부 추정이고, 합이 S4의 "콜드스타트 4분"에 맞도록 쪼갠 것이다.

시각인프라에서 일어나는 일사용자 화면
10:45:0012건이 ENDED. CompleteMultipartUpload 12회 → S3에 86MB × 12"정리 중"
10:45:02화자 분리 잡 12건을 SQS에 넣는다. 0~180초 지터를 걸어 가시 시각을 편다
10:45:60CloudWatch가 ApproximateNumberOfMessagesVisible 를 처음 게시. 1분 해상도가 여기서 걸린다
10:46:30알람 평가 1주기 → capacity provider가 desired 2로 올린다 (스케일 판단에 90초)
10:47:30EC2 2대 부팅 + ECS 에이전트 등록 (60초)
10:48:40GPU 이미지 pull — ECR endpoint 경유 (70초). 모델 가중치가 이미지 안이라 HuggingFace를 안 탄다
10:49:00태스크 2개 RUNNING. long polling 시작. 여기까지 4분"누가 말했는지 정리 중"
10:49~잡 처리. visibility timeout 900초 + 처리 중 ChangeMessageVisibility heartbeat
10:51:40Spot 중단 통지 — 2분 뒤 회수. ECS_ENABLE_SPOT_INSTANCE_DRAINING 이 인스턴스를 DRAINING으로, 태스크에 SIGTERM그대로
10:51:41태스크가 끝낼 수 없다고 판단 → 처리 중 메시지에 ChangeMessageVisibility(0). 큐로 즉시 돌려보낸다
10:51:45남은 태스크 1대가 그 메시지를 집는다. ①단계는 다시 안 돈다 — ②만 다시 돈다그대로
10:51:55ASG가 대체 인스턴스를 요청. Spot 용량이 없으면 온디맨드로 폴백
10:5411건 완료화자 라벨 표시
10:55재시도 1건 ② 완료. 10:45+15분 안이라 ④ 가 화자를 기다릴 수 있다(AA §4)
11:13큐가 빈 지 15분 → scale-in → GPU 0대

요약은 왜 늦지 않나

S4가 "요약은 예정대로 나온다"고 못박은 이유는 ④요약이 ②화자 분리를 기다리지 않기 때문이다. ④ 는 큐가 아니라 ai REST 디스패치(⑥)라 GPU 큐와 경로 자체가 다르다. SQS를 타는 경계는 ⑧ server→speech 하나뿐이다(SA §3). 인프라 조건은 하나뿐이다 — GPU 적체가 Fargate 쪽 동시성을 안 먹는 것. 단계 구조는 AA §3이 갖는다.

실패 경로 — 여기서 실제로 깨지는 것 셋

무엇어떻게
Spot 재시도가 DLQ를 태운다ChangeMessageVisibility(0)receiveCount 를 올린다. Spot을 세 번 맞으면 멀쩡한 잡이 DLQ로 간다maxReceiveCount 를 5로 두고, 중단 사유를 DB 시도 이력에 남겨 인프라 중단과 잡 실패를 갈라 센다
Spot 용량이 아예 없다서울 g5·g6 확보 가능성이 미확인(§18)capacity provider에 온디맨드를 2순위로 붙인다. 원가 3배를 알람으로 본다(§12)
알람 지연이 콜드스타트보다 크다1분 해상도 + 평가 1주기 = 90초. 4분 중 1.5분이 여기다큐 진입 시점에 server가 직접 desired를 올린다(예약 스케일). 메트릭을 기다리지 않는다

8. 오디오 경로 — 릴레이에서 S3, S3에서 pyannote

규칙 다섯

규칙
브라우저가 Int16 PCM으로 보낸다1만 기준 확정 형식이다. 지금 스트림은 시간당 230MB(≈512kbps · Float32 추정)이고 Int16 이면 115MB 다. Opus는 Soniox 수용 포맷 확인 후(§18) — 받으면 시간당 14MB로 떨어져 egress·S3·Soniox 대역폭이 한꺼번에 준다
uploadId 와 파트 목록을 DB에 둔다태스크가 죽고 재연결하면 다른 태스크가 이어받는다. 메모리에만 있으면 그 회의 녹음이 통째로 날아간다
미완료 멀티파트는 1일 뒤 abortS3 lifecycle. 안 걸면 안 보이는 데서 과금된다
오디오는 7일 뒤 삭제화자 분리 재실행 여유만 준다
speech는 ephemeral storage에 받는다Fargate가 아니라 EC2라 여유가 있다. 처리 후 태스크와 함께 사라진다

저장량

형식회의당정상 상태 (7일)비용
등록 1만 · 지금Int16 PCM 16kHz · 45분약 86MB약 150GB월 $4 미만
등록 1만 · Opus 전환 후Opus 32kbps · 45분약 11MB약 15GB월 $1 미만
등록 10만 · Opus 전환 후약 150GB월 $5 미만

저장 비용은 문제가 아니다. 보관 자체가 문제다. 회의 음성을 오래 들고 있으면 그게 사고의 크기가 된다. 기본값을 짧게 잡고, 늘리려는 조직만 정책으로 늘린다.

9. Neo4j — 관리형을 쓰지 않는다

AuraDBEC2 자체 호스팅
운영 부담없다있다
비용비싸다m7g.large 한 대
백업관리형필요 없다

마지막 줄이 결론을 정한다. DA §1온톨로지 §6에서 그래프를 파생으로 못박았다. 날아가면 Postgres에서 다시 만든다. 백업·PITR·HA가 필요 없는 저장소에 관리형 프리미엄을 낼 이유가 없다.

대신 재생성을 분기마다 스테이징에서 돌려본다 — 주기와 합격 기준은 DA §10가 갖는다(§14).

ECS에 안 올리고 EC2로 두는 이유는 상태를 갖기 때문이다. Fargate에 상태 저장소를 올리면 EFS를 붙여야 하고, 그러면 관리형보다 복잡해진다.

10. 관측 — Fargate는 방식이 다르다

지금은 EC2에 Alloy를 올려 쓴다. Fargate 에는 host가 없다. 노드마다 에이전트를 하나 두는 방식이 성립하지 않는다.

신호지금Fargate
메트릭Alloy가 스크레이프앱이 OTLP로 push → ADOT 사이드카 → Grafana Cloud
로그Alloy가 파일 tailFireLens (Fluent Bit 사이드카) → Loki
트레이스AlloyADOT 사이드카
호스트 메트릭공백 (감수 중)ECS 태스크 메트릭으로 대체

push 모델이 강제된다. 태스크마다 ENI가 새로 뜨고 IP가 바뀌므로 스크레이프 대상으로 잡기가 나쁘다. 어차피 Grafana Cloud로 보내고 있으니 방향만 바꾸면 된다.

CloudWatch로 돌아가지 않는다. 현재 판 CA가 "비용이 아니라 일원화 때문에" CloudWatch Logs를 제거했다고 기록해 뒀다. FireLens를 쓰면 그 결정이 유지된다.

파이프라인 트레이스가 1급 요구사항이 된다

회의 하나가 web → server → Soniox → server → SQS → speech → server → ai → server → Neo4j 를 지난다. 어디서 막혔는지를 로그로 찾는 건 불가능하다.

해야 할 것어떻게
SQS 메시지 속성으로 trace context 전파MessageAttributestraceparent(W3C)를 넣는다. 큐를 건너면 트레이스가 끊긴다 — 안 하면 5단계가 무관한 트레이스 5개가 된다. 속성은 10개 상한이라 traceparent·tracestate·meetingId 정도로 끝낸다
speech 태스크는 종료 전 flush짧게 살고 죽는다. SIGTERM 핸들러에서 ForceFlush 를 부르고, Spot 회수 2분 안에 끝나도록 export timeout을 10초로 짧게. 안 하면 실패한 잡의 텔레메트리가 통째로 없다
span 이름을 단계로 고정pipeline.stage.diarize 처럼. 회의 ID는 속성으로. 이름에 넣으면 카디널리티가 터진다
실패 트레이스는 100% 보존지금 감수 중인 "tail sampling 불가"를 여기서 다시 봐야 한다

11. 대시보드 셋 — 무엇을 보고 사나

먼저 SLO — 알람 임계는 여기서 나온다

알람 임계를 SLO 없이 정하면, 임계를 손볼 때마다 사용자와의 약속이 조용히 바뀐다. 순서를 뒤집는다 — 약속을 먼저 쓰고 임계를 거기서 역산한다. 전부 추정이고 2주 실측 뒤 다시 잡는다.

약속SLO알람 임계(§12)SLO의 몇 배
종료 → 전사 열람즉시 (대기 화면 없음)실시간 전사가 정본이라 만들 것이 없다
종료 → 화자 라벨p95 8분>12분1.5배
종료 → 요약 게시p95 10분>15분1.5배
회의 중 발화 → 카드p50 5초 · p95 8초대시보드만(B). 늦은 카드는 안 뜨는 편이 낫다
노트 열기p95 1.5초대시보드만. 넘기면 온톨로지 §7의 맥락 배너를 끈다

화자 라벨 8분은 GPU가 이미 떠 있는 평시 값이다. §7의 정각 버스트는 콜드스타트 4분을 물어 12분까지 가고, 그게 알람 임계에 정확히 닿는다 — 예약 스케일로 그 90초를 줄이는 일(§7 실패 경로)이 곧 SLO를 지키는 일이다. 콜드스타트 실측(§18)이 바뀌면 이 표를 먼저 고치고 알람을 그다음에 고친다. 반대로 하지 않는다.

대시보드는 질문 셋으로 나눈다

대시보드를 서비스별로 만들면 아무도 안 본다. 질문 세 개로 나눈다 — 처리가 흐르고 있나 · 지금 회의가 괜찮나 · 돈이 새고 있나.

A. 파이프라인 (회의 종료 후)

패널무엇
종료 → 요약 게시 end-to-end p50/p95이 시스템의 진짜 SLA. 위 SLO 선(p95 10분)을 패널에 그어둔다
단계별 소요 p50/p95 (5단계 스택)어디서 시간이 가는가
단계별 성공 · 실패 · 건너뜀화자 분리를 몇 %나 포기하고 있나
SQS 큐 깊이 + 최고령 메시지 (큐별)깊이보다 나이가 먼저 아프다
GPU 태스크 수 + Spot 중단 이벤트 주석S4의 10:51:40이 여기 점으로 찍힌다
정각 코호트 소진 곡선10:45에 들어온 12건이 몇 분에 걸쳐 빠지나
DLQ 건수0 이어야 한다
재시도 사유 분해 (Spot · 타임아웃 · 코드)§7의 함정을 여기서 본다

B. 실시간 (회의 중)

패널무엇
동시 ACTIVE 회의 · 동시 STOMP 세션부하 모델 대비 어디쯤인가
태스크별 세션 수한 태스크에 몰리면 least_outstanding_requests 가 안 듣는 것
오디오 청크 지연 (브라우저 → server → Soniox)사용자가 "느리다"고 하는 실체
Soniox 오류율 · 재연결 횟수업스트림
끊긴 세션 수 · 재연결 성공률APP-189 계열이 살아 있는지
토큰 만료로 끊긴 세션TTL 30분이 2시간 회의를 못 버티는 것을 수치로
미완료 uploadId오디오 유실 예고
브로커 큐 깊이 — 상위 구독자느린 관전자 하나가 쌓으면 그 회의 팬아웃 전체가 같이 죽는다
강제로 끊은 구독 수백프레셔가 실제로 발동한 횟수. 0이 아니면 상한이 낮거나 관전자가 느린 것
에이전트 게이트 통과율 · LLM 호출/분기준선 분당 3~10

C. 원가

AWS 청구는 하루 늦게 온다. 그래서 이 대시보드는 청구서를 보지 않는다 — 물량을 앱이 재고 단가를 상수로 곱한다. 월말에 실제 청구서와 대조해 상수를 고친다.

패널무엇
회의 1건당 원가 (인프라 · Soniox · OpenAI 분해)단 하나만 본다면 이것
서비스별 태스크시간 (vCPU-h)
GPU 인스턴스 시간 · Spot / 온디맨드 비율폴백이 조용히 원가를 3배로 만든다
egress GB/일 — Soniox 방향과 그 외 분리Opus 전환의 효과가 여기 보인다
NAT 처리 GBendpoint를 더 만들 근거
워크스페이스별 상위 소비한도 근접(IA §11)
S3 저장량 · 미완료 멀티파트 용량
예산 대비 월 진행률

Grafana Cloud Free(10K series · 50GB/월)는 서비스 다섯 + 태스크 여러 개에서 넘는다. 유료 전환이 1만 구간 항목이다.

12. 알람 — 무엇을 언제 울리나

임계값은 전부 추정이다. 2주 실측 뒤 다시 잡는다. 원칙 하나 — 호출은 "사람이 지금 안 하면 나빠지는 것"만. 나머지는 티켓.

알람조건 (추정)등급
SQS 최고령 메시지>10분 이 15분 지속GPU가 안 떴거나 큐가 막혔다호출
종료→요약 p95>15분 이 30분 지속SLO 10분의 1.5배. 사용자와의 약속이 깨지는 지점호출
종료→화자 라벨 p95>12분 이 30분 지속SLO 8분의 1.5배호출(업무시간)
DLQ 메시지≥1잡 하나가 완전히 실패했다호출(업무시간)
STOMP 동시 세션 급감5분 만에 50% 이상 감소태스크가 죽어 전 회의가 끊긴 모양호출
ALB 5xx 비율>1% 5분호출
Soniox 오류율>5% 5분전사가 통째로 안 된다호출
RDS CPU · 연결수>80% 10분 / max의 80%호출
화자 분리 실패율>10% 30분모델·GPU 문제티켓
온디맨드 폴백 발생Spot 실패 >0원가 3배티켓
GPU 인스턴스 상시 가동6시간 연속 >0scale-in 실패 = 돈이 샌다티켓
잡 하나 처리 시간>20분무한 루프티켓
미완료 uploadId1시간 이상 열린 것 ≥5오디오 유실 위험티켓
OTLP 전송 실패>0 5분현재 판의 401 사고 재발. 관측이 죽은 것을 관측티켓
Grafana active series한도의 80%조용히 잘린다티켓
예산 소진율월 예상의 120%티켓
MCP 토큰당 호출상한의 80%폭주 탐지티켓

일부러 안 거는 것 — GPU 0대(정상이다) · Neo4j 프로세스 down(파생이라 급하지 않다, 티켓으로 충분) · 개별 태스크 재시작(ECS가 알아서 띄운다).

울리면 무엇을 하나

알람만 걸고 받은 사람이 무엇을 할지 안 적으면 알람은 소음이다. 자동 복구가 안 되는 경로만 적는다.

알람먼저 볼 패널첫 조치안 되면
SQS 최고령 >10분A · GPU 태스크 수 + 큐 깊이태스크가 0 이면 예약 스케일(§7)이 안 걸린 것 — desired를 손으로 올린다인스턴스가 안 뜨면 Spot 용량 부족이다. capacity provider를 온디맨드 1순위로 바꾼다(원가 3배를 감수한다)
DLQ ≥1A · DLQ 건수 · 재시도 사유 분해사유가 Spot·타임아웃이면 redrive 로 큐에 되돌린다코드 실패면 되돌리지 않는다. 단계를 FAILED 로 내려 AA §3 의 건너뛰기로 보내고 티켓
OTLP 전송 실패 >0없다 — 관측이 죽었다토큰 401 인지 본다(현재 판 사고 재발). 맞으면 시크릿을 회전하고 강제 배포 — 주입은 태스크 기동 시 1회다(§15)사이드카가 essential=false 라 회의는 산다. 복구 전까지 판단 근거가 없다는 것을 공지하고 A·B를 눈으로 본다
Soniox 오류율 >5%B · Soniox 오류율·재연결공급자 상태를 먼저 본다. 우리 쪽이면 NAT·egress(§5)전사가 통째로 안 된다. 진행 중 회의에 공지 — 오디오는 S3에 계속 쌓이므로 복구 후 화자 분리는 정상이다
온디맨드 폴백C · Spot / 온디맨드 비율버스트가 끝났으면 그대로 둔다. 원가 3배는 몇 분이면 무시할 수 있다몇 시간 이어지면 ASG max 를 낮춰 상한을 건다. 큐는 늦게 빠지고 화자 라벨 SLO를 그만큼 포기한다
미완료 uploadId ≥5B · 미완료 uploadId그 노트가 진행 중 회의인지 본다. 아니면 오디오가 끊긴 것lifecycle이 1일 뒤 abort 한다(§8). 그 회의는 화자 분리를 포기하고 전사만 남긴다

운영자 화면은 안 만든다. 개입은 전부 운영 API로 한다 — 목록과 권한은 AA §6pipeline 모듈이 갖는다. 화면 하나를 만들기 시작하면 권한·감사·목록·필터가 따라오고, 그건 제품이 아니라 콘솔이다.

13. 배포

지금ECS
방식dev push → GHA → SSM → compose 교체GHA OIDC → 태스크 정의 새 리비전 → 서비스 업데이트
server 롤백없다 — 이전 이미지 캡처가 없고 ddl-auto: validate 라 옛 JAR이 안 뜬다이전 리비전으로 되돌린다 + circuit breaker 자동 롤백
마이그레이션앱과 함께앞으로만 호환되게 두 판에 나눠서 — ai가 이미 그렇게 한다
무중단불가능성립 (아래 S5)

마이그레이션 규칙을 server 에도 강제한다. ai는 이미 파괴적 변경을 두 번에 나눠 올린다(그래서 0004 가 옛 칼럼을 남겼다). server는 롤백 경로가 없어서 그 규율이 없었는데, 롤백이 가능해지는 순간 규율이 필요해진다.

마이그레이션은 앱 태스크가 아니라 별도 일회성 태스크로 돌린다. 태스크 여럿이 동시에 뜨면서 각자 마이그레이션을 도는 것을 막는다. 이미지는 IMMUTABLE 태그로 ECR에 남긴다 — 롤백의 전제다.

S5 — 14:00 배포, 진행 중 회의 3건

시각무엇진행 중 회의 3건
14:00:00GHA가 새 리비전 등록 → UpdateService. max 200%새 태스크가 먼저 뜬다그대로
14:01:30새 태스크 RUNNING → ALB 타깃 등록 → healthy 2회(20초)그대로
14:02:00새 세션이 새 태스크로 간다그대로
14:02:05옛 태스크 ALB deregistration 시작. 새 연결이 안 온다 = "새 세션 안 받음"이 여기서 공짜로 된다그대로
14:02:05옛 태스크가 스스로 UpdateTaskProtection(true, 180분)회의를 들고 있는 동안 ECS가 못 죽인다그대로
14:20회의 A 종료 → 파이프라인은 새 태스크가 아니라 큐가 받는다. 태스크 소속과 무관A 정상 종료
14:45회의 B·C 종료 → 옛 태스크가 마지막 세션을 잃는다 → UpdateTaskProtection(false)B·C 정상 종료
14:45:10ECS가 옛 태스크를 STOPPING → 사이드카 flush → 종료

왜 장치가 두 개나 필요한가

장치상한이것만으로 안 되는 이유
ALB deregistration_delay최대 3600초45분 회의는 버티지만 2시간 회의는 못 버틴다
ECS stopTimeout최대 120초SIGTERM 뒤 2분이면 SIGKILL. 회의 길이와 자릿수가 다르다
ECS task scale-in protection최대 48시간✅ 이게 실제로 회의를 지킨다. 롤링 배포에도 적용되는지 확인 대상(§18)

버린 대안:

대안왜 버렸나
배포 창을 회의 없는 시간대로10만 규모에는 그런 시간이 없다
deregistration_delay 만 3600으로2시간 회의가 잘린다. 그리고 태스크는 ECS가 죽인다
전사 릴레이를 분리해 배포 빈도를 낮춘다맞는 방향이지만 10만 구간 항목(AA §1)

그래도 안 되면 세션을 옮긴다. 3시간을 넘겼거나 protection이 안 먹으면, 클라이언트가 재연결하고 서버가 DB의 uploadId·파트 목록으로 멀티파트를 이어받는다(§8). 사용자에게는 재연결 배지 몇 초. 재연결마저 실패하면 오디오는 끊긴 시점까지만 S3에 남고 파이프라인은 거기까지 돈다 — 회의가 통째로 날아가지는 않는다.

배포마다 옛 태스크가 최대 1시간 더 산다. 하루 3회 배포면 월 $30 안팎의 잔여 요금(추정)이고, 이건 무중단의 가격표다.

14. 재해 복구 — 무엇이 날아가면 무엇으로 되돌리나

잃은 것무엇으로RTO · RPO (추정)조건
Postgres (정본)RDS PITRRTO 30~60분 · RPO 5분PITR 보존을 35일로. Multi-AZ는 장애 조치이지 백업이 아니다. deletion_protection 은 현재 판 미확인 항목이라 먼저 켠다
Neo4j (파생)Postgres에서 재투영RTO = 재생성 소요(§18) · RPO 0백업을 안 만든다. 분기마다 스테이징 리허설
pgvector 임베딩 (파생)전사에서 재색인RTO = 재색인 소요 · RPO 0돈으로 복구한다. 임베딩 API 재호출 비용이 든다
S3 오디오복구 없음7일 수명이라 버전 관리를 안 켠다. 잃으면 화자 분리 재실행만 못 한다 — 이미 붙은 라벨은 Postgres에 있다
voice print사용자가 다시 등록복구 대상이 아니다. 생체정보는 사본이 늘수록 위험이 는다
SQS 인플라이트워치독이 다시 넣는다단계가 멱등이라 잃어도 된다
컨테이너 이미지ECR (IMMUTABLE · 최근 30개)롤백의 전제
SecretsSecrets Manager 복구 창지워도 7~30일 남는다
인프라 형상IaC 저장소지금은 콘솔에만 있어 복구 불가(§15 IaC)

리전 전체를 잃는 경우는 다루지 않는다. 멀티 리전은 원가와 복잡도를 배로 만들고, 회의 도구는 리전 장애 시간 동안 못 쓰는 것을 감수할 수 있다. 대신 Postgres 스냅샷만 다른 리전으로 복사한다 — 회사가 없어지지 않을 정도의 방어.

백업이 길수록 "지웠다"가 흐려진다

S6에서 김민수가 노트를 지우고, S7에서 최유진이 voice print를 지운다. PITR 35일이면 지운 노트가 35일간 스냅샷 안에 산다. 삭제와 복구는 반대 방향이고, 어느 쪽도 공짜가 아니다.

보존근거
Postgres PITR35일운영 사고 복구 창
voice printPITR에서 제외할 수 있나 — 미확인(§18)같은 DB 안이면 못 뺀다. 뺄 수 없으면 DA 가 저장 위치를 다시 정해야 한다
S3 오디오7일 · 버전 관리 없음삭제가 진짜 삭제

이 구분을 IA §9의 삭제 확인 화면이 말해야 한다. "즉시 삭제"라고 써놓고 스냅샷에 35일 남기면 거짓말이다.

15. AWS를 어떻게 굴리나

IaC — 무엇으로 쓸지가 열려 있다

현재 판 CA가 "IaC가 없다. 운영 형상이 콘솔에만 있어 재생성이 전부 수작업이다"를 열려 있는 것 넷 중 하나로 등재해 뒀다. 저장소에서 Terraform 언급이 걷힌 이력이 있어 선택을 다시 해야 한다.

후보이 형상에서버리는 것
AWS CDK (TypeScript)ECS 태스크 정의·서비스·ALB를 다루기 제일 편하다. 팀이 이미 TS를 쓴다합성 결과가 CloudFormation이라 드리프트 진단이 한 겹 멀다
Terraform표준적이고 상태·plan이 명시적뺀 이유를 모르므로 단정하지 않는다(§18)
CloudFormation 직접의존성 없음ECS 형상을 YAML로 쓰면 사람이 못 읽는다
콘솔안 된다

무엇을 고르든 규칙은 같다 — 클러스터·ALB를 만드는 시점부터 코드다. 리소스가 EC2 2대일 때는 콘솔이 감당됐지만, 위 형상은 클러스터·capacity provider·큐·브로커·엔드포인트·수명 정책이다. 콘솔로 만들면 두 달 뒤에 무엇이 왜 그렇게 설정됐는지 아무도 모른다.

환경 셋

환경무엇크기
localcompose 유지. speech는 CPU 폴백, Neo4j·RabbitMQ는 컨테이너
stagingprod와 같은 형상. Neo4j 재생성 리허설이 도는 곳(§9·§14) · 마이그레이션 예행 · 정각 버스트 부하 테스트(30건 동시 종료를 넣고 §11 A의 소진 곡선과 SLO를 본다) · 게임데이(Spot 강제 중단 · Neo4j kill · 브로커 kill 셋을 분기마다 손으로 일으킨다)태스크 1개씩 · RDS Single-AZ · GPU는 필요할 때만
prod§1

계정 분리(prod/nonprod)는 10만 구간에서 본다. 그전에는 태그와 IAM으로 가른다. staging이 없으면 5단계 파이프라인·GPU 배치·그래프 투영을 prod에서 처음 돌리게 된다.

시크릿 — 파일을 없앤다

현재 판 CA에 이런 경고가 있다 — JSON nullKEY=null 이라는 문자열이 되고, INTERNAL_API_TOKEN 이 그렇게 되면 서버가 "null" 을 공유 시크릿으로 쓰면서 정상 기동한다.

ECS 태스크 정의의 secrets 로 Secrets Manager·SSM을 주입하면 .env 변환 단계가 사라져서 이 함정이 없어진다. 주입은 태스크 기동 시 1회이므로 회전은 태스크 교체로만 반영된다 — RDS 관리형 시크릿 7일 회전과 맞물리는 지점이라 회전 뒤 강제 배포를 건다.

권한 — 태스크 role을 서비스마다 가른다

executionRole 은 넷이 공유해도 된다(ECR pull · 시크릿 읽기). taskRole 은 절대 공유하지 않는다.

태스크줄 것주지 않을 것
serversecretsmanager:GetSecretValue(server 시크릿 1개) · S3 PutObject·AbortMultipartUpload(오디오 prefix 한정) · sqs:SendMessage(화자 분리 큐 1개) · MQ 접속 · ecs:UpdateTaskProtection(자기 태스크) · GPU 서비스 desired 조정S3 DeleteObject(수명 정책이 지운다) · Neo4j 관리자 · ai 시크릿
aiai 시크릿 · RDS heymoa_ai · Neo4j 읽기 전용 계정S3 접근 없음 · sqs:SendMessage 없음 · server 시크릿 없음
speechS3 GetObject(오디오 prefix) · sqs:ReceiveMessage·DeleteMessage·ChangeMessageVisibility(화자 분리 큐 1개)DB 크레덴셜 없음 · S3 쓰기 없음 · 다른 큐 없음
mcpserver 호출용 설정뿐DB 크레덴셜 없음. 보안그룹으로도 5432에 못 간다

마지막 줄이 AA §2 규칙 4의 강제 장치다. 문서에 "mcp는 DB에 직접 붙지 않는다"고 적는 것으로는 안 지켜진다. 크레덴셜을 안 주면 지켜진다. speech에 DB를 안 주는 것도 같은 이유다 — 콜백으로만 결과를 넘기라고 정해 놨는데 크레덴셜이 있으면 언젠가 직접 쓴다.

비용 가드

장치
AWS Budgets서비스별 태그 기준 · 80%/100%/120%
Cost Anomaly DetectionGPU · NAT · egress
애플리케이션 레벨 사용량 상한AWS 밖(Soniox·OpenAI)은 AWS가 못 막는다. 워크스페이스별 한도가 유일한 방어선(IA §11)
GPU 상한ASG max 를 하드 상한으로. 큐가 폭주해도 인스턴스가 무한히 안 뜬다
태그 강제IaC에서. 태그 없는 리소스는 §11 C 대시보드에서 사라진다

16. 비용 — 추정

ap-northeast-2, 온디맨드·정가 기준. Savings Plan·Spot 할인 미반영.

항목1만 (월)10만 (월)
ALB$25$60
server (Fargate ARM)$170$560
ai (Fargate ARM)$90$350
mcp (Fargate ARM)$25$100
speech (GPU Spot + EC2)$150~300$1,500~3,000
Neo4j (EC2)$70$150
RDS Multi-AZ (+복제본)$280$900
벡터 전용$280
Amazon MQ$30$150
NAT · endpoint$60$120
S3 · SQS$5$20
egress (오디오 릴레이)$110$1,190
관측$80$300
전사 릴레이 분리$250
인프라 소계약 $1,100약 $5,500~7,500
Soniox 전사4,125시간41,250시간
OpenAI 요약·채팅5,500건 + 채팅55,000건 + 채팅

10만 소계는 확장 개요 §8$5,000~7,000 으로 적혀 있다. 이 표의 항목 합은 $5,930~7,430 이고, 위 소계는 그 합을 반올림한 값이다. 개요 쪽이 GPU 폭을 좁게 잡은 판이므로 항목별 근거는 이 표가 갖는다.

읽어야 할 것 다섯

① egress를 줄이는 유일한 레버는 코덱이다. 오디오가 server를 지나기로 확정됐으므로(확장 개요 §5) 경로는 못 바꾼다. 지금 시간당 230MB(≈512kbps)이고 이 줄은 그 값으로 잡았다. Int16 PCM 이면 절반이고(§8), Opus 32kbps까지 가면 1/16이 된다. Soniox가 무엇을 받는지에 달려 있다(§18).

② GPU가 인프라 최대 항목이고, 이제 조건부가 아니다. Soniox 실시간 화자 분리는 품질이 못 미쳐 배제됐다. §6의 운용 규칙이 곧 비용이다.

③ Fargate가 EC2보다 크게 비싸지 않다. ARM 기준으로 온디맨드 EC2와 비슷한 수준이고, 없어지는 운영 시간이 그 차액보다 크다. 상시 부하가 확정되면 그때 Savings Plan 이나 EC2로 내린다.

NAT · endpoint $60은 endpoint를 아껴야 지켜지는 숫자다. interface endpoint를 6종 × 2AZ로 깔면 그것만 월 $100을 넘는다(§5).

⑤ 아래 두 줄에 단가를 안 넣었다. 모른다. 시간당 $0.2만 돼도 10만에서 전사만 월 $8,000 이고 인프라 전부보다 크다. 배치 재전사는 하지 않으므로 두 배가 되지는 않는다.

17. 설계 전제

  • 컨테이너는 Fargate 위에서 돈다. GPU만 예외다. 인스턴스를 손으로 굴리는 일을 다시 만들지 않는다.
  • ECS 하나로 통일한다. GPU도 같은 클러스터의 capacity provider 다. 배포·관측·권한이 한 방식으로 유지된다.
  • GPU는 평소 0대다. 버스트는 인스턴스보다 평탄화로 먼저 줄인다. 평탄화가 콜드스타트까지 흡수한다.
  • 스케일 판단을 메트릭에만 맡기지 않는다. 1분 해상도가 콜드스타트에 1.5분을 더한다.
  • 파생 저장소에 관리형 프리미엄을 내지 않는다. Neo4j는 날아가면 재생성한다 — 대신 리허설을 돌린다.
  • 오디오는 목적이 끝나면 지운다. 기본 7일. 보관이 비용이 아니라 위험이다.
  • 멀티파트 업로드 상태를 메모리에 두지 않는다. 태스크는 언제든 죽는다.
  • 배포가 회의를 끊지 않는다. ALB와 ECS의 상한이 회의 길이보다 짧으므로 장치를 두 개 쓴다.
  • 관측은 push 모델이다. Fargate에 host는 없다. 트레이스가 로그보다 중요해지고, 큐를 건너는 전파가 그 조건이다.
  • 백업 기간은 삭제 약속과 충돌한다. 둘 다 사용자에게 정확히 말한다.
  • AWS 밖의 원가는 AWS가 못 막는다. 애플리케이션 한도가 유일한 방어선이다.

18. 미확인

무엇지금 아는 것확인해야 할 것
Soniox가 받는 오디오 포맷·비트레이트없다Opus를 받으면 egress가 1/16. 비용 영향이 제일 큰 확인
pyannote GPU 처리 시간 · CPU 가능 여부없다CPU로 되면 §6·§7이 통째로 단순해지고 최대 비용 항목이 사라진다
GPU 콜드스타트 실측3~5분으로 추정§7의 4분 쪼갬이 맞는지. IA §5 문구와 평탄화 지연값의 근거
task scale-in protection이 롤링 배포에도 적용되나scale-in을 막는다는 것까지§13 S5의 핵심 장치. 안 되면 세션 이동이 유일한 길
Fargate Spot의 ARM 지원안 될 가능성이 높다§2. 되면 비상시 원가 레버가 하나 는다
Spot 확보 가능성 (서울 g5·g6)없다안 되면 GPU 원가가 3배
전사 릴레이의 태스크당 동시 스트림시간당 230MB 라는 것까지§4 사이징과 AA §1 분리 조건
ai · mcp 헬스 엔드포인트 실제 경로server는 actuator 8081§4 표. ALB 타깃 그룹 설정의 전제
Terraform을 뺀 이유저장소에서 언급이 걷혔다는 사실만§15 IaC 선택의 전제
voice print를 PITR에서 뺄 수 있나같은 RDS 안이면 못 뺀다§14. 못 빼면 DA 가 저장 위치를 다시 정해야 한다
RDS deletion_protection 현재 값현재 판이 미확인으로 등재§14의 전제. 실사용자 데이터가 생기기 전에
Neo4j 재생성 소요 · 벡터 재색인 비용없다§9 결론과 §14 RTO
Soniox·OpenAI 실계약 단가모른다§16의 빈 두 줄