為什么你的Docker鏡像比我的大一倍?這五個(gè)致命差異是關(guān)鍵……
我還記得,有一次一個(gè)初級(jí)開發(fā)者對(duì)比了我們?yōu)橥粋€(gè) Node.js 應(yīng)用構(gòu)建的 Docker 鏡像后,這樣問我。
我們用了相同的基礎(chǔ)鏡像、相同的依賴、相同的應(yīng)用。但我的構(gòu)建結(jié)果更輕、更快、也更容易調(diào)試。
“秘訣是什么?”
沒有魔法。
只有經(jīng)驗(yàn)。
在本文中,我將展示 5 個(gè)真實(shí)可用的 Dockerfile 技巧——資深開發(fā)者每天都在用的技巧,這些技巧能節(jié)省時(shí)間、減少膨脹,并讓容器更適合生產(chǎn)環(huán)境。
每一條都會(huì)詳細(xì)解釋,并附帶真實(shí)可運(yùn)行的示例。
技巧 1:最小化層(Minimize Layers)
假設(shè)你要在基于 Debian 的鏡像里安裝一些工具:
# Junior way
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get clean每條 RUN 指令都會(huì)新建一個(gè)鏡像層。這不僅讓鏡像變大,還會(huì)拖慢構(gòu)建速度和緩存利用率。
資深開發(fā)者會(huì)這樣寫:
# Senior way
RUN apt-get update && \
apt-get install -y curl && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*為什么重要:
- 把命令合并到一條 RUN 里,能減少層數(shù)。
- rm -rf /var/lib/apt/lists/* 會(huì)清除臨時(shí)包元數(shù)據(jù)——省下幾 MB。
- && 保證只要有一條命令失敗,構(gòu)建會(huì)立刻停止,避免留下不完整狀態(tài)。
動(dòng)手試試:
- 分別用兩種寫法執(zhí)行 docker build,再 docker images 對(duì)比大小。
- 你會(huì)發(fā)現(xiàn)資深版本明顯更小、更干凈。
技巧 2:使用 .dockerignore
你不會(huì)把 .git 文件夾上傳到生產(chǎn)環(huán)境——那為什么要把它放進(jìn) Docker 構(gòu)建上下文?
默認(rèn)情況下,所有文件都會(huì)被送進(jìn) Docker 守護(hù)進(jìn)程,除非你顯式忽略。
問題所在:
# Without .dockerignore
COPY . .如果本地目錄里有:
- node_modules/
- .git/
- logs/
- test data…
它們?nèi)紩?huì)被復(fù)制進(jìn)去——既拖慢構(gòu)建,又撐爆鏡像。
解決辦法:
在項(xiàng)目根目錄新建一個(gè)名為 .dockerignore 的文件:
node_modules
.git
*.log
Dockerfile現(xiàn)在只有相關(guān)文件會(huì)被打包。
高級(jí)提示:
執(zhí)行下面這條命令,看看究竟哪些文件被送進(jìn)了 Docker:
docker build . --no-cache --progress=plain你會(huì)震驚于沒有 .dockerignore 時(shí)有多少“垃圾”被復(fù)制進(jìn)去。
技巧 3:多階段構(gòu)建,打造干凈的生產(chǎn)鏡像
這是 Docker 最強(qiáng)大、卻常被初級(jí)開發(fā)者忽略的功能之一。
場(chǎng)景:
- 你在構(gòu)建 React 應(yīng)用。
- 你需要 node 來構(gòu)建,但運(yùn)行時(shí)并不需要它。
初級(jí) Dockerfile:
FROM node:18
WORKDIR /app
COPY . .
RUN npm install && npm run build
CMD ["npx", "serve", "build"]最終鏡像里殘留了所有構(gòu)建工具——無謂的膨脹。
資深 Dockerfile(多階段):
# Stage 1: Builder
FROM node:18 as builder
WORKDIR /app
COPY . .
RUN npm install && npm run build
# Stage 2: Production
FROM nginx:alpine
COPY --from=builder /app/build /usr/share/nginx/html好處:
- 最終鏡像體積極小。
- 沒有 node_modules、沒有源碼、沒有構(gòu)建工具。
- 更安全、更快速。
多階段構(gòu)建尤其適用于:
- 前端應(yīng)用
- Java 應(yīng)用(Maven/Gradle 構(gòu)建)
- Go 應(yīng)用(靜態(tài)二進(jìn)制輸出)
構(gòu)建并運(yùn)行:
docker build -t myapp .
docker run -p 80:80 myapp你的應(yīng)用將由 nginx 提供,全程看不到 node。
技巧 4:鎖定鏡像版本(Pin Image Versions)
這一點(diǎn)常常導(dǎo)致生產(chǎn)環(huán)境噩夢(mèng)。
危險(xiǎn)寫法:
FROM python:latest今天能跑,可明天 latest 更新后呢?
構(gòu)建可能失敗,應(yīng)用可能崩潰。
穩(wěn)妥寫法:
FROM python:3.11.6-slim務(wù)必鎖定到:
- 具體版本號(hào)
- 盡可能用精簡(jiǎn)變體(slim、alpine)
為什么重要:
docker pull python:latest
docker pull python:3.11.6-slim對(duì)比體積——再想想 CI/CD 時(shí)的可靠性。
注意:
別盲目給所有語(yǔ)言都用 alpine——某些庫(kù)因 musl 兼容性問題會(huì)編譯失敗。務(wù)必先測(cè)試。
技巧 5:用 HEALTHCHECK 給容器加上自愈能力
資深工程師的標(biāo)志之一,就是讓系統(tǒng)能在故障時(shí)自動(dòng)恢復(fù)。
Docker 的 HEALTHCHECK 正好能做到。
初級(jí)開發(fā)者常忽略:
大多數(shù)初級(jí) Dockerfile 只是簡(jiǎn)單啟動(dòng)應(yīng)用:
CMD ["node", "server.js"]可一旦容器跑起來,就沒人知道應(yīng)用是否還活著。
- 應(yīng)用假死怎么辦?
- 拋出異常但進(jìn)程沒退怎么辦?
- 啟動(dòng)成功,幾分鐘后又崩了怎么辦?
Docker 會(huì)愉快地顯示容器“運(yùn)行中”,即使里面的應(yīng)用已經(jīng)壞了。
資深 Dockerfile:
資深開發(fā)者會(huì)定義 HEALTHCHECK,定期檢查應(yīng)用是否仍然正常。
HEALTHCHECK --interval=30s --timeout=10s \
CMD curl -f http://localhost:8080/health || exit 1每 30 秒執(zhí)行一次 curl:
- 返回 200 → Docker 認(rèn)為容器健康。
- 失敗 → Docker 標(biāo)記為不健康。
參數(shù)拆解:
- --interval=30s:每 30 秒檢查一次。
- --timeout=10s:超過 10 秒沒響應(yīng)就判失敗。
- --start-period=10s:給應(yīng)用 10 秒啟動(dòng)時(shí)間,期間不計(jì)入失敗。
- --retries=3:連續(xù) 3 次失敗才標(biāo)記為不健康。
- CMD:真正的健康檢查命令——這里訪問 /health。
實(shí)際項(xiàng)目中的價(jià)值:
- 開發(fā)階段:及早發(fā)現(xiàn)啟動(dòng)時(shí)的問題。
- 預(yù)發(fā)/生產(chǎn):Docker Swarm、Kubernetes、ECS 會(huì)自動(dòng)重啟被標(biāo)為不健康的容器。
- CI/CD:在運(yùn)行測(cè)試或發(fā)布前,更有信心容器真的啟動(dòng)成功了。
如果應(yīng)用沒有 /health 接口,也可以用其他方式:
- 檢查某個(gè)文件是否存在
- 看端口有沒有監(jiān)聽
- 甚至測(cè)數(shù)據(jù)庫(kù)連不連得通
附加小技巧:進(jìn)程級(jí)健康檢查
如果你的應(yīng)用沒 HTTP 接口——比如 CLI 工具、TCP 服務(wù)、后臺(tái)worker——照樣能用 HEALTHCHECK,只要盯著進(jìn)程在不在。
可以嘗試:
HEALTHCHECK --interval=30s --timeout=10s \
CMD pgrep myserver || exit 1pgrep myserver 查找名為 myserver 的進(jìn)程。
找到 → 退出碼 0,容器健康。
找不到 → 非 0,容器不健康。
適用于:
- 非 HTTP 服務(wù)
- 純二進(jìn)制或腳本
- 遺留守護(hù)進(jìn)程
真實(shí)示例:
Java 應(yīng)用這樣啟動(dòng):
CMD ["java", "-jar", "myapp.jar"]健康檢查可寫成:
HEALTHCHECK CMD pgrep java || exit 1簡(jiǎn)單有效,確保主進(jìn)程沒悄悄崩潰。
最后的話
這些 Dockerfile 技巧不是什么高深火箭科學(xué),而是讓容器更快、更小、更安全的實(shí)用習(xí)慣。
快速回顧:
- 合并 RUN 命令,減少鏡像層。
- 用 .dockerignore 提速構(gòu)建。
- 多階段構(gòu)建,生成精簡(jiǎn)生產(chǎn)鏡像。
- 鎖定版本,避免未來驚嚇。
- 加入 HEALTHCHECK,實(shí)現(xiàn)監(jiān)控與自愈。
下次用 Docker 時(shí),挑一兩招試試——看看你的配置能干凈多少。
作者丨Ujjawal Rohra 編譯丨Rio
來源丨網(wǎng)址:https://medium.com/@ujjawalr/5-dockerfile-tricks-that-separate-senior-developers-from-juniors-bcd9846d8b7f
























