chundev
日期:2026-04-01 環境:Ubuntu 24.04 (amd64)、Docker CE 29.x、Apple Silicon Mac 作為打包機
在完全無網路的 Linux 機器上部署 Docker 容器化應用,需要解決三個問題:(1) Docker Engine 本身的離線安裝(含所有遞迴依賴的 .deb 套件)、(2) 應用 Docker images 的跨平台打包、(3) 一鍵安裝腳本。最大的坑不是 image 打包,而是 Docker CE 的 .deb 套件有 30+ 個 transitive dependencies(iptables、nftables、libsystemd 等),必須用 apt-cache depends --recurse 完整解析後一起帶過去。
需要將一個容器化的 Node.js API 服務(含 nginx HTTPS reverse proxy)部署到一台完全沒有外網的 Ubuntu 24.04 伺服器。目標機器:
需要打包一個「USB 隨身碟就能帶過去」的離線安裝 bundle。
離線部署分為兩階段:
prepare-bundle.sh
├── [1] 下載 Docker CE .deb 套件(含所有遞迴依賴)
├── [2] Build 應用程式 Docker images(指定 linux/amd64 平台)
├── [3] docker save 匯出 images
└── [4] 複製 compose 設定 + 產生 SSL 憑證
install.sh
├── [1] dpkg -i 安裝 Docker CE(從 .deb 包)
├── [2] docker load 載入 images
└── [3] docker compose up -d 啟動服務
最終 bundle 結構:
offline-bundle/
├── docker-debs/ # Docker CE + 所有依賴的 .deb 套件
│ ├── containerd.io_*.deb
│ ├── docker-ce_*.deb
│ ├── docker-ce-cli_*.deb
│ ├── docker-compose-plugin_*.deb
│ ├── docker-buildx-plugin_*.deb
│ ├── iptables_*.deb
│ ├── libnftables1_*.deb
│ ├── ... (共 30+ 個套件)
│ └── install-docker.sh
├── app-images.tar.gz # docker save 匯出的 images
├── docker-compose.yml
├── nginx/
│ ├── nginx.conf
│ └── ssl/
│ ├── server.crt
│ └── server.key
└── install.sh # 一鍵安裝腳本
Docker 官方文件說下載 5 個 .deb 就好:containerd.io、docker-ce、docker-ce-cli、docker-buildx-plugin、docker-compose-plugin。但在全新的 Ubuntu 24.04 上 dpkg -i 這 5 個套件會失敗——因為它們還依賴 iptables、nftables、libsystemd0、libseccomp2 等系統套件。
dpkg: dependency problems prevent configuration of docker-ce:
docker-ce depends on iptables; however:
Package iptables is not installed.
docker-ce depends on libsystemd0; however:
Package libsystemd0 is not installed.
error while loading shared libraries: libnftables.so.1: cannot open shared object file
在一個相同 OS 版本的連網容器中,用 apt-cache depends --recurse 遞迴解析所有 transitive dependencies:
PKGS="containerd.io docker-ce docker-ce-cli docker-compose-plugin docker-buildx-plugin"
# 解析所有遞迴依賴(過濾掉非套件行)
ALL_DEPS=$(apt-cache depends --recurse \
--no-recommends --no-suggests \
--no-conflicts --no-breaks \
--no-replaces --no-enhances \
$PKGS 2>/dev/null | grep "^\w" | sort -u)
# 批次下載所有 .deb
apt-get download $ALL_DEPS 2>&1 | grep -v "^W:" || true
關鍵參數說明:
| 參數 | 作用 |
|---|---|
--recurse |
遞迴解析,不只看第一層依賴 |
--no-recommends |
排除「建議安裝」的套件 |
--no-suggests |
排除「推薦安裝」的套件 |
grep "^\w" |
過濾掉 apt-cache 輸出的中間行(如 Depends:、PreDepends: 標籤行) |
sort -u |
去重 |
Docker CE 在 Ubuntu 24.04 上的完整依賴約 30-35 個套件,包括:
containerd.io, docker-ce, docker-ce-cli, docker-compose-plugin, docker-buildx-plugin,
iptables, nftables, libnftables1, libnetfilter-conntrack3, libnfnetlink0,
libip4tc2, libip6tc2, libxtables12, libsystemd0,
libseccomp2, libapparmor1, libdevmapper1.02.1, ...
| 方法 | 優點 | 缺點 |
|---|---|---|
apt-cache depends --recurse + apt-get download |
簡單直接 | 可能包含虛擬套件名稱 |
apt-get install --download-only |
APT 自己解析最可靠 | 需要完整的 APT cache |
aptly |
可建立完整離線 repo | 學習成本高 |
apt-offline |
專為離線設計 | 需額外安裝工具 |
| Docker 靜態二進位檔 | 無套件依賴問題 | 缺少 systemd 整合、compose plugin |
建議: 如果只是偶爾做離線部署,apt-cache depends --recurse 最實用。如果要長期維護離線環境,考慮用 aptly 建立本地 repo mirror。
打包機是 Apple Silicon Mac (arm64),但目標部署機器是 Linux amd64。如果不指定平台,docker build 和 docker pull 會拉 arm64 的 image,在 amd64 機器上無法執行。
在所有 Docker 操作中加上 --platform linux/amd64:
# Build 應用 image(指定平台)
docker build --platform linux/amd64 -t myapp .
# Pull 第三方 image(指定平台)
docker pull --platform linux/amd64 nginx:alpine
# 啟動容器測試(指定平台)
docker run --platform linux/amd64 myapp
docker build --platform)通常穩定# 多個 image 可以一次 save
docker save myapp:latest nginx:alpine | gzip > images.tar.gz
壓縮選擇:
| 方法 | 壓縮率 | 速度 | 建議場景 |
|---|---|---|---|
gzip |
~3-5x | 中等 | 通用,相容性好 |
pigz |
~3-5x | 快(多核並行) | 大型 image |
zstd -T0 |
~3-6x | 最快 | 現代系統推薦 |
#!/bin/bash
set -e
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
if command -v docker &> /dev/null; then
echo "Docker 已安裝: $(docker --version)"
exit 0
fi
# 安裝所有 .deb(忽略順序問題,dpkg 會自己處理)
dpkg -i "$SCRIPT_DIR"/docker-debs/*.deb 2>&1 || true
# 啟動 Docker daemon
if command -v systemctl &>/dev/null; then
systemctl enable docker && systemctl start docker
else
# 無 systemd 環境的 fallback
dockerd &>/var/log/dockerd.log &
for i in $(seq 1 30); do
docker info &>/dev/null 2>&1 && break
sleep 1
done
fi
docker --version && docker compose version
重點:
dpkg -i *.deb 後面的 || true 是因為第一輪可能因依賴順序失敗,但所有 .deb 都在本地時,dpkg 最終會解析完成dpkg -i *.deb || true,再 dpkg --configure -a#!/bin/bash
set -e
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
# Step 1: 安裝 Docker(如果尚未安裝)
if ! command -v docker &> /dev/null; then
bash "$SCRIPT_DIR/docker-debs/install-docker.sh"
fi
# Step 2: 載入 Docker images
docker load < "$SCRIPT_DIR/images.tar.gz"
# Step 3: 啟動服務
cd "$SCRIPT_DIR"
docker compose up -d
離線部署最怕的是「以為帶齊了其實少了一個 .deb」。在開發機上測試離線安裝流程:
# 啟動乾淨 Ubuntu(privileged 模式以支援 Docker daemon)
docker run -d --name test --privileged --cgroupns=host \
--platform linux/amd64 ubuntu:24.04 sleep 3600
# 複製 bundle 進去
docker cp offline-bundle test:/root/bundle
# 斷網(關鍵步驟!)
docker exec test bash -c "
echo '127.0.0.1 localhost' > /etc/resolv.conf
ip route del default 2>/dev/null || true
"
# 驗證網路已斷
docker exec test bash -c "timeout 2 bash -c 'echo > /dev/tcp/8.8.8.8/53' 2>/dev/null" \
&& echo "⚠️ 網路未斷" || echo "✅ 網路已斷"
# 確認沒有預裝 Docker
docker exec test which docker && echo "❌ 測試無效" || echo "✅ 可以開始"
# 執行離線安裝
docker exec test bash /root/bundle/install.sh
| 步驟 | 驗證項目 |
|---|---|
| 啟動乾淨 OS | 確認沒有預裝 Docker |
| 複製 bundle | 確認檔案完整 |
| 立即斷網 | 確認之後的步驟全部離線 |
| 安裝 Docker | dpkg 不報錯、docker daemon 啟動 |
| 載入 images | docker load 成功 |
| 啟動服務 | docker compose up、API 可回應 |
在 Apple Silicon Mac 上透過 QEMU 模擬 amd64 容器做 DinD 測試時,最後一步 docker compose up 可能因 overlay fs mount 失敗而無法執行。這是 QEMU 模擬的限制,不是 bundle 本身的問題。 驗證 Docker 安裝成功(docker --version、docker compose version)即可。
完整的端對端測試應在原生 amd64 Linux 機器或 CI(如 GitHub Actions 的 ubuntu-latest runner)上進行。
開發這套離線部署流程時遇到的問題:
| 嘗試 | 做法 | 結果 | 原因 |
|---|---|---|---|
❌ 只帶 5 個 Docker .deb |
只下載官方文件列出的套件 | dpkg 安裝失敗 |
缺少 iptables、nftables 等 transitive dependencies |
| ❌ 用 Docker-in-Docker image 測試 | 用 docker:dind 當測試容器 |
測試無效 | DinD image 本身已預裝 Docker,無法驗證離線安裝 |
❌ 不指定 --platform |
Mac 上直接 build | 產出 arm64 image | Mac 預設建 arm64,目標機器是 amd64 |
| ❌ DinD + QEMU 做完整測試 | 在模擬的 amd64 容器內跑 Docker daemon 再 compose up | overlay mount failed | QEMU user-mode 不支援 overlay2 的 mount syscall |
✅ apt-cache depends --recurse |
遞迴解析所有依賴並下載 | 成功取得 30+ 個 .deb |
完整依賴樹,不遺漏 |
| ✅ 乾淨 Ubuntu + 斷網測試 | 全新 Ubuntu → 複製 bundle → 斷網 → 安裝 | Docker CE 安裝成功 | 正確的測試方法論 |
Docker CE 的 transitive dependencies 是離線部署最大的坑。 官方文件只列 5 個 .deb,但實際需要 30+ 個(iptables、nftables、libseccomp 全家桶)。apt-cache depends --recurse 是解析完整依賴的關鍵指令。
離線安裝測試必須「先斷網、再安裝」。 如果先安裝再斷網,無法驗證離線安裝的完整性。測試容器必須是全新 OS,不能用已有 Docker 的 image。
跨平台打包時,所有 Docker 操作都要加 --platform。 包括 build、pull、run、save。漏掉任何一個都會拿到錯誤架構的 artifact。
DinD + QEMU 模擬有根本性限制。 overlay2 storage driver 在 QEMU user-mode 下不工作。Mac 上做 amd64 DinD 測試只能驗證到「Docker 安裝成功」,完整的 compose 測試需要原生 amd64 機器。
dpkg -i *.deb || true 再 dpkg --configure -a 是安裝大量 .deb 的可靠模式。 不需要手動排序依賴順序,讓 dpkg 自己處理。