컨테이너를 지웠더니 데이터가 같이 사라졌다
MySQL을 컨테이너로 띄워서 며칠 잘 쓰다가, 이미지를 업데이트하려고 컨테이너를 내리고 다시 올렸더니 테이블이 텅 비어 있는 걸 본 적이 있다면, 이번 장이 그 이유를 설명합니다.
쓰기 가능 레이어는 컨테이너의 것입니다
2장에서 다뤘듯, 컨테이너 안에서 생기는 모든 변경 사항(새 파일, 수정된 파일, DB가 디스크에 쓰는 데이터 파일)은 그 컨테이너 전용의 얇은 쓰기 가능 레이어에 기록됩니다. 이 레이어는 이미지와 별개이고, 컨테이너가 삭제되면 함께 삭제됩니다.
docker run -d --name my-db mysql:8
# ... 며칠간 데이터를 쌓음 ...
docker rm -f my-db
docker run -d --name my-db mysql:8
# 새 컨테이너. 이전 데이터는 없음.docker stop 만으로는 데이터가 사라지지 않습니다. 멈춘 컨테이너는 docker start 로 다시 켜면 쓰기 레이어가 그대로 남아 있습니다. 문제는 docker rm (또는 docker run --rm, 혹은 컨테이너를 지우고 새로 만드는 배포 방식)입니다. 컨테이너 자체를 지우는 순간 그 안의 모든 데이터가 함께 사라집니다.
배포할 때 이미지를 새 버전으로 바꾼다는 건 사실상 "기존 컨테이너를 지우고 새 이미지로 새 컨테이너를 만드는" 과정입니다. 그러니 DB나 업로드 파일처럼 유지되어야 하는 데이터를 컨테이너의 쓰기 레이어에만 의존해서 저장하면, 다음 배포에서 100% 날아갑니다.
볼륨 — Docker가 관리하는 별도의 저장 공간
이 문제의 정석 해법이 볼륨입니다. 볼륨은 컨테이너의 생명주기와 무관하게 호스트에 존재하는 저장 공간이고, 컨테이너의 특정 디렉터리에 연결(mount)해서 씁니다.
docker volume create db-data
docker run -d --name my-db \
-v db-data:/var/lib/mysql \
mysql:8이제 MySQL이 /var/lib/mysql 에 쓰는 모든 데이터는 실제로는 db-data 라는 볼륨에 기록됩니다. 컨테이너를 지워도 볼륨은 남습니다.
docker rm -f my-db
docker run -d --name my-db \
-v db-data:/var/lib/mysql \
mysql:8
# 같은 db-data 볼륨을 다시 연결. 데이터 그대로.flowchart LR V["볼륨 db-data (호스트에 저장, 컨테이너 생명주기와 무관)"] C1["컨테이너 (v1)"] -.mount.-> V C2["컨테이너 (v2, 재배포 후)"] -.mount.-> V
컨테이너는 바뀌어도 됩니다. 어차피 배포할 때마다 새로 만들어지는 게 정상입니다. 대신 데이터는 볼륨에 있어야 그 교체와 무관하게 살아남습니다.
바인드 마운트 — 호스트의 특정 경로를 그대로 연결
볼륨과 자주 헷갈리는 게 바인드 마운트입니다. 이건 Docker가 관리하는 이름 붙은 저장 공간이 아니라, 호스트 파일시스템의 특정 경로를 그대로 컨테이너 안에 연결하는 방식입니다.
docker run -d --name web \
-v /home/user/my-project/src:/app/src \
my-web-image로컬 개발 환경에서 코드를 수정할 때마다 이미지를 새로 빌드하지 않고, 소스 코드 디렉터리를 통째로 컨테이너 안에 연결해서 바로 반영되게 하는 용도로 많이 씁니다. docker run -v $(pwd):/app 형태로 개발용 컨테이너를 띄우는 걸 자주 보게 될 겁니다.
바인드 마운트와 볼륨의 차이를 표로 정리하면 이렇습니다.
| 볼륨 | 바인드 마운트 | |
|---|---|---|
| 저장 위치 | Docker가 관리 (/var/lib/docker/volumes/...) |
호스트의 지정한 경로 그대로 |
| 용도 | 프로덕션 데이터 영속화 | 로컬 개발 시 코드 실시간 반영 |
| 이식성 | 다른 서버로 옮기기 쉬움 (docker volume 명령으로 백업/복원) | 호스트 경로에 의존적 |
| 실수로 삭제될 위험 | 명시적으로 docker volume rm 해야 함 |
호스트에서 실수로 지우면 그대로 사라짐 |
프로덕션 DB 데이터는 볼륨, 로컬 개발용 코드 반영은 바인드 마운트 — 이렇게 구분해서 쓰는 게 일반적인 관례입니다. 둘 다 같은 -v 옵션으로 지정하다 보니 처음에는 뭐가 뭔지 헷갈리는데, 콜론 앞이 볼륨 이름이면 볼륨이고 / 로 시작하는 절대경로면 바인드 마운트라고 구분하면 됩니다.
볼륨도 백업은 따로 해야 합니다
볼륨이 컨테이너보다 오래 산다고 해서 "절대 안 사라진다"는 뜻은 아닙니다. 서버 자체가 사라지거나(호스트 디스크 장애), 누군가 실수로 docker volume rm 을 실행하거나, docker system prune --volumes 를 무심코 돌리면 볼륨도 함께 날아갑니다. 이 책은 백업 전략까지 다루지는 않지만, "볼륨에 넣었으니 이제 안전하다"고 생각하고 백업을 아예 안 하는 건 다른 종류의 사고를 예약해두는 것과 같습니다.