From ecc
로컬 개발, 컨테이너 보안, 네트워킹, 볼륨 전략 및 멀티 서비스 오케스트레이션을 위한 Docker 및 Docker Compose 패턴입니다.
npx claudepluginhub sam42-lab/everything-claude-code-krThis skill uses the workspace's default tool permissions.
컨테이너화된 개발을 위한 Docker 및 Docker Compose 모범 사례입니다.
Mandates invoking relevant skills via tools before any response in coding sessions. Covers access, priorities, and adaptations for Claude Code, Copilot CLI, Gemini CLI.
Share bugs, ideas, or general feedback.
컨테이너화된 개발을 위한 Docker 및 Docker Compose 모범 사례입니다.
# docker-compose.yml
services:
app:
build:
context: .
target: dev # 멀티 스테이지 Dockerfile의 dev 스테이지 사용
ports:
- "3000:3000"
volumes:
- .:/app # 핫 리로드를 위한 바인드 마운트
- /app/node_modules # 익명 볼륨 -- 컨테이너의 의존성 보존
environment:
- DATABASE_URL=postgres://postgres:postgres@db:5432/app_dev
- REDIS_URL=redis://redis:6379/0
- NODE_ENV=development
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
command: npm run dev
db:
image: postgres:16-alpine
ports:
- "5432:5432"
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: app_dev
volumes:
- pgdata:/var/lib/postgresql/data
- ./scripts/init-db.sql:/docker-entrypoint-initdb.d/init.sql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 3s
retries: 5
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redisdata:/data
mailpit: # 로컬 이메일 테스트용
image: axllent/mailpit
ports:
- "8025:8025" # 웹 UI
- "1025:1025" # SMTP
volumes:
pgdata:
redisdata:
# 스테이지: 의존성 설치
FROM node:22-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
# 스테이지: 개발(dev) (핫 리로드, 디버그 도구 포함)
FROM node:22-alpine AS dev
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
EXPOSE 3000
CMD ["npm", "run", "dev"]
# 스테이지: 빌드
FROM node:22-alpine AS build
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build && npm prune --production
# 스테이지: 프로덕션(production) (최소 크기 이미지)
FROM node:22-alpine AS production
WORKDIR /app
RUN addgroup -g 1001 -S appgroup && adduser -S appuser -u 1001
USER appuser
COPY --from=build --chown=appuser:appgroup /app/dist ./dist
COPY --from=build --chown=appuser:appgroup /app/node_modules ./node_modules
COPY --from=build --chown=appuser:appgroup /app/package.json ./
ENV NODE_ENV=production
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s CMD wget -qO- http://localhost:3000/health || exit 1
CMD ["node", "dist/server.js"]
# docker-compose.override.yml (자동 로드됨, 개발 전용 설정)
services:
app:
environment:
- DEBUG=app:*
- LOG_LEVEL=debug
ports:
- "9229:9229" # Node.js 디버거용
# docker-compose.prod.yml (프로덕션 환경용 명시적 파일)
services:
app:
build:
target: production
restart: always
deploy:
resources:
limits:
cpus: "1.0"
memory: 512M
# 개발 환경 (override 파일을 자동으로 로드함)
docker compose up
# 프로덕션 환경
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
동일한 Compose 네트워크 내의 서비스들은 서비스 이름으로 서로를 찾을 수 있습니다:
# "app" 컨테이너 내부에서:
postgres://postgres:postgres@db:5432/app_dev # "db"는 db 컨테이너로 해석됨
redis://redis:6379/0 # "redis"는 redis 컨테이너로 해석됨
services:
frontend:
networks:
- frontend-net
api:
networks:
- frontend-net
- backend-net
db:
networks:
- backend-net # api에서만 접근 가능하며 frontend에서는 접근 불가
networks:
frontend-net:
backend-net:
services:
db:
ports:
- "127.0.0.1:5432:5432" # 호스트에서만 접근 가능하며 외부 네트워크에서는 접근 불가
# 프로덕션에서는 포트 설정을 생략하세요 -- Docker 네트워크 내부에서만 접근 가능하게 됩니다.
volumes:
# 이름이 있는 볼륨: 컨테이너 재시작 후에도 데이터가 유지되며 Docker가 관리함
pgdata:
# 바인드 마운트: 호스트 디렉토리를 컨테이너에 매핑함 (개발용)
# - ./src:/app/src
# 익명 볼륨: 바인드 마운트로 인해 덮어써지는 컨테이너 생성 콘텐츠를 보호함
# - /app/node_modules
services:
app:
volumes:
- .:/app # 소스 코드 (핫 리로드를 위한 바인드 마운트)
- /app/node_modules # 호스트의 node_modules로부터 컨테이너의 라이브러리 보호
- /app/.next # 빌드 캐시 보호
db:
volumes:
- pgdata:/var/lib/postgresql/data # 영구 데이터 저장
- ./scripts/init.sql:/docker-entrypoint-initdb.d/init.sql # 초기화 스크립트
# 1. 특정 버전을 명시하는 태그 사용 (절대 :latest 사용 금지)
FROM node:22.12-alpine3.20
# 2. root가 아닌 사용자로 실행
RUN addgroup -g 1001 -S app && adduser -S app -u 1001
USER app
# 3. 불필요한 기능 제거 (compose에서 설정)
# 4. 가능한 경우 root 파일시스템을 읽기 전용으로 설정
# 5. 이미지 레이어에 시크릿 정보를 포함하지 않음
services:
app:
security_opt:
- no-new-privileges:true
read_only: true
tmpfs:
- /tmp
- /app/.cache
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE # 1024 미만의 포트에 바인딩하는 경우에만 필요
# 권장: 환경 변수 사용 (런타임에 주입됨)
services:
app:
env_file:
- .env # .env 파일을 절대 git에 커밋하지 마세요.
environment:
- API_KEY # 호스트 환경에서 상속받음
# 권장: Docker 시크릿 (Swarm 모드 사용 시)
secrets:
db_password:
file: ./secrets/db_password.txt
services:
db:
secrets:
- db_password
# 금지: 이미지에 하드코딩
# ENV API_KEY=sk-proj-xxxxx # 절대 이렇게 하지 마세요.
node_modules
.git
.env
.env.*
dist
coverage
*.log
.next
.cache
docker-compose*.yml
Dockerfile*
README.md
tests/
# 로그 확인
docker compose logs -f app # app 로그 실시간 확인
docker compose logs --tail=50 db # db의 마지막 50줄 확인
# 실행 중인 컨테이너에서 명령 실행
docker compose exec app sh # app 컨테이너의 쉘 접속
docker compose exec db psql -U postgres # postgres 접속
# 상태 확인
docker compose ps # 실행 중인 서비스 목록
docker compose top # 각 컨테이너의 프로세스 확인
docker stats # 리소스 사용량 확인
# 재빌드
docker compose up --build # 이미지 재빌드 및 실행
docker compose build --no-cache app # 캐시 없이 완전 재빌드 강제
# 정리
docker compose down # 컨테이너 중지 및 제거
docker compose down -v # 볼륨까지 함께 제거 (주의: 데이터 삭제됨)
docker system prune # 사용하지 않는 이미지/컨테이너 제거
# 컨테이너 내부에서 DNS 해석 확인
docker compose exec app nslookup db
# 연결성 확인
docker compose exec app wget -qO- http://api:3000/health
# 네트워크 검사
docker network ls
docker network inspect <project>_default
# 나쁨: 오케스트레이션 도구 없이 프로덕션에서 docker compose 사용
# 프로덕션의 멀티 컨테이너 워크로드에는 Kubernetes, ECS 또는 Docker Swarm을 사용하세요.
# 나쁨: 볼륨 없이 컨테이너에 데이터 저장
# 컨테이너는 일시적입니다 -- 볼륨이 없으면 재시작 시 모든 데이터가 사라집니다.
# 나쁨: root 사용자로 실행
# 항상 root가 아닌 사용자를 생성하고 사용하세요.
# 나쁨: :latest 태그 사용
# 재현 가능한 빌드를 위해 특정 버전을 고정하세요.
# 나쁨: 모든 서비스를 하나의 거대한 컨테이너에 넣기
# 관심사를 분리하세요: 컨테이너당 하나의 프로세스만 실행하세요.
# 나쁨: docker-compose.yml에 시크릿 정보 노출
# .env 파일(git에서 제외됨)이나 Docker 시크릿을 사용하세요.