기존 방식:
서버에 Node.js 설치
서버에 npm install
서버에서 npm run build
서버 환경에 따라 문제 발생 가능
Docker 방식:
이미지에 실행 환경 포함
서버는 이미지를 실행
환경 차이 감소
환경 일관성
배포 안정성
롤백 편의성
의존성 관리
확장성
| 개념 | 의미 | 예시 |
|---|---|---|
| Image | 실행 환경을 담은 템플릿 | togethermall-api:20260707 |
| Container | 이미지를 실제로 실행한 프로세스 | 실행 중인 NestJS API 서버 |
| Dockerfile | 이미지를 만드는 설계도 | Node 설치, build, start 명령 |
| Registry | 이미지를 저장하는 저장소 | Docker Hub, ECR, GitHub Container Registry |
Dockerfile
↓ build
Docker Image
↓ run
Docker Container
EC2 접속
↓
git pull
↓
npm install
↓
npm run build
↓
npx prisma migrate deploy
↓
pm2 reload api
GitHub Actions에서 Docker Image 빌드
↓
Registry에 이미지 push
↓
EC2에서 새 이미지 pull
↓
기존 컨테이너 교체
↓
health check
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
CMD ["node", "dist/main.js"]
| 명령어 | 설명 |
|---|---|
FROM | 기반 이미지 선택 |
WORKDIR | 컨테이너 내부 작업 경로 |
COPY | 파일 복사 |
RUN | 이미지 빌드 중 실행할 명령 |
CMD | 컨테이너 시작 시 실행할 명령 |
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
COPY prisma ./prisma
RUN npm ci
COPY . .
RUN npx prisma generate
RUN npm run build
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY package*.json ./
COPY prisma ./prisma
RUN npm ci --omit=dev
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules/.prisma ./node_modules/.prisma
CMD ["node", "dist/main.js"]
.dockerignore는 Docker 이미지 빌드 시 제외할 파일을 정의합니다..gitignore처럼 불필요한 파일이 이미지에 들어가지 않게 막아줍니다.node_modules
dist
.git
.env
.env.*
coverage
.DS_Store
npm-debug.log
README.md
.env 같은 민감정보가 이미지에 들어가는 사고를 막을 수 있습니다.주의:
.env가 이미지에 포함되면
Docker Registry에 Secret이 올라가는 것과 같음
docker build -t togethermall-api:local .
docker run -d \
--name togethermall-api \
-p 3000:3000 \
--env-file .env.production \
togethermall-api:local
docker ps
docker logs togethermall-api
docker stop togethermall-api
docker rm togethermall-api
services:
api:
image: togethermall-api:local
container_name: togethermall-api
ports:
- "3000:3000"
env_file:
- .env.production
restart: always
depends_on:
- redis
redis:
image: redis:7-alpine
container_name: togethermall-redis
restart: always
ports:
- "6379:6379"
docker compose up -d
docker compose ps
docker compose logs -f api
docker compose down
services:
api:
image: togethermall-api:20260707
env_file:
- .env.production
services:
api:
image: togethermall-api:20260707
environment:
NODE_ENV: production
PORT: 3000
하면 안 되는 것:
Dockerfile에 ENV DATABASE_URL=...
이미지 안에 .env 복사
GitHub에 운영 .env 커밋
Registry에 Secret 포함 이미지 push
prisma generate, migration, DATABASE_URL 관리에 주의해야 합니다.COPY prisma ./prisma
RUN npx prisma generate
prisma generate를 실행해야 합니다.docker run --rm \
--env-file .env.production \
togethermall-api:20260707 \
npx prisma migrate deploy
또는 배포 스크립트에서:
docker compose run --rm api npx prisma migrate deploy
운영에서 금지:
npx prisma migrate reset
npx prisma db push --force-reset
운영에서 사용:
npx prisma migrate deploy
사용자
↓
Nginx :443
↓
localhost:3000
↓
Docker Container NestJS API
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
togethermall-api:20260705
togethermall-api:20260706
togethermall-api:20260707
togethermall-api:latest
새 이미지 20260707 배포
↓
장애 발생
↓
이전 이미지 20260706으로 컨테이너 재실행
↓
health check
services:
api:
image: togethermall-api:20260706
docker compose pull api
docker compose up -d api
#!/bin/bash
set -e
IMAGE_TAG=$1
if [ -z "$IMAGE_TAG" ]; then
echo "Usage: ./deploy.sh <image-tag>"
exit 1
fi
echo "Deploying togethermall-api:$IMAGE_TAG"
docker pull registry.example.com/togethermall-api:$IMAGE_TAG
docker compose down api
IMAGE_TAG=$IMAGE_TAG docker compose up -d api
docker compose ps
curl -f http://localhost:3000/health
echo "Deploy completed"
docker compose down이 순간 중단을 만들 수 있습니다.main 브랜치 push
↓
GitHub Actions 실행
↓
Docker image build
↓
Registry push
↓
EC2 SSH 접속
↓
docker pull
↓
docker compose up -d
↓
health check
주의할 것:
Registry 로그인 Secret 관리
운영 .env 서버에 안전하게 보관
DB migration 실행 시점 분리
이미지 태그 latest만 사용하지 않기
배포 실패 시 기존 컨테이너 유지 전략
latest만 쓰면 어떤 버전이 배포됐는지 추적하기 어렵습니다.togethermall-api:latest
latest만 있으면 현재 운영 중인 코드가 정확히 어떤 커밋인지 알기 어렵습니다.togethermall-api:20260707-1530
togethermall-api:git-a1b2c3d
togethermall-api:release-2026-07-07
docker logs 또는 docker compose logs로 확인할 수 있습니다.docker logs togethermall-api --tail 100 -f
docker compose logs -f api
컨테이너:
stdout/stderr로 로그 출력
EC2:
Docker logs 확인
AWS:
CloudWatch Logs로 중앙화 가능
확인할 것:
컨테이너 포트
호스트 포트 매핑
Nginx proxy_pass
보안 그룹
앱 PORT 환경변수
docker ps
docker inspect togethermall-api
curl -i http://localhost:3000/health
증상:
DATABASE_URL undefined
JWT_SECRET missing
S3_BUCKET undefined
docker exec -it togethermall-api printenv
증상:
Prisma Client did not initialize yet
Query Engine binary 문제
schema 변경 후 generate 누락
npx prisma generate가 실행되는지 확인해야 합니다.원인:
node_modules 전체 포함
devDependencies 포함
.git 포함
dist 중복 포함
불필요한 파일 COPY
.dockerignore와 multi-stage build로 줄일 수 있습니다.로컬과 서버 환경 차이로 자주 문제 발생
API 서버와 Worker를 분리 운영
Redis, PostgreSQL 등 여러 서비스 사용
배포 자동화를 안정적으로 만들고 싶음
이미지 태그 기반 롤백이 필요함
추후 ECS/Kubernetes 확장 가능성을 고려함
서버가 1대
서비스 구조가 단순
Docker 운영 경험이 부족
메모리 여유가 적음
배포 자동화가 아직 단순함
장애 대응 문서가 부족함
npm ci를 사용하는가?.dockerignore가 있는가?.env가 이미지에 포함되지 않는가?prisma generate가 실행되는가?docker ps로 컨테이너 상태를 확인할 수 있는가?docker logs로 API 로그를 확인할 수 있는가?NestJS + Prisma + PostgreSQL 백엔드를 Docker 기반으로 배포하고 싶어.
상황:
1. 현재는 EC2에서 git pull, npm install, npm run build, pm2 reload로 배포 중
2. DB는 AWS RDS PostgreSQL을 사용함
3. Prisma migration은 운영에서 migrate deploy를 사용해야 함
4. Redis Queue Worker도 같이 운영할 예정
5. Nginx가 api.example.com 요청을 백엔드로 proxy_pass함
6. GitHub Actions에서 Docker image를 빌드하고 Registry에 push하고 싶음
7. EC2에서는 이미지를 pull해서 docker compose로 실행하고 싶음
8. 문제가 생기면 이전 이미지 태그로 롤백하고 싶음
9. .env와 Secret은 이미지에 포함되면 안 됨
요청:
- 운영용 Dockerfile
- .dockerignore
- docker-compose.yml
- Prisma migration 실행 방식
- Nginx 연결 구조
- GitHub Actions 배포 흐름
- 이미지 태그 전략
- 롤백 절차
- 운영 체크리스트
를 실무 기준으로 정리해줘.
.env를 Docker 이미지에 넣으라고 하지 않는가?migrate reset이나 db push --force-reset을 운영에 권하지 않는가?.dockerignore를 고려하는가?latest만 쓰지 말라고 하는가?.dockerignore를 사용해 이미지 크기와 보안 위험을 줄이는 것이 좋습니다..env와 Secret은 이미지에 포함하면 안 되며, 컨테이너 실행 시 환경변수로 주입해야 합니다.prisma generate가 필요하고, 운영 DB migration은 배포 단계에서 migrate deploy로 별도 실행하는 것이 안전합니다.latest만 사용하지 말고 날짜/커밋 SHA/릴리즈 태그를 함께 관리하는 것이 좋습니다.