在 Docker 中掛載單一檔案所遇到的問題

程式技術

前幾天前輩來找我討論一個奇怪的問題,他說他在 Docker 容器中掛載單一檔案時,會遇到檔案同步效果時有時無的情況。他嘗試在啟動容器時掛載單一檔案,然後發現在主機上修改檔案的內容後,有時候修改的內容並不會同步到容器中。

這個問題我之前都沒有遇到過,因為我還真的沒有只掛載過單一檔案,都是選擇掛載一個資料夾。在找了一些資料後,我發現這個問題背後的原因很有趣,就趁機來水一篇文章 😆。

如何在 Docker 容器中掛載檔案?

在 Docker 中,你可以透過 --mount 來將主機上的某個資料夾掛載到容器中。 這個掛載是雙向同步的,當你在主機上修改資料夾底下的某個檔案,那麼容器中的檔案也會同步修改的內容。 反過來也是,當你在容器中修改某個檔案,那麼主機上的檔案也會同步修改。

過去掛載的指令是 --volume,但現在更建議使用功能更為方便且彈性的 --mount

想要啟動一個掛載本地資料夾的容器非常簡單,指令如下:

docker run -it --mount type=bind,source=./src,target=/app ubuntu:latest bash

如此一來就能啟動一個運行 Ubuntu 的容器,並將本機的 src/ 資料夾掛載到容器中的 app/。你對 src/ 底下檔案的任何修改,都會同步到容器中的 app/

如果你希望讓掛載資料夾底下的檔案在容器中是唯讀的,可以使用 readonlyro

docker run -it --mount type=bind,source=./src,target=/app,readonly ubuntu:latest bash

能不能掛載單一檔案?

簡單的介紹了 Docker 的掛載功能後,我們回到主題,容器能不能只掛載單一檔案,而不是資料夾呢?

可以!但不建議,因為同步的效果有可能會失效 😅。

為什麼會這樣呢?這是因為容器追蹤檔案的變化,是透過 inode (index node) 來達成的。如果掛載的 inode 發生變化產生一個新的 inode,那麼 Docker 並不會理會新的 inode,而是繼續關注舊的 inode。

inode 是許多類 Unix 檔案系統中的一種資料結構,主要用於描述檔案系統對象,包括檔案、資料夾、裝置檔案、Socket、管道等。

當我們在修改資料夾底下的檔案時,資料夾的 inode 是不會發生變化的,但是檔案的 inode 是有可能因為我們的修改,而產生一個新的 inode

例如 sed 或者 vim,透過這些工具來編輯檔案,會導致檔案的 inode 發生變更,帶有新 inode 的檔案並不會即時被掛載到容器中。

Docker 只會注意啟動時掛載的 inode 內容有沒有發生變化

簡單做個小實驗,建立一個新的檔案 plain.txt,並查看這個檔案的 inode。

touch plain.txt

# Use -i flag to print the file's file serial number (inode number).
ls -i

# Result:
# 262219571 plain.txt

我們可以看到 plain.txt 的 inode 是 262219571

接下來我們嘗試使用 Vim 來修改這個檔案,並在存檔後再次查看 inode。

# Use vim to update plain.txt, for example, adding 'Hello world!',
# then check inode again
ls -i

# Result:
# 262220007 plain.txt

你會發現 inode 發生了變化,變成了 262220007。因為 Docker 只會關注 inode 為 262219571 的檔案內容是否有變化,所以我們剛剛做的修改,並不會同步到容器中的檔案。

使用某些方式來修改檔案並不會導致檔案的 inode 發生變化,例如:

echo 'append a new line' >> plain.txt

使用 VSCode 修改檔案也能避免檔案 inode 發生變化。這就是為什麼掛載檔案的同步效果時有時無的原因。是不是很有趣呢?

參考資料

sharkHead
written by
sharkHead

持續努力中的後端打工仔,在下班後喜歡研究各種不同的技術。稍微擅長 PHP,並偶爾涉獵前端開發。個性就像動態語言般隨興,但渴望做事能像囉嗦的靜態語言那樣嚴謹。

0 則留言
新增留言
編輯留言