LogoSkills

aws-infrastructure-rules

AWS 인프라 운영 규칙 — private VPC에서의 SSM/RDS 접근. private VPC에서 RDS/SSM에 접근하거나, bastion/포트 포워딩을 설정하거나, AWS 인프라 접근 문제를 해결할 때 사용합니다.

AWS Infrastructure Operations Rules#

한마디로#

이 문서는 서비스가 돌아가는 "클라우드 서버실(AWS)"에서 문제가 생겼을 때 쓰는 응급조치 매뉴얼입니다. 마치 건물 관리실에 붙은 "정전 시 두꺼비집 확인하세요", "수도 잠겼을 때 밸브 여는 법" 같은 안내문과 비슷합니다. 서버가 멈추거나 느려졌을 때, 어디부터 점검하고 어떻게 되살리는지를 순서대로 정리해 둔 비상 대응집이라고 보면 됩니다.

무엇을·언제#

  • 무엇을 해주나요
    • 서버가 502/503 같은 에러로 "점검 중" 화면을 띄울 때 원인을 찾아 복구하는 절차를 알려줍니다.
    • 데이터 저장 공간(DB 디스크)이 꽉 찼을 때 긴급으로 공간을 늘리는 방법을 안내합니다.
    • 비밀번호(토큰) 불일치로 캐시 기능이 멈췄을 때 다시 맞추는 방법을 안내합니다.
    • 배포(새 버전 적용)가 실패하거나, 개발자 PC의 가상환경이 잠겼을 때 푸는 방법을 안내합니다.
    • 장애가 났을 때 "무엇부터 확인할지" 점검 순서를 제시합니다 (보통 DB 저장공간을 가장 먼저 봅니다).
  • 언제 작동하나요
    • 운영/스테이징 서버에 장애가 발생했을 때.
    • 비공개망(외부에서 직접 접속 불가) 안에 있는 데이터베이스를 점검해야 할 때.
    • 배포 후 새 버전이 실제로 잘 떴는지 확인하고 싶을 때.

핵심 용어#

용어쉬운 설명
AWS서비스가 실제로 돌아가는 아마존의 클라우드 서버실
RDS데이터를 보관하는 클라우드 데이터베이스 (저장 창고)
storage-full데이터 창고(디스크)가 꽉 차서 더 못 쓰는 상태
EC2 / ASG서버 컴퓨터 한 대(EC2)와, 트래픽에 맞춰 자동으로 대수를 늘리고 줄이는 묶음(ASG)
VPC (private)외부에서 직접 못 들어오게 막아 둔 내부 전용 네트워크
SSM그 내부망 서버에 안전하게 원격 명령을 보내는 통로
ElastiCache / Redis자주 쓰는 데이터를 빠르게 꺼내 쓰는 임시 저장소(캐시)
AUTH token캐시 접속용 비밀번호
WRONGPASS비밀번호가 안 맞아 캐시 접속이 거부된 에러
drift설계 문서(코드)와 실제 서버 상태가 어긋난 상황
Terraform서버 구성을 코드로 적어 두고 그대로 만들어 주는 도구 (설계도)
CodeDeploy새로 만든 버전을 서버에 자동으로 배포해 주는 도구
Blue/Green 배포새 버전을 옆에 따로 띄워 두고 한 번에 갈아끼우는 무중단 배포 방식
CloudWatch 알람서버 상태(CPU, 남은 공간 등)를 감시하다 위험하면 알려 주는 경보
Colima VM개발자 PC에서 컨테이너를 돌리기 위한 로컬 가상 머신

AWS 인프라 장애 대응 및 운영 규칙입니다.


count 에 빌드 산출물 존재 여부를 섞지 말 것#

count 는 "이 인프라가 존재해야 하는가"만 표현합니다. 거기에 fileexists() 를 곱하면 빌드 누락이 조용한 파괴가 됩니다.

# ❌ WRONG — zip 을 빌드하지 않은 머신에서 plan 하면 state 의 [0] 이 destroy 대상
count = var.enable_fcm_topic_manager  & &   local.fcm_zip_exists ? 1 : 0

# ✅ CORRECT — 의도는 count 로, 전제는 precondition 으로
count = var.enable_fcm_topic_manager ? 1 : 0

lifecycle {
  precondition {
    condition     = local.fcm_zip_exists
    error_message =  " zip 이 없습니다: ... (빌드: .../build.sh) " 
   }
}

왜 치명적인가 — 실패가 exit 0 이다#

결과exit
count 결합 1 to change, **8 to destroy** 0 — 정상 종료
precondition1 to change, 0 to destroy1 — 명시적 에러

전자가 exit 0 이므로 terraform apply 가 그대로 진행됩니다. 파괴 계획이 정상 종료로 통과하는 것이 이 함정의 본질입니다.

왜 알아채기 어려운가#

Lambda zip 같은 산출물은 .gitignore 에 걸려 git 에 없습니다 — 각자 빌드해야 하므로 clone 직후 전체 apply 가 가장 위험합니다. 경로 변수의 기본값이 ""아무것도 하지 않아도 조건이 false 입니다. CI 가 TF_VAR_... 로 경로를 주입해 이를 피해 왔더라도 그건 CI 경로만 덮습니다 — 로컬 apply 는 무방비입니다.

# `^\s*` 로 시작을 고정한다 — 없으면 이 규약을 설명하는 주석까지 잡힌다.
grep -rnE  " ^\s*count = .*(zip_exists|layer_exists) "   deploy/aws/terraform/   # 0건이어야 한다

⚠️ precondition 이 항상 발화하지는 않습니다 — 리소스가 plan 대상에 들어와야 평가됩니다. -target 스코프를 좁히면 우회될 수 있으니, 위 grep 가드를 함께 두세요.


⛔ 인프라 변경은 terraform 코드로만 — 수동 AWS 변경 금지#

terraform 이 관리하는 리소스(ALB target group·헬스체크, CloudWatch 알람, ASG, RDS, route53, SG 등)는 콘솔/CLI 로 직접 바꾸지 않습니다.

왜 (실측 사고)#

프로덕션 target group 6개의 헬스체크 경로를 수동 CLI 로 변경한 사고가 있었습니다.

  • 라이브가 코드와 조용히 어긋난 채 3주 넘게 방치됐고, drift-check 최초 실행에서야 발견됐습니다.
  • 그 상태에서 코드 그대로 blanket apply 하면 수동 변경을 리뷰 없이 되돌려 프로덕션 헬스체크 장애를 일으킬 뻔했습니다.
  • 근본 원인 규명에 CloudTrail·git·state·live·배포 파이프라인 다중 증거원 포렌식이 필요했습니다.

규칙#

  1. 관리 리소스 변경 = terraform 코드 PR + apply. 콘솔/CLI 직접 변경 금지.
  2. 불가피한 긴급 수동 변경 시: (a) 즉시 코드에 반영하는 후속 PR 로 state=code=live 를 재수렴시키고, (b) 사유·시각·주체를 이슈로 기록합니다. "임시로 손만 대고 나중에"가 바로 그 3주 drift 를 만들었습니다.
  3. out-of-band 변경 조기 검출: drift-check(주간 cron + 수동 트리거)를 정기 운용하고, non-empty plan 을 진짜 신호로 유지하기 위해 양성 드리프트는 ignore_changes 로 정리합니다.
  4. apply 는 통제된 단일 경로로만. root access key 상시 사용을 스코프 IAM role 로 대체합니다.

SSM으로 VPC 내부 RDS 쿼리 실행#

프로덕션/스테이징 RDS는 private VPC에 위치해 로컬에서 직접 접속 불가. SSM Send-command + EC2에서 일회성 postgres 컨테이너로 psql 실행한다.

절차#

# 1. EC2 인스턴스 ID 조회 (ASG 교체 대비 — 매번 다시 조회 필수)
INSTANCE_ID=$(aws ec2 describe-instances \
  --filters  " Name=tag:Environment,Values=production "   \
             " Name=instance-state-name,Values=running "   \
  --query  ' Reservations[0].Instances[0].InstanceId '   --output text)

# 2. DB 연결 정보 확인 (컨테이너 내부)
aws ssm send-command --instance-ids $INSTANCE_ID \
  --document-name  " AWS-RunShellScript "   \
  --parameters  ' commands=[ " docker exec kobic-server cat config/production.yaml | grep -A6 \ " ^database:\ " " ] ' 

 # 3. postgres 이미지 pre-pull (이미지 다운로드에 시간 걸리면 send-command 타임아웃)
aws ssm send-command --instance-ids $INSTANCE_ID \
  --document-name  " AWS-RunShellScript "   \
  --parameters  ' commands=[ " docker pull postgres:15-alpine " ] ' 

 # 4. psql 쿼리 실행
aws ssm send-command --instance-ids $INSTANCE_ID \
  --document-name  " AWS-RunShellScript "   \
  --parameters  ' commands=[ " docker run --rm --network host -e PGPASSWORD= < password >   postgres:15-alpine psql -h  < db-host >   -U postgres -d serverpod -c \ " < SQL > \ " " ] '

결과 polling 패턴#

CMD_ID=$(...)  # send-command 반환값

until aws ssm get-command-invocation \
  --command-id $CMD_ID --instance-id $INSTANCE_ID \
  --query  ' Status '   --output text | grep -qE  " ^(Success|Failed)$ " ; do
  sleep 3
done

aws ssm get-command-invocation --command-id $CMD_ID \
  --instance-id $INSTANCE_ID \
  --query  ' [Status,StandardOutputContent] '   --output text

주의#

  • psqlkobic-server 컨테이너에 포함되지 않음 → 반드시 별도 postgres:15-alpine 컨테이너 사용
  • ASG 교체로 EC2 인스턴스 ID는 자주 바뀜 → 하드코딩 금지, 매번 조회
  • DB 비밀번호는 kobic-server 컨테이너 내부 config/passwords.yaml에서 runmode 섹션별로 분리됨

참고: 2026-04-18 프로덕션 drift 진단·수동 migration 동기화 시 활용 (Issue #5022)


SSM 포트포워딩으로 RDS 상시 조회 (VPN 대안)#

위의 send-command 방식은 SQL 한 줄을 던지고 결과를 polling 하는 일회성 운영 조회에 적합합니다. psql/DBeaver/TablePlus 로 상시 인터랙티브 조회가 필요하면 SSM Session Manager 의 원격 호스트 포트포워딩을 쓰세요 — RDS 보안그룹·Terraform 변경이 전혀 필요 없습니다.

왜 되는가#

DB 보안그룹이 앱 EC2 에서 오는 5432 만 허용하고 publicly_accessible = false 여도, 그 앱 EC2 에는 이미 SSM 에이전트·IAM 인스턴스 프로필이 붙어 있습니다(위 send-command 가 되는 이유와 동일). 그 EC2 를 점프 서버로 삼아 로컬 포트 → RDS:5432 를 터널링하면 새 보안그룹 규칙도, bastion 인스턴스도 없이 기존 신뢰관계를 그대로 재사용합니다.

./scripts/rds_tunnel.sh production          # 로컬 5432
./scripts/rds_tunnel.sh staging 5433        # 두 환경 동시 터널 시 포트 직접 지정

# 다른 터미널에서 (config 의 requireSsl: true 때문에 sslmode 필요)
psql  " host=localhost port=5432 dbname=serverpod user=postgres sslmode=require "

사전 준비(최초 1회): 로컬에 Session Manager 플러그인 (brew install --cask session-manager-plugin), IAM 에 EC2 조회 + ssm:StartSession 권한.

항목내용
감사IAM 으로 세션 시작 권한 통제, CloudTrail/SSM 세션 로그에 자동 기록
비용무료 (데이터 전송비 외 추가 과금 없음)
ASG 교체스크립트가 인스턴스 ID 를 매번 재조회하므로 무영향
한계사람마다 세션을 각자 열어야 함 — "연결해 두면 계속 붙어 있는" VPN 과는 다름

여러 명이 GUI 툴로 상시 붙거나 RDS 외 Redis·내부 콘솔까지 필요하면 Client VPN 이 맞습니다 — 다만 그건 보안그룹 ingress 추가 등 실제 Terraform 변경과 인증서/SSO 설계가 필요한 별건입니다.

이 터널로 로컬 백엔드 풀스택도 실행 가능#

막는 것은 자격증명이 아니라 호스트명입니다 — 설정의 DB 호스트가 VPC 내부 전용 private DNS 라 로컬에서 해석되지 않습니다. 환경변수 오버라이드로 해결하세요.

  • Serverpod 설정 로더는 yaml 값보다 SERVERPOD_DATABASE_HOST/SERVERPOD_DATABASE_PORT 환경변수를 우선합니다.
  • requireSsl: trueSslMode.verifyFull 이 아니라 SslMode.require 로 매핑되어 암호화만 하고 호스트명/인증서 검증은 하지 않습니다 — 터널 뒤 localhost 로 접속해도 끊기지 않습니다.

ElastiCache AUTH와 SSM Parameter Store 동기화 이슈#

증상#

RedisError(WRONGPASS invalid username-password pair or user is disabled)

서버 로그에 반복적으로 출력. 애플리케이션의 Redis 의존 기능(캐시 등) 동작 실패.

원인#

Terraform이 관리하는 3개 리소스 간 동기화가 깨질 수 있음:

리소스역할
random_password.redis_auth_token토큰 생성
aws_ssm_parameter.redis_auth_tokenSSM Parameter Store 저장 (서버가 참조)
aws_elasticache_replication_group.xxx.auth_token실제 Redis 클러스터 AUTH

Terraform apply 부분 실패 또는 수동 변경으로 SSM과 ElastiCache 값이 어긋나면 WRONGPASS.

진단#

# SSM 파라미터 최종 변경일
aws ssm describe-parameters \
  --parameter-filters  " Key=Name,Values=/kobic/staging/redis/auth-token "   \
  --query  ' Parameters[0].[LastModifiedDate,Version] '   --output text

# ElastiCache AUTH 최종 변경일
aws elasticache describe-replication-groups \
  --replication-group-id kobic-staging \
  --query  ' ReplicationGroups[0].AuthTokenLastModifiedDate '   --output text

두 날짜가 크게 차이나면 drift 확정.

해결 — ROTATE 전략#

SSM 값을 기준으로 ElastiCache를 동기화:

# 1. SSM에서 AUTH 토큰 조회
TOKEN=$(aws ssm get-parameter \
  --name /kobic/staging/redis/auth-token --with-decryption \
  --query  ' Parameter.Value '   --output text)

# 2. ElastiCache AUTH ROTATE
aws elasticache modify-replication-group \
  --replication-group-id kobic-staging \
  --auth-token  " $TOKEN "   \
  --auth-token-update-strategy ROTATE \
  --apply-immediately

# 3. 완료 확인 (PendingAuthTokenStatus: None, Status: available)
aws elasticache describe-replication-groups \
  --replication-group-id kobic-staging \
  --query  ' ReplicationGroups[0].{Status:Status,PendingAuthTokenStatus:PendingModifiedValues.AuthTokenStatus} ' 

 # 4. 컨테이너 재시작 — 필수 (기존 connection 캐시가 old token 유지)
aws ssm send-command --instance-ids  < INSTANCE_ID >   \
  --parameters  ' commands=[ " docker restart kobic-server " ] '

전략 선택#

전략효과사용 시점
ROTATE기존 + 신규 토큰 둘 다 허용일반적 drift 복구 (권장)
SET신규 토큰만 허용급한 보안 조치
DELETEAUTH 비활성화드묾

ROTATE 한 번 후에도 기존 토큰이 여전히 허용 상태이므로, 완전 전환 시 한 번 더 ROTATE 필요.

참고: Issue #5023에서 Staging ElastiCache AUTH(2026-02-12)와 SSM(2026-04-02) 간 2개월 drift 복구.


RDS Storage-Full 장애 대응#

증상#

  • 서버 502/503 에러 (앱에서 "서버 점검 중" 화면)
  • RDS 인스턴스 상태: storage-full
  • CloudWatch FreeStorageSpace 메트릭이 0에 도달

진단 순서#

인프라 장애 발생 시 RDS를 가장 먼저 확인합니다 (가장 흔한 원인).

# 1. RDS 인스턴스 상태 확인
aws rds describe-db-instances \
  --db-instance-identifier kobic-staging \
  --query  ' DBInstances[0].{Status:DBInstanceStatus,Storage:AllocatedStorage,MaxStorage:MaxAllocatedStorage,FreeStorage:FreeStorageSpace} ' 

 # 2. FreeStorageSpace 메트릭 확인 (최근 1시간)
aws cloudwatch get-metric-statistics \
  --namespace AWS/RDS \
  --metric-name FreeStorageSpace \
  --dimensions Name=DBInstanceIdentifier,Value=kobic-staging \
  --start-time $(date -u -v-1H +%Y-%m-%dT%H:%M:%S) \
  --end-time $(date -u +%Y-%m-%dT%H:%M:%S) \
  --period 300 --statistics Minimum \
  --output table

# 3. 스토리지 사용 원인 분석 (DB 접속 후)
SELECT pg_size_pretty(pg_database_size( ' serverpod ' )) as db_size;

-- 테이블별 용량 확인
SELECT schemaname, tablename,
       pg_size_pretty(pg_total_relation_size(schemaname|| ' . ' ||tablename)) as total_size
FROM pg_tables
WHERE schemaname =  ' public ' 
 ORDER BY pg_total_relation_size(schemaname|| ' . ' ||tablename) DESC
LIMIT 20;

-- WAL 사이즈 확인
SELECT pg_size_pretty(pg_wal_size()) as wal_size;

스토리지 확장 절차#

# 긴급 스토리지 확장 (즉시 적용, 다운타임 없음)
aws rds modify-db-instance \
  --db-instance-identifier kobic-staging \
  --allocated-storage 200 \
  --max-allocated-storage 500 \
  --apply-immediately

# 확장 진행 상태 확인 (storage-optimization 상태가 됨)
aws rds describe-db-instances \
  --db-instance-identifier kobic-staging \
  --query  ' DBInstances[0].DBInstanceStatus '

주의사항:

  • RDS 스토리지 확장은 축소 불가 (한번 늘리면 줄일 수 없음)
  • 확장 후 6시간 쿨다운 (추가 확장 불가)
  • storage-optimization 상태에서도 DB는 정상 사용 가능

Replication Slot 정리#

CDC(Change Data Capture)용 replication slot이 스토리지를 소비하는 주요 원인입니다.

-- replication slot 상태 확인
SELECT slot_name, slot_type, active,
       pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) as retained_wal
FROM pg_replication_slots;

-- 비활성 slot 삭제 (ClickHouse ClickPipe 연결 끊긴 경우)
SELECT pg_drop_replication_slot( ' slot_name_here ' );

Terraform 코드 Drift 이슈#

긴급 확장 후 Terraform 코드와 실제 인프라가 불일치합니다. 별도 이슈로 코드를 업데이트해야 합니다.

환경Terraform 코드 (현재)실제 인프라 (확장 후)필요한 변경
staging allocated_storage=20, max=50 allocated_storage=200, max=500 코드 업데이트 필요
production allocated_storage=20, max=100 확인 필요 상황에 따라

파일 위치: backend/kobic_server/deploy/aws/terraform/variables.tf (line 203-235, db_configs 변수)


CodeDeploy ASG 누적 문제#

증상#

  • 배포 시 CodeDeploy가 오래된 (이미 삭제된) ASG에 배포 시도
  • 배포 실패 또는 지연

원인#

CodeDeploy Deployment Group에 과거 Auto Scaling Group 이름이 남아 있음. ASG를 재생성(terraform destroy/apply)할 때 CodeDeploy가 자동 정리하지 않음.

진단#

# CodeDeploy Deployment GroupASG 목록 확인
aws deploy get-deployment-group \
  --application-name kobic-app \
  --deployment-group-name kobic-staging-dg \
  --query  ' deploymentGroupInfo.autoScalingGroups[].name ' 

 # 실제 존재하는 ASG 확인
aws autoscaling describe-auto-scaling-groups \
  --query  ' AutoScalingGroups[?contains(AutoScalingGroupName, `kobic`)].AutoScalingGroupName '

해결#

# 올바른 ASG만 포함하도록 Deployment Group 업데이트
aws deploy update-deployment-group \
  --application-name kobic-app \
  --current-deployment-group-name kobic-staging-dg \
  --auto-scaling-groups  " kobic-staging-asg "    # 현재 유효한 ASG만 지정

예방#

  • Terraform에서 ASG 재생성 시 CodeDeploy Deployment Group도 함께 업데이트되는지 확인
  • CI/CD 파이프라인에 stale ASG 정리 스크립트 추가 검토

Colima VM 디스크 잠김 (로컬 개발)#

증상#

  • colima start 실패
  • 에러 메시지: disk is locked 또는 qemu: could not acquire disk lock

원인#

Colima VM이 비정상 종료되어 디스크 파일에 락이 남아 있음.

해결#

# 1. Colima 강제 종료
colima stop --force

# 2. 락 파일 제거
rm -f ~/.colima/default/diffdisk  # 또는 해당 프로파일 경로
rm -f ~/.lima/colima/diffdisk

# 3. QEMU 프로세스 정리
pkill -f qemu-system

# 4. 재시작
colima start

대안: 문제가 지속되면 VM 삭제 후 재생성

colima delete
colima start --cpu 4 --memory 8 --disk 60

Docker 기반 배포 워크플로우#

배포 흐름#

GitHub ActionsDocker BuildECR PushCodeDeployASG Instance
단계도구설명
빌드GitHub ActionsDocker 이미지 빌드
저장Amazon ECR이미지 레지스트리
배포CodeDeployRolling 배포
실행EC2 (ASG)Docker Compose로 실행

배포 대상별 브랜치#

환경코드 반영 브랜치배포 방법
Staging development GitHub Actions workflow_dispatch
Production main main 브랜치 push 시 자동 배포

CodeDeploy 배포 그룹 명명#

환경ApplicationDeployment Group배포 전략
Staging kobic-app kobic-staging-group in-place (kobic-staging-group-base ASG 재사용)
Production kobic-app kobic-production-group Blue/Green (신규 ASG 생성, 구 ASG 이름: CodeDeploy_kobic-production-group_d-XXX)

주의: 과거 문서에 kobic-staging-dg가 언급되었더라도 실제 이름은 kobic-staging-group 입니다. aws deploy list-deployment-groups --application-name kobic-app로 현재 상태 확인.

컨테이너 이미지 태그 형식#

{env}-{shortSha}-{timestamp}: production-8023d1ba-20260418005915
    staging-87eec5a2-20260418084056

shortSha는 머지 commit의 앞 8자리. 태그로 배포된 버전을 역추적할 수 있습니다.

배포 반영 검증 루틴#

프로덕션 배포 후 실제 컨테이너에 새 이미지가 실행 중인지 확인:

# 1. 최신 CodeDeploy 배포 ID 조회
aws deploy list-deployments \
  --application-name kobic-app \
  --deployment-group-name kobic-production-group \
  --max-items 1 \
  --query  ' deployments[0] '   --output text

# 2. 배포 상태 확인 (Succeeded 인지)
aws deploy get-deployment --deployment-id  < id >   \
  --query  ' deploymentInfo.{Status:status,CompleteTime:completeTime} ' 

 # 3. 현재 서비스 중인 EC2 인스턴스 조회 (Blue/Green이면 새 ASG)
aws autoscaling describe-auto-scaling-groups \
  --query  ' AutoScalingGroups[?contains(AutoScalingGroupName, `production`)].{Name:AutoScalingGroupName,ID:Instances[0].InstanceId} ' 

 # 4. 실행 중 컨테이너 이미지 확인 (SSM send-command)
aws ssm send-command --instance-ids  < id >   \
  --document-name  " AWS-RunShellScript "   \
  --parameters  ' commands=[ " docker ps --format \ " {{.Image}} {{.Status}}\ "   | grep kobic " ] '   \
  --query  ' Command.CommandId '   --output text

# 5. 결과 확인 (명령 실행 후 ~6초 대기)
aws ssm get-command-invocation \
  --command-id  < command-id >   --instance-id  < id >   \
  --query  ' StandardOutputContent '   --output text

서버 로그 추출 (IAP 등 기능별 디버깅):

# 최근 30IAP 관련 로그
aws ssm send-command --instance-ids  < id >   \
  --document-name  " AWS-RunShellScript "   \
  --parameters  ' commands=[ " docker logs $(docker ps --format \ " {{.ID}}\ "   | head -1) --since 30m 2 > & 1 | grep -E \ " \\[IAP\\]|App Store\ "   | tail -40 " ] '

CloudWatch 알람 설정 현황#

현재 설정된 알람#

알람임계값alarm_actions
database-high-cpu80%빈 배열 (알림 없음)
database-high-connections50개설정 없음

미설정 (권장 추가)#

알람권장 임계값용도
FreeStorageSpace< 5GBRDS 스토리지 부족 사전 감지
FreeableMemory< 256MBRDS 메모리 부족 감지
SwapUsage> 256MB메모리 스왑 발생 감지

참고: 현재 alarm_actions = []로 설정되어 있어 알람이 발생해도 알림이 전달되지 않음. SNS 토픽 연결이 필요합니다.

⚠️ "알람이 존재한다"와 "알람이 작동한다"는 별개다#

RDS 알람 6종이 한 번도 평가된 적 없던 실사고가 있었습니다. 두 결함이 겹쳤습니다.

결함 1 — dimension 값이 잘못됨. AWS provider v5 에서 aws_db_instance.id 는 인스턴스 식별자가 아니라 DbiResourceId(db-2JSOR5...)로 resolve 됩니다. AWS/RDS 네임스페이스는 그 값으로 메트릭을 발행하지 않으므로 알람이 영원히 INSUFFICIENT_DATA 입니다.

# ❌ WRONGDbiResourceId 로 resolve (provider v5)
dimensions = { DBInstanceIdentifier = aws_db_instance.main.id }

# ✅ CORRECT
dimensions = { DBInstanceIdentifier = aws_db_instance.main.identifier }

결함 2 — SNS 토픽에 구독자가 없음. 알람이 정상 평가돼도 어디로도 전달되지 않습니다. 개인 이메일 대신 팀 alias 를 구독시켜 bus-factor 를 없애세요(구독 후 확인 메일의 링크를 눌러야 PendingConfirmation 이 풀립니다).

# 진단 — 알람이 실제로 평가되는가 (INSUFFICIENT_DATA 면 dimension 의심)
aws cloudwatch describe-alarms --alarm-names  < name >   \
  --query  ' MetricAlarms[0].{state:StateValue,dims:Dimensions} ' 

 # 진단 — 알림이 실제로 전달되는가
aws sns list-subscriptions-by-topic --topic-arn  < topic-arn >

적용 시 주의: dimensions/threshold 변경은 in-place 업데이트지만, PutMetricAlarm 이 알람을 INSUFFICIENT_DATA 로 리셋한 뒤 재평가하므로 정상 메트릭을 처음 받는 순간 알람당 OK 알림이 1회씩 발생합니다. 구독자를 먼저 등록했다면 그만큼 알림을 받게 되니 순서를 고려하세요.

canary 규율: staging 알람 1종을 먼저 apply → INSUFFICIENT_DATA → OK 전이 확인 (= 메트릭을 실제로 받기 시작) → 그 다음 나머지·production 으로 확장합니다. apply 는 무관한 드리프트를 건드리지 않도록 알람 리소스만 개별 -target 으로 하세요.


인프라 디버깅 순서 (장애 발생 시)#

장애 발생 시 아래 순서로 확인합니다:

1. RDS 상태 확인 (storage-full이 가장 흔한 원인)2. EC2/ASG 인스턴스 상태 확인
   ↓
3. Target Group 헬스체크 확인
   ↓
4. ALB 접근 로그 확인
   ↓
5. CodeDeploy 배포 상태 확인
   ↓
6. CloudWatch 로그 확인

빠른 상태 확인 명령어#

# RDS 상태
aws rds describe-db-instances \
  --query  ' DBInstances[*].{ID:DBInstanceIdentifier,Status:DBInstanceStatus,Storage:AllocatedStorage} '   \
  --output table

# ASG 인스턴스 상태
aws autoscaling describe-auto-scaling-groups \
  --query  ' AutoScalingGroups[?contains(AutoScalingGroupName,`kobic`)].{Name:AutoScalingGroupName,Desired:DesiredCapacity,Instances:Instances[*].{ID:InstanceId,Health:HealthStatus}} '   \
  --output json

# Target Group 헬스
aws elbv2 describe-target-health \
  --target-group-arn  < target-group-arn >   \
  --output table

# 최근 배포 상태
aws deploy list-deployments \
  --application-name kobic-app \
  --deployment-group-name kobic-staging-dg \
  --max-items 3 \
  --query  ' deployments '

후속 작업 (TODO)#

아래 항목들은 인프라 안정성 향상을 위해 별도 이슈로 처리해야 합니다.

HIGH (긴급)#

  • RDS FreeStorageSpace CloudWatch 알람 추가: Terraform database.tf에 FreeStorageSpace < 5GB 알람 추가
  • alarm_actions에 SNS 토픽 연결: 현재 빈 배열로 설정된 기존 알람에 deployment_sns_topic_arn 연결
  • Terraform variables.tf staging db_configs 업데이트: allocated_storage: 20→200, max_allocated_storage: 50→500
  • Replication slot 스토리지 원인 분석: ClickHouse ClickPipe CDC slot의 WAL retention 확인 및 최적화
  • Redis AUTH 설정 확인: Redis 접속 시 비밀번호 관련 이슈 점검

MEDIUM#

  • stale ASG 정리 자동화: CodeDeploy Deployment Group에서 존재하지 않는 ASG 자동 제거
  • replication slot 모니터링 추가: retained WAL 크기 기반 알람 설정
  • 정기 정리 크론잡: 오래된 배포 아티팩트, 로그 등 자동 정리
  • deletion_protection 환경별 분리: production만 true, staging/development는 false 유지 (현재 모두 false)

LOW (자동화 스킬 후보)#

향후 Claude Code 스킬로 자동화 가능한 패턴들:

스킬명우선순위설명
aws/ssm-execHIGHSSM으로 EC2 인스턴스에 명령 실행
aws/rds-monitorHIGHRDS 상태/메트릭 실시간 확인
aws/healthHIGH전체 인프라 헬스체크 대시보드
aws/tg-healthMEDIUMTarget Group 헬스체크 상태 확인
aws/asg-cleanupMEDIUMstale ASG/배포 그룹 정리
aws/incident-responseLOW장애 대응 자동화 런북

관련 파일#

  • backend/kobic_server/deploy/aws/terraform/variables.tf - RDS/ASG 설정 (root)
  • backend/kobic_server/deploy/aws/terraform/modules/environment/database.tf - RDS 인스턴스 및 CloudWatch 알람
  • backend/kobic_server/deploy/aws/terraform/modules/environment/variables.tf - 환경별 변수 정의
  • backend/kobic_server/deploy/aws/terraform/modules/environment/compute.tf - ASG/EC2 설정
  • backend/kobic_server/deploy/aws/terraform/modules/shared/codedeploy.tf - CodeDeploy 앱 설정
  • backend/kobic_server/deploy/aws/terraform/modules/environment/codedeploy.tf - 환경별 Deployment Group