Docker Vibe Coding 가이드

Docker로 Vibe Coding을 활용하는 방법을 배웁니다. 컨테이너화 표준 도구를 AI로 구현해보세요.

개요

Docker는 애플리케이션을 컨테이너로 패키징하고 실행할 수 있는 플랫폼입니다. 일관된 환경, 빠른 배포, 리소스 격리가 장점입니다. 2013년 Solomon Hykes가 공개한 이래, Docker는 소프트웨어 배포와 개발 환경 구성의 표준으로 자리 잡았으며, OCI(Open Container Initiative) 표준의 기반이 되었습니다.

📋 Docker 장점
  • 일관된 환경: 개발/운영 동일
  • 빠른 배포: 컨테이너 순식간에 실행
  • 리소스 격리: 프로세스 격리
  • 이식성: 어디서나 실행
  • Docker Compose: 멀티 컨테이너
  • 레이어 캐싱: 변경된 부분만 다시 빌드하여 속도 극대화
  • 버전 관리: 이미지 태그로 롤백 및 배포 이력 관리

적합한 상황

  • 개발 환경 구축 - 팀 전체가 동일한 환경에서 작업
  • CI/CD 파이프라인 - 빌드, 테스트, 배포 자동화
  • 마이크로서비스 - 서비스별 독립 배포 및 스케일링
  • 클라우드 배포 - AWS ECS, GKE, Azure Container Instances
  • 로컬 데이터베이스 및 서비스 실행 - Redis, PostgreSQL, MongoDB 등 즉시 실행

컨테이너 vs VM

특성 컨테이너 VM
부팅 시간 초 단위 분 단위
크기 MB 단위 GB 단위
격리 수준 프로세스 (namespace/cgroup) 하드웨어 (하이퍼바이저)
오버헤드 낮음 높음
커널 공유 호스트 커널 공유 독립 커널
밀도 수십~수백 개 동시 실행 수 개~수십 개

이미지 레이어 구조

Docker 이미지는 여러 개의 읽기 전용 레이어로 구성됩니다. Dockerfile의 각 명령어(FROM, RUN, COPY 등)가 하나의 레이어를 생성하며, 컨테이너 실행 시 최상단에 쓰기 가능한 레이어가 추가됩니다. 이 구조 덕분에 여러 컨테이너가 동일한 베이스 레이어를 공유하여 디스크 공간을 절약합니다.

Docker 이미지 레이어 구조 및 컨테이너 공유 Docker 이미지 (읽기 전용) Layer 1: Base OS (alpine:3.19) Layer 2: Runtime (node:20) Layer 3: Dependencies (npm ci) Layer 4: App Code (COPY . .) READ-ONLY 실행 중인 컨테이너 Container A: Writable Layer 공유된 이미지 레이어 (Layer 1~4 참조) Container B: Writable Layer 공유된 이미지 레이어 (Layer 1~4 참조) Container C: Writable Layer 쓰기 가능 레이어 (CoW) 읽기 전용 레이어 레이어 공유 참조 각 컨테이너는 독립 Writable Layer를 가지며, Image Layer는 공유됨

Docker 이미지 레이어 구조 - 읽기 전용 이미지 레이어를 여러 컨테이너가 공유하고, 각 컨테이너는 독립적인 쓰기 가능 레이어를 가짐 (Copy-on-Write)

💡 Copy-on-Write (CoW) 전략

컨테이너가 이미지 레이어의 파일을 수정하면, 해당 파일이 쓰기 가능 레이어로 복사된 후 수정됩니다. 원본 이미지 레이어는 변경되지 않으므로, 다른 컨테이너에 영향을 주지 않습니다. 이 방식을 Copy-on-Write (CoW)라고 하며, 스토리지 효율성의 핵심입니다.

Dockerfile

이미지를 만들기 위한 설정 파일입니다. 각 명령어가 하나의 레이어를 생성하므로, 효율적인 레이어 구성이 빌드 성능과 이미지 크기에 직접 영향을 줍니다.

Dockerfile 예시

Dockerfile
# Node.js 앱
FROM node:20-alpine

WORKDIR /app

# 패키지 설치 (레이어 캐싱 활용)
COPY package*.json ./
RUN npm ci --only=production

# 소스 복사
COPY . .

# 포트 노출
EXPOSE 3000

# 실행
CMD ["node", "server.js"]

Multi-stage Build

빌드 환경과 런타임 환경을 분리하여 최종 이미지 크기를 대폭 줄입니다. 빌드 도구, 소스 코드, 개발 의존성은 최종 이미지에 포함되지 않습니다.

Dockerfile
# 빌드 스테이지
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# 런타임 스테이지
FROM node:20-alpine AS runner
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
USER node
CMD ["node", "dist/server.js"]

Multi-stage Build 고급 패턴

Go나 Rust처럼 정적 바이너리를 생성하는 언어에서는 최종 이미지를 scratch 또는 distroless로 구성하여 극한의 경량화가 가능합니다.

Dockerfile
# Go 앱 Multi-stage (최종 이미지 ~10MB)
FROM golang:1.22-alpine AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o /app/server ./cmd/server

# scratch: 완전 빈 이미지 (쉘 없음, 디버깅 불가)
FROM scratch
COPY --from=builder /app/server /server
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
EXPOSE 8080
ENTRYPOINT ["/server"]

.dockerignore

불필요한 파일을 빌드 컨텍스트에서 제외하여 빌드 속도를 높이고 이미지 크기를 줄입니다.

.dockerignore
node_modules
npm-debug.log
.git
.gitignore
README.md
Dockerfile
docker-compose.yml
.env
*.md
tests/
coverage/
.vscode/
.idea/
dist/
*.log

이미지 빌드 최적화

프로덕션 환경에서 Docker 이미지의 크기와 빌드 속도는 배포 효율에 직접적인 영향을 줍니다. 다음 전략들을 조합하면 빌드 시간과 이미지 크기를 크게 줄일 수 있습니다.

레이어 캐싱 전략

Docker는 각 레이어를 캐시하며, 이전 레이어가 변경되면 이후 모든 레이어를 재빌드합니다. 따라서 변경 빈도가 낮은 명령어를 위에 배치하는 것이 핵심입니다.

Dockerfile
# 잘못된 순서: 소스 변경마다 npm ci 재실행
COPY . .
RUN npm ci          # 매번 재실행됨

# 올바른 순서: 의존성 파일만 먼저 복사
COPY package*.json ./
RUN npm ci          # package.json 변경 시에만 재실행
COPY . .             # 소스 변경은 이 레이어만 영향

경량 베이스 이미지

베이스 이미지 선택은 최종 이미지 크기에 결정적 영향을 줍니다.

이미지 크기 특징 권장 용도
node:20 ~1.1GB Debian 기반, 전체 도구 포함 개발/디버깅
node:20-slim ~250MB Debian 최소 설치 일반 프로덕션
node:20-alpine ~180MB Alpine Linux 기반, musl libc 경량 프로덕션
gcr.io/distroless/nodejs20 ~130MB 쉘 없음, Node.js만 포함 보안 중시 프로덕션
⚠️ Alpine 이미지 주의사항

Alpine은 musl libc를 사용하므로 glibc 의존 네이티브 모듈(예: bcrypt, sharp)에서 호환성 문제가 발생할 수 있습니다. 이 경우 -slim 이미지를 사용하거나, Alpine에서 빌드 도구를 설치하세요: RUN apk add --no-cache python3 make g++

레이어 수 줄이기

여러 RUN 명령어를 &&로 결합하면 레이어 수를 줄이고, 중간 파일이 최종 레이어에 남지 않습니다.

Dockerfile
# 비효율: 3개 레이어, 캐시 파일 잔존
RUN apt-get update
RUN apt-get install -y curl
RUN rm -rf /var/lib/apt/lists/*

# 효율: 1개 레이어, 캐시 파일 제거됨
RUN apt-get update && \
    apt-get install -y --no-install-recommends curl && \
    rm -rf /var/lib/apt/lists/*

기본 명령어

자주 사용하는 Docker 명령어입니다.

명령어 예시

Bash
# 이미지 빌드
docker build -t myapp:latest .

# 컨테이너 실행
docker run -d -p 3000:3000 --name myapp myapp:latest

# 실행 중인 컨테이너 확인
docker ps

# 로그 확인
docker logs myapp

# 컨테이너 중지/삭제
docker stop myapp
docker rm myapp

# 이미지 목록
docker images

# 사용하지 않는 리소스 정리
docker system prune -a --volumes

볼륨 전략

Docker 컨테이너는 기본적으로 상태를 유지하지 않습니다(stateless). 컨테이너를 삭제하면 내부 데이터도 사라집니다. 영속적인 데이터를 관리하려면 볼륨(Volume)을 사용해야 합니다.

볼륨 유형 비교

유형 명령어 예시 특징 사용 사례
Named Volume -v pgdata:/var/lib/postgresql/data Docker가 관리, 호스트 경로 무관 데이터베이스, 영속 데이터
Bind Mount -v $(pwd)/src:/app/src 호스트 디렉토리 직접 마운트 개발 환경 소스 코드
tmpfs --tmpfs /tmp:rw,size=64m 메모리에만 저장, 컨테이너 종료 시 사라짐 임시 파일, 세션 데이터
Anonymous Volume -v /app/node_modules 자동 생성, 참조 어려움 빌드 캐시 (비권장)

볼륨 관리 명령어

Bash
# Named Volume 생성
docker volume create mydata

# Named Volume으로 컨테이너 실행
docker run -d -v mydata:/data --name mydb postgres:15-alpine

# Bind Mount로 개발 환경 구성
docker run -d \
  -v $(pwd)/src:/app/src:ro \
  -v $(pwd)/config:/app/config:ro \
  -v nodemodules:/app/node_modules \
  -p 3000:3000 myapp:dev

# 볼륨 목록 확인
docker volume ls

# 볼륨 상세 정보
docker volume inspect mydata

# 사용하지 않는 볼륨 정리
docker volume prune
💡 개발 환경 볼륨 패턴

개발 시 소스 코드는 Bind Mount로, node_modules는 Named Volume으로 분리하면 호스트와 컨테이너 간 의존성 충돌을 방지하면서 실시간 코드 변경 반영(Hot Reload)이 가능합니다.

Docker 네트워크

Docker는 컨테이너 간 통신과 외부 네트워크 연결을 위한 여러 네트워크 드라이버를 제공합니다. 기본 bridge 네트워크 외에도 host, overlay, macvlan, none 모드를 지원합니다.

Docker 네트워크 구조 (Bridge 모드) External Traffic (인터넷) Host Machine (eth0: 192.168.1.100) Port Mapping: -p host:container docker0 Bridge Network (172.17.0.0/16) Container A (web) 172.17.0.2 nginx:80 Port: 8080:80 (외부 접근 가능) Container B (api) 172.17.0.3 node:3000 Port: 3000:3000 (외부 접근 가능) Container C (db) 172.17.0.4 postgres:5432 포트 미노출 (내부 전용) Virtual Bridge (docker0) 컨테이너 간 통신 브리지 연결 트래픽 흐름

Docker Bridge 네트워크 구조 - 외부 트래픽이 호스트 포트 매핑을 통해 컨테이너에 도달하고, 컨테이너 간에는 브리지 네트워크를 통해 통신

네트워크 모드 상세

모드 명령어 격리 성능 사용 사례
bridge (기본) --network bridge 높음 약간의 오버헤드 일반적인 컨테이너 실행
host --network host 없음 (호스트 공유) 네이티브 성능 고성능 네트워크 요구
overlay --network overlay 높음 약간의 오버헤드 멀티 호스트 (Swarm/K8s)
macvlan --network macvlan 높음 네이티브에 가까움 물리 네트워크 직접 연결
none --network none 완전 격리 - 네트워크 불필요한 배치 작업

사용자 정의 네트워크

사용자 정의 브리지 네트워크는 기본 브리지보다 많은 장점을 제공합니다: 자동 DNS 해석, 더 나은 격리, 동적 연결/해제 등.

Bash
# 사용자 정의 네트워크 생성
docker network create --driver bridge myapp-network

# 네트워크에 컨테이너 연결
docker run -d --name api --network myapp-network myapi:latest
docker run -d --name db --network myapp-network postgres:15-alpine

# 컨테이너 간 통신 (DNS 자동 해석)
# api 컨테이너에서 "db" 호스트명으로 접속 가능
docker exec api ping db

# 네트워크 목록
docker network ls

# 네트워크 상세 정보
docker network inspect myapp-network

Docker Compose

멀티 컨테이너 애플리케이션을 YAML 파일 하나로 정의하고 관리합니다. 서비스 간 의존성, 네트워크, 볼륨을 선언적으로 구성할 수 있습니다.

docker-compose.yml

YAML
services:
  app:
    build: .
    ports:
      - "3000:3000"
    depends_on:
      - db
    environment:
      - DATABASE_URL=postgresql://db:5432/mydb

  db:
    image: postgres:15-alpine
    environment:
      - POSTGRES_USER=dev
      - POSTGRES_PASSWORD=dev
      - POSTGRES_DB=mydb
    volumes:
      - postgres-data:/var/lib/postgresql/data

volumes:
  postgres-data:

Compose 명령어

Bash
# 서비스 시작
docker-compose up -d

# 서비스 중지
docker-compose down

# 볼륨까지 함께 삭제
docker-compose down -v

# 로그 확인
docker-compose logs -f

# 특정 서비스만 실행
docker-compose up db

# 서비스 재빌드
docker-compose up --build -d

Health Check

컨테이너 상태를 주기적으로 확인하여 서비스의 정상 동작을 보장합니다.

YAML
services:
  app:
    image: node:20
    healthcheck:
      test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

  db:
    image: postgres:15
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user -d mydb"]
      interval: 10s
      timeout: 5s
      retries: 5

서비스 의존성 관리

depends_oncondition을 지정하면 단순 시작 순서가 아니라, 의존 서비스가 실제로 준비될 때까지 대기할 수 있습니다.

YAML
services:
  app:
    build: .
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started

  db:
    image: postgres:15-alpine
    healthcheck:
      test: ["CMD-SHELL", "pg_isready"]
      interval: 5s
      timeout: 3s
      retries: 10

  redis:
    image: redis:7-alpine

Compose Profiles

프로파일을 사용하면 환경에 따라 실행할 서비스를 선택적으로 제어할 수 있습니다.

YAML
services:
  app:
    build: .
    ports:
      - "3000:3000"

  db:
    image: postgres:15-alpine

  adminer:
    image: adminer
    ports:
      - "8080:8080"
    profiles:
      - debug

  prometheus:
    image: prom/prometheus
    profiles:
      - monitoring

  grafana:
    image: grafana/grafana
    profiles:
      - monitoring
Bash
# 기본 서비스만 실행 (app, db)
docker-compose up -d

# debug 프로파일 포함 실행
docker-compose --profile debug up -d

# 모니터링 포함 실행
docker-compose --profile monitoring up -d

Compose Secrets

민감한 정보를 환경 변수 대신 Docker Secrets로 안전하게 전달합니다.

YAML
services:
  db:
    image: postgres:15-alpine
    secrets:
      - db_password
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password

  app:
    build: .
    secrets:
      - db_password
      - api_key

secrets:
  db_password:
    file: ./secrets/db_password.txt
  api_key:
    file: ./secrets/api_key.txt
⚠️ 환경 변수 보안 주의

docker inspect로 컨테이너의 환경 변수를 확인할 수 있으므로, 비밀번호나 API 키를 환경 변수에 직접 넣는 것은 위험합니다. 프로덕션에서는 Docker Secrets, HashiCorp Vault, AWS Secrets Manager 등을 사용하세요.

Node.js + DB 예시

Node.js 앱과 PostgreSQL을 함께 실행하는 완전한 예시입니다.

전체 예시

YAML
services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgresql://user:pass@db:5432/mydb
      - REDIS_URL=redis://redis:6379
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    restart: always

  db:
    image: postgres:15-alpine
    environment:
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=pass
      - POSTGRES_DB=mydb
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: always

  redis:
    image: redis:7-alpine
    restart: always

volumes:
  pgdata:

보안 모범 사례

Docker 컨테이너는 기본적으로 root 사용자로 실행됩니다. 프로덕션 환경에서는 보안 강화를 위해 여러 조치를 취해야 합니다.

Non-root 사용자 실행

컨테이너 탈출(container escape) 취약점이 발생하더라도 피해를 최소화하려면 비root 사용자로 실행해야 합니다.

Dockerfile
FROM node:20-alpine

# 전용 사용자 생성
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

WORKDIR /app
COPY --chown=appuser:appgroup package*.json ./
RUN npm ci --only=production
COPY --chown=appuser:appgroup . .

# 비root 사용자로 전환
USER appuser

EXPOSE 3000
CMD ["node", "server.js"]

읽기 전용 파일 시스템

컨테이너 파일 시스템을 읽기 전용으로 마운트하면, 악성 코드가 파일을 생성하거나 수정하는 것을 방지할 수 있습니다.

Bash
# 읽기 전용 파일 시스템 + tmpfs로 필요한 쓰기 영역만 허용
docker run -d \
  --read-only \
  --tmpfs /tmp:rw,size=64m \
  --tmpfs /var/run:rw \
  -p 3000:3000 \
  myapp:latest

리소스 제한

CPU와 메모리 제한을 설정하여 단일 컨테이너가 호스트 전체 리소스를 소진하는 것을 방지합니다.

YAML
services:
  app:
    image: myapp:latest
    deploy:
      resources:
        limits:
          cpus: '0.5'      # CPU 50% 제한
          memory: 512M     # 메모리 512MB 제한
        reservations:
          cpus: '0.25'     # 최소 CPU 보장
          memory: 256M     # 최소 메모리 보장

이미지 보안 스캐닝

이미지에 알려진 취약점(CVE)이 포함되어 있는지 빌드 단계에서 스캔하세요.

Bash
# Docker Scout (Docker Desktop 내장)
docker scout cves myapp:latest

# Trivy (오픈소스 스캐너)
trivy image myapp:latest

# Snyk
snyk container test myapp:latest

# CI/CD에서 취약점 발견 시 빌드 실패
trivy image --exit-code 1 --severity HIGH,CRITICAL myapp:latest
💡 보안 체크리스트
  • 최신 베이스 이미지 유지 (docker pull로 주기적 업데이트)
  • 불필요한 패키지 미설치 (--no-install-recommends)
  • COPY 대신 ADD 사용 자제 (URL 다운로드 방지)
  • --cap-drop ALL로 Linux capabilities 제거 후 필요한 것만 추가
  • .dockerignore.env, .git, 인증서 파일 포함

디버깅

컨테이너 문제 해결을 위한 핵심 명령어들입니다.

로그 확인

Bash
# 실시간 로그 스트리밍
docker logs -f myapp

# 최근 100줄만 확인
docker logs --tail 100 myapp

# 타임스탬프 포함
docker logs -t myapp

# 특정 시간 이후 로그
docker logs --since 2h myapp

# Compose에서 특정 서비스 로그
docker-compose logs -f app

컨테이너 접속

Bash
# 실행 중인 컨테이너에 쉘 접속
docker exec -it myapp /bin/sh

# 특정 명령어 실행
docker exec myapp cat /app/config.json

# 환경 변수 확인
docker exec myapp env

# 네트워크 상태 확인
docker exec myapp netstat -tlnp

상세 정보 확인

Bash
# 컨테이너 상세 정보 (JSON)
docker inspect myapp

# IP 주소만 추출
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' myapp

# 마운트된 볼륨 확인
docker inspect -f '{{json .Mounts}}' myapp | python3 -m json.tool

# 실시간 리소스 사용량 모니터링
docker stats

# 특정 컨테이너 리소스 확인
docker stats myapp --no-stream

# 컨테이너 프로세스 확인
docker top myapp

# 파일 시스템 변경 사항 확인
docker diff myapp
💡 디버깅 순서

문제 해결 시 권장 순서: 1) docker ps -a로 상태 확인 → 2) docker logs로 에러 로그 확인 → 3) docker inspect로 설정 검증 → 4) docker exec로 내부 진입하여 상세 조사 → 5) docker stats로 리소스 문제 확인

프로덕션 배포 팁

개발 환경에서 잘 동작하던 컨테이너도 프로덕션에서는 추가적인 설정이 필요합니다.

재시작 정책

정책 설명 사용 사례
no 재시작 안 함 (기본값) 일회성 작업
on-failure[:max] 비정상 종료 시 재시작 (횟수 제한 가능) 배치 작업
always 항상 재시작 (수동 중지 제외) 일반 서비스
unless-stopped 수동 중지 전까지 항상 재시작 프로덕션 서비스

로깅 드라이버

프로덕션에서는 컨테이너 로그를 중앙 로그 시스템으로 전송해야 합니다.

YAML
services:
  app:
    image: myapp:latest
    logging:
      driver: "json-file"
      options:
        max-size: "10m"     # 로그 파일 최대 크기
        max-file: "3"       # 로그 파일 최대 개수
        compress: "true"    # 로테이션된 로그 압축
⚠️ 로그 크기 제한 필수

json-file 드라이버는 기본적으로 로그 크기 제한이 없습니다. 로그가 무한히 쌓이면 디스크 공간이 소진되어 호스트 전체가 중단될 수 있습니다. 반드시 max-sizemax-file을 설정하세요.

프로덕션 Compose 예시

YAML
services:
  app:
    image: myapp:1.2.3       # 명시적 태그 (latest 금지)
    ports:
      - "3000:3000"
    restart: unless-stopped
    read_only: true
    tmpfs:
      - /tmp:rw,size=64m
    healthcheck:
      test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 512M
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:15-alpine
    restart: unless-stopped
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app"]
      interval: 10s
      timeout: 5s
      retries: 5
    deploy:
      resources:
        limits:
          memory: 1G
    secrets:
      - db_password
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password

volumes:
  pgdata:

secrets:
  db_password:
    file: ./secrets/db_password.txt

Docker vs Podman 비교

Podman은 Red Hat이 개발한 Docker 호환 컨테이너 런타임입니다. 데몬리스 아키텍처와 기본 rootless 실행이 특징이며, Docker CLI와 거의 동일한 명령어를 사용합니다.

특성 Docker Podman
아키텍처 데몬 기반 (dockerd) 데몬리스 (fork/exec)
기본 실행 권한 root (rootless 가능) rootless (기본)
Compose 지원 Docker Compose (네이티브) podman-compose / docker-compose 호환
Kubernetes 통합 별도 도구 필요 podman generate kube 내장
OCI 호환 지원 지원
Systemd 통합 제한적 podman generate systemd 내장
이미지 호환 Docker Hub 기본 Docker Hub 호환 + 다중 레지스트리
Windows/macOS Docker Desktop Podman Desktop
라이선스 기업용 유료 (250인 이상) Apache 2.0 (완전 무료)
Bash
# Docker와 Podman 명령어 비교 (거의 동일)
docker run -d -p 8080:80 nginx     # Docker
podman run -d -p 8080:80 nginx     # Podman

# alias 설정으로 전환
alias docker=podman
💡 어떤 것을 선택할까?
  • Docker 추천: Docker Desktop 생태계가 필요하거나, 기존 Docker 기반 CI/CD가 구축된 경우
  • Podman 추천: 보안이 중요한 엔터프라이즈 환경, RHEL/CentOS 기반 시스템, 데몬 없는 아키텍처 선호 시
  • 두 도구 모두 OCI 호환이므로 빌드한 이미지는 서로 교환 가능

모범 사례 요약

  • Multi-stage Build: 빌드 결과물만 포함하여 이미지 크기 최소화
  • .dockerignore: 불필요 파일 제외로 빌드 컨텍스트 경량화
  • 명시적 태그: 버전 명시 (latest 피하기, myapp:1.2.3 사용)
  • 시크릿 관리: Docker Secrets 또는 외부 비밀 관리 시스템 사용
  • Non-root 사용자: USER 명령어로 비root 실행
  • Healthcheck: 모든 서비스에 상태 확인 설정
  • 리소스 제한: CPU/메모리 limits 필수 설정
  • 로그 관리: 로그 크기 제한 및 중앙 로깅 시스템 연동
  • 보안 스캐닝: CI/CD 파이프라인에 이미지 취약점 스캔 포함
  • 레이어 최적화: 변경 빈도 기준 Dockerfile 명령어 정렬
💡 Vibe Coding에서의 Docker 활용

AI 도구(Claude, ChatGPT 등)에게 Docker 관련 작업을 요청할 때는 다음 정보를 함께 제공하세요: 1) 사용 언어/프레임워크, 2) 필요한 서비스(DB, 캐시 등), 3) 개발/프로덕션 용도 구분. 이 세 가지만 명확히 하면 AI가 최적의 Dockerfile과 Compose 설정을 생성해줍니다.

참고자료