Docker Volume 資料卷詳解

深入理解 Docker Volume 的概念、類型、使用方式和最佳實踐


目錄


Volume 基本概念

Volume 是什麼?

Volume = Docker 的資料持久化機制
       = 獨立於 Container 生命週期的儲存
       = 可以在 Container 間共享的資料

核心特點

  • Container 刪除後,Volume 資料仍保留
  • 可以在多個 Container 間共享
  • 由 Docker 統一管理
  • 效能優於 Bind Mount

Volume vs Container 檔案系統

Container 檔案系統(臨時)
├─ 可讀寫層
├─ 資料隨 Container 消失
└─ 無法跨 Container 共享

Volume(持久化)
├─ 獨立儲存
├─ Container 刪除後保留
└─ 可以跨 Container 共享

為什麼需要 Volume

問題場景

# 啟動 MySQL Container
docker run -d --name db mysql

# 在 Container 中寫入資料
docker exec db mysql -e "CREATE DATABASE myapp;"

# 刪除 Container
docker rm -f db

# 重新啟動
docker run -d --name db mysql

# ❌ 資料消失了!
# Database 'myapp' 不存在

Volume 解決方案

# 使用 Volume
docker run -d \
  --name db \
  -v mysql-data:/var/lib/mysql \
  mysql

# 寫入資料
docker exec db mysql -e "CREATE DATABASE myapp;"

# 刪除 Container
docker rm -f db

# 重新啟動(使用同一個 Volume)
docker run -d \
  --name db \
  -v mysql-data:/var/lib/mysql \
  mysql

# ✅ 資料還在!
# Database 'myapp' 存在

Volume 的類型

三種 Volume 類型

1. Named Volume(具名 Volume)
   docker run -v mydata:/app/data

2. Bind Mount(綁定掛載)
   docker run -v /host/path:/container/path

3. Anonymous Volume(匿名 Volume)
   docker run -v /app/data

類型對比

特性 Named Volume Bind Mount Anonymous Volume
管理方式 Docker 管理 手動管理 Docker 管理
路徑位置 Docker 目錄 任意位置 Docker 目錄
可移植性
效能 最佳 較好 最佳
跨平台
識別方式 名稱 完整路徑 Volume ID
生命週期 手動刪除 永久 隨 Container
適用場景 生產環境 開發環境 臨時資料

Volume 的架構

Named Volume 架構

主機(Host)
├─ /var/lib/docker/volumes/
│  ├─ mydata/
│  │  └─ _data/            ← Volume 實際資料
│  │     ├─ file1.txt
│  │     └─ file2.txt
│  │
│  └─ mysql-data/
│     └─ _data/
│        └─ mysql/         ← MySQL 資料檔案

Container
└─ /app/data/              ← 掛載點
   ├─ file1.txt           ← 對應 Volume 中的檔案
   └─ file2.txt

Bind Mount 架構

主機(Host)
└─ /home/user/project/
   ├─ src/
   │  └─ app.js
   └─ config/
      └─ config.json

Container
└─ /app/                   ← 掛載點
   ├─ src/
   │  └─ app.js           ← 直接對應主機檔案
   └─ config/
      └─ config.json

三種類型的視覺對比

Named Volume
┌──────────────────────────────────────┐
│ Container: /app/data                 │
│    ↕                                 │
│ Docker Volume: mydata                │
│    ↕                                 │
│ Host: /var/lib/docker/volumes/mydata │
└──────────────────────────────────────┘

Bind Mount
┌──────────────────────────────────────┐
│ Container: /app/src                  │
│    ↕                                 │
│ Host: /home/user/project/src         │
└──────────────────────────────────────┘

Anonymous Volume
┌──────────────────────────────────────┐
│ Container: /app/temp                 │
│    ↕                                 │
│ Docker Volume: a1b2c3d4e5f6...       │
│    ↕                                 │
│ Host: /var/lib/docker/volumes/a1b2.. │
└──────────────────────────────────────┘

Volume 使用方式

Named Volume

# 1. 創建 Volume
docker volume create mydata

# 2. 查看 Volume 列表
docker volume ls
# DRIVER    VOLUME NAME
# local     mydata

# 3. 查看 Volume 詳細資訊
docker volume inspect mydata
# Mountpoint: /var/lib/docker/volumes/mydata/_data

# 4. 使用 Volume
docker run -d \
  --name app \
  -v mydata:/app/data \
  myapp

# 5. 查看 Volume 使用情況
docker ps --format "table {{.Names}}\t{{.Mounts}}"

# 6. 刪除 Volume(必須先停止使用的 Container)
docker rm -f app
docker volume rm mydata

# 7. 刪除所有未使用的 Volume
docker volume prune

Bind Mount

# 方式 1:使用 -v
docker run -d \
  --name dev \
  -v $(pwd)/src:/app/src \
  -v $(pwd)/config:/app/config \
  myapp

# 方式 2:使用 --mount(更明確)
docker run -d \
  --name dev \
  --mount type=bind,source=$(pwd)/src,target=/app/src \
  --mount type=bind,source=$(pwd)/config,target=/app/config \
  myapp

# 唯讀掛載
docker run -d \
  --name app \
  -v $(pwd)/config:/app/config:ro \
  myapp

Anonymous Volume

# 創建匿名 Volume
docker run -d \
  --name temp \
  -v /app/cache \
  myapp

# 查看 Anonymous Volume
docker volume ls
# DRIVER    VOLUME NAME
# local     a1b2c3d4e5f6...

# Container 停止後自動刪除 Volume
docker run -d \
  --name temp \
  --rm \
  -v /app/cache \
  myapp

Volume 指令速查

# 創建 Volume
docker volume create <name>

# 列出所有 Volume
docker volume ls

# 查看 Volume 詳細資訊
docker volume inspect <name>

# 刪除 Volume
docker volume rm <name>

# 刪除所有未使用的 Volume
docker volume prune

# 刪除特定 Volume(強制)
docker volume rm -f <name>


實戰範例

範例 1:MySQL 資料持久化

# 創建 Volume
docker volume create mysql-data

# 啟動 MySQL
docker run -d \
  --name mysql \
  -e MYSQL_ROOT_PASSWORD=secret \
  -v mysql-data:/var/lib/mysql \
  mysql:8

# 連接並創建資料
docker exec mysql mysql -uroot -psecret \
  -e "CREATE DATABASE myapp; USE myapp; CREATE TABLE users (id INT, name VARCHAR(50));"

# 刪除 Container
docker rm -f mysql

# 重新啟動(資料仍在)
docker run -d \
  --name mysql \
  -e MYSQL_ROOT_PASSWORD=secret \
  -v mysql-data:/var/lib/mysql \
  mysql:8

# 驗證資料
docker exec mysql mysql -uroot -psecret \
  -e "USE myapp; SHOW TABLES;"
# ✅ 'users' 表仍然存在

範例 2:PostgreSQL 資料持久化

# 創建 Volume
docker volume create postgres-data

# 啟動 PostgreSQL
docker run -d \
  --name postgres \
  -e POSTGRES_PASSWORD=secret \
  -v postgres-data:/var/lib/postgresql/data \
  postgres:15

# 資料持久化位置
# Container: /var/lib/postgresql/data
# Volume: postgres-data

Volume 實際應用

MongoDB

# 創建 Volume
docker volume create mongo-data

# 啟動 MongoDB
docker run -d \
  --name mongo \
  -v mongo-data:/data/db \
  mongo:6

# 資料持久化位置
# Container: /data/db
# Volume: mongo-data

2. 開發環境(Bind Mount)

# Node.js 開發環境
docker run -d \
  --name node-dev \
  -v $(pwd):/app \
  -v /app/node_modules \
  -w /app \
  -p 3000:3000 \
  node:18 \
  npm run dev

# 說明:
# -v $(pwd):/app              ← 掛載專案目錄
# -v /app/node_modules        ← 排除 node_modules(使用 Container 中的)
# -w /app                     ← 設定工作目錄
# -p 3000:3000                ← 映射 Port
# Python 開發環境
docker run -d \
  --name python-dev \
  -v $(pwd):/app \
  -w /app \
  -p 8000:8000 \
  python:3.11 \
  python -m http.server 8000

# 修改本機檔案 → Container 即時更新

3. 共享配置檔案

# 創建配置 Volume
docker volume create app-config

# 初始化配置檔案
docker run --rm \
  -v app-config:/config \
  alpine \
  sh -c "echo 'API_URL=https://api.example.com' > /config/app.env"

# App 1 使用配置
docker run -d \
  --name app1 \
  -v app-config:/app/config:ro \
  myapp

# App 2 使用相同配置
docker run -d \
  --name app2 \
  -v app-config:/app/config:ro \
  myapp

# 配置集中管理,多個 Container 共享

4. 日誌收集

# 創建日誌 Volume
docker volume create app-logs

# 應用程式寫入日誌
docker run -d \
  --name app \
  -v app-logs:/var/log/app \
  myapp

# 日誌收集器讀取日誌
docker run -d \
  --name logstash \
  -v app-logs:/logs:ro \
  logstash

# 多個 Container 共享日誌目錄

5. 靜態網站部署

# 使用 Bind Mount 部署靜態網站
docker run -d \
  --name nginx \
  -v $(pwd)/html:/usr/share/nginx/html:ro \
  -p 80:80 \
  nginx

# 修改本機 HTML → 網站即時更新

Volume 常見問題

問題 1:Volume 資料找不到

# 查看 Volume 實際位置
docker volume inspect mydata
# "Mountpoint": "/var/lib/docker/volumes/mydata/_data"

# 進入 Volume 查看檔案
# macOS/Windows(Docker Desktop)
docker run --rm -v mydata:/data alpine ls -la /data

# Linux
sudo ls -la /var/lib/docker/volumes/mydata/_data

問題 2:權限問題

# ❌ 錯誤:Permission denied
docker run -v $(pwd):/app myapp
# Error: Permission denied

# ✅ 解決方案 1:指定使用者
docker run --user $(id -u):$(id -g) \
  -v $(pwd):/app \
  myapp

# ✅ 解決方案 2:在 Dockerfile 中設定權限
FROM node:18
RUN groupadd -r appuser && useradd -r -g appuser appuser
USER appuser

問題 3:檔案不同步

# 問題:修改本機檔案,Container 中看不到

# 檢查掛載點
docker inspect <container> | grep Mounts -A 20

# 確認路徑正確
docker run -v $(pwd):/app alpine ls -la /app

# macOS/Windows:確認 Docker Desktop 檔案共享設定
# Settings → Resources → File Sharing

問題 4:Volume 佔用空間過大

# 查看所有 Volume
docker volume ls

# 查看 Volume 大小
docker system df -v

# 刪除未使用的 Volume
docker volume prune

# 刪除特定 Volume
docker volume rm <name>

問題 5:Bind Mount 效能問題(macOS/Windows)

# ❌ 慢:大量小檔案(如 node_modules)
docker run -v $(pwd):/app myapp

# ✅ 快:排除 node_modules,使用 Volume
docker run \
  -v $(pwd):/app \
  -v /app/node_modules \
  myapp

# 或使用 delegated/cached(macOS)
docker run -v $(pwd):/app:delegated myapp

Volume 備份與還原

備份 Volume

# 方法 1:使用 tar 備份
docker run --rm \
  -v mydata:/data \
  -v $(pwd):/backup \
  alpine \
  tar czf /backup/mydata-backup.tar.gz -C /data .

# 方法 2:使用臨時 Container
docker run --rm \
  -v mydata:/source:ro \
  -v $(pwd):/backup \
  alpine \
  sh -c "cd /source && tar czf /backup/backup-$(date +%Y%m%d).tar.gz ."

還原 Volume

# 創建新 Volume
docker volume create mydata-restore

# 還原資料
docker run --rm \
  -v mydata-restore:/data \
  -v $(pwd):/backup \
  alpine \
  tar xzf /backup/mydata-backup.tar.gz -C /data

# 使用還原的 Volume
docker run -d \
  --name app \
  -v mydata-restore:/app/data \
  myapp

自動備份腳本

#!/bin/bash
# backup-volume.sh

VOLUME_NAME=$1
BACKUP_DIR="/backups"
DATE=$(date +%Y%m%d-%H%M%S)
BACKUP_FILE="$BACKUP_DIR/${VOLUME_NAME}-${DATE}.tar.gz"

docker run --rm \
  -v $VOLUME_NAME:/data:ro \
  -v $BACKUP_DIR:/backup \
  alpine \
  tar czf /backup/$(basename $BACKUP_FILE) -C /data .

echo "Backup completed: $BACKUP_FILE"

# 使用方式
# ./backup-volume.sh mysql-data

在 Container 間複製資料

# 從 Volume A 複製到 Volume B
docker run --rm \
  -v volume-a:/source:ro \
  -v volume-b:/dest \
  alpine \
  sh -c "cp -a /source/. /dest/"

Volume 效能優化

效能對比

Named Volume     → 最快(原生 Docker 儲存)
Bind Mount       → 較快(Linux)/ 較慢(macOS/Windows)
Anonymous Volume → 最快(原生 Docker 儲存)

Linux 效能

# Linux:所有類型效能都很好
# Named Volume 和 Bind Mount 效能接近
docker run -v mydata:/data myapp         # 快
docker run -v /host/path:/data myapp     # 快

macOS/Windows 效能優化

# ❌ 慢:大量檔案的 Bind Mount
docker run -v $(pwd):/app node:18 npm install

# ✅ 快:使用 delegated
docker run -v $(pwd):/app:delegated node:18 npm install

# ✅ 快:排除大量小檔案(如 node_modules)
docker run \
  -v $(pwd):/app \
  -v /app/node_modules \
  node:18 npm install

# ✅ 最快:使用 Named Volume
docker volume create node-modules
docker run \
  -v $(pwd):/app \
  -v node-modules:/app/node_modules \
  node:18 npm install

快取模式(macOS)

# consistent:強一致性(預設,最慢)
-v $(pwd):/app:consistent

# cached:主機 → Container 快取(較快)
-v $(pwd):/app:cached

# delegated:Container → 主機快取(最快)
-v $(pwd):/app:delegated

# 使用建議
# - 開發環境:delegated(程式碼修改)
# - 輸出檔案:cached(build 輸出)
# - 資料庫:consistent(資料一致性)

Volume 最佳實踐

1. 選擇適當的 Volume 類型

# ✅ 生產環境:使用 Named Volume
docker run -d -v mysql-data:/var/lib/mysql mysql

# ✅ 開發環境:使用 Bind Mount
docker run -d -v $(pwd):/app myapp

# ✅ 臨時資料:使用 Anonymous Volume
docker run -d -v /tmp myapp

2. 使用有意義的 Volume 名稱

# ❌ 不好
docker volume create vol1

# ✅ 好
docker volume create myapp-mysql-data
docker volume create myapp-uploads
docker volume create myapp-logs

3. Volume 生命週期管理

# 定期清理未使用的 Volume
docker volume prune

# 為重要 Volume 設定備份
# 參考「Volume 備份與還原」章節

# 使用 --rm 自動清理臨時 Volume
docker run --rm -v /tmp myapp

4. 權限管理

# 設定適當的使用者權限
docker run --user $(id -u):$(id -g) \
  -v $(pwd):/app \
  myapp

# 唯讀掛載(防止意外修改)
docker run -v config-data:/app/config:ro myapp

5. 使用 Docker Compose 管理 Volume

# docker-compose.yml
version: '3.8'

services:
  db:
    image: mysql:8
    volumes:
      - mysql-data:/var/lib/mysql

  app:
    build: .
    volumes:
      - ./src:/app/src          # 開發環境
      - app-logs:/app/logs       # 日誌
      - app-uploads:/app/uploads # 上傳檔案

volumes:
  mysql-data:
    driver: local
  app-logs:
    driver: local
  app-uploads:
    driver: local

6. 安全性考量

# 敏感資料使用 Volume(不要用 Bind Mount)
docker volume create secrets
docker run -v secrets:/secrets:ro myapp

# 不要在 Volume 中存放敏感配置
# 使用 Docker Secrets 或環境變數

# 限制 Volume 權限
docker run -v data:/data:ro myapp  # 唯讀

7. 監控 Volume 使用情況

# 查看 Volume 使用空間
docker system df -v

# 定期檢查未使用的 Volume
docker volume ls -f dangling=true

# 設定 Volume 清理腳本
# 參考「Volume 備份與還原」章節的自動備份腳本

常見問題

Q1: Volume 和 Bind Mount 有什麼區別?

A:

  • Named Volume:由 Docker 管理,跨平台,效能最佳,推薦生產環境
  • Bind Mount:手動管理路徑,適合開發環境,macOS/Windows 效能較差

Q2: Volume 資料存在哪裡?

A: Linux 在 /var/lib/docker/volumes/,macOS/Windows 在 Docker Desktop 虛擬機中。使用 docker volume inspect 查看。

Q3: Container 刪除後 Volume 會消失嗎?

A: Named Volume 不會消失,需要手動刪除。Anonymous Volume 會隨 Container 刪除。

Q4: 如何備份 Volume?

A: 使用臨時 Container 將 Volume 打包成 tar 檔案:

docker run --rm -v mydata:/data -v $(pwd):/backup alpine \
  tar czf /backup/backup.tar.gz -C /data .

總結

核心要點

  • Volume 提供獨立於 Container 的持久化儲存
  • 三種類型:Named Volume(推薦)、Bind Mount(開發)、Anonymous Volume(臨時)
  • Named Volume 效能最佳,跨平台,由 Docker 統一管理
  • 使用 Volume 實現資料庫持久化、配置共享、日誌收集
  • 定期備份重要 Volume,使用 prune 清理未使用資源

快速參考

操作 指令 說明
創建 docker volume create 建立 Volume
查看 docker volume ls 列出所有
刪除 docker volume rm 刪除指定
清理 docker volume prune 刪除未使用
使用 -v name:/path 掛載 Volume
備份 tar czf backup.tar.gz 打包備份

建立日期:2025-11-10 最後更新:2025-11-18

🔗相關文章