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 — 정상 종료 |
precondition | 1 to change, 0 to destroy | 1 — 명시적 에러 |
전자가 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·배포 파이프라인 다중 증거원 포렌식이 필요했습니다.
규칙#
- 관리 리소스 변경 = terraform 코드 PR + apply. 콘솔/CLI 직접 변경 금지.
- 불가피한 긴급 수동 변경 시: (a) 즉시 코드에 반영하는 후속 PR 로 state=code=live 를 재수렴시키고, (b) 사유·시각·주체를 이슈로 기록합니다. "임시로 손만 대고 나중에"가 바로 그 3주 drift 를 만들었습니다.
-
out-of-band 변경 조기 검출: drift-check(주간 cron + 수동 트리거)를 정기 운용하고,
non-empty plan 을 진짜 신호로 유지하기 위해 양성 드리프트는
ignore_changes로 정리합니다. - 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
주의#
-
psql은kobic-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: true는SslMode.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_token | SSM 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 | 신규 토큰만 허용 | 급한 보안 조치 |
DELETE | AUTH 비활성화 | 드묾 |
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 Group의 ASG 목록 확인
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 Actions → Docker Build → ECR Push → CodeDeploy → ASG Instance
| 단계 | 도구 | 설명 |
|---|---|---|
| 빌드 | GitHub Actions | Docker 이미지 빌드 |
| 저장 | Amazon ECR | 이미지 레지스트리 |
| 배포 | CodeDeploy | Rolling 배포 |
| 실행 | EC2 (ASG) | Docker Compose로 실행 |
배포 대상별 브랜치#
| 환경 | 코드 반영 브랜치 | 배포 방법 |
|---|---|---|
| Staging | development |
GitHub Actions workflow_dispatch |
| Production | main |
main 브랜치 push 시 자동 배포 |
CodeDeploy 배포 그룹 명명#
| 환경 | Application | Deployment 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 등 기능별 디버깅):
# 최근 30분 IAP 관련 로그
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-cpu | 80% | 빈 배열 (알림 없음) |
database-high-connections | 50개 | 설정 없음 |
미설정 (권장 추가)#
| 알람 | 권장 임계값 | 용도 |
|---|---|---|
FreeStorageSpace | < 5GB | RDS 스토리지 부족 사전 감지 |
FreeableMemory | < 256MB | RDS 메모리 부족 감지 |
SwapUsage | > 256MB | 메모리 스왑 발생 감지 |
참고: 현재 alarm_actions = []로 설정되어 있어 알람이 발생해도 알림이 전달되지 않음.
SNS 토픽 연결이 필요합니다.
⚠️ "알람이 존재한다"와 "알람이 작동한다"는 별개다#
RDS 알람 6종이 한 번도 평가된 적 없던 실사고가 있었습니다. 두 결함이 겹쳤습니다.
결함 1 — dimension 값이 잘못됨. AWS provider v5 에서 aws_db_instance.id 는 인스턴스
식별자가 아니라 DbiResourceId(db-2JSOR5...)로 resolve 됩니다. AWS/RDS 네임스페이스는 그
값으로 메트릭을 발행하지 않으므로 알람이 영원히 INSUFFICIENT_DATA 입니다.
# ❌ WRONG — DbiResourceId 로 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-exec | HIGH | SSM으로 EC2 인스턴스에 명령 실행 |
aws/rds-monitor | HIGH | RDS 상태/메트릭 실시간 확인 |
aws/health | HIGH | 전체 인프라 헬스체크 대시보드 |
aws/tg-health | MEDIUM | Target Group 헬스체크 상태 확인 |
aws/asg-cleanup | MEDIUM | stale ASG/배포 그룹 정리 |
aws/incident-response | LOW | 장애 대응 자동화 런북 |
관련 파일#
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