docker-patterns
로컬 개발, 컨테이너 보안, 네트워킹, 볼륨 전략, 멀티 서비스 오케스트레이션을 위한 Docker 및 Docker Compose 패턴.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
로컬 개발, 컨테이너 보안, 네트워킹, 볼륨 전략, 멀티 서비스 오케스트레이션을 위한 Docker 및 Docker Compose 패턴.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
새 Spring Boot 서비스를 api-gateway + auth-api 에코시스템에 연결한다. auth-api 클라이언트 등록 → Gateway 라우팅 추가 → 서비스에 econo-passport 연동 → 동작 확인까지 한 번에 처리. 다음 상황에서 반드시 이 스킬을 사용한다: - "새 서비스 Gateway에 연결해줘", "새 서비스 auth 연동" - "서비스 등록해줘", "Gateway 뒤에 붙여줘" - "/register-service" 직접 호출 - 새 Spring Boot 서비스가 추가되고 인증이 필요할 때 ARGUMENTS: 서비스명, 서비스 경로(선택), 업스트림 URL(선택) 예: "EEOS-BE /Users/mando/study/eeos/EEOS-BE/eeos"
이 스킬은 사용자가 "PR 생성해줘", "PR 만들어줘", "pull request 생성", "pr 올려줘", "/git-pr" 등을 요청할 때 호출된다. 현재 브랜치의 커밋을 원격으로 push하고, `.github/PULL_REQUEST_TEMPLATE.md`를 채워 GitHub Pull Request를 생성한다.
신규 기능을 개발할 때 정책 문서 → 코드 → 테스트 순서로 작성한다. ADR은 기술적 결정에만 사용하고, 기능 정책은 docs/features/ 에 작성한다. 다음 상황에서 반드시 이 스킬을 사용한다: - "기능 추가해줘", "기획부터 해봐", "설계해봐" - 새로운 API 엔드포인트 또는 도메인 규칙이 생길 때 - "/new-feature" 직접 호출 ARGUMENTS: 기능 이름 또는 요구사항 (없으면 대화에서 추출)
Architecture Decision Record(ADR)를 작성한다. 기술적 결정사항, 설계 선택, 트레이드오프를 문서화하여 나중에 "왜 이렇게 했지?"를 알 수 있게 한다. 다음 상황에서 반드시 이 스킬을 사용한다: - "ADR 써줘", "결정사항 문서화해줘", "이 결정 기록해줘" - 기술 방향 선택 후 ("A 대신 B 쓰기로 했어") - 설계 논의가 끝났을 때 - 나중에 이 결정이 왜 내려졌는지 설명이 필요할 것 같을 때 - "/adr" 직접 호출 ARGUMENTS: 결정 내용 또는 결정 번호 (없으면 대화에서 추출)
AI 에이전트의 액션 스페이스, 도구 정의, 관측(Observation) 포맷을 설계·최적화해 작업 완수율을 높일 때 사용한다.
브랜치 전략, 커밋 컨벤션, 머지 vs 리베이스, 충돌 해결, 협업 개발 모범 사례를 포함하는 Git 워크플로 패턴. 모든 규모의 팀에 적용 가능하다.
SOC 직업 분류 기준
| name | docker-patterns |
| description | 로컬 개발, 컨테이너 보안, 네트워킹, 볼륨 전략, 멀티 서비스 오케스트레이션을 위한 Docker 및 Docker Compose 패턴. |
| origin | ECC |
컨테이너 기반 개발을 위한 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
# 스테이지: 운영 (최소 이미지)
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" # 호스트에서만 접근 가능, 외부 네트워크에서는 불가
# 운영에서는 ports를 아예 생략 -- Docker 네트워크 내부에서만 접근 가능
volumes:
# 명명 볼륨(named volume): 컨테이너 재시작 간 영속, 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. non-root로 실행
RUN addgroup -g 1001 -S app && adduser -S app -u 1001
USER app
# 3. capability 제거 (compose에서)
# 4. 가능하면 read-only 루트 파일시스템
# 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 미만 포트 바인딩이 필요할 때만
# GOOD: 환경 변수 사용 (런타임에 주입)
services:
app:
env_file:
- .env # .env 파일은 절대 git에 커밋하지 않는다
environment:
- API_KEY # 호스트 환경에서 상속
# GOOD: Docker secrets (Swarm 모드)
secrets:
db_password:
file: ./secrets/db_password.txt
services:
db:
secrets:
- db_password
# BAD: 이미지에 하드코딩
# 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 로그 follow
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
# BAD: 오케스트레이션 없이 운영에서 docker compose 사용
# 운영용 멀티 컨테이너 워크로드는 Kubernetes, ECS, Docker Swarm을 사용한다
# BAD: 볼륨 없이 컨테이너 안에 데이터 저장
# 컨테이너는 휘발성 -- 볼륨이 없으면 재시작 시 모든 데이터 손실
# BAD: root로 실행
# 항상 non-root 사용자를 만들고 사용한다
# BAD: :latest 태그 사용
# 재현 가능한 빌드를 위해 명시적 버전을 고정한다
# BAD: 모든 서비스를 하나의 거대한 컨테이너에
# 관심사를 분리한다: 컨테이너 하나당 프로세스 하나
# BAD: docker-compose.yml에 시크릿 저장
# .env 파일(gitignore)이나 Docker secrets를 사용한다