
容器不是魔法,也不是輕量 VM。它是作業系統用 namespace 和 cgroup 隔離出來的 process。搞清楚這一點,你就不會在容器出問題時一臉茫然。
先講結論
容器 = Image(不可變的檔案層)+ Runtime(啟動管理)+ Isolation(namespace + cgroup)。Docker 只是其中一個實作。理解 OCI 標準,你就能在 Docker、containerd、Kubernetes 之間自由切換,而不是被某個工具綁死。
Image:為什麼你的 build 這麼慢?
Image 是一層一層疊起來的檔案系統差異。改了第 5 層,第 1-4 層可以從快取拿。所以把「不常變動」的東西放前面(裝套件),「常變動」的放後面(複製程式碼)。
很多人的 Dockerfile 第一行就 COPY . .,然後每次改一行 code 就得重裝所有套件。那不是 Docker 慢,是你的 Dockerfile 寫法讓快取完全失效。
FROM node:20-alpine AS builder
WORKDIR /app
# 先複製 package files,裝套件(這層很少變)
COPY package.json package-lock.json ./
RUN npm ci
# 再複製程式碼(這層常變)
COPY . .
RUN npm run build
# 最終 image 不需要 build 工具
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package.json ./
EXPOSE 3000
CMD ["node", "dist/server.js"]多階段 build 除了讓 image 變小,更重要的是減少攻擊面——最終 image 裡沒有 npm、沒有 build tool、沒有原始碼。
Runtime:Docker 不是唯一選擇
容器執行有分層:Docker CLI / API → containerd(管理 lifecycle)→ runc(實際用 namespace + cgroup 建立容器)。
Kubernetes 早就不直接用 Docker 了,改用 containerd + runc。所以當你聽到「K8s 棄用 Docker」時不用恐慌,你的 image 完全不受影響,因為 OCI 標準讓大家講同一種語言。
Namespace 和 Cgroup:容器隔離的真相
Namespace 讓每個 container「以為」自己有獨立的 PID、網路、檔案系統。Cgroup 控制 CPU 和記憶體上限。
但這裡有個重要的認知:namespace 不是安全邊界。容器共享同一個 kernel,如果 kernel 有漏洞,容器內的 process 有機會逃逸到 host。所以高安全需求的場景會用 gVisor(加一層 kernel 代理)或 Kata Containers(直接跑 micro VM)。
如果你不設 cgroup limit 呢?一個容器吃光記憶體,整台 host 上所有容器一起死。我看過有人跑 npm install 把 8GB RAM 吃完,其他 container 全部 OOM killed。
治理重點:不要只用 latest
# 不要這樣
docker pull nginx:latest
# 要這樣
docker pull nginx:1.25latest 不是「最新穩定版」的意思,它只是一個普通的 tag 名稱。今天的 latest 和昨天的 latest 可能是完全不同的東西。prod 用 latest 就是在玩俄羅斯輪盤。
其他治理基本功:image 做漏洞掃描(Trivy / Grype)、rootless 模式跑容器、log 走 stdout/stderr 讓外部收集器處理。
容器讓部署變簡單了,但也讓「不懂底層就出事」變得更容易了。花 30 分鐘搞懂 namespace 和 cgroup,比讀 10 篇 Docker 教學有用。