← 전체 글로 돌아가기

Docker

SQLite DB 파일을 Docker 볼륨에 두는 이유

컨테이너 레이어에 쓴 파일은 컨테이너가 재시작되면 사라진다. SQLite처럼 단일 파일 DB는 반드시 볼륨에 마운트해야 한다.

Docker 컨테이너는 기본적으로 stateless다. 컨테이너 안에서 파일을 만들고 수정해도, 컨테이너를 재시작하거나 새 이미지로 재배포하면 그 파일은 없어진다. 이건 이미지 레이어 위에 쌓이는 쓰기 레이어(writable layer)가 컨테이너 수명에 묶여 있기 때문이다.

SQLite는 DB 전체가 .db 파일 하나다. 이 파일을 볼륨 밖에 두면 배포할 때마다 DB가 초기화된다.

볼륨 마운트 방법

docker-compose.yml 예시:

services:
  app:
    image: myapp:latest
    volumes:
      - sqlite_data:/app/data
    environment:
      - DATABASE_URL=file:/app/data/prod.db

volumes:
  sqlite_data:

sqlite_data는 네임드 볼륨이다. Docker가 /var/lib/docker/volumes/sqlite_data/_data에 실제 파일을 보관한다. 컨테이너가 죽어도, 이미지가 바뀌어도 이 위치의 파일은 그대로 남는다.

네임드 볼륨 vs 바인드 마운트

바인드 마운트(./data:/app/data)를 쓰면 호스트의 특정 경로와 직접 연결된다. 경로를 직접 관리할 수 있어서 백업이 쉽고 ls로 바로 볼 수 있다는 장점이 있다. 단, 호스트의 파일시스템 권한과 소유자 문제가 생길 수 있다.

네임드 볼륨은 Docker가 관리하는 격리된 공간이다. 호스트 경로를 신경 쓸 필요가 없고, Swarm 환경에서도 동작한다는 장점이 있다. 개인 프로젝트나 단일 서버라면 바인드 마운트가 오히려 직관적이다.

WAL 모드 활성화

여러 프로세스나 스레드가 SQLite에 동시 접근할 때는 WAL(Write-Ahead Logging) 모드를 켜두는 게 안전하다. 기본 모드(journal mode=DELETE)는 쓰기 중에 읽기가 블로킹된다.

PRAGMA journal_mode=WAL;

Prisma를 쓴다면 datasource에서 설정할 수 있다.

datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
  // connection_limit=1 도 SQLite에서는 유용하다
}

볼륨 백업

볼륨에 두면 데이터가 유지되지만, 서버 자체가 날아가면 볼륨도 함께 사라진다. 주기적으로 파일을 복사해두는 게 필요하다.

# 실행 중 컨테이너에서 DB 파일 꺼내기
docker cp app_container:/app/data/prod.db ./backup/prod-$(date +%Y%m%d).db

# 또는 볼륨에서 직접
docker run --rm -v sqlite_data:/data -v "$(pwd)/backup":/backup \
  alpine cp /data/prod.db /backup/prod-$(date +%Y%m%d).db

SQLite 파일을 복사할 때는 쓰기가 없는 시점에 하거나, .backup 명령으로 일관된 스냅샷을 만드는 게 안전하다.