Docker 볼륨과 바인드 마운트 — 데이터를 컨테이너 밖에 저장하는 방법
컨테이너를 삭제하면 그 안에 있던 데이터베이스 파일도 전부 사라집니다. 컨테이너는 일회성인데, 데이터는 어떻게 보존해야 할까요?
컨테이너의 데이터 문제
Docker 컨테이너는 기본적으로 **일시적(ephemeral)**입니다. 컨테이너 내부에서 파일을 생성하거나 수정하면, 그 변경사항은 컨테이너의 **쓰기 가능 레이어(writable layer)**에 저장됩니다.
# 컨테이너 내부에 파일 생성
docker run -d --name mydb postgres:16-alpine
# 데이터가 컨테이너 내부에 저장됨
docker exec mydb ls /var/lib/postgresql/data/
# 컨테이너 삭제 → 데이터도 사라짐!
docker rm -f mydb
이 문제를 해결하기 위해 Docker는 세 가지 마운트 방식을 제공합니다.
세 가지 마운트 방식
┌── 호스트 ──────────────────────────┐
│ │
│ ┌── Volume ──┐ ┌── tmpfs ──┐ │
│ │/var/lib/ │ │ (메모리) │ │
│ │docker/ │ │ │ │
│ │volumes/ │ └─────┬─────┘ │
│ └─────┬──────┘ │ │
│ │ │ │
│ ┌─────┴────────────────┴────┐ │
│ │ 컨테이너 │ │
│ └───────────┬───────────────┘ │
│ │ │
│ ┌───────────┴───────────┐ │
│ │ Bind Mount │ │
│ │ (호스트 경로 직접) │ │
│ └───────────────────────┘ │
└──────────────────────────────────┘
Docker Volume
Docker가 관리하는 영역(/var/lib/docker/volumes/)에 데이터를 저장합니다.
기본 사용법
# 볼륨 생성
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
볼륨 관리 명령어
# 볼륨 목록
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가 자동으로 해시 이름을 생성합니다.
# 익명 볼륨 (관리하기 어려움)
docker run -v /var/lib/postgresql/data postgres:16-alpine
# 항상 이름을 지정하는 것을 권장
docker run -v pg-data:/var/lib/postgresql/data postgres:16-alpine
Bind Mount
호스트의 특정 디렉토리를 컨테이너에 직접 마운트합니다.
# 현재 디렉토리를 마운트
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사용 시 호스트에 경로가 없으면 디렉토리를 자동 생성 (의도하지 않은 빈 디렉토리 생성 주의)
개발 환경에서 활용
# 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에서 파일 시스템 성능이 저하됩니다.
읽기 전용 마운트
# :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
호스트의 메모리에만 존재하는 마운트입니다. 디스크에 전혀 쓰지 않습니다.
# 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 | 지원 | 지원 |
| 권장 | 간단한 경우 | 복잡한 옵션 |
# 같은 동작, 다른 문법
docker run -v my-vol:/data myapp
docker run --mount type=volume,source=my-vol,target=/data myapp
공식 문서에서는 --mount를 권장합니다. 더 명시적이고 실수를 줄여주기 때문입니다.
볼륨 백업과 복원
백업
# 볼륨 데이터를 tar로 백업
docker run --rm \
-v my-data:/source:ro \
-v $(pwd):/backup \
alpine tar czf /backup/my-data-backup.tar.gz -C /source .
이 명령어는:
my-data볼륨을/source에 읽기 전용으로 마운트- 현재 디렉토리를
/backup에 마운트 - alpine 컨테이너에서 tar로 압축하여 백업
복원
# 새 볼륨 생성 후 복원
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
볼륨 간 복사
# 한 볼륨에서 다른 볼륨으로 데이터 복사
docker run --rm \
-v source-vol:/from:ro \
-v dest-vol:/to \
alpine cp -a /from/. /to/
볼륨 드라이버
기본 local 드라이버 외에 원격 스토리지를 사용할 수 있습니다.
NFS 볼륨
# 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에서 볼륨
# 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"
데이터베이스 컨테이너의 볼륨 패턴
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같은 의존성은 볼륨으로 분리하세요.
댓글 로딩 중...