Zeabur에서 Google Cloud Run으로: Dockerfile 없이 이전, 실제로 배포해 본 절차

Zeabur에서 GitHub로 바로 배포하던 Dockerfile 없는 백엔드는 소스 배포로 Cloud Run에 옮길 수 있습니다. 실제로 한 번 배포해 보고, Secret 권한 오류와 해결 방법까지 정리했습니다.

핵심 요약

  • Dockerfile 필요 없음: --source로 배포하면 buildpacks가 언어를 자동 감지한다.
  • 키는 Secret Manager에 두고 --set-secrets로 마운트한다. 서비스 계정에 Secret Manager Secret Accessor 역할이 필요하다.
  • Cloud Run의 파일 시스템은 임시다. 업로드 파일은 Cloud Storage나 R2에 저장한다.
  • 이전 후 챗봇의 Webhook URL(예: LINE Developers)을 바꿔야 한다.
이 글은 어떻게 검증했나요? (직접 테스트 3개, 공식 문서 기준 3개)
직접 테스트한 내용
  • Dockerfile 없는 Node 서비스를 소스 배포로 asia-east1에 배포, URL 정상 응답 (테스트 후 삭제)
  • --set-env-vars로 환경 변수, --set-secrets로 Secret Manager 값을 설정하고 앱에서 읽히는 것을 확인
  • 서비스 계정에 Secret 권한이 없을 때의 오류와, 권한 부여 후 재배포 성공
공식 문서를 바탕으로 작성 (직접 실행하지 않음)
  • 완전히 새 GCP 프로젝트에서 켜야 할 API와 부여할 역할: 기존 프로젝트에서 테스트했으므로 이 부분은 Google 공식 문서를 따랐습니다
  • LINE Developers 콘솔 조작: 저희가 LINE Bot을 운영해 온 경험에 따른 것이며, 이번 테스트에서 다시 해 보지는 않았습니다
  • 커스텀 도메인 연결: 테스트하지 않았으니 Google 공식 문서를 따르세요
목차 (8개 섹션)
  1. 이런 경우에 맞습니다
  2. 0단계: 새 프로젝트 준비 (공식 문서 기준)
  3. 1단계: 앱이 PORT를 리슨하는지 확인
  4. 2단계: 배포
  5. 3단계: 키는 환경 변수가 아니라 Secret Manager에
  6. 4단계: 데이터베이스
  7. 5단계: LINE 봇의 Webhook URL 바꾸기
  8. 6단계: 커스텀 도메인 연결

이런 경우에 맞습니다

  • Zeabur에서 GitHub로 배포한 백엔드: Node.js·Python·Go API, LINE·Discord 봇
  • 트래픽이 적어서, 아무도 쓰지 않을 때는 요금이 나가지 않았으면 할 때 (Cloud Run은 0까지 축소됩니다)
  • 가까운 리전을 원할 때: 한국 사용자가 많다면 asia-northeast3(서울). 아래 예시는 테스트에 쓴 asia-east1(대만) 기준입니다

맞지 않는 경우: 영구 디스크가 필요한 서비스(업로드 파일을 로컬 폴더에 저장하는 경우 등). Cloud Run의 파일 시스템은 임시라서 재배포하거나 0으로 축소되면 써 둔 파일이 사라집니다. 이런 서비스는 파일을 Cloud Storage나 R2에 저장하도록 바꾸거나 VPS를 쓰세요(n8n 편 참고).

0단계: 새 프로젝트 준비 (공식 문서 기준)

  1. gcloud CLI를 설치하고 gcloud init으로 로그인한 뒤 프로젝트를 고릅니다. 프로젝트에는 결제 계정이 연결되어 있어야 합니다
  2. API를 켭니다.
gcloud services enable run.googleapis.com cloudbuild.googleapis.com
  1. 소스에서 배포할 때 Cloud Build는 기본적으로 Compute Engine 기본 서비스 계정을 씁니다. 공식 문서는 이 계정에 Cloud Run Builder 역할을 부여하라고 안내합니다.
gcloud projects add-iam-policy-binding 프로젝트ID \
  --member=serviceAccount:프로젝트번호[email protected] \
  --role=roles/run.builder

문서에 따르면 이 권한이 적용되기까지 몇 분 걸립니다.

지금 어떤 프로젝트를 쓰고 있는지 꼭 확인하세요. 모든 gcloud 명령어에 --project=프로젝트ID를 붙이거나, 먼저 gcloud config get-value project로 확인합니다. 저희 컴퓨터에도 프로젝트가 여러 개 있어서, 기본 프로젝트가 배포하려는 곳이 아닌 경우가 많습니다.

1단계: 앱이 PORT를 리슨하는지 확인

Zeabur와 Cloud Run 모두 어느 포트를 리슨할지 PORT 환경 변수로 앱에 알려 줍니다. Zeabur 문서대로 이미 $PORT를 쓰고 있다면 바꿀 것이 없습니다. 3000처럼 하드코딩되어 있다면 다음과 같이 바꿉니다.

app.listen(process.env.PORT || 3000);

2단계: 배포

프로젝트 루트에서 실행합니다(Dockerfile 불필요, Google buildpacks가 언어를 자동 감지).

gcloud run deploy my-api --source . --region asia-east1 \
  --allow-unauthenticated \
  --set-env-vars "NODE_ENV=production" \
  --project=프로젝트ID

끝나면 Service URL: https://my-api-xxxx.asia-east1.run.app이 표시됩니다. 테스트에서는 Dockerfile 없는 Node 서비스가 첫 배포에서 바로 응답했습니다.

프로젝트 루트에 Dockerfile이 있으면 Cloud Run은 그것을 우선 사용합니다.

3단계: 키는 환경 변수가 아니라 Secret Manager에

--set-env-vars 값은 서비스 설정을 볼 수 있는 사람이라면 누구나 볼 수 있습니다. 데이터베이스 비밀번호와 API 키는 Secret Manager에 넣으세요.

# Secret 만들기 (printf는 끝에 줄바꿈을 붙이지 않음)
printf '%s' 'postgresql://...' | gcloud secrets create database-url --data-file=- --project=프로젝트ID

# 서비스에 마운트. 앱에서는 그대로 process.env.DATABASE_URL로 읽음
gcloud run services update my-api --region asia-east1 \
  --set-secrets "DATABASE_URL=database-url:latest" --project=프로젝트ID

테스트에서 만난 오류

서비스 계정에 Secret을 읽을 권한이 없으면 배포가 실패합니다.

ERROR: (gcloud.run.deploy) ... Permission denied on secret: projects/.../secrets/database-url/versions/latest
for Revision service account [email protected]. The service account used must be granted
the 'Secret Manager Secret Accessor' role (roles/secretmanager.secretAccessor) at the secret, project or higher level.

해결 방법은 그 Secret 하나에만 권한을 주고(가장 좁은 범위) 다시 배포하는 것입니다.

gcloud secrets add-iam-policy-binding database-url \
  --member="serviceAccount:오류 메시지에 나온 서비스 계정" \
  --role="roles/secretmanager.secretAccessor" --project=프로젝트ID

테스트에서는 권한을 준 뒤 다시 배포하자 바로 성공했습니다.

4단계: 데이터베이스

Cloud Run에는 데이터베이스가 없습니다. PostgreSQL은 Neon, Supabase 같은 관리형 서비스나 Google Cloud SQL로 옮길 수 있습니다. 방법은 데이터베이스 편을 참고하세요.

5단계: LINE 봇의 Webhook URL 바꾸기

  1. LINE Developers 콘솔에서 채널을 고릅니다
  2. Messaging API 탭의 Webhook URL을 새 Cloud Run 주소(앱 경로 포함, 예: /webhook)로 바꿉니다
  3. Verify를 눌러 성공하는지 확인합니다

Channel Secret과 Access Token도 이번에 함께 교체하기를 권합니다. 보안 편을 참고하세요.

6단계: 커스텀 도메인 연결

Cloud Run에 커스텀 도메인을 연결하는 방법은 여러 가지(Cloud Run 도메인 매핑, 부하 분산기, Firebase Hosting)이고 지원 리전과 비용이 다릅니다. Google 공식 문서를 보고 고르세요. 이 단계는 이번에 테스트하지 않았습니다. DNS 전환 시 주의점은 도메인과 DNS 편을 참고하세요.

자주 묻는 질문

Zeabur에서 Dockerfile 없이 돌리던 프로젝트도 Cloud Run에 배포할 수 있나요?

네. 소스 배포(--source)는 Dockerfile이 없으면 Google Cloud buildpacks가 언어를 감지해 빌드합니다. Node.js 서비스로 테스트했더니 첫 배포에서 바로 성공했습니다.

Cloud Run에서 Permission denied on secret이 나오면 어떻게 하나요?

서비스가 쓰는 서비스 계정에 해당 Secret을 읽을 권한이 없는 것입니다. roles/secretmanager.secretAccessor 역할을(해당 Secret에만 한정해도 됨) 부여하고 다시 배포하세요.