Command Palette

Search for a command to run...

devops

Next.js + Docker + Cloud Run 자동 배포 파이프라인 구축

GitHub에 푸시하면 자동으로 Docker 이미지 빌드 → GCR 푸시 → Cloud Run 배포되는 CI/CD 파이프라인을 구축한 경험을 공유합니다.


목표

Git에 코드를 푸시하면 5분 이내에 프로덕션에 반영되는 자동화 파이프라인을 구축합니다.

git push origin main
        ↓
GitHub Actions 트리거
        ↓
Docker 이미지 빌드
        ↓
GCR(Container Registry) 푸시
        ↓
Cloud Run 배포
        ↓
서비스 URL 확인

1. Next.js standalone 모드 설정

Next.js는 standalone 출력 모드를 제공합니다. 이 모드를 사용하면 node_modules를 포함하지 않고도 실행 가능한 최소 번들을 생성합니다.

// next.config.ts
const config = {
  output: "standalone",  // 핵심 설정
};

standalone의 장점:

  • Docker 이미지 크기 대폭 감소 (수백 MB → 수십 MB)
  • 빌드 캐시 효율 향상
  • Cold start 시간 단축

2. Dockerfile 멀티스테이지 빌드

효율적인 Docker 이미지를 위해 멀티스테이지 빌드를 사용합니다.

# ========== Base ==========
FROM node:20-alpine AS base
RUN apk add --no-cache libc6-compat
WORKDIR /app
 
# ========== Dependencies ==========
FROM base AS deps
COPY package.json package-lock.json* ./
COPY prisma ./prisma/
RUN npm ci
 
# ========== Builder ==========
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
 
# Prisma generate
RUN npx prisma generate
 
# Next.js 빌드
ENV NEXT_TELEMETRY_DISABLED=1
RUN npm run build
 
# ========== Runner ==========
FROM base AS runner
WORKDIR /app
 
ENV NODE_ENV=production
ENV PORT=8080
ENV HOSTNAME="0.0.0.0"
 
# 보안: non-root 사용자
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
 
# 필요한 파일만 복사
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
COPY --from=builder /app/src/generated/prisma ./src/generated/prisma
 
USER nextjs
 
EXPOSE 8080
 
CMD ["node", "server.js"]

각 스테이지 역할

스테이지역할결과
baseNode.js 베이스 이미지공통 기반
deps의존성 설치node_modules 캐시
builder빌드 수행.next 산출물
runner실행 환경최종 이미지

Prisma 주의사항

Prisma는 빌드 시점에 prisma generate로 클라이언트를 생성해야 합니다. 그리고 생성된 클라이언트를 runner 스테이지로 복사해야 런타임에 작동합니다.

# builder에서
RUN npx prisma generate
 
# runner에서
COPY --from=builder /app/src/generated/prisma ./src/generated/prisma

3. GitHub Actions 워크플로우

.github/workflows/deploy.yml 파일을 생성합니다.

name: Deploy to Cloud Run
 
on:
  push:
    branches:
      - main
      - develop
 
env:
  PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }}
  SERVICE_NAME: develop-blog
  REGION: asia-northeast3  # 서울 리전
 
jobs:
  deploy:
    runs-on: ubuntu-latest
 
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
 
      - name: Google Auth
        uses: google-github-actions/auth@v2
        with:
          credentials_json: ${{ secrets.GCP_SA_KEY }}
 
      - name: Set up Cloud SDK
        uses: google-github-actions/setup-gcloud@v2
 
      - name: Configure Docker
        run: gcloud auth configure-docker --quiet
 
      - name: Build Docker image
        run: |
          docker build -t gcr.io/$PROJECT_ID/$SERVICE_NAME:${{ github.sha }} .
          docker tag gcr.io/$PROJECT_ID/$SERVICE_NAME:${{ github.sha }} \
                     gcr.io/$PROJECT_ID/$SERVICE_NAME:latest
 
      - name: Push to Container Registry
        run: |
          docker push gcr.io/$PROJECT_ID/$SERVICE_NAME:${{ github.sha }}
          docker push gcr.io/$PROJECT_ID/$SERVICE_NAME:latest
 
      - name: Deploy to Cloud Run
        run: |
          gcloud run deploy $SERVICE_NAME \
            --image gcr.io/$PROJECT_ID/$SERVICE_NAME:${{ github.sha }} \
            --region $REGION \
            --platform managed \
            --allow-unauthenticated \
            --memory 512Mi \
            --cpu 1 \
            --min-instances 0 \
            --max-instances 3 \
            --set-env-vars "DATABASE_URL=${{ secrets.DATABASE_URL }}" \
            --set-env-vars "GEMINI_API_KEY=${{ secrets.GEMINI_API_KEY }}"

Cloud Run 설정 설명

옵션설명
--memory512Mi인스턴스당 메모리 (무료 티어: 최대 2GB)
--cpu1CPU 할당
--min-instances0트래픽 없을 때 0으로 스케일 다운 (비용 절감)
--max-instances3최대 인스턴스 수
--allow-unauthenticated-공개 접근 허용

4. GCP 서비스 계정 설정

GitHub Actions가 GCP에 접근하려면 서비스 계정이 필요합니다.

1) 서비스 계정 생성

# 서비스 계정 생성
gcloud iam service-accounts create github-actions \
    --display-name="GitHub Actions"
 
# 필요한 역할 부여
gcloud projects add-iam-policy-binding $PROJECT_ID \
    --member="serviceAccount:github-actions@$PROJECT_ID.iam.gserviceaccount.com" \
    --role="roles/run.admin"
 
gcloud projects add-iam-policy-binding $PROJECT_ID \
    --member="serviceAccount:github-actions@$PROJECT_ID.iam.gserviceaccount.com" \
    --role="roles/storage.admin"
 
gcloud projects add-iam-policy-binding $PROJECT_ID \
    --member="serviceAccount:github-actions@$PROJECT_ID.iam.gserviceaccount.com" \
    --role="roles/iam.serviceAccountUser"

2) 키 생성 및 GitHub Secrets 등록

# JSON 키 생성
gcloud iam service-accounts keys create key.json \
    --iam-account=github-actions@$PROJECT_ID.iam.gserviceaccount.com

생성된 key.json 내용을 GitHub 저장소의 Settings > Secrets > GCP_SA_KEY에 등록합니다.

5. 환경 변수 관리

프로덕션 환경의 민감한 정보는 GitHub Secrets에 저장하고, Cloud Run 배포 시 주입합니다.

--set-env-vars "DATABASE_URL=${{ secrets.DATABASE_URL }}" \
--set-env-vars "GEMINI_API_KEY=${{ secrets.GEMINI_API_KEY }}" \
--set-env-vars "GMAIL_USER=${{ secrets.GMAIL_USER }}" \
--set-env-vars "GMAIL_APP_PASSWORD=${{ secrets.GMAIL_APP_PASSWORD }}"

Secret Manager를 사용하면 더 안전하게 관리할 수 있지만, 사이드 프로젝트 수준에서는 GitHub Secrets로 충분합니다.

6. Cloud SQL 연결

Cloud Run에서 Cloud SQL에 연결할 때는 --add-cloudsql-instances 옵션을 사용합니다.

--add-cloudsql-instances=$PROJECT_ID:$REGION:toy-db

DATABASE_URL은 Unix 소켓 형식으로 설정합니다:

postgresql://user:password@localhost/dbname?host=/cloudsql/project:region:instance

결과

현재 이 블로그는 위 파이프라인으로 운영 중입니다:

  • 빌드 시간: 약 2분
  • 배포 시간: 약 1분
  • Cold start: 약 3초
  • 월 비용: 거의 0원 (트래픽이 적으면 인스턴스 자동 종료)

배포 로그 예시

🚀 Deployed to:
https://develop-blog-xxxxxxxxxx-du.a.run.app

트러블슈팅

1. Prisma 클라이언트 not found

Error: @prisma/client did not initialize yet

→ runner 스테이지에서 src/generated/prisma 복사 확인

2. PORT 환경변수

Cloud Run은 PORT=8080을 사용합니다. Next.js standalone은 기본 3000 포트를 사용하므로 환경변수로 오버라이드해야 합니다.

ENV PORT=8080
ENV HOSTNAME="0.0.0.0"

3. 이미지 크기 최적화

node:20-alpine 기반으로 이미지 크기를 최소화합니다. 최종 이미지는 약 150MB 정도입니다.

다음 단계

  • Preview 환경 자동 생성 (PR 별 임시 배포)
  • Canary 배포 전략
  • 모니터링 대시보드 (Cloud Monitoring)

작은 사이드 프로젝트라도 자동화된 배포 파이프라인이 있으면 개발 속도가 확연히 달라집니다.