在有限空間的電腦上跑大檔案的 docker 儲存
Available in English
大家好!最近我手邊一台閒置已久的 Chromebook,心想著別讓它白白吃灰,就動了念頭想把它變成我的 Docker 開發簡單的伺服器。畢竟,用一台輕量級的機器來跑一些輕量的服務,很多事情都會方便很多。
但很快我就碰到了第一個,也是最直接的瓶頸:儲存空間。這台 Chromebook 內建的 eMMC 硬碟只有可憐的 32GB。對於日常使用或許還行,但你要知道,Docker 隨便 build
個 image、拉幾個 layer 下來,空間就迅速告急,根本不夠用!
問題: exFAT 權限問題
既然內建空間不夠,我的直覺反應就是把 Docker 的資料根目錄 (data root) 掛載到我平時沒怎麼在用的外接 USB 硬碟上。這樣一來,儲存空間的問題似乎迎刃而解了。
然而,新的問題接踵而至。之前為了讓這顆 USB 硬碟能在我的 Mac 和 Linux 上都能順利讀寫資料,我將它格式化成了 exFAT。結果現在問題來了,雖然儲存空間有了,但用在 Docker 上就有了 權限問題!尤其是一些 image 在 build
過程中會執行 chown
(變更檔案擁有者)這樣的操作,在 exFAT 格式下完全搞不定。
exFAT 雖然跨平台兼容性好,但它本身並不支援 Linux 系統所需的 POSIX 權限模型,這正是 chown
等命令無法生效的根本原因。起初試錯了好幾次才發覺這顆硬碟被我格式化成 exFAT(ex stands for 噁心,又稱 「又肥膩又噁心」)。
解方: 虛擬磁碟
既然 exFAT 不支援,那我可不可以在 exFAT 硬碟上 「偽造」一個支援 POSIX 權限的檔案系統 呢?
我的解決方案是:在 exFAT 硬碟上,先用 fallocate
或 dd
創造一個大大的虛擬磁碟檔案 (.img)。然後,將這個 .img
檔案 mount
到系統上,並把它 格式化成 ext4 格式。這樣一來,Docker 運行在 ext4 裡面,所有權限問題就都迎刃而解了!這就像是在一個不支援特定語言的國家裡,設立了一個說特定語言的「大使館」區域,讓所有與該語言相關的活動都能正常進行。
這個方法不僅完美解決了權限問題,還能讓我靈活地規劃 Docker 的儲存空間大小,不至於被 Chromebook 內建的捉襟見肘的 eMMC 空間所限制。
Step 1: 建立虛擬磁碟檔案 (.img)
首先,我需要在我那顆 exFAT 格式的 USB 硬碟上,建立一個足夠大的空檔案,這個檔案將作為我們的虛擬磁碟。我將它命名為 docker_disk.img
。
# 首先,進入你的 USB 硬碟掛載點。假設你的 USB 硬碟掛載在 /mnt/usb_drive
cd /mnt/usb_drive
# 建立一個 20GB 的虛擬磁碟檔案。你可以根據你的需求調整大小。
# 我使用了 status=progress 來顯示進度條,這樣等待時不會那麼焦慮。
sudo dd if=/dev/zero of=docker_disk.img bs=1M count=20000 status=progress
if=/dev/zero
:從/dev/zero
讀取資料,基本上就是用零來填充檔案。of=docker_disk.img
:輸出檔案的名稱。bs=1M
:設定區塊大小為 1 Megabyte。count=20000
:寫入 20000 個區塊,所以總大小為 20000 * 1MB = 20GB。如果你要更精確一點也可以以 2 為底計算。- ( $log(size) \in \mathbb{N}$)
Step 2: 格式化虛擬磁碟檔案為 ext4
現在,這個 docker_disk.img
檔案只是個空殼。我們需要將它內部格式化為 Linux 原生的 ext4
檔案系統,這樣它才能支援 POSIX 權限。
sudo mkfs.ext4 docker_disk.img
執行後,你會看到 mkfs.ext4
創建檔案系統的過程,包括 inode 數量、區塊大小等資訊。
Step 3: 掛載虛擬磁碟
檔案系統格式化好後,我們需要將這個虛擬磁碟檔案掛載到系統上的一個目錄,這樣它才能被當作一個正常的磁碟分區來使用。
# 建立一個掛載點,例如在 /mnt 或你的家目錄下
sudo mkdir /mnt/docker_data
# 掛載虛擬磁碟
sudo mount -o loop docker_disk.img /mnt/docker_data
-o loop
:這個選項至關重要,它告訴mount
命令將docker_disk.img
檔案視為一個迴圈設備 (loop device),就像它是一個物理硬碟一樣進行掛載。
Step 4: 整合 Docker 儲存
現在 /mnt/docker_data
已經是一個由 ext4
檔案系統支援的目錄了,它完全支援 chown
以及其他 POSIX 權限操作。接下來,我將其整合到 Docker 中。
Option A: 變更 Docker 的資料根目錄 (data-root) – 適用於進階需求
如果你希望將 Docker 的所有資料(包括 image、container、volume 等)都儲存在這個虛擬磁碟上,你可以修改 Docker 的 daemon.json
設定檔。這是一個比較徹底的改變,建議在沒有現有 Docker 資料時操作,或做好備份。
-
編輯或創建
/etc/docker/daemon.json
檔案:{ "data-root": "/mnt/docker_data/docker_root" }
-
建立
data-root
指定的目錄,並重啟 Docker 服務:sudo mkdir -p /mnt/docker_data/docker_root sudo systemctl restart docker
Option B: 透過 Volume 掛載到容器中 (我個人推薦,更靈活)
這是我最推薦且更靈活的方式。你可以將 /mnt/docker_data
中的特定目錄,以 Volume 的形式掛載到你需要權限支持的 Docker 容器內部。這意味著只有需要特殊權限的資料才需要透過這個 ext4 虛擬磁碟,其他 Docker 資料仍可留在預設位置。
例如,如果你的容器需要一個 /app/data
目錄來進行 chown
操作:
# 在虛擬磁碟中建立一個目錄,用於儲存容器的資料
sudo mkdir /mnt/docker_data/my_container_data
# 運行你的 Docker 容器,並將該目錄掛載進去
sudo docker run -v /mnt/docker_data/my_container_data:/app/data my_image
現在,在容器內的 /app/data
目錄中,無論是 chown
還是其他任何權限相關的操作,都能被底層的 ext4
檔案系統正確處理。
注意
掛載點權限: 在掛載後,/mnt/docker_data
目錄的擁有者通常是 root
。如果你希望你的普通使用者也能直接在這個目錄中寫入資料(例如建立檔案或目錄),你可能需要變更它的擁有權:
sudo chown -R youruser:youruser /mnt/docker_data
將 youruser
替換為你的實際使用者名稱。
性能考量: 這種虛擬磁碟的方式,相較於直接在原生 ext4
分區上運行,會存在輕微的性能開銷,因為多了一層抽象。但對於大多數開發或輕量級應用來說,這種開銷通常可以接受。
如何擴大 (Grow) 虛擬磁碟檔案的容量
隨著你的 Docker 專案越來越龐大,20GB 的虛擬磁碟可能不再夠用。不用擔心,這個虛擬磁碟檔案 (docker_disk.img
) 的大小是可以修改的。擴大 (Grow) 虛擬磁碟是比較簡單且安全的操作。
在進行任何大小調整之前:
1. 卸載檔案系統: 雖然 ext4
支援熱擴展(已掛載時調整大小),但為了安全起見,我建議先卸載它。
sudo umount /mnt/docker_data
2. 檔案系統檢查: 在調整大小之前,檢查以確保其完整性。
sudo e2fsck -f docker_disk.img
e2fsck
:檢查並修復ext2
/ext3
/ext4
檔案系統。-f
:forced alias,即使檔案系統看起來是乾淨的。
1. 擴展磁碟 image 檔案
你可以使用 truncate
或 dd
來增加底層 image 檔案的大小。truncate
對於稀疏檔案(dd if=/dev/zero
所創建的檔案)通常更快。
使用 truncate
(推薦,快速增加空間):
# 在現有檔案基礎上,額外增加 10GB 的空間
# 如果原始檔案是 20GB,這將使其變成 30GB
sudo truncate -s +10G docker_disk.img
+10G
:表示在當前檔案大小的基礎上增加 10 gigabytes。你也可以使用M
代表 megabytes 等單位。
使用 dd
(如果你偏好,但對於大容量增加會較慢):
# 在檔案末尾追加 10GB 的零
sudo dd if=/dev/zero of=docker_disk.img bs=1M seek=$(stat -c %s docker_disk.img) count=10000 conv=notrunc
seek=$(stat -c %s docker_disk.img)
:這告訴dd
從檔案的當前末尾開始寫入,stat -c %s
會返回檔案的位元組大小。count=10000
:追加 10000 MB (10GB)。conv=notrunc
:確保dd
是追加而不是截斷檔案。
2. 重新掛載磁碟 image
sudo mount -o loop docker_disk.img /mnt/docker_data
3. 調整 ext4
檔案系統大小
一旦底層檔案變大,你就可以告訴 ext4
檔案系統去使用這些新增的空間。
# 查找你的虛擬磁碟被分配到的 loop 設備 (例如:/dev/loop0)
losetup -a | grep docker_disk.img
# 執行 resize2fs 命令來擴展檔案系統
sudo resize2fs /dev/loop0 # 將 /dev/loop0 替換為實際的 loop 設備名稱
# 或者,如果檔案系統已經掛載,通常也可以直接:
# sudo resize2fs /mnt/docker_data
resize2fs
會自動檢測可用的最大空間,並將檔案系統調整到填滿為止。- 如果檔案系統已掛載,
resize2fs
通常可以進行「線上」擴展,意味著你不必先卸載它。然而,如果可能的話,卸載仍然是最安全的做法。
結語
雖然 Chromebook 內建的 eMMC 空間和 exFAT 硬碟的權限問題讓我碰了一鼻子灰,但透過在 exFAT 硬碟上建立 ext4 虛擬磁碟的巧妙方法,我成功地為我的 Docker 環境開闢了一片新天地。這不僅解決了儲存和權限的雙重痛點,也讓我對 Linux 檔案系統和 Docker 的底層運作有了更深刻的理解。
這是我首次使用 LLM 幫我潤稿,把我的備忘錄轉換成一篇完整的 blog 筆記。因為我的文筆本來就不太好,使用 LLM 潤飾後真的比較能夠容易閱讀許多。儘管許多地方都還是有濃厚的 「AI 味兒」,例如過多的條列和過於誇大的口述,都不太像是我會說出的話。之後我會再試試看怎麼樣寫出更容易閱讀的文章。(這段文字是我親筆寫出來的,要不然給 AI 潤我真的不知道他會給我潤成什麼鬼樣)
參考資料
- Can I expand the size of a file based disk image?
- Refined by Gemini 2.5 Flash