容器的可寫層不是 volume,rebuild 會整層丟掉。哪些路徑會存活不能用猜的,docker inspect 一行就能問出來。
發布於 2026-09-06・約 4 分鐘
我在自己的開發容器裡工作。要用某個工具,順手裝:
npm install -g some-cli apt install -y some-tool
裝好了,能用了,繼續工作。三週後改了 Dockerfile 重建映像檔,兩個工具都不見了,~/.bashrc 裡的別名不見了,/etc 底下調過的設定不見了。
~/.bashrc
/etc
「它能用」跟「它明天還在」是兩件完全不同的事。
一個跑著的容器,檔案系統是疊出來的:
映像檔層(唯讀):Dockerfile 裡 RUN、COPY 產生的東西。重建映像檔會照 Dockerfile 重跑一次。
RUN
COPY
可寫層 / overlay(讀寫,但是暫時的):容器啟動之後你新增或修改的所有檔案,只要不在 volume 裡,都落在這一層。它跟著容器實體走:docker rm 掉容器,這層就沒了;docker compose build 之後重建容器,也是新的一層。
docker rm
docker compose build
Volume / bind mount(讀寫,會留下):明確掛進去的路徑。
在容器裡打字裝的東西,全部落在第二層。那一層不會被備份,因為備份工具備的是 volume;也不會被 Dockerfile 記錄,因為它不在 Dockerfile 裡。它只存在於「這一個當下跑著的容器」裡面。
哪些路徑有掛載,docker inspect 直接告訴你:
docker inspect
docker inspect <容器> --format '{{range .Mounts}}{{.Destination}}{{"\n"}}{{end}}'
輸出就是這個容器裡「會留下來」的路徑清單。沒出現在這份清單裡的路徑,全部是 overlay。
想連來源一起看:
docker inspect <容器> \ --format '{{range .Mounts}}{{.Type}} {{.Source}} -> {{.Destination}}{{"\n"}}{{end}}'
我把我這台的答案寫進專案文件裡,四個路徑:程式碼與設定檔的工作目錄、兩個工具的狀態目錄、一個唯讀的備份來源。除此之外全部是暫時的——包括 /usr/local/lib/node_modules、/usr/local/bin、/etc、/root/.bashrc、/root/.config、/root/.ssh、/tmp。
/usr/local/lib/node_modules
/usr/local/bin
/root/.bashrc
/root/.config
/root/.ssh
/tmp
寫下來這件事有用,因為需要判斷的當下通常很趕,而「這個路徑到底有沒有備份」不是可以憑印象回答的問題。
新的相依套件一律進 Dockerfile。
RUN npm install -g some-cli@1.4.2
版本要釘。從外部下載的二進位檔連 SHA256 一起驗:
RUN curl -fsSL -o /tmp/tool.bz2 https://example.com/tool-0.18.1.bz2 \ && echo "abc123... /tmp/tool.bz2" | sha256sum -c - \ && bunzip2 -c /tmp/tool.bz2 > /usr/local/bin/tool \ && chmod +x /usr/local/bin/tool \ && rm /tmp/tool.bz2
嫌麻煩就會在容器裡直接裝,然後三週後再經歷一次同樣的事。
新的祕密一律進 env 檔,而且那個檔要在 volume 上。 不要 export、不要寫進 ~/.bashrc、不要硬寫在原始碼裡。export 連重開機都活不過,.bashrc 活不過 rebuild。
export
.bashrc
程式碼與資料一律放在有掛載的路徑底下。 放在別的地方就等於沒存檔。
上面那些規則的敵人不是「不知道」,是「趕時間,之後再補」。之後不會補。
比較有效的做法是讓偏離變得會被發現。我排了一支每天跑的檢查腳本,比對「Dockerfile 宣告要裝的東西」跟「容器裡實際有的東西」,不一致就推一則通知給我。這樣偏離最多存活一天,而不是存活到下一次 rebuild 才用消失的方式讓我知道。
最小版本不用寫程式,記一份基準然後比對就好:
# 基準(剛 build 完的乾淨容器) docker exec app sh -c 'ls /usr/local/bin; npm ls -g --depth=0' > baseline.txt # 之後任何時候 docker exec app sh -c 'ls /usr/local/bin; npm ls -g --depth=0' > now.txt diff baseline.txt now.txt
有 diff 就代表有東西是「只存在於這個容器實體裡」的。要嘛補進 Dockerfile,要嘛承認它是暫時的。
Volume 的可攜性比想像中好。它不是綁在這台機器的某個資料夾上——docker volume 建出來的具名 volume 是 Docker 自己管的,換機器時用 docker run --rm -v vol:/data -v $PWD:/backup alpine tar czf /backup/vol.tgz /data 打包搬過去就好。
docker volume
docker run --rm -v vol:/data -v $PWD:/backup alpine tar czf /backup/vol.tgz /data
所以「把程式碼放在 volume 裡」不會讓你被綁死在這台機器上。反而是「放在 overlay 裡」才會——那份東西哪裡都去不了,連留在原地都做不到。
標籤:Docker、volume、備份、自架服務