Node.js 빌드 환경과 Nginx 실행 환경을 분리하여 최종 이미지 용량을 최소화합니다.
# ===== 1. Build Stage =====
# Node.js 환경에서 프로젝트 빌드
FROM node:lts AS builder
# pnpm 활성화
RUN corepack enable && corepack prepare pnpm@latest --activate
# 작업 디렉터리 설정
WORKDIR /app
# 의존성 정보 복사 및 다운로드
COPY pnpm-lock.yaml ./
RUN pnpm fetch
# 프로젝트 복사 및 의존성 설치
COPY . .
RUN pnpm install --offline
# 프로젝트 빌드
RUN pnpm --prefix packages/admin build
RUN pnpm --prefix packages/user build
# ===== 2. Runtime Stage =====
# Nginx만 포함된 경량 이미지 사용
FROM nginx:stable-alpine
# Nginx 설정 복사
COPY --from=builder /app/nginx.conf /etc/nginx/nginx.conf
# 빌드 결과물 복사
COPY --from=builder /app/packages/admin/dist /usr/share/nginx/html/admin
COPY --from=builder /app/packages/user/dist /usr/share/nginx/html/user
4. .gitlab-ci.yml 작성
① Stage 1: Kaniko를 이용한 이미지 빌드 & Push
Docker Daemon이나 privileged 권한 없이 이미지 빌드가 가능합니다.
# 실행할 Stage 순서
stages:
- build_push
- deploy
# 이미지 빌드 및 Registry Push
build_push:
stage: build_push
# Docker Daemon 없이 이미지를 빌드하는 Kaniko 사용
image:
name: gcr.io/kaniko-project/executor:debug
entrypoint: [""]
# main 브랜치 Push 시에만 실행
rules:
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "main"'
script:
# Docker Registry 인증 정보 생성
- mkdir -p /kaniko/.docker
- AUTH="$(printf '%s:%s' "$REGISTRY_USER" "$REGISTRY_PASSWORD" | base64 | tr -d '\n')"
- printf '{"auths":{"https://index.docker.io/v1/":{"auth":"%s"}}}' "$AUTH" > /kaniko/.docker/config.json
# Docker 이미지 빌드 및 Push
- >
/kaniko/executor
--context "$CI_PROJECT_DIR"
--dockerfile "$CI_PROJECT_DIR/Dockerfile"
--destination "$IMAGE_NAME"
② Stage 2: SSH 접속을 통한 Docker Compose 배포
서버의 compose.sh 래퍼 스크립트를 호출하여 컨테이너를 안전하게 교체합니다.
# 배포 Stage
deploy:
stage: deploy
# SSH 접속을 위한 Alpine 이미지 사용
image: alpine:3.20
# main 브랜치 Push 시에만 실행
rules:
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "main"'
before_script:
# SSH 클라이언트 설치
- apk add --no-cache openssh-client
# SSH 디렉터리 생성
- mkdir -p ~/.ssh && chmod 700 ~/.ssh
# Private Key 등록 (파일/문자열 모두 지원)
- |
if [ -f "$SSH_PRIVATE_KEY" ]; then
cp "$SSH_PRIVATE_KEY" ~/.ssh/id_ed25519
else
printf '%s\n' "$SSH_PRIVATE_KEY" > ~/.ssh/id_ed25519
fi
# Private Key 권한 설정
- chmod 600 ~/.ssh/id_ed25519
# Known Hosts 등록
- printf '%s\n' "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts
- chmod 644 ~/.ssh/known_hosts
script:
# 배포 서버에서 Docker Compose 실행
- >
ssh -p "$DEPLOY_PORT" "$DEPLOY_USER@$DEPLOY_HOST"
"cd \"$DEPLOY_PATH\" &&
./compose.sh --env-file \"$DEPLOY_ENV_FILE\" up -d --pull always --force-recreate --no-deps frontend"
💡 --pull always 옵션이 중요한 이유 latest 태그를 사용할 때 배포 서버가 캐시된 예전 로컬 이미지를 바라보는 문제를 방지합니다.