본문 바로가기
IT/Cloud

[Cloud] Cloud Run 마이그레이션: 온프레미스 앱 전환 전략

by 수누다 2026. 7. 11.
반응형

[클라우드] Cloud Run 마이그레이션으로 온프레미스 앱 전환하기

온프레미스에서 오래 돌리던 앱을 클라우드로 옮기는 일, 말은 쉬워 보여도 막상 들어가면 손이 꽤 갑니다. 특히 Cloud Run 마이그레이션은 서버 한 대만 없앤다고 끝나는 작업이 아니거든요. 프로세스가 어떻게 뜨는지, 파일을 어디에 쓰는지, 배치 작업은 어떻게 분리할지, 외부 트래픽은 어떤 방식으로 받을지까지 다시 봐야 합니다. 저도 홈랩과 운영 환경에서 비슷한 전환을 몇 번 해보니, 잘된 사례보다 중간에 막혔던 경험에서 더 많이 배우게 되더라고요. 이번 글에서는 온프레미스 클라우드 전환을 고민하시는 분들을 위해, 실제로 점검하는 순서대로 Cloud Run 도입 전략과 컨테이너 마이그레이션 포인트를 정리해보겠습니다.

기존 VM 기반 운영에 익숙하셨다면 Cloud Run의 요청 기반 실행 모델이 처음엔 좀 낯설 수 있습니다. 저도 처음엔 감이 잘 안 왔는데, 구조를 한 번 이해하고 나니 운영 포인트가 오히려 단순해졌습니다. 핵심은 애플리케이션을 억지로 클라우드에 끼워 맞추는 게 아니라, Cloud Run이 안정적으로 실행할 수 있는 형태로 먼저 정리한 뒤 올리는 것입니다.

Cloud Run 마이그레이션을 위한 온프레미스 클라우드 전환 아키텍처 다이어그램

온프레미스 애플리케이션이 컨테이너 이미지로 패키징되고 Cloud Run, 데이터베이스, 로드밸런서로 연결되는 전체 흐름을 보여주는 개요 이미지입니다.

1. 왜 Cloud Run 마이그레이션이 현실적인 선택인지

쉽게 말해 Cloud Run은 컨테이너만 잘 만들면 실행 환경은 서비스가 관리해주는 플랫폼입니다. 서버 패치, 런타임 노드 관리, 자동 확장 같은 운영 부담을 꽤 덜 수 있죠. 그래서 기존 온프레미스 앱 중에서도 아래 조건에 맞는 서비스는 전환 효과가 큰 편입니다.

  • HTTP 또는 gRPC 기반으로 요청을 처리하는 웹 애플리케이션
  • 상태(state)를 메모리나 로컬 디스크가 아닌 외부 저장소로 분리할 수 있는 서비스
  • 트래픽 변동이 있어 자동 확장의 이점을 볼 수 있는 서비스
  • 배포 속도와 롤백 단순화가 중요한 팀 서비스

반대로 항상 실행 중이어야 하는 장기 백그라운드 프로세스나, 로컬 파일시스템 의존도가 높은 구조라면 그대로 옮기기 어렵습니다. 이런 경우는 웹 서비스는 Cloud Run 서비스로, 배치성 작업은 Cloud Run Jobs 같은 다른 실행 방식으로 나눠 보는 편이 낫습니다. 결국 마이그레이션에서 제일 중요한 건 클라우드 기능을 많이 아는 게 아니라, 현재 앱이 어떤 전제 위에서 돌아가는지 정확히 파악하는 것입니다.

2. Cloud Run 마이그레이션 전에 알아둘 개념 차이

온프레미스에서는 보통 서버를 만들고, 프로세스를 띄워두고, 로그 위치를 정하고, 방화벽을 열고, 필요하면 리버스 프록시를 붙입니다. 그런데 Cloud Run은 접근 방식이 꽤 다릅니다.

항목 온프레미스 앱 운영 Cloud Run 운영
실행 단위 서버/VM 위 프로세스 컨테이너
확장 방식 수동 증설 또는 별도 오토스케일링 요청량 기반 자동 확장
상태 저장 로컬 디스크 활용 빈번 영속 데이터는 외부 저장소 권장
배포 서버 접속 후 배포 또는 파이프라인 이미지 배포 중심
네트워크 공개 방화벽, 프록시, 로드밸런서 직접 구성 Ingress 정책과 IAM으로 제어

핵심은 두 가지입니다. 첫째, 애플리케이션은 지정된 포트에서 요청을 받아야 합니다. 둘째, 인스턴스의 영속 상태에 의존하면 안 됩니다. 로그는 stdout/stderr, 설정은 환경 변수, 민감한 값은 Secret Manager 같은 외부 비밀 저장소, 파일은 Cloud Storage 같은 외부 스토리지를 쓰는 방식이 잘 맞습니다.

여기서 하나 짚고 갈 부분이 있습니다. Cloud Run 컨테이너 파일시스템은 쓰기가 가능하긴 하지만 영구 디스크가 아니라 인메모리 기반 임시 저장소라서, 인스턴스가 내려가면 데이터가 유지되지 않습니다. 임시 파일 정도는 괜찮지만 업로드 원본이나 세션 파일을 계속 거기에 두는 구조는 금방 문제를 만들더라고요.

3. 마이그레이션 전에 꼭 하는 사전 점검

실전 들어가기 전에 저는 항상 아래 체크리스트부터 봅니다. 이 단계 건너뛰면 뒤에서 더 오래 걸립니다.

  1. 프로세스 시작 방식 확인
    앱이 포그라운드로 실행되는지 확인합니다. 백그라운드로 포크되면 컨테이너 환경에서 예상과 다르게 동작할 수 있습니다.
  2. 포트 바인딩 방식 확인
    앱이 환경 변수로 주입된 포트를 사용할 수 있는지 봅니다. Cloud Run은 컨테이너에 PORT 환경 변수를 주입합니다.
  3. 세션/업로드 저장 위치 확인
    세션 파일, 업로드 파일, 캐시가 로컬 디스크에 쌓이면 장애 포인트가 됩니다.
  4. 환경 변수 정리
    DB 접속 정보, API 키, 운영 모드 값을 코드나 파일에 박아뒀다면 외부 설정으로 분리합니다.
  5. 헬스체크와 기동 시간 점검
    Cloud Run에서는 시작 프로브와 라이브니스 프로브를 설정할 수 있으니, 앱 기동 시간이 긴 편이면 같이 검토합니다.
  6. 장기 실행 작업 분리
    웹 요청 처리와 오래 걸리는 배치를 분리하지 않으면 타임아웃 때문에 고생할 수 있습니다. Cloud Run 서비스의 요청 타임아웃은 기본 5분이고 최대 60분까지 설정할 수 있습니다.

혹시 지금 운영 중인 앱이 이런 상태이신가요? “서버 켜지면 systemd로 자동 실행되고, 로그는 /var/log 아래에 쌓이고, 업로드 파일은 /data에 저장한다.” 정말 흔한 구조입니다. 저도 많이 봤고 직접 운영도 했거든요. 그런데 이 구조를 그대로 온프레미스 클라우드 전환에 들이밀면 문제가 생기기 쉽습니다. 그래서 먼저 상태 의존성을 줄이는 게 중요합니다.

4. 실전 구현 1: 앱을 컨테이너로 감싸기

이제 실제 작업으로 들어가 보겠습니다. 예시는 Python Flask 기준이지만 Node.js, Go, Java도 원리는 같습니다. 중요한 건 프레임워크가 아니라 컨테이너 안에서 단일 프로세스로 뜨고, 지정 포트를 리스닝하는지입니다.

4-1. 애플리케이션 코드에서 포트 환경 변수 받기

import os
from flask import Flask

app = Flask(__name__)

@app.route("/")
def index():
    return {"status": "ok", "message": "hello from cloud run"}

if __name__ == "__main__":
    port = int(os.environ.get("PORT", "8080"))
    app.run(host="0.0.0.0", port=port)

여기서 중요한 포인트는 127.0.0.1이 아니라 0.0.0.0으로 바인딩해야 한다는 점입니다. Cloud Run은 컨테이너가 0.0.0.0에서 요청을 받기를 기대하거든요. 이거 한 번 놓치면 배포는 성공했는데 접속이 안 되는 전형적인 상황이 나옵니다. 저도 예전에 이걸로 로그만 한참 뒤졌습니다.

4-2. Dockerfile 작성

FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

ENV PORT=8080
CMD ["python", "app.py"]

프로덕션에서는 Gunicorn 같은 WSGI 서버를 두는 구성이 더 일반적입니다. 다만 여기서는 흐름을 설명하려고 단순 예시로 적었습니다. 실제 운영에서는 애플리케이션 특성에 맞는 프로세스 모델을 고르시면 됩니다.

4-3. 로컬에서 컨테이너 실행 확인

docker build -t my-onprem-app:local .
docker run --rm -p 8080:8080 my-onprem-app:local
curl http://localhost:8080/

로컬에서 먼저 확인하는 이유는 단순합니다. Cloud Run 문제인지, 내 컨테이너 문제인지 먼저 구분해야 하거든요. 이 단계에서 응답이 안 오면 클라우드로 올려도 똑같이 막힙니다.

5. Cloud Run 마이그레이션 배포 절차

컨테이너가 정상 동작하면 이제 이미지를 레지스트리에 올리고 서비스를 배포합니다. 보통은 CI/CD에서 처리하지만, 처음 구조 검증할 때는 CLI로 한 번 직접 해보는 게 좋습니다. 해보면 서비스 계정, 리전, 이미지 태그 전략이 한 번에 정리되더라고요.

PROJECT_ID="your-gcp-project"
REGION="asia-northeast3"
REPO="app-images"
SERVICE="my-onprem-app"
IMAGE="$REGION-docker.pkg.dev/$PROJECT_ID/$REPO/$SERVICE:manual-001"

gcloud auth login
gcloud config set project "$PROJECT_ID"
gcloud artifacts repositories create "$REPO" \
  --repository-format=docker \
  --location="$REGION"

gcloud auth configure-docker "$REGION-docker.pkg.dev"
docker build -t "$IMAGE" .
docker push "$IMAGE"

gcloud run deploy "$SERVICE" \
  --image "$IMAGE" \
  --region "$REGION" \
  --allow-unauthenticated

사내 서비스처럼 외부 공개가 필요 없다면 --allow-unauthenticated 대신 인증된 호출만 허용하는 구성을 검토하시면 됩니다. 이때는 Ingress 정책과 IAM을 같이 봐야 합니다. 인증을 막는 설정과 네트워크 진입 경로 설정은 역할이 다르니 묶어서 보는 게 좋습니다.

Cloud Run 도입 과정의 컨테이너 마이그레이션 배포 흐름 이미지

컨테이너 이미지가 Artifact Registry에 저장되고 Cloud Run 서비스로 배포되며 환경 변수와 시크릿이 주입되는 흐름을 설명하는 구성 이미지입니다.

5-1. 환경 변수와 시크릿 분리

운영하다 보면 제일 위험한 게 자격 증명을 이미지 안에 넣어버리는 겁니다. 이건 정말 피하셔야 합니다.

gcloud run deploy "$SERVICE" \
  --image "$IMAGE" \
  --region "$REGION" \
  --set-env-vars APP_ENV=prod,LOG_LEVEL=info \
  --set-secrets DB_PASSWORD=db-password:latest

설정값은 환경 변수로, 비밀번호나 토큰은 시크릿으로 나누는 습관을 들이면 운영이 훨씬 편해집니다. 나중에 키 교체할 때 체감이 확 옵니다. 이거 진짜 편하더라고요.

5-2. 데이터베이스 연결은 어떻게 할까

온프레미스 앱이 데이터베이스를 쓰고 있다면, 가장 먼저 볼 건 연결 방식입니다. 앱과 DB를 같은 서버에 붙여 놓았던 구조라면 분리가 필요합니다. 보통은 다음 두 방향 중 하나를 검토합니다.

  • 관리형 데이터베이스로 이전해 네트워크와 자격 증명을 정리한다
  • 기존 데이터베이스를 유지하되, 안전한 네트워크 경로와 연결 풀을 재설계한다

중요한 건 애플리케이션 인스턴스 수가 늘어나도 DB 연결 수가 급격히 폭증하지 않게 하는 것입니다. Cloud Run은 빠르게 확장될 수 있어서, 연결 풀 값을 아무 생각 없이 잡으면 DB가 먼저 버거워질 수 있습니다.

6. 운영 관점에서 꼭 챙겨야 할 설정

배포가 됐다고 끝은 아닙니다. 실제로는 여기서부터 운영 품질이 갈립니다.

6-1. 동시성 이해하기

Cloud Run은 한 인스턴스가 여러 요청을 동시에 처리할 수 있습니다. CPU 바운드 작업인지, I/O 바운드 작업인지에 따라 적정값이 달라집니다. 기본 동시성은 콘솔 배포 기준 80이고, CLI나 Terraform으로 새 서비스를 만들 때는 vCPU 수에 따라 계산되는 기본값이 적용될 수 있습니다. 그래서 그냥 숫자 하나만 외워두기보다, 실제 지연 시간과 CPU 사용률을 보고 조정하는 편이 더 안전합니다.

6-2. 타임아웃과 장기 작업 분리

웹 요청 하나가 오래 걸린다면, 그 작업을 직접 처리하기보다 큐나 배치로 넘기는 게 낫습니다. 예를 들어 대용량 리포트 생성, 비디오 인코딩, 대량 동기화 작업은 웹 요청 경로에서 분리하는 편이 안전합니다. 서비스 타임아웃은 기본 5분이고 최대 60분까지 늘릴 수 있지만, 길게 준다고 구조 문제가 사라지진 않더라고요.

6-3. 로그는 파일이 아니라 표준 출력으로

온프레미스에선 로그 파일을 tail로 보던 습관이 강하죠. 그런데 컨테이너 환경에서는 애플리케이션 로그를 stdout/stderr로 내보내는 방식이 관리가 훨씬 쉽습니다. 로그 포맷도 가능하면 JSON 같은 구조화 로그로 맞춰두면 검색이 편합니다.

6-4. 파일 업로드는 외부 스토리지로

앱이 업로드 파일을 로컬 경로에 저장하고 있다면 구조를 바꾸는 게 좋습니다. Cloud Run의 로컬 파일시스템은 영속 저장소가 아니라 임시 저장소에 가깝기 때문입니다. 이 부분은 컨테이너 마이그레이션에서 자주 놓치는 항목 중 하나입니다.

7. 실제로 많이 겪는 문제와 트러블슈팅

이 섹션은 정말 경험담 위주입니다. 문서만 읽으면 잘 안 보이는데, 실제로 해보면 여기서 많이 막힙니다.

  • 문제 1: 배포는 성공했는데 접속이 안 됨
    원인으로는 포트 바인딩 오류, 0.0.0.0 미사용, 애플리케이션 시작 실패가 많습니다. 로그에서 startup 관련 메시지를 먼저 확인합니다.
  • 문제 2: 특정 요청에서만 500 에러 발생
    로컬 파일 경로에 쓰기 시도하는 경우가 많습니다. 업로드, 임시 파일, 세션 저장 위치를 다시 확인합니다.
  • 문제 3: 예상보다 응답이 느림
    콜드 스타트, 외부 DB 연결 지연, 동시성 설정 부적합을 의심합니다.
  • 문제 4: 갑자기 DB 연결이 부족해짐
    오토스케일링과 연결 풀 값이 충돌할 수 있습니다. 인스턴스 수와 DB 허용 연결 수를 같이 봐야 합니다.
  • 문제 5: 배치 작업이 중간에 끊김
    HTTP 요청 처리 경로에 오래 걸리는 작업을 넣은 경우가 많습니다. 작업 큐나 Cloud Run Jobs 기반으로 재설계하는 편이 낫습니다.

제가 한 번은 기존 앱이 이미지 업로드 후 썸네일까지 같은 요청 안에서 만들고, 결과 파일도 로컬 디스크에 저장하는 구조를 본 적이 있습니다. 로컬 테스트에선 멀쩡했는데 클라우드에 올리니 간헐적으로 실패하더라고요. 처음엔 네트워크 문제인 줄 알았는데, 결국 저장소 구조가 원인이었습니다. 업로드 원본과 결과물을 외부 스토리지로 빼고 나서야 안정화됐습니다. 그때 진짜 숨이 좀 놓였죠.

8. 검증 방법: 전환 후 무엇을 확인해야 하나요?

배포가 끝난 뒤에는 기능만 보지 말고 운영 지표도 같이 봐야 합니다. 최소한 아래 항목은 확인하시는 걸 권장드립니다.

  1. 기능 검증
    핵심 API, 로그인, 업로드, 외부 연동을 실제 시나리오로 테스트합니다.
  2. 로그 검증
    오류 로그가 구조적으로 남는지, 요청 추적이 가능한지 확인합니다.
  3. 성능 검증
    트래픽이 몰릴 때 응답 시간이 급격히 늘지 않는지 봅니다.
  4. 확장 검증
    동시 요청 상황에서 인스턴스 확장이 의도대로 이뤄지는지 확인합니다.
  5. 운영 절차 검증
    재배포, 롤백, 설정 변경, 시크릿 교체가 얼마나 단순해졌는지 봅니다.
SERVICE_URL="https://your-service-url"

curl -i "$SERVICE_URL/"

for i in $(seq 1 20); do
  curl -s "$SERVICE_URL/" &
done
wait

초기 검증은 이렇게 단순하게 시작해도 충분합니다. 중요한 건 숫자 자체보다, 어떤 조건에서 앱이 흔들리는지 빨리 파악하는 것입니다. 운영은 결국 재현성과 관찰 가능성 싸움이거든요. 관련해서 이전 글의 컨테이너 이미지 최적화 내용이 있다면 여기와 함께 내부 링크로 연결해두는 것도 SEO와 체류 시간 측면에서 꽤 도움이 됩니다.

Cloud Run 마이그레이션 이후 검증을 위한 운영 대시보드 이미지

배포 이후 요청 성공률, 응답 시간, 로그 확인 과정을 한눈에 볼 수 있는 운영 검증용 대시보드 이미지입니다.

9. 결과적으로 무엇이 좋아졌나

모든 앱이 Cloud Run에 맞는 건 아닙니다. 이건 분명합니다. 하지만 웹 API, 내부 업무 서비스, 이벤트 기반 백엔드처럼 구조만 잘 맞추면 얻는 장점이 꽤 큽니다.

  • 배포 단위가 서버가 아니라 이미지가 되면서 재현성이 좋아집니다
  • 운영 환경 표준화가 쉬워집니다
  • 트래픽 변화 대응이 단순해집니다
  • 서버 패치와 런타임 관리 부담이 줄어듭니다
  • 개발팀과 인프라팀의 경계가 조금 더 명확해집니다
전환 전 전환 후
서버 접속 후 수동 점검 이미지 중심 배포와 설정 관리
로컬 로그 파일 추적 중앙화된 로그 확인
확장 시 서버 증설 고민 요청량 기반 확장 고려
앱과 인프라 설정이 강하게 결합 컨테이너 기준으로 역할 분리

물론 대가도 있습니다. 앱이 상태 비저장에 가깝게 움직이도록 구조를 손봐야 하고, 기존 운영 습관도 일부 바꿔야 합니다. 그런데 이 과정을 한 번 거치고 나면 이후 운영이 꽤 가벼워집니다. 저는 그게 가장 크게 느껴졌습니다.

온프레미스 클라우드 전환과 Cloud Run 도입 비교 인포그래픽

온프레미스 운영 방식과 Cloud Run 도입 이후의 배포, 확장, 로그 관리 차이를 요약한 비교 인포그래픽입니다.

10. 마무리: 성공적인 Cloud Run 도입을 위한 정리

Cloud Run 마이그레이션의 핵심은 기술 자체보다 애플리케이션 구조를 얼마나 잘 정리하느냐에 달려 있습니다. 서버를 옮기는 작업으로 보면 자꾸 꼬이고, 컨테이너가 요청을 안정적으로 처리하도록 재구성하는 작업으로 보면 길이 보입니다. 저도 처음엔 단순 배포 문제라고 생각했는데, 막상 해보니 설정 파일, 로그, 세션, 업로드, 배치 작업까지 전부 연결돼 있더라고요.

정리하면 이렇습니다.

  1. 앱이 PORT 환경 변수로 실행되게 바꿉니다.
  2. 상태와 파일 저장을 외부로 분리합니다.
  3. 시크릿과 환경 변수를 이미지에서 분리합니다.
  4. 동시성, 타임아웃, DB 연결 수를 함께 봅니다.
  5. 배포보다 검증 자동화를 먼저 습관화합니다.

혹시 지금 온프레미스 앱을 들고 계시고 어디서부터 손대야 할지 막막하셨다면, 위 순서대로만 체크해도 꽤 정리가 됩니다. 다음 글에서는 Cloud Run 앞단에 로드밸런서와 도메인, 그리고 내부 전용 서비스 노출 전략까지 이어서 다뤄보셔도 좋겠습니다. 이전 글이 있다면 여기서 자연스럽게 내부 링크를 걸어 주제 클러스터를 만드는 것도 추천드립니다.

11. 자주 묻는 질문

Q1. 기존 온프레미스 앱을 수정 없이 그대로 올릴 수 있나요?

대부분은 어렵습니다. 특히 로컬 파일 저장, 백그라운드 데몬 방식, 고정 포트 전제는 수정이 필요할 가능성이 큽니다.

Q2. Cloud Run 도입은 작은 팀에도 괜찮나요?

오히려 작은 팀일수록 운영 단순화 효과를 크게 느끼는 경우가 많습니다. 다만 앱 구조가 맞아야 합니다.

Q3. 모든 마이크로서비스에 적합한가요?

아닙니다. 요청 기반 처리와 컨테이너 실행 모델에 잘 맞는 서비스부터 시작하시는 게 안전합니다.

Q4. 마이그레이션 첫 단계는 무엇이 가장 중요할까요?

현재 앱의 상태 의존성과 파일 저장 습관을 파악하는 겁니다. 이거 놓치면 뒤에서 계속 발목을 잡습니다.

반응형