📌 상황: Cloud Run 인스턴스가 예상보다 많은 비용을 차지
🔍 문제 발견
- Google Cloud Billing을 확인해보니 예상보다 많은 비용이 청구됨.
- 사용자가 요청을 보낸 후 바로 서버가 종료되길 원했지만, Cloud Run 특성상 15분 동안 유지됨.
- 즉, 1분만 사용해도 15분+ 추가 시간만큼 비용이 발생하는 상황 🚨
- 예상보다 비용이 많이 나와서 서비스 지속 시 적자가 날 가능성이 있음.
🔥 문제 원인: 서버가 즉시 종료되지 않음
- 서버 종료를 명확히 하는 코드가 없었음.
- Cloud Run의 자동 스케일링 정책 때문에 사용자가 없어도 15분 동안 인스턴스가 유지됨.
- os._exit(0)을 사용하여 서버를 종료하려 했지만, Flask 프로세스만 종료되고 Cloud Run 컨테이너는 종료되지 않음.
- Cloud Run의 idle timeout을 조정하는 기능이 없었음 → 15분 기본 유지 (구글 공식 문서에서도 수정 불가능한 것으로 확인됨).
✅ 해결 방법: 사용자가 없으면 즉시 종료하는 방식으로 변경
1️⃣ 사용자 요청이 없으면 서버 즉시 종료
- 사용자가 앱을 완전히 종료하거나(백그라운드 제거) 또는 120초 동안 요청이 없으면 서버를 종료.
- 이를 위해 WebSocket 연결 관리 및 큐 시스템을 활용하여 자동 종료 로직 추가.
2️⃣ exit(0) 대신 os.kill(os.getpid(), signal.SIGTERM) 사용
- exit(0)을 사용하면 Flask 프로세스만 종료되고 Cloud Run 인스턴스는 유지됨 ❌.
- os.kill(os.getpid(), signal.SIGTERM)을 사용하면 Cloud Run 인스턴스 자체가 종료됨 ✅.
🚀 구현 코드: 클라이언트가 없으면 서버 종료
import os
import signal
import eventlet
from flask_socketio import SocketIO
socketio = SocketIO()
# 클라이언트 연결 상태 관리
connected_clients = set()
client_queues = {}
def cleanup_client(client_id):
"""사용자가 없으면 서버 종료"""
if client_id in client_queues:
del client_queues[client_id] # 큐에서 제거
if client_id in connected_clients:
connected_clients.remove(client_id) # 클라이언트 목록에서 삭제
# 모든 사용자가 연결을 끊었으면 서버 종료
if len(connected_clients) == 0:
logger.info("모든 클라이언트가 끊어졌습니다. 웹소켓 닫기 중...")
# 🔥 **현재 연결된 모든 웹소켓을 닫음**
active_clients = list(socketio.server.manager.get_participants('/', '/'))
for sid in active_clients:
logger.info(f"Closing WebSocket connection for {sid}")
socketio.server.disconnect(sid)
eventlet.sleep(3) # 3초 대기 후 종료
logger.info("서버 종료 진행 중...")
# 🚀 **Cloud Run 인스턴스 종료**
os.kill(os.getpid(), signal.SIGTERM)
📊 결과: 서버가 즉시 종료되면서 비용 절감 성공
✅ 이전 방식
- 사용자가 요청을 보낸 후 Cloud Run이 15분 동안 유지됨 ❌
- 짧게 사용해도 15분 비용이 청구됨 ❌
- exit(0)을 사용했지만 Cloud Run 컨테이너가 유지됨 ❌
✅ 새로운 방식
- 사용자가 앱을 종료하거나 요청이 없으면 즉시 종료 ✅
- 120초 동안 요청이 없으면 자동으로 WebSocket과 서버 종료 ✅
- os.kill(os.getpid(), signal.SIGTERM)을 사용하여 Cloud Run 인스턴스까지 종료 ✅
- 불필요한 비용 절감 성공 💰
🔍 추가적인 Cloud Run 관련 이슈 정리
❌ Cloud Run의 idle timeout(15분)은 변경 불가능
- 공식 문서를 확인한 결과, Cloud Run의 idle timeout(유휴 시간 후 자동 종료)은 기본 15분으로 고정됨.
- gcloud run deploy 명령어에서 --max-instance-idle-timeout 같은 옵션이 없었음.
- 즉, Cloud Run 자체적으로 15분을 강제하는 것을 조정할 방법이 없음.
🎯 결론: Cloud Run 비용 절감을 위한 최적화 방법
1️⃣ 모든 클라이언트(WebSocket 연결)가 끊어지면 서버 종료
→ 120초 타임아웃 후 서버 종료하도록 큐 기반 로직 추가
2️⃣ os._exit(0) 대신 os.kill(os.getpid(), signal.SIGTERM) 사용
→ Flask 종료가 아니라 Cloud Run 컨테이너 자체 종료하도록 변경
3️⃣ Cloud Run의 idle timeout(15분)은 변경 불가능
→ 따라서 서버가 요청이 없을 때 직접 종료하는 방식으로 해결
🚀 최종 정리
- 처음에는 Cloud Run이 15분 동안 유지되어 불필요한 비용이 발생.
- os._exit(0)을 사용했지만 Cloud Run 컨테이너가 종료되지 않음.
- os.kill(os.getpid(), signal.SIGTERM)을 사용하여 Cloud Run 인스턴스를 즉시 종료하도록 수정.
- 비용 절감에 성공하여 불필요한 서버 비용을 없앰! 🎉
👉 이제 유저가 없으면 Cloud Run 인스턴스가 즉시 종료됨! 💰
'플러팅 AI > Flask Server' 카테고리의 다른 글
| 플러팅 AI 1.6 업데이트 GPT 모델 변경 (0) | 2025.04.16 |
|---|---|
| 📌 GCP Artifact Registry 비용 문제 및 최적화 과정 정리 (0) | 2025.02.20 |
| Flask 서버 시작 시간 최적화하기: 지연 로딩(Lazy Loading) 전략 (0) | 2024.12.13 |
| 이미지 업로드 방식을 HTTP에서 WebSocket으로 변경한 이유 (0) | 2024.12.13 |
| Google Cloud Run에서 딥러닝 기반 서버 최적화: Connect 이벤트 비동기화와 Lazy Import 적용 사례 (2) | 2024.12.11 |