4.1 資料持久化:容器毀滅資料還在#
一個危險的事實#
複習 1.2 節:容器的修改寫在可寫層,刪除容器(docker rm),可寫層就消失。
用 PostgreSQL 來看這件事有多危險:
docker run -d -p 5432:5432 --name db -e POSTGRES_PASSWORD=x postgres
# ……使用一段時間,資料庫裡累積了重要資料……
docker rm db # 刪除容器
docker run -d -p 5432:5432 --name db -e POSTGRES_PASSWORD=x postgres
# 資料全沒了。
容器天生是「用完即丟」的設計,但資料需要活得比容器久。 解法就是掛載(Mount):把主機上的某個位置「借給」容器存資料,容器刪了,主機上的資料還在。
flowchart LR
subgraph 主機
H[主機磁碟<br/>資料在這裡,活得比容器久]
end
subgraph 容器
C[容器內部路徑<br/>/var/lib/postgresql/data]
end
H <-.->|掛載 -v| C方式一:Bind Mounts(掛載本地目錄)#
直接把主機的某個目錄映射進容器,語法與 -p 類似:
docker run -d -p 5432:5432 --name db \
-v /Users/me/pgdata:/var/lib/postgresql/data \
-e POSTGRES_PASSWORD=mysecret \
postgres
語法:-v <主機路徑>:<容器路徑>。容器把資料寫進 /var/lib/postgresql/data 時,實際寫在主機的 /Users/me/pgdata。
Bind Mounts 最經典的用途:開發時的熱重載——把程式碼目錄掛進容器,改主機上的檔案,容器內立即生效:
docker run --rm -p 8000:8000 \
-v $(pwd):/app \
-w /app \
python:3.12 python app.py
# 改主機上的 app.py,重新請求即看到新結果(搭配自動重載工具)
方式二:Docker Volumes(容器專用磁區)#
Docker 管理的專用儲存區(存在 Docker 自己的目錄下,主機使用者不必管路徑):
# 建立與查看 volume
docker volume create pgdata
docker volume ls
# 使用:只寫 volume 名稱,不寫主機路徑
docker run -d -p 5432:5432 --name db \
-v pgdata:/var/lib/postgresql/data \
-e POSTGRES_PASSWORD=mysecret \
postgres
# 刪除
docker volume rm pgdata
Bind Mounts 與 Volumes 怎麼選?#
| Bind Mounts | Docker Volumes | |
|---|---|---|
| 路徑由誰管理 | 你自己指定 | Docker 管理 |
| 適合場景 | 開發時掛程式碼(要改檔案) | 存放資料庫、正式環境資料 |
| 可攜性 | 依賴主機路徑(macOS/Windows 路徑不同) | 跨平台、可由 volume 驅動支援 |
| 效能 | macOS/Windows 上較慢(檔案系統轉譯) | 較好 |
實務慣用法:開發掛程式碼用 Bind Mounts,存資料庫資料用 Volumes。這樣記就對了。
驗證持久化#
親手驗證一次,印象最深刻:
docker run --rm -v pgdata:/data alpine sh -c "echo hello > /data/a.txt"
docker run --rm -v pgdata:/data alpine cat /data/a.txt # hello
兩個容器(甚至都已刪除)透過同一個 volume 共享資料——資料屬於 volume,不屬於任何容器。
本章小結#
- 刪除容器,容器內資料就消失——容器天生用完即丟
- 掛載讓資料活得比容器久:
-v 主機路徑:容器路徑 - Bind Mounts:掛本地目錄,適合開發熱重載
- Volumes:Docker 管理的專用磁區,適合資料庫與正式環境
- 慣用法:開發用 Bind Mounts,存資料用 Volumes
想一想#
- 為什麼「刪除容器資料就消失」?對應到 1.2 節的哪個機制?
- 開發時想「改主機上的程式碼、容器內立即生效」,該用哪種掛載?
- 為什麼正式環境的資料庫建議用 Volumes 而不是 Bind Mounts?