跳至主要內容

Docker Volume 資料持久化:別讓容器重啟就丟失資料

Docker Volume 資料持久化:別讓容器重啟就丟失資料

Docker 容器的核心特性是「無狀態」——容器停止後,容器內的所有資料都會消失。對於資料庫、上傳的檔案、設定資料,這顯然不是我們想要的行為。Docker Volume 就是解決資料持久化問題的官方機制。

為什麼需要 Volume?

# 示範問題
docker run -d --name my-db postgres:16
docker exec my-db psql -U postgres -c "CREATE TABLE test (id serial, name text);"
docker exec my-db psql -U postgres -c "INSERT INTO test (name) VALUES ('hello');"

# 停止並移除容器
docker stop my-db && docker rm my-db

# 重新啟動
docker run -d --name my-db postgres:16
docker exec my-db psql -U postgres -c "SELECT * FROM test;"
# ERROR: relation "test" does not exist
# 資料消失了!

Volume 的三種類型

1. Named Volume(命名卷,推薦)

由 Docker 管理,存儲在 Docker 的數據目錄中:

# 建立 Volume
docker volume create my-data

# 查看 Volume 列表
docker volume ls

# 查看 Volume 詳細資訊
docker volume inspect my-data

# 使用 Volume 啟動容器
docker run -d \
  --name my-db \
  -v my-data:/var/lib/postgresql/data \
  postgres:16

# 容器移除後 Volume 仍然存在
docker rm -f my-db
docker volume ls  # my-data 仍在

# 重新啟動,資料恢復
docker run -d \
  --name my-db \
  -v my-data:/var/lib/postgresql/data \
  postgres:16

2. Bind Mount(綁定掛載)

將主機上的特定路徑掛載到容器:

# 將當前目錄掛載到容器
docker run -d \
  --name my-app \
  -v $(pwd)/data:/app/data \
  my-app-image

# 適用於開發時同步程式碼
docker run -d \
  --name dev-server \
  -v $(pwd):/app \
  -p 3000:3000 \
  node:20 \
  npm run dev

3. tmpfs Mount(記憶體掛載)

資料存在記憶體中,容器停止後消失,適用於敏感資料:

docker run -d \
  --name my-app \
  --tmpfs /tmp:rw,size=100m \
  my-app-image

Docker Compose 中的 Volume

# docker-compose.yml
version: '3.9'

services:
  postgres:
    image: postgres:16
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: admin
      POSTGRES_PASSWORD: secret
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql  # 初始化腳本
    ports:
      - "5432:5432"

  redis:
    image: redis:7-alpine
    volumes:
      - redis_data:/data
    command: redis-server --appendonly yes  # 啟用持久化

  minio:
    image: minio/minio
    command: server /data --console-address ":9001"
    environment:
      MINIO_ROOT_USER: minioadmin
      MINIO_ROOT_PASSWORD: minioadmin
    volumes:
      - minio_data:/data
    ports:
      - "9000:9000"
      - "9001:9001"

  app:
    build: .
    volumes:
      - ./uploads:/app/uploads  # bind mount,方便直接存取上傳的檔案
      - app_logs:/app/logs      # named volume,日誌
    depends_on:
      - postgres
      - redis

# 在最外層定義所有 named volumes
volumes:
  postgres_data:
  redis_data:
  minio_data:
  app_logs:

Volume 備份與還原

備份

# 方法一:使用輔助容器備份
docker run --rm \
  -v my-data:/source \
  -v $(pwd)/backup:/backup \
  alpine \
  tar czf /backup/my-data-$(date +%Y%m%d).tar.gz -C /source .

# 方法二:PostgreSQL 備份
docker exec my-db pg_dump -U postgres myapp > backup.sql

還原

# 從 tar 備份還原
docker run --rm \
  -v my-data:/target \
  -v $(pwd)/backup:/backup \
  alpine \
  tar xzf /backup/my-data-20240101.tar.gz -C /target

# 從 SQL 還原
cat backup.sql | docker exec -i my-db psql -U postgres myapp

Volume 清理

# 移除未使用的 Volume
docker volume prune

# 移除特定 Volume
docker volume rm my-data

# 查看 Volume 磁碟使用量
docker system df -v

實際案例:完整開發環境

# docker-compose.dev.yml
version: '3.9'

services:
  db:
    image: postgres:16
    volumes:
      - db_data:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: devpassword
      POSTGRES_DB: myapp_dev

  app:
    build:
      context: .
      target: development
    volumes:
      - .:/app              # 程式碼同步(bind mount)
      - /app/node_modules   # 避免覆蓋容器內的 node_modules
    environment:
      NODE_ENV: development
      DATABASE_URL: postgresql://postgres:devpassword@db/myapp_dev
    ports:
      - "3000:3000"
    command: npm run dev
    depends_on:
      - db

volumes:
  db_data:

注意 /app/node_modules 這個匿名 Volume 的技巧:它防止主機上的 node_modules(或不存在的情況)覆蓋容器內正確安裝的 node_modules

最佳實踐

  1. 生產環境用 Named Volume,不要用 bind mount,因為路徑在不同主機上可能不同
  2. 定期備份重要 Volume,特別是資料庫
  3. 在 Compose 中顯式宣告 Volume,避免意外的匿名 Volume 堆積
  4. 善用 docker system df 監控磁碟使用量
  5. 開發環境可以用 bind mount 同步程式碼,但要注意效能問題(特別是 macOS)

總結

Docker Volume 是容器化應用資料持久化的核心機制。Named Volume 適合生產環境的資料庫和檔案儲存;Bind Mount 適合開發時的程式碼同步。掌握 Volume 的備份還原流程,是確保生產環境資料安全的關鍵。

分享這篇文章