任務五:資料持久化¶
開始之前¶
任務目標
在這個任務中,你將學習:
- 理解容器資料短暫性(ephemeral)的問題
- 掌握三種掛載方式的差異與適用場景
- 學會使用 Volume 管理指令
- 理解開發與生產環境的掛載方式選擇
- 透過實作練習為資料庫容器設定資料持久化
容器資料的短暫性問題¶
Docker 容器具備短暫性(ephemeral)的特性,這意味著容器的檔案系統是暫時的。當容器被刪除時,容器內部的所有資料變更也會一併消失。
問題場景:
假設你在容器內執行一個資料庫,當容器被刪除後:
- 所有儲存的資料會消失
- 應用程式的設定檔變更會遺失
- 日誌檔案無法保留
這對需要持久化資料的應用程式(如資料庫、日誌系統、檔案儲存服務)來說是個嚴重問題。
解決方案:
Docker 提供了掛載(Mount)機制,讓容器可以存取 Host 的檔案系統或 Docker 管理的儲存空間,實現資料持久化。
三種掛載方式¶
Docker 提供三種掛載方式,各有不同的特性與適用場景:
Volume(Docker 管理的持久化儲存)¶
Volume 是由 Docker 完全管理的儲存空間,儲存在 Host 的特定目錄下(Linux 預設是 /var/lib/docker/volumes/)。
特性:
- Docker 負責管理 Volume 的生命週期
- 可以在多個容器間共享
- 支援 Volume 驅動程式(可擴充至遠端儲存)
- 資料與容器生命週期獨立
- 效能最佳(不受作業系統檔案權限影響)
語法:
適用場景:
- 生產環境的資料庫持久化
- 需要在多個容器間共享的資料
- 需要備份與遷移的資料
Bind Mount(指定 Host 路徑掛載)¶
Bind Mount 將 Host 的特定目錄或檔案掛載到容器內,直接對應實體路徑。
特性:
- 掛載 Host 的任意路徑
- 容器內的變更會直接反映到 Host
- Host 的變更也會直接反映到容器
- 受 Host 檔案權限影響
- 路徑必須在建立容器時明確指定
語法:
docker run -v /host/path:/container/path image_name
# 或使用 --mount(更明確的語法)
docker run --mount type=bind,source=/host/path,target=/container/path image_name
適用場景:
- 開發環境(即時同步程式碼變更)
- 掛載設定檔
- 需要直接存取 Host 檔案的情境
tmpfs(記憶體暫存)¶
tmpfs 將資料儲存在 Host 的記憶體中,不寫入檔案系統。容器停止時,資料會消失。
特性:
- 資料儲存在記憶體,讀寫速度極快
- 容器停止後資料消失
- 不會寫入 Host 的檔案系統
- 僅 Linux 支援
語法:
適用場景:
- 敏感資料暫存(如臨時密碼、金鑰)
- 需要高速讀寫的暫存檔案
- 不需要持久化的快取資料
三種掛載方式比較¶
| 特性 | Volume | Bind Mount | tmpfs |
|---|---|---|---|
| 管理方式 | Docker 管理 | 手動指定 Host 路徑 | 記憶體暫存 |
| 資料位置 | /var/lib/docker/volumes/ |
Host 的任意路徑 | Host 記憶體 |
| 資料持久化 | ✓ | ✓ | ✗(容器停止即消失) |
| 容器間共享 | ✓ | ✓ | ✗ |
| Host 存取 | 較不便 | 方便 | 無法存取 |
| 效能 | 最佳 | 良好 | 極快 |
| 適用場景 | 生產環境資料持久化 | 開發環境程式碼同步 | 敏感資料暫存 |
Volume 管理指令¶
Docker 提供一系列指令來管理 Volume,以下是最常用的指令:
建立 Volume¶
# 建立一個具名 Volume
docker volume create my_volume
# 建立時指定驅動程式與選項
docker volume create --driver local \
--opt type=nfs \
--opt o=addr=192.168.1.1,rw \
--opt device=:/path/to/dir \
my_nfs_volume
列出 Volume¶
# 列出所有 Volume
docker volume ls
# 使用過濾條件
docker volume ls --filter name=my
# 自訂輸出欄位
docker volume ls --format "{{.Name}}: {{.Driver}}"
查看 Volume 詳細資訊¶
# 檢視 Volume 詳細資訊
docker volume inspect my_volume
# 以 JSON 格式輸出
docker volume inspect --format '{{json .}}' my_volume
輸出範例:
[
{
"CreatedAt": "2024-01-15T10:30:00Z",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/my_volume/_data",
"Name": "my_volume",
"Options": {},
"Scope": "local"
}
]
刪除 Volume¶
注意: Volume 必須未被任何容器使用才能刪除。
清理未使用的 Volume¶
# 清理所有未被容器使用的 Volume
docker volume prune
# 清理時不詢問確認
docker volume prune -f
# 清理時顯示詳細資訊
docker volume prune --filter "label!=keep"
警告: prune 會刪除所有未掛載的 Volume,請謹慎使用。
Volume 使用範例¶
# 建立具名 Volume
docker volume create postgres_data
# 使用 Volume 執行容器
docker run -d \
--name postgres \
-v postgres_data:/var/lib/postgresql \
-e POSTGRES_PASSWORD=secret \
postgres:18
# 查看 Volume 資訊
docker volume inspect postgres_data
# 停止並刪除容器(Volume 不會被刪除)
docker stop postgres
docker rm postgres
# 使用相同 Volume 建立新容器(資料仍然存在)
docker run -d \
--name postgres_new \
-v postgres_data:/var/lib/postgresql \
-e POSTGRES_PASSWORD=secret \
postgres:18
# 清理(刪除容器與 Volume)
docker stop postgres_new
docker rm postgres_new
docker volume rm postgres_data
開發環境 vs 生產環境的使用建議¶
選擇適合的掛載方式取決於使用場景:
開發環境¶
推薦使用:Bind Mount
原因:
- 程式碼變更即時反映到容器(支援熱重載)
- 方便使用 IDE 編輯 Host 端檔案
- 可以直接存取容器產生的檔案(如日誌、測試報告)
範例:Node.js 開發環境¶
範例:Python 開發環境¶
docker run -d \
--name dev_api \
-v $(pwd):/app \
-w /app \
-p 8000:8000 \
python:3.12 \
sh -c "pip install -r requirements.txt && python app.py"
生產環境¶
推薦使用:Volume
原因:
- Docker 管理,無需擔心路徑問題
- 效能最佳化(不受 Host 檔案權限影響)
- 支援備份與遷移
- 與容器生命週期解耦,更可靠
範例:PostgreSQL 資料庫¶
docker run -d \
--name prod_postgres \
-v postgres_prod_data:/var/lib/postgresql \
-e POSTGRES_PASSWORD=secret \
postgres:18
範例:Nginx 日誌持久化¶
混合使用¶
實際專案中常會混合使用多種掛載方式:
docker run -d \
--name myapp \
-v app_data:/app/data \ # Volume:持久化應用資料
-v /host/config:/app/config:ro \ # Bind Mount:掛載設定檔(唯讀)
--tmpfs /app/tmp \ # tmpfs:暫存檔案
myapp:latest
實作練習:資料庫容器搭配 Volume 持久化¶
讓我們透過實作練習,體驗容器資料持久化的重要性。
步驟一:建立沒有 Volume 的 PostgreSQL 容器¶
# 啟動 PostgreSQL 容器(沒有 Volume)
docker run -d \
--name postgres_no_volume \
-e POSTGRES_PASSWORD=mysecret \
-e POSTGRES_USER=testuser \
-e POSTGRES_DB=testdb \
postgres:18
# 等待 PostgreSQL 啟動(約 5-10 秒)
docker logs postgres_no_volume
# 進入容器並建立測試資料
docker exec -it postgres_no_volume psql -U testuser -d testdb
在 PostgreSQL 命令列中執行:
-- 建立測試資料表
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
email VARCHAR(100)
);
-- 插入測試資料
INSERT INTO users (name, email) VALUES
('Alice', 'alice@example.com'),
('Bob', 'bob@example.com'),
('Charlie', 'charlie@example.com');
-- 查詢資料確認
SELECT * FROM users;
-- 離開 psql
\q
步驟二:刪除容器並觀察資料消失¶
# 停止並刪除容器
docker stop postgres_no_volume
docker rm postgres_no_volume
# 重新建立容器(沒有 Volume)
docker run -d \
--name postgres_no_volume \
-e POSTGRES_PASSWORD=mysecret \
-e POSTGRES_USER=testuser \
-e POSTGRES_DB=testdb \
postgres:18
# 等待啟動後進入容器
docker exec -it postgres_no_volume psql -U testuser -d testdb
在 PostgreSQL 命令列中執行:
結論:容器內的資料隨著容器刪除而消失。
步驟三:使用 Volume 實現資料持久化¶
# 建立 Volume
docker volume create postgres_data
# 啟動 PostgreSQL 容器並掛載 Volume
docker run -d \
--name postgres_with_volume \
-v postgres_data:/var/lib/postgresql \
-e POSTGRES_PASSWORD=mysecret \
-e POSTGRES_USER=testuser \
-e POSTGRES_DB=testdb \
postgres:18
# 等待啟動後進入容器
docker exec -it postgres_with_volume psql -U testuser -d testdb
在 PostgreSQL 命令列中執行:
-- 建立測試資料表
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
email VARCHAR(100)
);
-- 插入測試資料
INSERT INTO users (name, email) VALUES
('Alice', 'alice@example.com'),
('Bob', 'bob@example.com'),
('Charlie', 'charlie@example.com');
-- 查詢資料確認
SELECT * FROM users;
-- 離開 psql
\q
步驟四:驗證資料持久化¶
# 停止並刪除容器
docker stop postgres_with_volume
docker rm postgres_with_volume
# 使用相同 Volume 重新建立容器
docker run -d \
--name postgres_with_volume \
-v postgres_data:/var/lib/postgresql \
-e POSTGRES_PASSWORD=mysecret \
-e POSTGRES_USER=testuser \
-e POSTGRES_DB=testdb \
postgres:18
# 進入容器查詢資料
docker exec -it postgres_with_volume psql -U testuser -d testdb
在 PostgreSQL 命令列中執行:
-- 查詢資料(資料仍然存在!)
SELECT * FROM users;
-- 輸出:
-- id | name | email
-- ----+---------+---------------------
-- 1 | Alice | alice@example.com
-- 2 | Bob | bob@example.com
-- 3 | Charlie | charlie@example.com
\q
結論:使用 Volume 後,即使刪除容器,資料仍然保存在 Volume 中。
步驟五:查看 Volume 資訊¶
# 查看 Volume 詳細資訊
docker volume inspect postgres_data
# 查看 Volume 佔用空間
docker system df -v | grep postgres_data
步驟六:清理環境¶
# 停止並刪除容器
docker stop postgres_with_volume
docker rm postgres_with_volume
# 刪除 Volume
docker volume rm postgres_data
# 確認 Volume 已刪除
docker volume ls
練習延伸¶
嘗試以下變化來加深理解:
-
使用 Bind Mount 掛載設定檔
# 建立 PostgreSQL 設定檔 echo "max_connections = 200" > $(pwd)/postgresql.conf # 掛載設定檔(唯讀) docker run -d \ --name postgres_custom \ -v postgres_data:/var/lib/postgresql \ -v $(pwd)/postgresql.conf:/etc/postgresql/postgresql.conf:ro \ -e POSTGRES_PASSWORD=mysecret \ postgres:18 -c 'config_file=/etc/postgresql/postgresql.conf' -
備份與還原 Volume 資料
任務結束¶
完成!
恭喜你完成了這個任務!現在你已經學會:
- 理解容器資料短暫性(ephemeral)的問題
- 掌握三種掛載方式的差異與適用場景
- 學會使用 Volume 管理指令
- 理解開發與生產環境的掛載方式選擇
- 透過實作練習為資料庫容器設定資料持久化
你現在已經掌握 Docker 資料持久化的核心概念,能夠為不同類型的應用程式選擇適合的掛載方式,並使用 Volume 管理指令進行資料管理。在實際專案中,資料持久化是確保應用程式可靠運行的關鍵技術!