Storage - Docker의 Storage

2026. 1. 3. 19:45·CKA

컨테이너를 운영하다 보면 “왜 컨테이너를 지우면 데이터가 날아가지?”, “이미지는 왜 빌드할수록 빨라지지?”, “-v랑 --mount는 뭐가 달라?” 같은 질문을 꼭 만나게 됩니다.
이 글은 Docker가 어디에 데이터를 저장하고, 레이어(계층) 구조로 파일 시스템을 어떻게 구성하며, 스토리지 드라이버가 무엇을 담당하는지, 그리고 데이터를 영구 보존하기 위해 볼륨/바인드 마운트를 어떻게 쓰는지를 흐름대로 정리합니다.


1) Docker 데이터는 어디에 저장될까? (/var/lib/docker)

리눅스에서 Docker Engine을 설치하면 기본적으로 Docker의 데이터 루트(data-root)는 보통 다음 경로입니다.

  • /var/lib/docker: Docker가 사용하는 대부분의 로컬 데이터(이미지/컨테이너/볼륨/메타데이터 등)의 기본 저장소

이 안에는 대략 다음과 같은 구성 요소가 들어 있습니다.

  • 이미지 관련 데이터(레이어/메타데이터)
  • 컨테이너 관련 데이터(컨테이너별 writable layer, 로그 등)
  • 볼륨 데이터(영구 데이터)
  • 스토리지 드라이버별 디렉터리(예: overlay2/)

확인 커맨드:

# Docker가 실제로 쓰는 데이터 루트 확인
docker info | grep -E 'Docker Root Dir|Storage Driver'

# 디렉터리 구조 훑기
sudo ls -al /var/lib/docker

참고: Docker Desktop(Windows/macOS)이나 rootless Docker 환경에서는 data-root 경로가 다를 수 있습니다(예: rootless는 ~/.local/share/docker 계열).


2) 핵심 개념: “레이어드(계층형) 이미지” + “컨테이너 Writable Layer”

Docker 이미지가 효율적인 이유는 레이어드 아키텍처에 있습니다.

2.1 Dockerfile 한 줄 = 이미지 레이어 하나(대체로)

Dockerfile의 각 명령은 “이전 레이어 대비 변경분”을 담은 새 레이어를 만들고, 최종적으로 여러 레이어가 쌓여 하나의 이미지가 됩니다.

예시(개념):

  • Base OS 레이어 (예: Ubuntu)
  • apt-get install ... 레이어
  • Python/Flask 의존성 레이어
  • 소스코드 COPY 레이어
  • Entrypoint/CMD 레이어

이 구조 덕분에 두 이미지가 앞부분 레이어(베이스/의존성)가 같다면 재사용할 수 있습니다. 즉, 앱 코드만 바뀌면 “앞 레이어는 캐시 재사용 + 마지막 레이어만 새로 생성”이 되어 빌드가 빨라집니다.

레이어 확인 커맨드:

docker image history <IMAGE_NAME>

2.2 컨테이너를 실행하면 “쓰기 가능한 레이어”가 위에 추가된다

이미지는 기본적으로 읽기 전용(read-only) 레이어 집합입니다.
docker run으로 컨테이너를 만들면, Docker는 이미지 레이어 위에 컨테이너 전용 writable layer(읽기/쓰기 레이어)를 얹습니다.

  • 컨테이너 실행 중 생성되는 로그/임시파일/수정된 파일은 writable layer에 저장
  • 컨테이너를 삭제하면 writable layer도 함께 삭제 → 데이터가 사라짐

개념 그림:

[Container Writable Layer]  <- 컨테이너 런타임 변경분(임시/수정/생성)
-------------------------
[Image Layer N]            <- read-only
[Image Layer N-1]          <- read-only
...
[Base Image Layer]         <- read-only

3) Copy-on-Write: “이미지 파일을 수정하면 실제로는 복사본을 만든다”

이미지 레이어는 읽기 전용인데, 컨테이너에서 파일 편집이 되는 이유는 Copy-on-Write(CoW) 때문입니다.

예를 들어 이미지에 포함된 /app/app.py를 컨테이너에서 수정하려고 하면:

  1. Docker/스토리지 드라이버가 해당 파일을 writable layer로 “복사(copy-up)”
  2. 이후 수정은 writable layer의 복사본에 반영
  3. 원본 이미지 레이어는 그대로 유지(불변)

즉, “이미지 자체를 수정”하는 게 아니라, “컨테이너 writable layer에 사본을 만들어 그걸 수정”합니다.


4) 컨테이너 삭제해도 데이터 유지하기: 볼륨(Volume)과 마운트(Mount)

컨테이너 writable layer는 수명이 컨테이너에 종속됩니다. DB 같은 데이터를 유지하려면 컨테이너 밖(호스트 측)에 데이터 저장소를 붙여야 합니다.

Docker는 컨테이너의 writable layer 밖에 데이터를 두기 위해 대표적으로 아래 마운트를 제공합니다. (Docker Documentation)

  • Volume mounts: Docker가 관리하는 영구 저장소
  • Bind mounts: 호스트의 특정 경로를 컨테이너에 직접 연결
  • tmpfs mounts: 메모리에만 두는 임시 저장소(디스크 미기록)

4.1 Volume Mount (Docker-managed)

볼륨을 만들면 기본적으로 Docker data-root 아래(예: /var/lib/docker/volumes/...)에 저장됩니다.

docker volume create data_volume
docker volume ls
docker volume inspect data_volume

MySQL 예시(개념 동일):

docker run -d \
  --name mysql \
  --mount type=volume,src=data_volume,dst=/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=pass \
  mysql:8

볼륨/--mount 문법은 공식 문서에서도 --mount를 권장합니다(더 명시적이고 옵션을 모두 지원). (Docker Documentation)

4.2 Bind Mount (Host path ↔ Container path)

이미 호스트에 /data/mysql 같은 디렉터리가 있고, 그 위치에 그대로 저장하고 싶다면 바인드 마운트를 씁니다.

docker run -d \
  --name mysql \
  --mount type=bind,src=/data/mysql,dst=/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=pass \
  mysql:8

Bind mount는 “호스트 경로”가 핵심이고, 컨테이너는 그 경로를 마치 자기 디렉터리처럼 사용합니다. (Docker Documentation)

4.3 -v vs --mount (결론: --mount가 더 안전/명확)

  • -v(또는 --volume)는 짧고 편하지만, 문법이 암묵적이고 실수 여지가 큼
  • --mount는 길지만 type=...,src=...,dst=...로 명시적이라 운영에서 선호됨 (Docker Documentation)

4.4 tmpfs mount (메모리 기반)

“컨테이너가 내려가면 사라져도 되고, 디스크에 쓰고 싶지 않은” 임시 데이터는 tmpfs를 고려할 수 있습니다.

docker run -d \
  --name nginx \
  --mount type=tmpfs,dst=/cache \
  nginx:alpine

tmpfs는 source가 없고 메모리에만 존재합니다. (Docker Documentation)


5) 이 모든 걸 누가 처리할까? 스토리지 드라이버(Storage Driver)

여기까지의 핵심 동작들:

  • 레이어 아키텍처 유지
  • 컨테이너 writable layer 생성
  • Copy-on-Write(copy-up)
  • 레이어들을 합쳐 컨테이너에서 “하나의 파일 시스템처럼” 보이게 만들기

이런 작업을 실제로 수행하는 게 스토리지 드라이버(graph driver) 입니다.

Docker 문서에서 설명하는 대표 드라이버 예시는 다음과 같습니다.

  • overlay2 (OverlayFS 기반)
  • btrfs
  • zfs
  • (과거) aufs, devicemapper, legacy overlay 등 (Docker Documentation)

5.1 현재 사용 중인 스토리지 드라이버 확인

docker info | grep -i "Storage Driver"

5.2 “어떤 드라이버가 기본이냐”는 OS/환경에 따라 다르지만, 요즘 리눅스는 보통 overlay2

Docker 공식 문서의 “Select a storage driver” 가이드는 리눅스 배포판에서 classic driver 기본값으로 overlay2를 폭넓게 안내합니다(예: Ubuntu의 기본 classic driver도 overlay2). (Docker Documentation)

또한 Docker는 제거/폐기되는 드라이버 목록을 별도 문서로 관리하는데, 예를 들어 AUFS는 제거된 항목으로 정리되어 있습니다. (Docker Documentation)

현업 팁: 스토리지 드라이버 변경은 데이터 마이그레이션 이슈가 생길 수 있어(기존 /var/lib/docker 구조가 달라짐) 사전에 백업/이관 계획이 필요합니다.


6) 실전에서 바로 써먹는 확인/디버깅 커맨드 모음

레이어/이미지 캐시 확인

docker image history <IMAGE>
docker image inspect <IMAGE> | jq '.[0].RootFS'

컨테이너의 마운트(볼륨/바인드) 확인

docker inspect <CONTAINER> | jq '.[0].Mounts'

디스크 사용량 확인

docker system df
docker system df -v

overlay2를 쓰는 경우(리눅스) “합쳐진 뷰(merged)”가 실제로 마운트되어 있음

mount | grep overlay
# 또는
cat /proc/mounts | grep overlay

7) Kubernetes와 연결해서 보면 더 잘 보이는 포인트(짧게)

쿠버네티스에서도 컨테이너 런타임이 결국 같은 계열 개념을 씁니다.

  • 이미지 레이어(불변) + 컨테이너 writable layer(휘발)
  • 영구 데이터는 볼륨(PV/PVC) 같은 외부 스토리지로 분리
  • 노드의 스토리지 드라이버/파일시스템 특성이 성능과 운영 안정성에 영향

Docker에서 이 구조를 확실히 잡아두면, 쿠버네티스의 볼륨/스토리지/이미지 동작도 훨씬 자연스럽게 연결됩니다.


마무리 정리

  • Docker 데이터의 기본 저장 위치는 리눅스에서 대개 /var/lib/docker
  • 이미지는 레이어드(read-only) 구조, 컨테이너는 그 위에 writable layer를 얹어 실행
  • 이미지 파일을 수정하는 것처럼 보여도 실제로는 Copy-on-Write로 writable layer에 복사본을 만들어 수정
  • 컨테이너 삭제에도 데이터를 유지하려면 Volume mount / Bind mount / tmpfs를 목적에 맞게 사용 (Docker Documentation)
  • 이 모든 레이어/CoW/마운트 동작의 핵심 구현체가 스토리지 드라이버이며, 요즘 리눅스는 보통 overlay2가 주류 (Docker Documentation)

'CKA' 카테고리의 다른 글

Storage - Kubernetes CSI(Container Storage Interface)  (0) 2026.01.03
Storage - Docker의 Storage Driver vs Volume Driver  (0) 2026.01.03
Security - 정리(2)  (1) 2026.01.02
Security - Custom Controller & Operator  (0) 2026.01.02
Security - Custom Resource Definition  (0) 2026.01.02
'CKA' 카테고리의 다른 글
  • Storage - Kubernetes CSI(Container Storage Interface)
  • Storage - Docker의 Storage Driver vs Volume Driver
  • Security - 정리(2)
  • Security - Custom Controller & Operator
5jyan5
5jyan5
  • 5jyan5
    jyan
    5jyan5
  • 전체
    오늘
    어제
    • 분류 전체보기 (243)
      • 김영한의 스프링 핵심 원리(기본편) (8)
      • 김영한의 스프링 핵심 원리 - 고급편 (11)
      • 김영한의 스프링 MVC 1편 (1)
      • 김영한의 스프링 DB 1편 (3)
      • 김영한의 스프링 MVC 2편 (3)
      • 김영한의 ORM 표준 JPA 프로그래밍(기본편) (9)
      • 김영한의 스프링 부트와 JPA 활용2 (2)
      • 김영한의 실전 자바 - 중급 1편 (1)
      • 김영한의 실전 자바 - 고급 1편 (9)
      • 김영한의 실전 자바 - 고급 2편 (9)
      • Readable Code: 읽기 좋은 코드를 작성.. (2)
      • 김영한의 실전 자바 - 고급 3편 (9)
      • CKA (119)
      • 개발 (37)
      • 경제 (4)
      • 리뷰 (1)
      • 정보 (2)
  • 블로그 메뉴

    • 링크

    • 공지사항

    • 인기 글

    • 태그

      Target
      락
      스레드
      김영한
      cglib
      typequery
      hibernate5module
      requset scope
      @args
      조회 성능 최적화
      양방향 맵핑
      프록시
      @discriminatorcolumn
      jpq
      프록시 팩토리
      @discriminatorvalue
      WAS
      빈 후처리기
      jdk 동적 프록시
      자바
      Thread
      JPQL
      gesingleresult
      고급
      log trace
      @within
      버퍼
      페치 조인
      reentarantlock
      단방향 맵핑
    • 최근 댓글

    • 최근 글

    • hELLO· Designed By정상우.v4.10.2
    5jyan5
    Storage - Docker의 Storage
    상단으로

    티스토리툴바