Theme:

컨테이너를 삭제하면 그 안에 있던 데이터베이스 파일도 전부 사라집니다. 컨테이너는 일회성인데, 데이터는 어떻게 보존해야 할까요?

컨테이너의 데이터 문제

Docker 컨테이너는 기본적으로 **일시적(ephemeral)**입니다. 컨테이너 내부에서 파일을 생성하거나 수정하면, 그 변경사항은 컨테이너의 **쓰기 가능 레이어(writable layer)**에 저장됩니다.

BASH
# 컨테이너 내부에 파일 생성
docker run -d --name mydb postgres:16-alpine

# 데이터가 컨테이너 내부에 저장됨
docker exec mydb ls /var/lib/postgresql/data/

# 컨테이너 삭제 → 데이터도 사라짐!
docker rm -f mydb

이 문제를 해결하기 위해 Docker는 세 가지 마운트 방식을 제공합니다.

세 가지 마운트 방식

PLAINTEXT
┌── 호스트 ──────────────────────────┐
│                                    │
│  ┌── Volume ──┐   ┌── tmpfs ──┐  │
│  │/var/lib/   │   │  (메모리)  │  │
│  │docker/     │   │           │  │
│  │volumes/    │   └─────┬─────┘  │
│  └─────┬──────┘         │        │
│        │                │        │
│  ┌─────┴────────────────┴────┐   │
│  │        컨테이너            │   │
│  └───────────┬───────────────┘   │
│              │                    │
│  ┌───────────┴───────────┐       │
│  │    Bind Mount         │       │
│  │  (호스트 경로 직접)    │       │
│  └───────────────────────┘       │
└──────────────────────────────────┘

Docker Volume

Docker가 관리하는 영역(/var/lib/docker/volumes/)에 데이터를 저장합니다.

기본 사용법

BASH
# 볼륨 생성
docker volume create my-data

# 볼륨과 함께 컨테이너 실행
docker run -d --name mydb \
    -v my-data:/var/lib/postgresql/data \
    postgres:16-alpine

# --mount 문법 (더 명시적)
docker run -d --name mydb \
    --mount type=volume,source=my-data,target=/var/lib/postgresql/data \
    postgres:16-alpine

볼륨 관리 명령어

BASH
# 볼륨 목록
docker volume ls

# 볼륨 상세 정보
docker volume inspect my-data
# {
#   "CreatedAt": "2026-03-19T10:00:00Z",
#   "Driver": "local",
#   "Mountpoint": "/var/lib/docker/volumes/my-data/_data",
#   "Name": "my-data",
#   "Scope": "local"
# }

# 사용하지 않는 볼륨 정리
docker volume prune

# 볼륨 삭제
docker volume rm my-data

볼륨의 장점

  • Docker가 관리: 호스트 파일 시스템과 독립적
  • 이식성: Linux, macOS, Windows에서 동일하게 동작
  • 볼륨 드라이버: 원격 스토리지 연동 가능
  • 성능: macOS/Windows에서 바인드 마운트보다 빠름 (Docker Desktop 환경)
  • 백업/마이그레이션: Docker 명령어로 처리 가능

익명 볼륨

볼륨 이름을 지정하지 않으면 Docker가 자동으로 해시 이름을 생성합니다.

BASH
# 익명 볼륨 (관리하기 어려움)
docker run -v /var/lib/postgresql/data postgres:16-alpine

# 항상 이름을 지정하는 것을 권장
docker run -v pg-data:/var/lib/postgresql/data postgres:16-alpine

Bind Mount

호스트의 특정 디렉토리를 컨테이너에 직접 마운트합니다.

BASH
# 현재 디렉토리를 마운트
docker run -d \
    -v $(pwd)/html:/usr/share/nginx/html:ro \
    nginx:1.25-alpine

# --mount 문법
docker run -d \
    --mount type=bind,source=$(pwd)/html,target=/usr/share/nginx/html,readonly \
    nginx:1.25-alpine

바인드 마운트의 특징

  • 호스트 파일 시스템의 정확한 경로를 사용
  • 호스트에서 파일을 수정하면 컨테이너에 즉시 반영됨 (반대도 마찬가지)
  • 개발 환경에서 코드를 실시간으로 반영할 때 유용
  • -v 사용 시 호스트에 경로가 없으면 디렉토리를 자동 생성 (의도하지 않은 빈 디렉토리 생성 주의)

개발 환경에서 활용

YAML
# compose.yaml — 개발 환경
services:
  app:
    image: node:20-alpine
    command: npm run dev
    volumes:
      - ./src:/app/src          # 소스 코드 실시간 반영
      - ./package.json:/app/package.json
      - node_modules:/app/node_modules  # 볼륨으로 분리 (성능)
    ports:
      - "3000:3000"

volumes:
  node_modules:  # 이름 있는 볼륨

node_modules를 볼륨으로 분리하는 이유: 바인드 마운트로 호스트의 node_modules를 사용하면 OS별 네이티브 모듈 호환성 문제가 생기고, macOS/Windows에서 파일 시스템 성능이 저하됩니다.

읽기 전용 마운트

BASH
# :ro 옵션으로 읽기 전용 설정
docker run -d \
    -v $(pwd)/config:/etc/app/config:ro \
    myapp:latest

# 컨테이너 내부에서 쓰기 시도 시 에러
docker exec myapp touch /etc/app/config/new-file
# touch: /etc/app/config/new-file: Read-only file system

tmpfs Mount

호스트의 메모리에만 존재하는 마운트입니다. 디스크에 전혀 쓰지 않습니다.

BASH
# tmpfs 마운트
docker run -d \
    --tmpfs /tmp:rw,size=100m \
    myapp:latest

# --mount 문법
docker run -d \
    --mount type=tmpfs,destination=/tmp,tmpfs-size=100m \
    myapp:latest

사용 사례

  • 민감한 임시 파일 (비밀번호, 토큰 등이 디스크에 남지 않도록)
  • 고속 임시 스토리지 (캐시, 세션 파일)
  • /tmp 디렉토리 격리

-v vs --mount

특성-v (--volume)--mount
문법-v name:path:opt--mount type=...,source=...,target=...
가독성간결함명시적
없는 경로자동 생성에러 발생
Docker Compose지원지원
권장간단한 경우복잡한 옵션
BASH
# 같은 동작, 다른 문법
docker run -v my-vol:/data myapp
docker run --mount type=volume,source=my-vol,target=/data myapp

공식 문서에서는 --mount를 권장합니다. 더 명시적이고 실수를 줄여주기 때문입니다.

볼륨 백업과 복원

백업

BASH
# 볼륨 데이터를 tar로 백업
docker run --rm \
    -v my-data:/source:ro \
    -v $(pwd):/backup \
    alpine tar czf /backup/my-data-backup.tar.gz -C /source .

이 명령어는:

  1. my-data 볼륨을 /source에 읽기 전용으로 마운트
  2. 현재 디렉토리를 /backup에 마운트
  3. alpine 컨테이너에서 tar로 압축하여 백업

복원

BASH
# 새 볼륨 생성 후 복원
docker volume create my-data-restored

docker run --rm \
    -v my-data-restored:/target \
    -v $(pwd):/backup:ro \
    alpine tar xzf /backup/my-data-backup.tar.gz -C /target

볼륨 간 복사

BASH
# 한 볼륨에서 다른 볼륨으로 데이터 복사
docker run --rm \
    -v source-vol:/from:ro \
    -v dest-vol:/to \
    alpine cp -a /from/. /to/

볼륨 드라이버

기본 local 드라이버 외에 원격 스토리지를 사용할 수 있습니다.

NFS 볼륨

BASH
# NFS 볼륨 생성
docker volume create \
    --driver local \
    --opt type=nfs \
    --opt o=addr=192.168.1.100,rw \
    --opt device=:/shared/data \
    nfs-vol

Docker Compose에서 볼륨

YAML
# compose.yaml
services:
  db:
    image: postgres:16-alpine
    volumes:
      - pg-data:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: secret

  backup:
    image: alpine
    volumes:
      - pg-data:/data:ro
      - ./backups:/backups
    command: tar czf /backups/db-backup.tar.gz -C /data .
    profiles:
      - backup

volumes:
  pg-data:
    driver: local
    # NFS 드라이버 사용 예시
    # driver_opts:
    #   type: nfs
    #   o: addr=192.168.1.100,rw
    #   device: ":/shared/pg-data"

데이터베이스 컨테이너의 볼륨 패턴

YAML
services:
  postgres:
    image: postgres:16-alpine
    volumes:
      - pg-data:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: admin
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password

  redis:
    image: redis:7-alpine
    command: redis-server --appendonly yes
    volumes:
      - redis-data:/data

  mysql:
    image: mysql:8.0
    volumes:
      - mysql-data:/var/lib/mysql
      - ./my.cnf:/etc/mysql/conf.d/custom.cnf:ro

volumes:
  pg-data:
  redis-data:
  mysql-data:

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

정리

  • Volume: Docker가 관리하는 영역에 저장. 프로덕션 데이터에 가장 적합합니다.
  • Bind Mount: 호스트 경로를 직접 마운트. 개발 시 코드 실시간 반영에 유용합니다.
  • tmpfs: 메모리에만 존재. 민감한 임시 데이터에 적합합니다.
  • 볼륨에는 항상 이름을 부여하고, --mount 문법을 사용하는 것이 실수를 줄여줍니다.
  • 데이터 백업은 별도 컨테이너에서 tar로 처리하는 패턴이 일반적입니다.
  • 개발 환경에서는 바인드 마운트로 소스 코드를 반영하되, node_modules 같은 의존성은 볼륨으로 분리하세요.
댓글 로딩 중...