PLT-NFR-004 99.9% 가용성 요구사항 구현 가이드
📋 목차
1. 개요
1.1 요구사항 정의
PLT-NFR-004: 시스템은 99.9% 이상의 서비스 가용성을 제공해야 한다.
- 월간 허용 다운타임: 43.2분 (30일 기준)
- 주간 허용 다운타임: 10.1분 (7일 기준)
- 일간 허용 다운타임: 1.4분 (24시간 기준)
1.2 비즈니스 영향
99.9% 가용성은 의료 기기 소프트웨어(DTx)로서 필수적인 요구사항입니다:
- 환자 안전: 치료 연속성 보장
- 규제 준수: DiGA, FDA, K-FDA 요구사항 충족
- 신뢰성: 의료진 및 환자의 시스템 신뢰도 확보
- 사업 연속성: 서비스 중단으로 인한 손실 최소화
1.3 구현 범위
- 대상 서비스:
- dta-wide-api (핵심 API)
- dta-wide-agent-flow (플로우 에이전트)
- 인프라 범위: Cloud Run, Cloud SQL, Redis, 로드 밸런서
- 모니터링 범위: 실시간 가용성 측정, 알림, 대시보드
1.4 관련 문서
- Cloud Run 고가용성 설정 가이드: Cloud Run 특화 설정 및 CI/CD 최적화
2. 아키텍처 설계
2.1 설계 원칙
2.1.1 고가용성 원칙
2.1.2 장애 격리 (Fault Isolation)
- 서비스 분리: 마이크로서비스 아키텍처
- 데이터베이스 분리: 서비스별 스키마 격리
- 리소스 격리: 컨테이너 기반 격리
- 네트워크 분리: VPC 서브넷 분리
2.1.3 자동 복구 (Auto-healing)
- Health Check: HTTP 기반 상태 확인
- Auto Scaling: 트래픽 기반 자동 확장
- Circuit Breaker: 장애 전파 방지
- Retry Logic: 지수 백오프 재시도
2.2 인프라 구성
2.2.1 Cloud Run 설정
Cloud Run의 환경별 최적화 전략:
| 환경 | 최소 인스턴스 | 리소스 | 목적 | 실제 대상 | 가용성 목표 |
|---|---|---|---|---|---|
| Dev | 0 (scale-to-zero) | 1CPU/1GB | 비용 절감 | 개발팀 (~10명) | 95% |
| Stage | 0 (scale-to-zero) | 1CPU/1GB | 베타 테스트 | 베타 테스터 (~30명) | 98% |
| Prod | 2 | 2CPU/2GB | 실제 서비스 | 정식 이용자 (10,000명) | 99.9% |
핵심 설정 요소:
- 자동 스케일링: 실제 트래픽 기반 (Dev: 0-20, Stage: 0-5, Prod: 2-15)
- Health Check:
/health,/ready엔드포인트 구성 - Cold Start 최적화: Dev, Stage 환경 웜업 전략
- 비용 최적화: 환경별 실제 사용량 기반 리소스 할당
실제 트래픽 고려사항:
| 환경 | 사용자 규모 | 동시 접속 예상 | API 호출량 | 설정 근거 |
|---|---|---|---|---|
| Dev | 개발팀 (~10명) | 1-3명 | 개발/테스트용 | Scale-to-zero로 비용 최적화 |
| Stage | 베타 테스터 (~30명) | 5-10명 | 10-50 RPS | 소규모 테스트 환경, 최소 리소스 |
| Prod | 정식 이용자 (10,000명) | 100명 (1%) | 100-600 RPS | 99.9% 가용성 보장 필요 |
Prod 환경 상세:
- 총 이용자: 10,000명 (의료 기기 소프트웨어)
- 동시 접속: 100명 (1% 동시 사용률)
- API 호출량: 100-200 RPS (일반), 300-600 RPS (피크)
📖 상세 설정 가이드: Cloud Run 고가용성 설정
- 환경별 Terragrunt 설정 및 최적화 전략
- 실제 트래픽 기반 리소스 할당
- Health Check 구현 방법
2.2.2 Cloud SQL 고가용성
resource "google_sql_database_instance" "main" {
name = "dta-wide-db-${var.environment}"
database_version = "POSTGRES_16"
region = var.region
settings {
tier = "db-custom-4-15360"
availability_type = "REGIONAL" # 고가용성 활성화
disk_type = "PD_SSD"
disk_size = 100
disk_autoresize = true
# 백업 설정
backup_configuration {
enabled = true
start_time = "02:00"
point_in_time_recovery_enabled = true
backup_retention_settings {
retained_backups = 30
retention_unit = "COUNT"
}
}
# 유지보수 창
maintenance_window {
day = 7 # Sunday
hour = 3
update_track = "stable"
}
# 데이터베이스 플래그
database_flags {
name = "log_checkpoints"
value = "on"
}
database_flags {
name = "log_connections"
value = "on"
}
database_flags {
name = "log_disconnections"
value = "on"
}
}
deletion_protection = true
}
2.2.3 Redis 고가용성
resource "google_redis_instance" "cache" {
name = "dta-wide-cache-${var.environment}"
tier = "STANDARD_HA" # 고가용성 모드
memory_size_gb = 4
region = var.region
redis_version = "REDIS_6_X"
display_name = "DTA Wide Cache - ${upper(var.environment)}"
# 네트워크 설정
authorized_network = var.vpc_network
connect_mode = "PRIVATE_SERVICE_ACCESS"
# Redis 설정
redis_configs = {
maxmemory-policy = "allkeys-lru"
notify-keyspace-events = "Ex"
}
# 유지보수 창
maintenance_policy {
weekly_maintenance_window {
day = "SUNDAY"
start_time {
hours = 2
minutes = 0
}
}
}
labels = {
environment = var.environment
purpose = "cache"
}
}
3. 구현 솔루션
3.1 가용성 모니터링 시스템 구성
99.9% 가용성 달성을 위해 포괄적인 모니터링 시스템을 구축합니다.
3.1.1 Terraform 모듈 활용
📁 모듈 위치: infrastructure/terraform/modules/availability-monitoring/
핵심 구성 요소:
- SLO 정의: 월간 99.9%, 주간 99.5%, 일간 99.0%
- BigQuery 메트릭 저장소: 장기 데이터 보관 및 트렌드 분석
- 실시간 알림: 이메일/Slack 통합, SLO 위반 시 즉시 알림
- 모니터링 대시보드: Cloud Monitoring 기반 가용성 현황
📖 상세 설정 가이드:
3.1.2 환경별 배포 설정
| 환경 | SLO 목표 | 기능 | 데이터 보관 |
|---|---|---|---|
| Dev | 99.5% | 기본 | 30일 |
| Stage | 99.9% | 고급 | 90일 |
| Prod | 99.9% | 전체 | 365일 |
배포 명령어:
# Dev 환경
cd infrastructure/terragrunt/dev/monitoring
terragrunt apply
# Stage 환경
cd infrastructure/terragrunt/stage/monitoring
terragrunt apply
# Prod 환경
cd infrastructure/terragrunt/prod/monitoring
terragrunt apply
4. 모니터링 시스템
4.1 SLI (Service Level Indicator) 정의
4.1.1 가용성 SLI
-- BigQuery에서 가용성 계산
WITH request_metrics AS (
SELECT
service_name,
EXTRACT(DATE FROM timestamp) as date,
COUNT(*) as total_requests,
COUNTIF(status_code < 500) as successful_requests
FROM `{project}.{dataset}.http_requests`
WHERE timestamp >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY)
GROUP BY service_name, date
)
SELECT
service_name,
date,
total_requests,
successful_requests,
SAFE_DIVIDE(successful_requests, total_requests) * 100 as availability_percentage,
CASE
WHEN SAFE_DIVIDE(successful_requests, total_requests) < 0.999
THEN true
ELSE false
END as slo_breach
FROM request_metrics
ORDER BY date DESC, service_name;
4.1.2 지연시간 SLI
-- P95 응답시간 측정
SELECT
service_name,
EXTRACT(DATE FROM timestamp) as date,
APPROX_QUANTILES(response_time_ms, 100)[OFFSET(95)] as p95_latency_ms,
APPROX_QUANTILES(response_time_ms, 100)[OFFSET(99)] as p99_latency_ms,
AVG(response_time_ms) as avg_latency_ms
FROM `{project}.{dataset}.http_requests`
WHERE timestamp >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY)
GROUP BY service_name, date
ORDER BY date DESC, service_name;
4.2 알림 정책
4.2.1 Critical 알림 (즉시)
- 가용성 < 99.9% (5분 지속)
- P95 지연시간 > 2초 (10분 지속)
- 에러율 > 1% (3분 지속)
4.2.2 Warning 알림 (10분 지연)
- 가용성 < 99.95% (10분 지속)
- P95 지연시간 > 1초 (15분 지속)
- 에러율 > 0.5% (10분 지속)
4.2.3 알림 채널 구성
# 알림 채널 예시
resource "google_monitoring_notification_channel" "email_critical" {
display_name = "Critical Alerts"
type = "email"
labels = {
email_address = "critical-alerts@company.com"
}
}
resource "google_monitoring_notification_channel" "slack_ops" {
display_name = "Ops Team Slack"
type = "slack"
labels = {
url = var.slack_webhook_url
}
}
resource "google_monitoring_notification_channel" "pagerduty" {
display_name = "PagerDuty"
type = "pagerduty"
labels = {
service_key = var.pagerduty_service_key
}
}
4.3 대시보드 구성
4.3.1 Executive Dashboard
- 전체 가용성 현황 (99.9% SLO 대비)
- 월간/주간/일간 트렌드
- 서비스별 상태 요약
- SLO 에러 버짓 현황
4.3.2 Operations Dashboard
- 실시간 트래픽 현황
- 응답시간 분포
- 에러율 추이
- 인프라 리소스 사용률
4.3.3 Service-specific Dashboard
- 서비스별 상세 메트릭
- 의존성 상태
- 배포 영향 분석
- 커스텀 비즈니스 메트릭
5. 배포 가이드
5.1 사전 준비사항
5.1.1 권한 설정
# 필요한 IAM 권한 확인
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
--member="user:${USER_EMAIL}" \
--role="roles/monitoring.admin"
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
--member="user:${USER_EMAIL}" \
--role="roles/bigquery.admin"
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
--member="user:${USER_EMAIL}" \
--role="roles/cloudscheduler.admin"
5.1.2 API 활성화
# 필요한 API 활성화
gcloud services enable monitoring.googleapis.com
gcloud services enable bigquery.googleapis.com
gcloud services enable cloudscheduler.googleapis.com
gcloud services enable cloudfunctions.googleapis.com
5.2 단계별 배포
5.2.1 Dev 환경 배포
# 1. Dev 환경 배포
cd infrastructure/terragrunt/dev/monitoring
terragrunt init
terragrunt plan
terragrunt apply
# 2. 배포 확인
terragrunt output dashboard_url
terragrunt output monitoring_summary
5.2.2 Stage 환경 배포
# 1. Dev 환경에서 검증 완료 후
cd infrastructure/terragrunt/stage/monitoring
terragrunt init
terragrunt plan
terragrunt apply
# 2. 스모크 테스트 실행
./scripts/smoke-test-monitoring.sh stage
5.2.3 Prod 환경 배포
# 1. Stage 환경에서 충분한 검증 후
cd infrastructure/terragrunt/prod/monitoring
terragrunt init
terragrunt plan
# 2. 프로덕션 배포 (승인 필요)
terragrunt apply
# 3. 프로덕션 검증
./scripts/validate-production-monitoring.sh
5.3 배포 자동화 스크립트
5.3.1 환경별 배포 스크립트
#!/bin/bash
# scripts/deploy-availability-monitoring.sh
set -e
ENVIRONMENT=${1:-dev}
DRY_RUN=${2:-false}
echo "🚀 Deploying availability monitoring to ${ENVIRONMENT} environment"
# 환경 검증
if [[ ! "$ENVIRONMENT" =~ ^(dev|stage|prod)$ ]]; then
echo "❌ Invalid environment: ${ENVIRONMENT}"
echo "Valid environments: dev, stage, prod"
exit 1
fi
# Terragrunt 디렉토리로 이동
cd "infrastructure/terragrunt/${ENVIRONMENT}/monitoring"
# 사전 검사
echo "🔍 Pre-deployment checks..."
terragrunt validate
terragrunt plan -out=tfplan
# Dry run 모드 체크
if [[ "$DRY_RUN" == "true" ]]; then
echo "📋 Dry run completed. Plan saved to tfplan"
exit 0
fi
# 프로덕션 환경 추가 확인
if [[ "$ENVIRONMENT" == "prod" ]]; then
echo "⚠️ You are about to deploy to PRODUCTION"
read -p "Are you sure? (yes/no): " confirm
if [[ "$confirm" != "yes" ]]; then
echo "❌ Deployment cancelled"
exit 1
fi
fi
# 배포 실행
echo "🔧 Applying Terraform configuration..."
terragrunt apply tfplan
# 배포 검증
echo "✅ Deployment verification..."
terragrunt output dashboard_url
terragrunt output monitoring_summary
# 헬스체크
echo "🏥 Running health checks..."
./scripts/health-check-monitoring.sh
echo "🎉 Deployment to ${ENVIRONMENT} completed successfully!"
5.3.2 헬스체크 스크립트
#!/bin/bash
# scripts/health-check-monitoring.sh
set -e
PROJECT_ID=$(terragrunt output -raw project_id 2>/dev/null || echo $PROJECT_ID)
ENVIRONMENT=$(terragrunt output -raw environment 2>/dev/null || echo $ENVIRONMENT)
echo "🏥 Running health checks for monitoring system..."
# BigQuery 데이터셋 확인
echo "📊 Checking BigQuery dataset..."
DATASET_ID=$(terragrunt output -raw bigquery_dataset_id)
if gcloud bigquery datasets describe "$DATASET_ID" --project="$PROJECT_ID" >/dev/null 2>&1; then
echo "✅ BigQuery dataset exists: $DATASET_ID"
else
echo "❌ BigQuery dataset not found: $DATASET_ID"
exit 1
fi
# 알림 정책 확인
echo "🚨 Checking alert policies..."
ALERT_POLICIES=$(gcloud alpha monitoring policies list --project="$PROJECT_ID" --filter="displayName:*${ENVIRONMENT}*" --format="value(name)" | wc -l)
if [[ "$ALERT_POLICIES" -gt 0 ]]; then
echo "✅ Found $ALERT_POLICIES alert policies"
else
echo "❌ No alert policies found"
exit 1
fi
# 대시보드 확인
echo "📈 Checking monitoring dashboard..."
DASHBOARD_URL=$(terragrunt output -raw dashboard_url)
if curl -s -o /dev/null -w "%{http_code}" "$DASHBOARD_URL" | grep -q "200\|302"; then
echo "✅ Dashboard is accessible: $DASHBOARD_URL"
else
echo "❌ Dashboard is not accessible: $DASHBOARD_URL"
fi
# Cloud Scheduler 작업 확인
echo "⏰ Checking Cloud Scheduler jobs..."
SCHEDULER_JOBS=$(gcloud scheduler jobs list --location="europe-west3" --project="$PROJECT_ID" --filter="name:*${ENVIRONMENT}*" --format="value(name)" | wc -l)
if [[ "$SCHEDULER_JOBS" -gt 0 ]]; then
echo "✅ Found $SCHEDULER_JOBS scheduler jobs"
else
echo "⚠️ No scheduler jobs found (normal for basic setup)"
fi
echo "🎉 Health check completed successfully!"
6. 운영 절차
6.1 일상 운영
6.1.1 모니터링 체크리스트 (일간)
- 전체 가용성 현황 확인 (99.9% 목표 대비)
- 활성 알람 상태 검토
- 대시보드 이상 징후 확인
- SLO 에러 버짓 현황 점검
6.1.2 주간 리뷰
- 주간 가용성 보고서 생성
- SLO 위반 사례 분석
- 성능 트렌드 분석
- 알림 정책 효과성 평가
6.1.3 월간 리뷰
- 월간 SLO 달성률 평가
- 인시던트 후 검토 (Post-mortem)
- 시스템 개선 사항 도출
- 용량 계획 수립
6.2 사고 대응 절차
6.2.1 Critical 사고 (가용성 < 99%)
대응 절차:
- 1분 내: 알림 확인 및 상황 인지
- 3분 내: 대응팀 소집 및 War Room 개설
- 5분 내: 초기 원인 분석 시작
- 10분 내: 복구 조치 실행 또는 Rollback
- 15분 내: 상황 업데이트 및 이해관계자 공지
6.2.2 War Room 프로세스
# War Room 체크리스트
echo "🚨 CRITICAL INCIDENT - WAR ROOM ACTIVATED"
echo "Time: $(date)"
echo "Incident ID: INC-$(date +%Y%m%d-%H%M%S)"
echo ""
echo "📋 CHECKLIST:"
echo "□ Incident Commander assigned"
echo "□ Technical Lead assigned"
echo "□ Communication Lead assigned"
echo "□ Current status assessed"
echo "□ Customer impact evaluated"
echo "□ Stakeholders notified"
echo "□ Recovery plan activated"
6.3 정기 유지보수
6.3.1 시스템 점검 (주간)
#!/bin/bash
# scripts/weekly-maintenance-check.sh
echo "📅 Weekly System Maintenance Check - $(date)"
# 1. 데이터베이스 성능 확인
echo "🗄️ Database Performance Check..."
gcloud sql instances describe dta-wide-db-prod --format="table(state,backendType,gceZone)"
# 2. 백업 상태 확인
echo "💾 Backup Status Check..."
gcloud sql backups list --instance=dta-wide-db-prod --limit=7
# 3. Redis 메모리 사용률 확인
echo "🔄 Redis Memory Usage Check..."
gcloud redis instances describe dta-wide-cache-prod --region=europe-west3 --format="table(memorySizeGb,currentLocationId,state)"
# 4. 인증서 만료일 확인
echo "🔐 SSL Certificate Expiry Check..."
gcloud compute ssl-certificates list --format="table(name,managed.domains,expireTime)"
# 5. 디스크 사용률 확인
echo "💿 Disk Usage Check..."
gcloud sql instances describe dta-wide-db-prod --format="table(settings.dataDiskSizeGb,settings.dataDiskType)"
echo "✅ Weekly maintenance check completed"
6.3.2 용량 계획 (월간)
-- 월간 용량 트렌드 분석
WITH monthly_metrics AS (
SELECT
FORMAT_DATE('%Y-%m', DATE(timestamp)) as month,
service_name,
AVG(cpu_usage_percent) as avg_cpu,
AVG(memory_usage_percent) as avg_memory,
AVG(request_count) as avg_requests,
MAX(request_count) as peak_requests
FROM `{project}.{dataset}.performance_metrics`
WHERE timestamp >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 12 MONTH)
GROUP BY month, service_name
)
SELECT
month,
service_name,
avg_cpu,
avg_memory,
avg_requests,
peak_requests,
-- 성장률 계산
LAG(avg_requests) OVER (PARTITION BY service_name ORDER BY month) as prev_month_requests,
ROUND(
(avg_requests - LAG(avg_requests) OVER (PARTITION BY service_name ORDER BY month)) /
LAG(avg_requests) OVER (PARTITION BY service_name ORDER BY month) * 100,
2
) as growth_rate_percent
FROM monthly_metrics
ORDER BY service_name, month DESC;
7. 문제 해결
7.1 일반적인 문제들
7.1.1 가용성 저하 시나리오
문제: 특정 서비스의 가용성이 갑자기 떨어짐
# 1. 즉시 확인 사항
kubectl get pods -l app=dta-wide-api # Cloud Run 인스턴스 상태
gcloud logging read "resource.type=cloud_run_revision" --limit=50 # 최근 로그 확인
# 2. 트래픽 패턴 분석
gcloud monitoring metrics list --filter="metric.type=run.googleapis.com/request_count"
# 3. 의존성 확인
gcloud sql operations list --instance=dta-wide-db-prod --limit=10
gcloud redis instances describe dta-wide-cache-prod --region=europe-west3
해결 방법:
- 서비스 재시작 (임시 조치)
- 스케일링 조정
- 의존성 서비스 확인
- 코드 롤백 (필요시)
7.1.2 알림 누락 문제
문제: SLO 위반이 발생했으나 알림이 오지 않음
# 1. 알림 정책 상태 확인
gcloud alpha monitoring policies list --filter="enabled=true"
# 2. 알림 채널 확인
gcloud alpha monitoring channels list --filter="enabled=true"
# 3. 알림 히스토리 확인
gcloud logging read "protoPayload.serviceName=monitoring.googleapis.com AND protoPayload.methodName=NotificationChannelService"
해결 방법:
- 알림 채널 재활성화
- 필터 조건 검토
- 임계값 조정
- 테스트 알림 발송
7.1.3 대시보드 접근 불가
문제: 모니터링 대시보드에 접근할 수 없음
# 1. IAM 권한 확인
gcloud projects get-iam-policy $PROJECT_ID --flatten="bindings[].members" --format='table(bindings.role)' --filter="bindings.members:user:$USER_EMAIL"
# 2. API 활성화 상태 확인
gcloud services list --enabled --filter="name:monitoring.googleapis.com"
# 3. 대시보드 존재 확인
gcloud alpha monitoring dashboards list
7.2 성능 최적화
7.2.1 쿼리 성능 개선
-- 느린 가용성 계산 쿼리 최적화
-- Before (느린 버전)
SELECT
service_name,
COUNT(*) as total,
COUNT(CASE WHEN status < 500 THEN 1 END) as success
FROM http_requests
WHERE timestamp >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 DAY)
GROUP BY service_name;
-- After (최적화된 버전)
SELECT
service_name,
COUNTIF(true) as total,
COUNTIF(status < 500) as success
FROM http_requests
WHERE _PARTITIONTIME >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 DAY)
AND service_name IS NOT NULL
GROUP BY service_name;
7.2.2 알림 최적화
# 알림 빈도 제한으로 노이즈 감소
resource "google_monitoring_alert_policy" "availability_optimized" {
display_name = "Availability SLO Breach - Optimized"
conditions {
display_name = "Availability below 99.9% for 5 minutes"
condition_threshold {
filter = "resource.type=\"cloud_run_revision\""
duration = "300s" # 5분 지속 시에만 알림
comparison = "COMPARISON_LESS_THAN"
threshold_value = 0.999
# 집계 최적화
aggregations {
alignment_period = "60s"
per_series_aligner = "ALIGN_RATE"
cross_series_reducer = "REDUCE_MEAN"
group_by_fields = ["resource.label.service_name"]
}
}
}
# 알림 빈도 제한
alert_strategy {
auto_close = "3600s" # 1시간 후 자동 닫기
}
# 문서화
documentation {
content = "Availability has been below 99.9% for 5 minutes. Check runbook at https://runbook.company.com/availability"
mime_type = "text/markdown"
}
}
8. 성능 지표
8.1 KPI 정의
8.1.1 가용성 지표
| 지표 | 목표 | 측정 방법 | 리포팅 주기 |
|---|---|---|---|
| 월간 가용성 | ≥ 99.9% | (성공 요청 / 전체 요청) × 100 | 월간 |
| 주간 가용성 | ≥ 99.5% | (성공 요청 / 전체 요청) × 100 | 주간 |
| MTBF (평균 무장애 시간) | ≥ 30일 | 장애 간 평균 시간 | 분기 |
| MTTR (평균 복구 시간) | ≤ 10분 | 장애 감지부터 복구까지 시간 | 월간 |
8.1.2 성능 지표
| 지표 | 목표 | 측정 방법 | 리포팅 주기 |
|---|---|---|---|
| P95 응답시간 | ≤ 200ms | 95번째 백분위수 | 일간 |
| P99 응답시간 | ≤ 500ms | 99번째 백분위수 | 일간 |
| 처리량 | 변동 추적 | TPS (Transactions Per Second) | 시간별 |
| 에러율 | ≤ 0.1% | (에러 응답 / 전체 응답) × 100 | 일간 |
8.2 리포팅 대시보드
8.2.1 Executive Summary (경영진용)
-- 월간 요약 리포트
WITH monthly_summary AS (
SELECT
FORMAT_DATE('%Y-%m', DATE(timestamp)) as month,
service_name,
COUNT(*) as total_requests,
COUNTIF(status_code < 500) as successful_requests,
ROUND(COUNTIF(status_code < 500) / COUNT(*) * 100, 3) as availability_percent,
ROUND(AVG(response_time_ms), 2) as avg_response_time,
ROUND(APPROX_QUANTILES(response_time_ms, 100)[OFFSET(95)], 2) as p95_response_time
FROM `{project}.{dataset}.http_requests`
WHERE timestamp >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 12 MONTH)
GROUP BY month, service_name
)
SELECT
month,
service_name,
total_requests,
availability_percent,
CASE
WHEN availability_percent >= 99.9 THEN 'PASS'
WHEN availability_percent >= 99.5 THEN 'WARNING'
ELSE 'FAIL'
END as slo_status,
avg_response_time,
p95_response_time
FROM monthly_summary
ORDER BY month DESC, service_name;
8.2.2 Operations Summary (운영팀용)
-- 일간 운영 현황
SELECT
DATE(timestamp) as date,
service_name,
COUNT(*) as total_requests,
COUNTIF(status_code >= 500) as error_count,
ROUND(COUNTIF(status_code >= 500) / COUNT(*) * 100, 3) as error_rate_percent,
ROUND(AVG(response_time_ms), 2) as avg_response_time,
ROUND(MAX(response_time_ms), 2) as max_response_time,
COUNT(DISTINCT user_id) as unique_users
FROM `{project}.{dataset}.http_requests`
WHERE timestamp >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY)
GROUP BY date, service_name
ORDER BY date DESC, service_name;
8.3 자동화된 리포팅
8.3.1 일간 리포트 생성
# scripts/generate-daily-report.py
import os
from google.cloud import bigquery
from google.cloud import monitoring_v3
from datetime import datetime, timedelta
import json
def generate_daily_availability_report():
"""일간 가용성 리포트 생성"""
client = bigquery.Client()
project_id = os.environ['PROJECT_ID']
# 어제 데이터 조회
yesterday = datetime.now() - timedelta(days=1)
query = f"""
SELECT
service_name,
COUNT(*) as total_requests,
COUNTIF(status_code < 500) as successful_requests,
ROUND(COUNTIF(status_code < 500) / COUNT(*) * 100, 3) as availability_percent,
ROUND(AVG(response_time_ms), 2) as avg_response_time,
ROUND(APPROX_QUANTILES(response_time_ms, 100)[OFFSET(95)], 2) as p95_response_time
FROM `{project_id}.monitoring_dataset.http_requests`
WHERE DATE(timestamp) = '{yesterday.strftime('%Y-%m-%d')}'
GROUP BY service_name
ORDER BY service_name
"""
results = client.query(query).to_dataframe()
# 리포트 생성
report = {
"date": yesterday.strftime('%Y-%m-%d'),
"summary": {
"total_services": len(results),
"services_meeting_slo": len(results[results['availability_percent'] >= 99.9]),
"overall_availability": round(results['availability_percent'].mean(), 3)
},
"services": results.to_dict('records')
}
# 파일로 저장
with open(f'/tmp/daily-report-{yesterday.strftime("%Y%m%d")}.json', 'w') as f:
json.dump(report, f, indent=2)
return report
if __name__ == "__main__":
report = generate_daily_availability_report()
print("Daily report generated successfully")
print(f"Overall availability: {report['summary']['overall_availability']}%")
8.3.2 SLO 대시보드 URL 생성
#!/bin/bash
# scripts/generate-dashboard-urls.sh
PROJECT_ID="dta-cloud-de-prod"
BASE_URL="https://console.cloud.google.com/monitoring"
echo "📈 Monitoring Dashboard URLs"
echo "============================"
echo ""
echo "🎯 SLO Dashboard:"
echo "${BASE_URL}/dashboards?project=${PROJECT_ID}&dashboardResource=projects%2F${PROJECT_ID}%2Fdashboards%2F${DASHBOARD_ID}"
echo ""
echo "🚨 Alerts Overview:"
echo "${BASE_URL}/alerting?project=${PROJECT_ID}"
echo ""
echo "📊 Metrics Explorer:"
echo "${BASE_URL}/metrics-explorer?project=${PROJECT_ID}"
echo ""
echo "📋 Logs Explorer:"
echo "https://console.cloud.google.com/logs/query?project=${PROJECT_ID}"
이 문서는 PLT-NFR-004 (99.9% 가용성) 요구사항을 충족하기 위한 완전한 구현 가이드를 제공합니다. Dev → Stage → Prod 순서로 점진적 배포가 가능하며, 각 환경에 맞는 모니터링 설정을 제공합니다.
다음 단계
1. Cloud Run 고가용성 설정
- Terragrunt 설정 최적화 (완료)
- Health Check 엔드포인트 구현
- 환경별 배포 전략
2. 모니터링 시스템 배포
# Terraform 모듈 배포
cd infrastructure/terragrunt/dev/monitoring
terragrunt apply
# 배포 확인
terragrunt output dashboard_url
3. 통합 검증
- SLO 대시보드 확인
- 알림 정책 테스트
- 가용성 메트릭 검증
4. 점진적 확장
- Dev → Stage → Prod 순서로 배포
- 각 환경별 성능 벤치마크
- 지속적 모니터링 및 최적화
99.9% 가용성 목표 달성을 위한 완전한 시스템이 준비되었습니다! 🎉
변경 이력
| 버전 | 날짜 | 작성자 | 변경 내용 |
|---|---|---|---|
| 0.1.0 | 2025-08-13 | bok@weltcorp.com | 최초 작성 |