GCP 무료 티어 비용 최적화 전략
무료 티어 한도 분석
Cloud Functions
- 호출 한도: 200만 호출/월
- 컴퓨팅 시간: 40만 GB-초/월
- 네트워크 송신: 5GB/월
예상 사용량:
- 일일 키워드 조회: 1,000건
- 월간 총 호출: 30,000건 (한도의 1.5%)
- ✅ 충분히 여유 있음
Cloud Run
- CPU 시간: 180만 vCPU-초/월
- 메모리: 360만 GiB-초/월
- 요청 수: 200만 요청/월
예상 사용량:
- 평균 응답 시간: 500ms
- 일일 API 요청: 500건
- 월간 vCPU-초: 7,500초 (한도의 0.4%)
- ✅ 충분히 여유 있음
Firestore
- 읽기: 50,000 문서/일
- 쓰기: 20,000 문서/일
- 삭제: 20,000 문서/일
- 저장: 1GiB
예상 사용량:
- 일일 쓰기: 100건 (한도의 0.5%)
- 일일 읽기: 200건 (한도의 0.4%)
- ✅ 충분히 여유 있음
Firebase Hosting
- 저장: 10GB
- 전송: 360MB/일
예상 사용량:
- React 앱 크기: 2MB
- 일일 방문자: 50명
- 일일 전송량: 100MB (한도의 28%)
- ✅ 여유 있음
비용 모니터링 설정
1. 예산 알림 설정
# 예산 생성 (월 $1 한도)
gcloud billing budgets create \
--billing-account=YOUR_BILLING_ACCOUNT \
--display-name="무료 티어 모니터링" \
--budget-amount=1USD \
--threshold-rule=percent:50 \
--threshold-rule=percent:902. 비용 알림 Cloud Function
# budget-alert-function/main.py
import functions_framework
import json
from google.cloud import logging
@functions_framework.cloud_event
def budget_alert(cloud_event):
"""예산 초과 시 알림"""
pubsub_message = json.loads(cloud_event.data["message"]["data"])
cost_amount = pubsub_message["costAmount"]
budget_amount = pubsub_message["budgetAmount"]
if cost_amount >= budget_amount * 0.8:
# 80% 초과 시 서비스 일시 중단
logging_client = logging.Client()
logger = logging_client.logger("budget-alert")
logger.error(f"예산 80% 초과! 비용: ${cost_amount}, 예산: ${budget_amount}")
# TODO: 서비스 중단 로직 추가3. 사용량 대시보드
# monitoring/usage_tracker.py
from google.cloud import monitoring_v3
def get_usage_metrics():
"""GCP 사용량 조회"""
client = monitoring_v3.MetricServiceClient()
project_name = f"projects/{PROJECT_ID}"
# Cloud Functions 호출 수
functions_query = monitoring_v3.ListTimeSeriesRequest(
name=project_name,
filter='resource.type="cloud_function"',
interval=monitoring_v3.TimeInterval({
"end_time": {"seconds": int(time.time())},
"start_time": {"seconds": int(time.time()) - 86400} # 24시간
})
)
return client.list_time_series(request=functions_query)비용 최적화 기법
1. Cloud Functions 최적화
# 콜드 스타트 최소화
import functions_framework
from functools import lru_cache
# 전역 변수로 연결 재사용
firestore_client = None
@functions_framework.http
def optimized_function(request):
global firestore_client
# 클라이언트 재사용
if firestore_client is None:
firestore_client = firestore.Client()
# 캐싱으로 중복 호출 방지
@lru_cache(maxsize=100)
def cached_api_call(keyword):
return call_naver_api(keyword)
return cached_api_call(request.args.get('keyword'))2. Firestore 쿼리 최적화
# 복합 인덱스 활용
def get_keyword_data(keyword, start_date, end_date):
"""효율적인 쿼리 구성"""
# 단일 쿼리로 필요한 데이터만 조회
query = db.collection('keyword_data') \
.where('keyword', '==', keyword) \
.where('timestamp', '>=', start_date) \
.where('timestamp', '<=', end_date) \
.order_by('timestamp', direction=firestore.Query.DESCENDING) \
.limit(10) # 필요한 만큼만 조회
return [doc.to_dict() for doc in query.stream()]3. 캐싱 전략
# Redis 대신 메모리 캐싱 활용
import time
from functools import wraps
cache = {}
CACHE_EXPIRE = 3600 # 1시간
def memory_cache(expire_time=CACHE_EXPIRE):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
cache_key = f"{func.__name__}:{str(args)}:{str(kwargs)}"
current_time = time.time()
# 캐시 확인
if cache_key in cache:
cached_data, timestamp = cache[cache_key]
if current_time - timestamp < expire_time:
return cached_data
# 캐시 미스 시 실행 및 저장
result = func(*args, **kwargs)
cache[cache_key] = (result, current_time)
return result
return wrapper
return decorator
@memory_cache(expire_time=1800) # 30분 캐시
def get_keyword_trends(keyword):
"""트렌드 데이터 캐싱"""
return call_datalab_api(keyword)스케일링 전략
단계별 확장 계획
1단계 (무료 티어): ~100 사용자/일
2단계 (유료 전환): ~1,000 사용자/일
3단계 (최적화): ~10,000 사용자/일
2단계 진입 시점 판단
- Cloud Functions 호출: 월 100만건 초과
- Firestore 읽기: 일 25,000건 초과
- 예상 월 비용: $10-20
3단계 최적화 방안
- CDN 도입: Cloud CDN으로 응답 캐싱
- 데이터 파티셔닝: Firestore 컬렉션 분리
- 배치 처리: Cloud Scheduler로 주기적 데이터 갱신
비용 대비 성능 최적화
1. 지역 선택 최적화
# 서울 리전 사용 (레이턴시 최소화)
gcloud config set run/region asia-northeast3
gcloud config set functions/region asia-northeast32. 리소스 할당 최적화
# Cloud Run 서비스 설정
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: naver-api-service
spec:
template:
metadata:
annotations:
# 최소 리소스로 비용 절약
run.googleapis.com/cpu: "1"
run.googleapis.com/memory: "256Mi"
# 콜드 스타트 최소화
run.googleapis.com/execution-environment: gen2
spec:
containers:
- image: gcr.io/project/image
resources:
limits:
memory: "256Mi"
cpu: "1000m"3. 자동 확장 설정
# 트래픽 기반 자동 확장
metadata:
annotations:
run.googleapis.com/ingress: all
autoscaling.knative.dev/minScale: "0" # 비용 절약
autoscaling.knative.dev/maxScale: "5" # 한도 제한
autoscaling.knative.dev/target: "10" # 동시 요청 수모니터링 대시보드
비용 추적 쿼리
-- BigQuery 비용 분석
SELECT
service.description as service,
location.location as region,
SUM(cost) as total_cost,
SUM(usage.amount) as usage_amount,
usage.unit
FROM `project.dataset.gcp_billing_export_v1_XXXXXX`
WHERE _PARTITIONTIME >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY)
GROUP BY service, region, usage.unit
ORDER BY total_cost DESC;Grafana 대시보드 설정
{
"dashboard": {
"title": "GCP 무료 티어 사용량",
"panels": [
{
"title": "Cloud Functions 호출 수",
"type": "graph",
"targets": [
{
"query": "gcp_cloud_function_execution_count"
}
]
},
{
"title": "Firestore 읽기/쓰기",
"type": "graph",
"targets": [
{
"query": "gcp_firestore_document_read_count"
}
]
}
]
}
}위험 관리
1. 사용량 급증 대응
# 사용량 제한 미들웨어
def rate_limiter(max_requests_per_hour=100):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
client_ip = request.environ.get('HTTP_X_FORWARDED_FOR', request.remote_addr)
# Redis 대신 Firestore로 요청 수 추적
doc_ref = db.collection('rate_limits').document(client_ip)
doc = doc_ref.get()
current_hour = int(time.time()) // 3600
if doc.exists:
data = doc.to_dict()
if data.get('hour') == current_hour:
if data.get('count', 0) >= max_requests_per_hour:
return {'error': 'Rate limit exceeded'}, 429
doc_ref.update({'count': firestore.Increment(1)})
else:
doc_ref.set({'hour': current_hour, 'count': 1})
else:
doc_ref.set({'hour': current_hour, 'count': 1})
return func(*args, **kwargs)
return wrapper
return decorator2. 비상 차단 스위치
# 환경변수로 서비스 제어
import os
def circuit_breaker(func):
@wraps(func)
def wrapper(*args, **kwargs):
if os.environ.get('SERVICE_DISABLED', 'false').lower() == 'true':
return {'error': 'Service temporarily disabled'}, 503
return func(*args, **kwargs)
return wrapper업데이트: 2025-11-08
목표: 월 비용 $0 유지
작성일: 2025-11-08