Cloud/GCP
Google Calendar 기반 GCE(VM) Managed Instance Group 오토스케일 자동화
달빛궁전-
2025. 8. 18. 10:30
목표 및 배경
Autoscaling은 클라우드 환경에서 서비스의 안정성과 장애 예방을 위한 핵심 도구입니다.
주로 각종 메트릭(예: CPU 사용률, 지연시간, 메모리 사용량 등)에 따라 인스턴스 수를 동적으로 조절하거나, 사전에 예약된 스케줄에 맞춰 리소스를 증설/감축하는 방식이 사용됩니다.
비즈니스 관점에서는, 특정 이벤트나 캠페인 등 예외적인 상황에 맞추어서 미리 예약하거나, 콘솔에서 수동으로 인스턴스 수를 조절하기도 합니다.
하지만 이는 PC 환경에 한정되거나 콘솔에서의 작업이 필요하며 이에 절차가 번거로울 수 있습니다. 모바일 등 다양한 디바이스에서 손쉽게 접근 가능하고, 단순하게 관리할 수 있는 자동화 도구의 필요성이 높아집니다.
이러한 요구를 충족시키기 위해, 본 문서에서는 Google Calendar에 등록된 일정의 시작/종료 시간에 맞춰 Google Compute Engine(GCE) Managed Instance Group(MIG)의 인스턴스 수를 자동으로 증설하거나 축소하는 시스템의 구축 방법을 상세히 안내합니다.
1. 시스템 개요 및 작동 원리
이 시스템은 Google Calendar에 등록된 일정(시작/종료 시간)에 맞춰 Google Compute Engine (GCE) Managed Instance Group (MIG)의 인스턴스 수를 자동으로 증설하거나 축소합니다.
최종 아키텍처 흐름:
1. Google Calendar (이벤트 등록: 시작/종료 시간, 스케일링 명령 포함)
↓ (Google Calendar API 호출: 주기적 조회 - Polling)
2. Cloud Run (캘린더 폴링 및 스케일링 로직)
↑ (HTTP 요청 - OIDC 토큰 인증)
3. Cloud Scheduler (주기적 트리거 - 예: 매 5분마다 Cloud Run 호출)
↓ (GCE API 호출)
4. Google Compute Engine (GCE) Managed Instance Group (MIG)
↓ (target_size 조절을 통한 인스턴스 증설/감소)
GCE 인스턴스 증설/감소
각 구성 요소의 역할:
-
Google Calendar: 사용자가 인스턴스 증설/감소 명령과 시간을 등록하는 인터페이스입니다.
-
Cloud Scheduler: 설정된 주기(예: 매 1시간)마다 Cloud Run를 호출하여 자동화 흐름을 시작합니다. 이때, OIDC 토큰을 사용하여 Cloud Run을 안전하게 인증하고 호출합니다.
-
Cloud Run (Python):
-
Google Compute Engine (GCE) Managed Instance Group (MIG): 인스턴스 그룹을 관리하고, Cloud Run의 지시에 따라 자동으로 인스턴스 수를 증설/감소시킵니다. MIG 자체의 자동 확장(Autoscaling)은 비활성화되어야 합니다.
2. Cloud Run 환경 변수 (env.list)
아래의 내용을 환경변수로 지정합니다.
env.list로 따로 저장을 합니다.
키:값 쌍으로 표현되어야 하기 때문에 YAML 포맷으로 작성합니다.
소스코드상 환경변수 설명
PROJECT_ID = os.environ.get('GCP_PROJECT_ID') # GCP 프로젝트 ID
ZONE = os.environ.get('GCE_ZONE') # 단일 영역 MIG인 경우 (예: asia-northeast3-a)
REGION = os.environ.get('GCE_REGION') # 리전별 MIG인 경우 (예: asia-northeast3)
MIG_NAME = os.environ.get('MIG_NAME') # 제어할 Managed Instance Group (MIG) 이름
CALENDAR_ID = os.environ.get('CALENDAR_ID') # Google Calendar ID
LOCAL_TIMEZONE_STR = os.environ.get('LOCAL_TIMEZONE', 'Asia/Seoul') # 캘린더 시간대 (예: Asia/Seoul)
CHECK_WINDOW_MINUTES = int(os.environ.get('CHECK_WINDOW_MINUTES', '60')) # 이벤트 확인 시간 창 (분 단위)
실제 입력되어야할 환경변수 설명
# GCP 프로젝트 및 자원 정보
GCP_PROJECT_ID: your-gcp-project-id
GCE_ZONE: asia-northeast3-a # 또는 GCE_REGION 사용 시 이 줄을 주석 처리
# GCE_REGION: asia-northeast3 # 리전 MIG인 경우 ZONE 대신 이 줄의 주석을 해제
MIG_NAME: my-mig-web-prod
# Google Calendar 정보
CALENDAR_ID: your-calendar-id@gmail.com
# 함수 동작 설정
LOCAL_TIMEZONE: Asia/Seoul
CHECK_WINDOW_MINUTES: "60" # 이벤트 확인 시간 창 (분 단위)
GOOGLE_FUNCTION_TARGET: scheduled_gce_scaler # functions-framework에게 어떤 함수를 타겟으로 서버를 실행하는지 설정
# GCP 프로젝트 및 자원 정보
GCP_PROJECT_ID: ctu-gcp-dsa2-unit
GCE_REGION: asia-northeast3 # 리전 MIG인 경우 ZONE 대신 이 줄의 주석을 해제
MIG_NAME: my-mig-web-prod
# Google Calendar 정보
CALENDAR_ID: calendar-watcher@ctu-gcp-dsa2-unit.iam.gserviceaccount.com
# 함수 동작 설정
LOCAL_TIMEZONE: Asia/Seoul
CHECK_WINDOW_MINUTES: "60" # 이벤트 확인 시간 창 (분 단위)
GOOGLE_FUNCTION_TARGET: scheduled_gce_scaler
3. main.py 파일 (Cloud Run code)
# main.py
import json
import os
import datetime
import re
import pytz # 시간대(TimeZone) 변환용 라이브러리
from google.cloud import compute_v1 # GCE Managed Instance Group 제어용 클라이언트
import functions_framework # Cloud Functions HTTP 트리거 지원
from googleapiclient.discovery import build # Google Calendar API 클라이언트
from google.auth import default # Google Cloud 기본 서비스 계정 인증
# ==============================================================================
# --- 클라이언트 전역 초기화 ---
# 컨테이너 콜드 스타트 시 딱 한 번 실행되어, 같은 인스턴스 재사용 시 매번 생성 비용을 줄임
# ==============================================================================
try:
compute_client = compute_v1.InstanceGroupManagersClient() # 단일 영역(Zonal) MIG 클라이언트
region_compute_client = compute_v1.RegionInstanceGroupManagersClient() # 리전(Regional) MIG 클라이언트
print("GCE InstanceGroupManagersClient 및 RegionInstanceGroupManagersClient 초기화 완료.")
except Exception as e:
print(f"GCE 클라이언트 초기화 오류: {e}")
# ==============================================================================
# --- 환경변수 로드 ---
# Cloud Run / Cloud Functions 배포 시 --set-env-vars 로 설정
# 예: GCP_PROJECT_ID=my-project, GCE_ZONE=asia-northeast3-a, ...
# ==============================================================================
PROJECT_ID = os.environ.get('GCP_PROJECT_ID') # GCP 프로젝트 ID
ZONE = os.environ.get('GCE_ZONE') # 단일 영역 MIG에 사용
REGION = os.environ.get('GCE_REGION') # 리전 MIG에 사용
MIG_NAME = os.environ.get('MIG_NAME') # 제어 대상 MIG 이름
CALENDAR_ID = os.environ.get('CALENDAR_ID') # Google Calendar ID
LOCAL_TIMEZONE_STR = os.environ.get('LOCAL_TIMEZONE', 'Asia/Seoul') # 로컬 시간대
CHECK_WINDOW_MINUTES = int(os.environ.get('CHECK_WINDOW_MINUTES', '20')) # 시작/종료 체크 시간 범위 (분)
# ==============================================================================
# --- Google Calendar 이벤트 description 업데이트 ---
# 이벤트 처리 여부 [PROCESSED_START]/[PROCESSED_END]와 기준선 사이즈를 기록
# ==============================================================================
def update_calendar_event_description(event_id, current_description, tag):
"""
Google Calendar 이벤트 설명(description)에 특정 태그를 추가하여
처리 여부 또는 기준선 인스턴스 크기를 기록하는 함수
"""
try:
# GCP 기본 인증(서비스 계정)으로 Google Calendar API 클라이언트 생성
credentials_calendar, _ = default()
service_calendar_api = build('calendar', 'v3', credentials=credentials_calendar)
new_description = current_description if current_description else ""
# 동일 태그가 없는 경우만 추가
if tag not in new_description:
new_description += f"\n{tag}"
# Calendar API의 patch 메서드로 description만 변경
updated_event_body = {'description': new_description}
service_calendar_api.events().patch(
calendarId=CALENDAR_ID,
eventId=event_id,
body=updated_event_body
).execute()
print(f"Event {event_id} description updated with tag: {tag}")
return True
except Exception as e:
print(f"Failed to update event {event_id} description with tag {tag}: {e}")
return False
# ==============================================================================
# --- GCE MIG 스케일링 ---
# MIG 크기(targetSize)를 변경하는 로직
# ==============================================================================
def execute_gce_scaling(mig_name, target_size, action_type):
"""
MIG 크기를 target_size로 조정 (스케일 업/다운 공통)
mig_name: MIG 이름
target_size: 목표 인스턴스 개수
action_type: 호출 맥락 (start_time_scale_up / end_time_scale_down 등)
"""
print(f"Attempting to resize GCE MIG '{mig_name}' to {target_size} instances. Action: {action_type}")
try:
# 단일 영역 MIG 조정
if ZONE:
operation = compute_client.resize(
project=PROJECT_ID,
zone=ZONE,
instance_group_manager=mig_name,
size=target_size
)
# 리전 MIG 조정
elif REGION:
operation = region_compute_client.resize(
project=PROJECT_ID,
region=REGION,
instance_group_manager=mig_name,
size=target_size
)
else:
raise ValueError("ZONE 또는 REGION 환경 변수가 설정되어야 합니다.")
print(f"GCE MIG '{mig_name}' resize operation initiated: {operation.name}")
return True
except Exception as gce_e:
print(f"Failed to resize GCE MIG '{mig_name}': {gce_e}")
return False
# ==============================================================================
# --- Cloud Function 엔트리포인트 ---
# Cloud Scheduler → HTTP 호출 → Google Calendar 조회 → MIG 스케일링
# ==============================================================================
@functions_framework.http
def scheduled_gce_scaler(request):
"""
주기적으로 Google Calendar 이벤트를 확인하여
이벤트 시작 시간에는 스케일 업,
이벤트 종료 시간에는 스케일 다운을 수행하는 함수
"""
print("Function scheduled_gce_scaler 시작.")
# 필수 환경변수 확인
if not all([PROJECT_ID, (ZONE or REGION), MIG_NAME, CALENDAR_ID, LOCAL_TIMEZONE_STR]):
error_msg = "필수 환경 변수 (PROJECT_ID, ZONE/REGION, MIG_NAME, CALENDAR_ID, LOCAL_TIMEZONE)가 모두 설정되지 않았습니다."
print(f"Error: {error_msg}")
return error_msg, 400
try:
# Google Calendar API 클라이언트 생성
credentials_calendar, _ = default()
service_calendar_api = build('calendar', 'v3', credentials=credentials_calendar)
# 현재 로컬 시간
local_tz = pytz.timezone(LOCAL_TIMEZONE_STR)
now_local = datetime.datetime.now(local_tz)
# 조회 시간 범위 계산 (과거/미래 CHECK_WINDOW_MINUTES)
time_min_local = (now_local - datetime.timedelta(minutes=CHECK_WINDOW_MINUTES)).isoformat()
time_max_local = (now_local + datetime.timedelta(minutes=CHECK_WINDOW_MINUTES)).isoformat()
print(f"Current local time ({LOCAL_TIMEZONE_STR}): {now_local}")
print(f"Checking events for calendar ID: {CALENDAR_ID} in local window: {time_min_local} to {time_max_local}")
# 캘린더 이벤트 조회
events_result = service_calendar_api.events().list(
calendarId=CALENDAR_ID,
timeMin=time_min_local,
timeMax=time_max_local,
timeZone=LOCAL_TIMEZONE_STR,
singleEvents=True,
orderBy='startTime',
showDeleted=False
).execute()
events = events_result.get('items', [])
if not events:
print("No relevant events found in the current time window.")
return "No events to process", 200
# 조회된 이벤트별로 처리
for event_item in events:
summary = event_item.get('summary', '')
event_id = event_item.get('id')
current_description = event_item.get('description', '')
# 시작 / 종료 시간 확인
start_time_iso = event_item['start'].get('dateTime', event_item['start'].get('date'))
end_time_iso = event_item['end'].get('dateTime', event_item['end'].get('date'))
try:
event_start_local = datetime.datetime.fromisoformat(start_time_iso).astimezone(local_tz)
event_end_local = datetime.datetime.fromisoformat(end_time_iso).astimezone(local_tz)
except ValueError:
print(f"Skipping event {event_id}: Time parse error.")
continue
print(f"Processing event: ID={event_id}, Summary='{summary}', Start={event_start_local}, End={event_end_local}")
# 캘린더 제목에서 스케일 명령 추출
match = re.search(r"\[GCE_SCALE:([^:]+):(\d+)\]", summary)
if not match:
print(f"No scaling command pattern found in event summary: '{summary}'")
continue
parsed_mig_name = match.group(1) # [GCE_SCALE:MIG_NAME:...] 의 MIG_NAME
parsed_target_size = int(match.group(2)) # 목표 인스턴스 수
# 환경 변수의 MIG_NAME과 일치하는 이벤트만 처리
if parsed_mig_name != MIG_NAME:
print(f"Skipping event: MIG name {parsed_mig_name} != configured {MIG_NAME}")
continue
# ===============================
# 1. 이벤트 시작 시각 스케일 업
# ===============================
if "[PROCESSED_START]" not in current_description:
if event_start_local <= now_local < (event_start_local + datetime.timedelta(minutes=CHECK_WINDOW_MINUTES)):
print(f"Triggering SCALE UP for MIG='{parsed_mig_name}' to {parsed_target_size}")
try:
# 스케일 업 전 기존 목표 사이즈 확인
if ZONE:
mig_details = compute_client.get(
project=PROJECT_ID,
zone=ZONE,
instance_group_manager=MIG_NAME
)
elif REGION:
mig_details = region_compute_client.get(
project=PROJECT_ID,
region=REGION,
instance_group_manager=MIG_NAME
)
else:
raise ValueError("ZONE 또는 REGION 환경 변수가 설정되어야 합니다.")
original_size = mig_details.target_size
print(f"MIG '{MIG_NAME}' current targetSize before scale up: {original_size}")
# 이벤트 description에 기준선 및 PROCESSED_START 기록 후 스케일 업 실행
if update_calendar_event_description(event_id, current_description, f"[PROCESSED_START][BASELINE_SIZE:{original_size}]"):
execute_gce_scaling(parsed_mig_name, parsed_target_size, "start_time_scale_up")
except Exception as e:
print(f"Failed to get current MIG size: {e}")
# ===============================
# 2. 이벤트 종료 시각 스케일 다운
# ===============================
elif "[PROCESSED_END]" not in current_description:
if event_end_local <= now_local < (event_end_local + datetime.timedelta(minutes=CHECK_WINDOW_MINUTES)):
print(f"Triggering SCALE DOWN for MIG='{parsed_mig_name}'")
# 이전 스케일 업 시 기록한 BASELINE_SIZE로 복원
baseline_match = re.search(r"\[BASELINE_SIZE:(\d+)\]", current_description)
target_down_size = int(baseline_match.group(1)) if baseline_match else 0
if update_calendar_event_description(event_id, current_description, "[PROCESSED_END]"):
execute_gce_scaling(parsed_mig_name, target_down_size, "end_time_scale_down")
print("Function scheduled_gce_scaler 실행 완료.")
return "OK", 200
except Exception as e:
print(f"Unhandled error: {e}")
return f"Internal Server Error: {e}", 500
4. requirements.txt 파일
Run-framework
google-cloud-compute
google-api-python-client
google-auth
pytz
5. Google Calendar 기반 GCE 오토스케일 자동화
5.1. 설정
-
gcloud CLI 설치 및 인증:
-
필수 API 활성화: (GCP 콘솔에서 API 및 서비스 > 라이브러리로 이동하여 검색 후 활성화)
5.2. IAM 서비스 계정 및 권한 설정
5.2.1. Cloud Run, Cloud Scheduler 실행을 위한 서비스 계정 생성 및 권한 부여 (ex:calendar-watcher)
이 서비스 계정은 Cloud Run이 Google Calendar를 읽고 GCE MIG를 제어할 수 있는 권한을 가집니다.
-
GCP 콘솔 > 좌측 메뉴에서 IAM 및 관리자 > 서비스 계정으로 이동합니다.
-
상단의 + 서비스 계정 만들기를 클릭합니다.
-
서비스 계정 이름: calendar-watcher (이름은 자유, 식별하기 쉽게)
-
서비스 계정 ID: 자동으로 생성됩니다. (예: calendar-watcher@your-project-id.iam.gserviceaccount.com)
-
만들고 계속하기 클릭.
-
이 서비스 계정에 프로젝트 액세스 권한 부여: 다음 역할을 검색하여 하나씩 추가합니다.
-
계속 클릭.
-
완료 클릭.
5.2.2. Cloud Scheduler 실행을 위한 서비스 계정 권한 확인 (PROJECT_NUMBER@appspot.gserviceaccount.com)
2개로 분리하여 이야기하였지만, 5.2.1에 나오는대로 전용 서비스 계정을 사용해도 됩니다.
Cloud Scheduler는 작업을 실행할 때 기본적으로 Google App Engine 기본 서비스 계정을 사용합니다. 이 계정은 Cloud Run을 호출할 수 있는 권한이 필요합니다.
-
GCP 콘솔 > 좌측 메뉴에서 IAM 및 관리자 > IAM으로 이동합니다.
-
PROJECT_NUMBER@appspot.gserviceaccount.com (예: 361234030411@appspot.gserviceaccount.com) 형태의 계정을 찾습니다.
-
이 계정에 Cloud Run 호출자 (Cloud Run Invoker) 역할이 부여되어 있는지 확인합니다.
5.3. Google Calendar 설정 및 서비스 계정 공유
Cloud Run이 읽을 특정 Google Calendar를 준비하고, 5.2.1단계에서 생성한 서비스 계정에 읽기 및 수정 권한을 부여합니다.
-
Google Calendar 웹사이트 (calendar.google.com)로 이동합니다.
-
왼쪽의 내 캘린더 목록에서 오토스케일링 일정을 관리할 캘린더를 선택하거나, 새 캘린더를 생성합니다.
-
해당 캘린더 이름 옆의 점 3개 아이콘을 클릭하고 설정 및 공유를 선택합니다.
-
좌측 메뉴에서 공유 대상 섹션을 클릭합니다.
-
사용자 및 그룹 추가를 클릭합니다.
-
사용자 또는 그룹 추가 필드에 5.2.1단계에서 생성한 Cloud Run 서비스 계정의 이메일 주소를 입력합니다.
(예: calendar-watcher@your-project-id.iam.gserviceaccount.com) -
권한 설정 드롭다운에서 일정 변경 및 공유 관리 권한을 부여합니다. (이벤트 설명을 업데이트하기 위함)
-
캘린더 ID 확인: 캘린더 설정 페이지에서 캘린더 ID를 복사해 둡니다.
이 ID는 나중에 Cloud Run 배포 시 환경 변수에 사용됩니다. 보통 자신의 Gmail ID 입니다.
5.4. Cloud Run (Python 폴링 함수) 개발 및 배포
Cloud Run에 적재된 코드는 Cloud Scheduler에 의해 주기적으로 호출되어 캘린더를 조회하고 스케일링 명령을 실행합니다.
5.4.1. 함수 코드 작성 (main.py)
위의 3. main.py 파일 섹션에 있는 코드를 복사하여 Cloud Shell 또는 로컬 개발 환경에 main.py 파일로 저장합니다.
5.4.2. 의존성 파일 작성 (requirements.txt)
위의 4. requirements.txt 파일 섹션에 있는 코드를 복사하여 main.py 파일과 같은 디렉토리에 requirements.txt 파일로 저장합니다.
5.4.3. 환경변수 파일 작성 (requirements.txt)
5.4.3. Cloud Run (폴링 함수) 배포 명령어
Cloud Shell에서 main.py와 requirements.txt 파일이 있는 디렉토리로 이동한 후 다음 명령어를 실행하여 Cloud Run을 배포합니다.
주의: --set-env-vars 플래그의 모든 값(GCP_PROJECT_ID, GCE_ZONE 또는 GCE_REGION, MIG_NAME, CALENDAR_ID, LOCAL_TIMEZONE, CHECK_WINDOW_MINUTES)은 실제 환경에 맞게 정확히 변경해야 합니다.
아래에서는 env.yaml 파일에 환경변수를 저장하고 실행하는 방향으로 진행했습니다.
아래에서는 env.yaml 파일에 환경변수를 저장하고 실행하는 방향으로 진행했습니다.
gcloud functions deploy gce-autoscaler-scheduler-trigger-func \
--gen2 \
--region=asia-northeast3 \
--runtime=python310 \
--source=. \
--trigger-http \
--env-vars-file=env.yaml \
--service-account=calendar-watcher@ctu-gcp-dsa2-unit.iam.gserviceaccount.com \
--entry-point scheduled_gce_scaler \
--no-allow-unauthenticated
gcloud functions deploy gce-autoscaler-scheduler-trigger \ # 배포할 함수 이름
--gen2 \ # Cloud Functions 2세대(Cloud Run 기반) 사용
--region=asia-northeast3 \ # 배포 리전 (서울)
--runtime=python310 \ # 실행 런타임 (Python 3.10)
--source=. \ # 소스 코드 경로 (현재 디렉토리)
--trigger-http \ # HTTP 요청으로 트리거
--entry-point=scheduled_gce_scaler \ # 코드 내 함수 이름 (진입점)
--env-vars-file=env.yaml \ # 환경변수 YAML 파일
--service-account=calendar-watcher@project-name.iam.gserviceaccount.com \ # 함수 실행 서비스 계정
--no-allow-unauthenticated # 인증된 호출만 허용 (익명 접근 차단)
배포 완료 후 환경변수와 config에 대한 설명 확인
5.5. Cloud Scheduler 작업 생성
주기적으로 Cloud Run을 호출하여 캘린더를 폴링하도록 설정합니다.
-
GCP 콘솔 > 좌측 메뉴에서 Cloud Scheduler로 이동합니다.
-
작업 만들기를 클릭합니다.
-
이름: check-calendar-for-gce-scaling (직관적인 이름)
-
리전: Cloud Run과 동일한 리전 (asia-northeast3)
-
빈도: * 0 * * * * (매 1시간마다)
-
시간대: Asia/Seoul (매우 중요! Google Calendar에 이벤트 시간을 입력하는 기준 시간대와 일치시켜야 합니다.)
-
대상: HTTP
-
URL: 배포한 gce-autoscaler-scheduler-trigger 함수의 HTTP 트리거 URL
-
이 URL은 Cloud Run 콘솔에서 해당 함수의 트리거 탭에서 확인할 수 있습니다.
(예: https://asia-northeast3-ctu-gcp-dsa2-unit.cloudRun.net/gce-autoscaler-scheduler-trigger)
-
-
HTTP 메서드: GET (또는 POST, 함수가 request 객체를 받으므로 모두 가능)
-
인증 헤더 :
-
인증 헤더 유형을 OIDC 토큰 추가로 설정합니다.
-
서비스 계정 필드에 Cloud Run을 동작시키는 계정이나, Scheduler의 기본 서비스 계정 이메일 (예: PROJECT_NUMBER@appspot.gserviceaccount.com, 5.2.2단계에서 확인)을 선택합니다.
해당 서비스 계정에는 Cloud Run에 대한 Cloud Run invoke 역할이 부여되어 있어야 합니다.
-
-
만들기를 클릭합니다.
5.6. GCE Managed Instance Group (MIG) 준비
Cloud Function이 제어할 GCE 인스턴스 그룹이 미리 준비되어 있어야 합니다.
-
인스턴스 템플릿: GCE 인스턴스 템플릿이 생성되어 있어야 합니다.
-
관리형 인스턴스 그룹 (MIG):
-
GCP 콘솔 > Compute Engine > 인스턴스 그룹으로 이동합니다.
-
스케일링할 MIG가 존재하고, MIG_NAME 환경 변수에 설정된 이름과 일치하는지 확인합니다.
-
MIG의 위치(단일 영역 또는 리전)가 GCE_ZONE 또는 GCE_REGION 환경 변수와 일치하는지 확인합니다.
-
자동 확장(Autoscaling) 설정: MIG의 자동 확장 모드를 사용 안함(Off)으로 설정해야 합니다.
(Cloud Run이 직접 크기를 조절할 것이므로, MIG 자체의 오토스케일러와 충돌을 방지합니다.)
-
6. Google Calendar 이벤트 등록 형식
이제 Cloud Run이 조회할 캘린더에 이벤트를 등록합니다.
-
이벤트 제목 (Summary):
-
형식: [GCE_SCALE:MIG_NAME:TARGET_SIZE]
-
예시:
-
[GCE_SCALE:my-mig-web-prod:10] (web프로덕션 MIG를 10개 인스턴스로 증설)
-
[GCE_SCALE:my-dev-web-mig:1] (개발 MIG를 1개 인스턴스로 축소)
-
-
-
이벤트 시간:
- 이벤트의 **시작 시간**과 **종료 시간**을 원하는 로컬 시간대(예: 서울 시간대)로 정확하게 지정합니다. Cloud Run은 이 시간을 기준으로 스케일 업/다운을 판단합니다.
- 이벤트의 **시작 시간**과 **종료 시간**을 원하는 로컬 시간대(예: 서울 시간대)로 정확하게 지정합니다. Cloud Run은 이 시간을 기준으로 스케일 업/다운을 판단합니다.
7. 테스트 및 모니터링
-
캘린더 이벤트 생성:
-
Google Calendar에 새로운 이벤트를 생성합니다. 시작 시간을 지금으로부터 5~10분 후로 설정하고, 제목에 올바른 스케일링 명령을 입력합니다.
-
종료 시간도 설정하고, 스케일 다운이 되는지 확인합니다.
-
-
Cloud Scheduler 수동 실행 (초기 테스트용):
-
GCP 콘솔 > Cloud Scheduler > 생성한 작업 옆의 지금 실행 버튼을 클릭하여 수동으로 Cloud Function을 호출해 보세요. (자동 실행까지 기다리지 않고 즉시 테스트 가능)
-
-
Cloud Function 로그 확인:
-
GCP 콘솔 > Cloud Run > gce-autoscaler-scheduler-trigger 함수 > 로그 탭으로 이동합니다.
-
-
Current local time (Asia/Seoul): ...
-
Checking events for calendar ID: ...
-
Processing event: ID=..., Summary='...', Start='...', End='...' (Asia/Seoul). (Event가 있을때 생성)
-
Triggering SCALE UP/DOWN for MIG='...' to Target Size=... (이 메시지가 보이면 GCE API 호출에 대한 메시지 발생)
-
GCE MIG '...' resize operation initiated: operation-... (이 메시지가 보이면 GCE API 호출 성공!)
-
MIG '...' current targetSize before scale up: X (스케일 업 전 기준선 인스턴스 수)
-
Event "" description updated with tag: [PROCESSED_START][BASELINE_SIZE:X] (이벤트 시작 처리 및 기준선 기록 확인)
-
Event "" description updated with tag: [PROCESSED_END] (이벤트 종료 처리 확인)
-
혹은 에러 메시지(403 권한 에러, 404 리소스 없음 등)가 없는지 확인합니다.로그에서 다음을 확인합니다.

-
GCE MIG 상태 확인:
-
GCP 콘솔 > Compute Engine > 인스턴스 그룹 > 해당 MIG 클릭.
-
현재 인스턴스 수가 변경 되었는지 확인합니다.
-
8. 유의사항
-
중복 실행 방지: 현재 코드는 [PROCESSED_START] 및 [PROCESSED_END] 태그를 사용하여 중복 실행을 방지합니다. 이 태그가 캘린더 이벤트 설명에 올바르게 기록되는지 확인해야 합니다.
-
에러 알림: TODO로 표시된 부분에 Cloud Logging에 ERROR 레벨 로그를 기록하거나, Cloud Monitoring Alerts를 설정하여 스케일링 실패 시 Slack, 이메일 등으로 알림을 받도록 구성하는 것이 좋습니다.
-
MIG의 최소/최대 인스턴스 수: Cloud Function이 설정하는 target_size는 MIG의 최소 인스턴스 수와 최대 인스턴스 수 범위 내에서만 작동합니다. 이 범위를 벗어나는 target_size는 오류를 발생시키므로, 캘린더에 입력하는 TARGET_SIZE가 이 범위 내에 있도록 주의해야 합니다.
-
시간대 일관성: Cloud Scheduler, Google Calendar, Cloud Function의 LOCAL_TIMEZONE 설정이 모두 일치하는 것이 중요합니다.