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

🔗相關文章