TIL - 20260707

juni·2026년 7월 7일

TIL

목록 보기
396/468

0707 백엔드 실무 심화 (14/N): Docker 기반 배포와 컨테이너 운영 기초


✅ 1. Docker란 무엇인가?

  • Docker는 애플리케이션 실행 환경을 컨테이너 단위로 패키징하고 실행할 수 있게 해주는 도구입니다.
  • 쉽게 말하면, Node.js 버전, 패키지, 환경설정, 실행 명령어를 하나의 이미지로 묶어서 어디서든 같은 방식으로 실행하게 만드는 기술입니다.
  • 로컬에서는 잘 되는데 서버에서는 안 되는 문제를 줄이는 데 큰 도움이 됩니다.
기존 방식:
서버에 Node.js 설치
서버에 npm install
서버에서 npm run build
서버 환경에 따라 문제 발생 가능

Docker 방식:
이미지에 실행 환경 포함
서버는 이미지를 실행
환경 차이 감소

➕ 1-1. Docker가 필요한 이유

  • 환경 일관성

    • 로컬, 개발 서버, 운영 서버에서 같은 환경으로 실행할 수 있습니다.
  • 배포 안정성

    • 서버에서 직접 빌드하지 않고, 검증된 이미지를 실행할 수 있습니다.
  • 롤백 편의성

    • 이전 이미지 태그로 되돌리면 빠르게 롤백할 수 있습니다.
  • 의존성 관리

    • Node.js 버전, OS 패키지, 실행 명령어를 코드로 관리할 수 있습니다.
  • 확장성

    • 나중에 ECS, Kubernetes 같은 컨테이너 오케스트레이션으로 확장하기 쉽습니다.

✅ 2. Docker 이미지와 컨테이너

  • Docker를 이해하려면 이미지와 컨테이너를 구분해야 합니다.
개념의미예시
Image실행 환경을 담은 템플릿togethermall-api:20260707
Container이미지를 실제로 실행한 프로세스실행 중인 NestJS API 서버
Dockerfile이미지를 만드는 설계도Node 설치, build, start 명령
Registry이미지를 저장하는 저장소Docker Hub, ECR, GitHub Container Registry

➕ 2-1. 이미지와 컨테이너 관계

Dockerfile
  ↓ build
Docker Image
  ↓ run
Docker Container
  • 이미지는 실행 파일 묶음이고, 컨테이너는 그 이미지를 실행한 실제 인스턴스입니다.
  • 같은 이미지로 여러 컨테이너를 실행할 수 있습니다.

✅ 3. Docker를 쓰기 전과 후의 배포 차이

➕ 3-1. 기존 PM2 직접 배포

EC2 접속
  ↓
git pull
  ↓
npm install
  ↓
npm run build
  ↓
npx prisma migrate deploy
  ↓
pm2 reload api
  • 구조가 단순하고 초기에 이해하기 쉽습니다.
  • 하지만 서버 환경에 직접 의존합니다.
  • 서버마다 Node.js 버전, 패키지 상태, 빌드 결과가 달라질 수 있습니다.

➕ 3-2. Docker 기반 배포

GitHub Actions에서 Docker Image 빌드
  ↓
Registry에 이미지 push
  ↓
EC2에서 새 이미지 pull
  ↓
기존 컨테이너 교체
  ↓
health check
  • 빌드 환경과 실행 환경을 이미지로 고정할 수 있습니다.
  • 운영 서버는 이미지를 받아 실행하는 역할에 집중합니다.
  • 이전 이미지 태그로 롤백하기가 쉬워집니다.

✅ 4. Dockerfile 기본 구조

  • Dockerfile은 이미지를 어떻게 만들지 정의하는 파일입니다.
  • NestJS 백엔드 프로젝트에서는 보통 Node.js 이미지를 기반으로 합니다.

➕ 4-1. 기본 Dockerfile 예시

FROM node:20-alpine

WORKDIR /app

COPY package*.json ./

RUN npm ci

COPY . .

RUN npm run build

CMD ["node", "dist/main.js"]

➕ 4-2. 각 줄 설명

명령어설명
FROM기반 이미지 선택
WORKDIR컨테이너 내부 작업 경로
COPY파일 복사
RUN이미지 빌드 중 실행할 명령
CMD컨테이너 시작 시 실행할 명령
  • 이 구조는 이해하기 쉽지만 운영용으로는 이미지 크기와 보안 측면에서 더 개선할 수 있습니다.

✅ 5. Multi-stage Build

  • Multi-stage Build는 빌드 단계와 실행 단계를 분리해 최종 이미지 크기를 줄이는 방식입니다.
  • 운영 컨테이너에는 소스 전체와 개발 의존성을 넣지 않는 것이 좋습니다.

➕ 5-1. NestJS Multi-stage Dockerfile 예시

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"]

➕ 5-2. 장점

  • 최종 이미지 크기를 줄일 수 있습니다.
  • 빌드에 필요한 파일과 실행에 필요한 파일을 분리할 수 있습니다.
  • 운영 컨테이너에 불필요한 개발 도구가 들어가는 것을 줄일 수 있습니다.

✅ 6. .dockerignore

  • .dockerignore는 Docker 이미지 빌드 시 제외할 파일을 정의합니다.
  • .gitignore처럼 불필요한 파일이 이미지에 들어가지 않게 막아줍니다.

➕ 6-1. 예시

node_modules
dist
.git
.env
.env.*
coverage
.DS_Store
npm-debug.log
README.md

➕ 6-2. 중요한 이유

  • 이미지 빌드 속도가 빨라집니다.
  • 이미지 크기를 줄일 수 있습니다.
  • .env 같은 민감정보가 이미지에 들어가는 사고를 막을 수 있습니다.
주의:
.env가 이미지에 포함되면
Docker Registry에 Secret이 올라가는 것과 같음
  • 운영 Secret은 이미지에 넣지 말고 컨테이너 실행 시 환경변수로 주입해야 합니다.

✅ 7. Docker 이미지 빌드와 실행

➕ 7-1. 이미지 빌드

docker build -t togethermall-api:local .

➕ 7-2. 컨테이너 실행

docker run -d \
  --name togethermall-api \
  -p 3000:3000 \
  --env-file .env.production \
  togethermall-api:local

➕ 7-3. 컨테이너 확인

docker ps
docker logs togethermall-api

➕ 7-4. 컨테이너 중지/삭제

docker stop togethermall-api
docker rm togethermall-api
  • Docker를 사용하면 컨테이너 단위로 실행, 중지, 교체할 수 있습니다.
  • 운영에서는 컨테이너 이름, 포트, 환경변수, 네트워크를 명확히 관리해야 합니다.

✅ 8. Docker Compose란 무엇인가?

  • Docker Compose는 여러 컨테이너를 하나의 설정 파일로 실행할 수 있게 해주는 도구입니다.
  • 로컬 개발 환경에서 API 서버, PostgreSQL, Redis를 함께 띄울 때 유용합니다.
  • 작은 운영 서버에서도 API + Redis + Nginx 구조를 관리하는 데 사용할 수 있습니다.

➕ 8-1. docker-compose.yml 예시

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"

➕ 8-2. 실행 명령어

docker compose up -d
docker compose ps
docker compose logs -f api

➕ 8-3. 종료 명령어

docker compose down
  • Compose는 여러 컨테이너 실행 구조를 파일로 남길 수 있어 운영 문서화에도 도움이 됩니다.

✅ 9. 환경변수 관리

  • Docker 이미지에는 Secret을 넣으면 안 됩니다.
  • 환경변수는 컨테이너 실행 시 주입해야 합니다.

➕ 9-1. env_file 방식

services:
  api:
    image: togethermall-api:20260707
    env_file:
      - .env.production

➕ 9-2. environment 방식

services:
  api:
    image: togethermall-api:20260707
    environment:
      NODE_ENV: production
      PORT: 3000

➕ 9-3. 주의점

하면 안 되는 것:
Dockerfile에 ENV DATABASE_URL=...
이미지 안에 .env 복사
GitHub에 운영 .env 커밋
Registry에 Secret 포함 이미지 push
  • 이미지와 Secret은 분리해야 합니다.
  • 운영에서는 AWS Secrets Manager, SSM Parameter Store, GitHub Actions Secrets 등을 사용할 수 있습니다.

✅ 10. Prisma와 Docker

  • Prisma를 Docker에서 사용할 때는 prisma generate, migration, DATABASE_URL 관리에 주의해야 합니다.

➕ 10-1. 이미지 빌드 중 Prisma Client 생성

COPY prisma ./prisma
RUN npx prisma generate
  • Prisma Client는 schema.prisma를 기준으로 생성됩니다.
  • Docker 이미지 빌드 과정에서 prisma generate를 실행해야 합니다.

➕ 10-2. Migration은 언제 실행할까?

  • 운영 DB migration은 이미지 빌드 중에 하면 안 됩니다.
  • 이미지 빌드는 DB 연결 없이도 가능해야 합니다.
  • 운영 migration은 배포 과정에서 별도 명령으로 실행하는 것이 좋습니다.
docker run --rm \
  --env-file .env.production \
  togethermall-api:20260707 \
  npx prisma migrate deploy

또는 배포 스크립트에서:

docker compose run --rm api npx prisma migrate deploy

➕ 10-3. 주의점

운영에서 금지:
npx prisma migrate reset
npx prisma db push --force-reset

운영에서 사용:
npx prisma migrate deploy
  • Docker를 써도 DB migration 위험은 그대로입니다.
  • 오히려 배포 자동화에 들어가면 더 빠르게 사고가 날 수 있으므로 명령어를 명확히 분리해야 합니다.

✅ 11. Nginx와 Docker

  • EC2에서 Docker로 NestJS API를 실행하더라도, 외부 요청은 Nginx가 받아서 컨테이너로 전달하는 구조를 사용할 수 있습니다.

➕ 11-1. 구조

사용자
  ↓
Nginx :443
  ↓
localhost:3000
  ↓
Docker Container NestJS API

➕ 11-2. Nginx 설정 예시

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;
    }
}
  • 컨테이너는 내부적으로 3000번 포트에서 실행하고, Nginx가 외부 80/443 요청을 프록시합니다.
  • SSL 인증서, 도메인, reverse proxy는 Nginx에서 관리할 수 있습니다.

✅ 12. Docker 기반 롤백

  • Docker의 큰 장점 중 하나는 이미지 태그 기반 롤백이 쉽다는 점입니다.

➕ 12-1. 이미지 태그 예시

togethermall-api:20260705
togethermall-api:20260706
togethermall-api:20260707
togethermall-api:latest

➕ 12-2. 롤백 흐름

새 이미지 20260707 배포
  ↓
장애 발생
  ↓
이전 이미지 20260706으로 컨테이너 재실행
  ↓
health check

➕ 12-3. Compose 롤백 예시

services:
  api:
    image: togethermall-api:20260706
docker compose pull api
docker compose up -d api
  • 코드 롤백보다 이미지 태그 롤백이 더 명확합니다.
  • 단, DB migration이 이미 적용되었다면 이미지 롤백만으로 해결되지 않을 수 있습니다.

✅ 13. Docker 배포 스크립트 예시

  • EC2에서 Docker 이미지를 받아 실행하는 배포 스크립트를 만들 수 있습니다.
#!/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이 순간 중단을 만들 수 있습니다.
  • 무중단에 가깝게 하려면 Blue-Green, reverse proxy 전환, rolling 방식 등을 고려해야 합니다.
  • 초기에는 단순 스크립트로 시작하되, health check와 롤백 기준은 꼭 넣는 것이 좋습니다.

✅ 14. GitHub Actions + Docker 배포 흐름

  • CI/CD에서는 GitHub Actions에서 이미지를 빌드하고 Registry에 push한 뒤, EC2에서 pull해서 실행할 수 있습니다.

➕ 14-1. 전체 흐름

main 브랜치 push
  ↓
GitHub Actions 실행
  ↓
Docker image build
  ↓
Registry push
  ↓
EC2 SSH 접속
  ↓
docker pull
  ↓
docker compose up -d
  ↓
health check

➕ 14-2. 장점

  • 서버에서 직접 빌드하지 않아도 됩니다.
  • 빌드 결과물이 이미지로 고정됩니다.
  • 배포한 이미지 태그를 기록하기 쉽습니다.
  • 이전 이미지로 롤백하기 쉽습니다.

➕ 14-3. 주의점

주의할 것:
Registry 로그인 Secret 관리
운영 .env 서버에 안전하게 보관
DB migration 실행 시점 분리
이미지 태그 latest만 사용하지 않기
배포 실패 시 기존 컨테이너 유지 전략
  • latest만 쓰면 어떤 버전이 배포됐는지 추적하기 어렵습니다.
  • 커밋 SHA나 날짜 기반 태그를 함께 사용하는 것이 좋습니다.

✅ 15. 이미지 태그 전략

  • 이미지 태그는 배포 이력과 롤백에 중요합니다.

➕ 15-1. 좋지 않은 방식

togethermall-api:latest
  • latest만 있으면 현재 운영 중인 코드가 정확히 어떤 커밋인지 알기 어렵습니다.

➕ 15-2. 좋은 방식

togethermall-api:20260707-1530
togethermall-api:git-a1b2c3d
togethermall-api:release-2026-07-07
  • 날짜, 시간, 커밋 SHA, 릴리즈 번호를 태그에 포함하면 추적이 쉬워집니다.
  • 운영 배포 기록에 이미지 태그를 남기는 것이 좋습니다.

✅ 16. Docker 로그 확인

  • Docker 컨테이너 로그는 docker logs 또는 docker compose logs로 확인할 수 있습니다.
docker logs togethermall-api --tail 100 -f
docker compose logs -f api

➕ 16-1. 로그 운영 기준

  • 컨테이너 내부 파일에만 로그를 쌓기보다 stdout/stderr로 출력하는 것이 좋습니다.
  • Docker와 CloudWatch Agent가 로그를 수집할 수 있게 구성할 수 있습니다.
  • 로그에 민감정보가 남지 않도록 해야 합니다.
컨테이너:
stdout/stderr로 로그 출력

EC2:
Docker logs 확인

AWS:
CloudWatch Logs로 중앙화 가능

✅ 17. Docker 운영에서 자주 발생하는 문제

➕ 17-1. 컨테이너는 떴는데 API 접속 안 됨

확인할 것:
컨테이너 포트
호스트 포트 매핑
Nginx proxy_pass
보안 그룹
앱 PORT 환경변수
docker ps
docker inspect togethermall-api
curl -i http://localhost:3000/health

➕ 17-2. 환경변수 누락

증상:
DATABASE_URL undefined
JWT_SECRET missing
S3_BUCKET undefined
docker exec -it togethermall-api printenv
  • 운영 Secret 확인 시 터미널 기록이나 로그에 노출되지 않도록 주의해야 합니다.

➕ 17-3. Prisma Client 오류

증상:
Prisma Client did not initialize yet
Query Engine binary 문제
schema 변경 후 generate 누락
  • Dockerfile에서 npx prisma generate가 실행되는지 확인해야 합니다.
  • Alpine 기반 이미지와 Prisma 바이너리 호환 이슈도 확인해야 할 수 있습니다.

➕ 17-4. 이미지 용량 과다

원인:
node_modules 전체 포함
devDependencies 포함
.git 포함
dist 중복 포함
불필요한 파일 COPY
  • .dockerignore와 multi-stage build로 줄일 수 있습니다.

✅ 18. Docker 도입 시점 판단

  • Docker가 무조건 정답은 아닙니다.
  • 1인 개발자나 작은 서비스에서는 직접 PM2 배포가 더 단순할 수 있습니다.
  • 하지만 서버 환경 일관성, 롤백, CI/CD, Redis/Worker 운영이 복잡해지면 Docker가 유리해집니다.

➕ 18-1. Docker 도입을 고려할 상황

로컬과 서버 환경 차이로 자주 문제 발생
API 서버와 Worker를 분리 운영
Redis, PostgreSQL 등 여러 서비스 사용
배포 자동화를 안정적으로 만들고 싶음
이미지 태그 기반 롤백이 필요함
추후 ECS/Kubernetes 확장 가능성을 고려함

➕ 18-2. 아직 PM2 직접 배포가 나을 수 있는 상황

서버가 1대
서비스 구조가 단순
Docker 운영 경험이 부족
메모리 여유가 적음
배포 자동화가 아직 단순함
장애 대응 문서가 부족함
  • Docker를 도입하면 운영 복잡도도 늘어납니다.
  • 지금 필요한 문제를 해결하는 방향으로 도입해야 합니다.

✅ 19. 실무 체크리스트

➕ 19-1. Dockerfile 체크리스트

  1. Node.js 버전이 명확한가?
  2. npm ci를 사용하는가?
  3. .dockerignore가 있는가?
  4. .env가 이미지에 포함되지 않는가?
  5. prisma generate가 실행되는가?
  6. 운영 이미지에서 devDependencies를 제외하는가?
  7. 불필요한 파일이 이미지에 들어가지 않는가?
  8. 컨테이너 시작 명령이 명확한가?

➕ 19-2. Docker 배포 체크리스트

  1. 이미지 태그가 명확한가?
  2. Registry에 정상 push 되었는가?
  3. EC2에서 정상 pull 되는가?
  4. 환경변수가 컨테이너에 주입되는가?
  5. migration 실행 시점이 분리되어 있는가?
  6. 컨테이너 health check가 통과하는가?
  7. Nginx proxy_pass와 포트가 맞는가?
  8. 실패 시 이전 이미지로 롤백 가능한가?

➕ 19-3. 운영 체크리스트

  1. docker ps로 컨테이너 상태를 확인할 수 있는가?
  2. docker logs로 API 로그를 확인할 수 있는가?
  3. 컨테이너 재시작 정책이 설정되어 있는가?
  4. 오래된 이미지와 컨테이너를 정리하는가?
  5. 로그가 CloudWatch로 수집되는가?
  6. Secret이 이미지나 로그에 포함되지 않는가?
  7. API 서버와 Worker를 구분해서 운영하는가?
  8. 서버 디스크 용량이 이미지 누적으로 부족해지지 않는가?

✅ 20. AI를 활용해 Docker 배포를 설계할 때 질문법

  • Docker 배포는 Dockerfile만 만드는 문제가 아닙니다.
  • 이미지 태그, 환경변수, migration, Nginx, health check, 롤백까지 함께 설계해야 합니다.

➕ 20-1. 좋은 질문 예시

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 배포 흐름
- 이미지 태그 전략
- 롤백 절차
- 운영 체크리스트
를 실무 기준으로 정리해줘.

➕ 20-2. AI 답변 검증 기준

  1. .env를 Docker 이미지에 넣으라고 하지 않는가?
  2. 운영 migration을 이미지 빌드 중이 아니라 배포 단계에서 실행하라고 하는가?
  3. migrate reset이나 db push --force-reset을 운영에 권하지 않는가?
  4. multi-stage build와 .dockerignore를 고려하는가?
  5. 이미지 태그를 latest만 쓰지 말라고 하는가?
  6. Nginx와 컨테이너 포트 연결을 설명하는가?
  7. health check와 로그 확인을 포함하는가?
  8. 이전 이미지 태그 기반 롤백을 설명하는가?

📌 요약

  • Docker는 애플리케이션 실행 환경을 이미지로 패키징해 어디서든 같은 방식으로 실행할 수 있게 해주는 도구입니다.
  • Docker를 사용하면 로컬/서버 환경 차이를 줄이고, 이미지 태그 기반 배포와 롤백이 쉬워집니다.
  • Dockerfile은 이미지를 만드는 설계도이며, 운영에서는 multi-stage build와 .dockerignore를 사용해 이미지 크기와 보안 위험을 줄이는 것이 좋습니다.
  • .env와 Secret은 이미지에 포함하면 안 되며, 컨테이너 실행 시 환경변수로 주입해야 합니다.
  • Prisma Client는 이미지 빌드 과정에서 prisma generate가 필요하고, 운영 DB migration은 배포 단계에서 migrate deploy로 별도 실행하는 것이 안전합니다.
  • EC2에서는 Nginx가 외부 요청을 받고, Docker 컨테이너의 NestJS API로 proxy_pass하는 구조를 사용할 수 있습니다.
  • Docker 기반 배포는 이미지 태그 전략이 중요하며, latest만 사용하지 말고 날짜/커밋 SHA/릴리즈 태그를 함께 관리하는 것이 좋습니다.
  • Docker를 도입하면 배포 안정성과 롤백 편의성은 좋아지지만, 컨테이너, 이미지, Registry, 환경변수, 로그, 디스크 정리 같은 운영 관리도 함께 필요합니다.

0개의 댓글