Remix 애플리케이션을 Docker와 GCP로 배포하기
1️⃣ 왜 Docker를 선택했을까요?
Driply 서비스는 Remix를 기반으로 개발되었고, 배포 방식을 고민하다가 결국 Docker를 활용하기로 했어요.
1.1. Docker가 매력적인 이유
- 이식성이 뛰어나요: 어떤 환경에서도 동일한 실행 환경을 보장할 수 있어요. 현재는 GCP의를 이용하지만, 추후 Fly.io 같은 서비스로 쉽게 이전할 수 있어요.
- 비용을 절감할 수 있어요: 초기 단계에서는 무료 배포 옵션을 최대한 활용하고 싶었어요. 뛰어난 이식성을 바탕으로 무료로 지원하는 플랫폼을 사용하고 싶었어요.
2️⃣ GCP Cloud Run을 선택한 이유
초기 서비스에서는 비용을 아끼면서도 확장성을 확보하는 것이 중요했어요. 그래서 Cloud Run을 선택했어요.
2.1. Cloud Run의 장점
- 사용한 만큼만 과금돼요: 서버리스로 설정할 수 있어, 트래픽이 많지 않은 초기 단계에서는 경제적이에요.
- 확장성이 좋아요: 필요할 때만 인스턴스 만들고, 실행하고 자동으로 확장할 수 있어요.
- Artifact Registry와 연동할 수 있어요: Docker 이미지를 효율적으로 관리할 수 있어요.
Cloud Run을 활용하면 Docker 이미지를 Artifact Registry에 저장한 후 쉽게 배포할 수 있어요.
3️⃣ Docker 이미지 빌드 & Artifact Registry 업로드
먼저, 로컬에서 Docker 이미지를 빌드하고 Artifact Registry에 업로드해 배포가 정상적으로 동작하는지 확인했어요.
3.1. 빌드 과정에서 배운 점
-
Docker Desktop이 필요했어요: 최신 Docker 빌드에서는 buildx가 필요하므로, CLI 설치 시 SDK 누락 문제가 발생할 수 있어요. Docker Desktop을 설치한다면 SDK가 포함되어 있어요.
-
Docker 이미지 빌드 & 확인하기: 이미지 빌드 역시 Docker Desktop을 이용해 손쉽게 확인할 수 있었어요.
docker build -t <name> <dir>
-
CDP Artifact Registry로 푸시하기: 빌드된 이미지를 Artifact Registry로 푸시하여 배포를 확인할 수 있어요. 우선적으로 cloud CLI를 이용해 Docker가 GCP Artifact Registry에 접근할 수 있도록 인증했고, 이후 다음 명령어를 통해 도커 이미지를 올릴 수 있었어요.
docker push <name>
-
Cloud Run으로 배포하기: Artifact Registry에 등록된 이미지를 바탕으로 배포할 수 있어요.
로컬에서 수동으로 배포가 잘 되었습니다! 이제 자동적 배포(CD)를 구축해볼거에요.
4️⃣ CI/CD: Github Actions를 활용한 자동 배포
처음엔 Cloud Build를 활용하려 했지만, 환경 변수 주입 문제가 있어서 Github Actions로 전환했어요.
4.1. 환경 변수 주입 문제
- Remix의 빌드 과정에서 Vite 환경 변수를 필요로 해요.
- Cloud Run은 동적 환경 변수 주입 방식이라, 빌드 시점에 값을 고정할 수 없어요.
- Cloud Build로 해결 방법을 찾지 못해, Github Actions에서 직접 환경 변수를 주입하는 방식으로 변경했어요.
4.2. Github Actions 설정
name: Deploy to Google Cloud Run
on:
push:
branches:
- main
env:
PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }}
GAR_LOCATION: asia-northeast3
SERVICE_NAME: driply
REGION: asia-northeast3
REPO_NAME: cloud-run-source-deploy
jobs:
build-and-deploy:
name: Build and Deploy
runs-on: ubuntu-latest
environment: production
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Authenticate with Google Cloud
uses: google-github-actions/auth@v2
with:
credentials_json: ${{ secrets.GCP_SA_KEY }}
- name: Configure Docker to use Google Artifact Registry
run: gcloud auth configure-docker $GAR_LOCATION-docker.pkg.dev
- name: Build and Push Docker Image
run: |
IMAGE_NAME=$GAR_LOCATION-docker.pkg.dev/$PROJECT_ID/$REPO_NAME/$SERVICE_NAME:latest
docker build \
--build-arg VITE_SUPABASE_URL=${{ secrets.VITE_SUPABASE_URL }} \
--build-arg VITE_SUPABASE_KEY=${{ secrets.VITE_SUPABASE_KEY }} \
--build-arg VITE_KAKAO_JS_KEY=${{ secrets.VITE_KAKAO_JS_KEY }} \
-t $IMAGE_NAME .
docker push $IMAGE_NAME
- name: Deploy to Cloud Run
run: |
gcloud run deploy $SERVICE_NAME \
--image $GAR_LOCATION-docker.pkg.dev/$PROJECT_ID/$REPO_NAME/$SERVICE_NAME:latest \
--platform managed \
--region $REGION \
--allow-unauthenticated
4.3. Github Actions에서 환경 변수 주입 방식
- GCP IAM Service Account를 활용해 보안성을 높였어요.
- Docker 빌드 시 -build-arg를 활용해 환경 변수를 직접 주입했어요.
- .env 파일 없이도 환경 변수를 안전하게 처리할 수 있어요.
5️⃣ Dockerfile 설정
# 1단계: 빌드 단계 (builder) FROM node:20 AS builder WORKDIR /app COPY package.json package-lock.json ./ RUN npm install --frozen-lockfile # Github Action에서 환경변수 주입 ARG VITE_SUPABASE_URL ARG VITE_SUPABASE_KEY ARG VITE_KAKAO_JS_KEY ENV VITE_SUPABASE_URL=${VITE_SUPABASE_URL} ENV VITE_SUPABASE_KEY=${VITE_SUPABASE_KEY} ENV VITE_KAKAO_JS_KEY=${VITE_KAKAO_JS_KEY} ENV NODE_ENV=production COPY . . RUN npm run build # 2단계: 런타임 단계 FROM node:20-slim WORKDIR /app COPY --from=builder /app/node_modules ./node_modules COPY --from=builder /app/build ./build COPY --from=builder /app/package.json ./ EXPOSE 8080 CMD ["npm", "run", "start"]
5.1. Dockerfile 주요 특징
- Github Actions에서 주입한 환경 변수를 빌드 시 적용했어요.
- .env 파일을 사용하지 않아 보안이 강화됐어요.
- 2단계 빌드를 활용하여 불필요한 파일을 제거하고 이미지 크기를 최적화했어요.
정리
Driply 서비스의 배포를 위해 Docker와 GCP Cloud Run을 활용했어요.
- Docker를 선택한 이유: 이식성, 비용 절감, 유연한 플랫폼 이전 가능해요.
- GCP Cloud Run을 선택한 이유: 서버리스로 비용 절감 및 확장성이 좋아요.
- Github Actions로 CI/CD 구축: Cloud Build의 환경 변수 주입 문제를 해결했어요.
- 보안성을 고려한 Dockerfile 설계: .env 파일 없이 환경 변수를 안전하게 관리했어요.
이제 배포 프로세스가 깔끔하게 정리됐고, 확장 가능한 구조를 갖췄어요. 앞으로 더 발전시키면서 최적화할 수 있을 것 같아요! 🚀