應該有不少人正為 Docker 環境太過沉重而頭痛吧。
我自己也曾多次接觸過非常卡頓的 Docker 環境,並且有一步一步把原因排除掉的經驗。
這次要介紹的是,針對本機的 Docker 環境(Windows)進行輕量化的方法,並搭配我實際執行過的步驟一起說明。
不過,其中包含像是刪除資源、移轉 named volume 等,如果省略確認與備份,可能會影響本機環境的操作。請先確認各步驟的注意事項,並先用工作副本或不重要的資料來測試。
請依照上方順序,從你自己的環境中看起來最可能有效的地方開始嘗試。
注意:.dockerignore、多階段建置、基底映像輕量化、善用建置快取,主要是改善建置、拉取、容器重建與啟動時間。它們不是直接加快運行中頁面回應速度的對策。
Docker 不會自動刪除建置快取、舊映像檔、已停止的容器。這些東西累積起來會壓迫磁碟空間,甚至讓整個主機的運作變慢。
步驟
docker system df
RECLAIMABLE 欄位的數值。如果累積到數十 GB,就要注意了docker ps -a
docker images
docker volume ls
docker system prune
如果你有保留停止中的容器作為復原用,執行前請務必先確認目標。刪除後若要重新建立由 Compose 管理的服務,請執行 docker compose up -d。
如果只是「感覺很慢」就直接挑對策,很容易做白工。先確認到底是哪裡出問題。
步驟
docker stats --no-stream
CPU % 和 MEM USAGE 欄位,檢查是否只有特定容器特別突出如果只有某一個容器異常高負載,問題大多不在 Docker 本身,而是應用程式程式碼的可能性較高。
頁面回應時間也要在修改前後進行測量。由於 PowerShell 裡的 curl 可能是 Invoke-WebRequest 的別名,因此這裡明確使用 curl.exe。
1..30 | ForEach-Object {
curl.exe -s -o NUL -w "TTFB=%{time_starttransfer}s TOTAL=%{time_total}s`n" http://localhost:8080/
}
TTFB 是第一個資料回傳前的時間,TOTAL 是整體回應完成的時間。
curl 能測到的是 HTTP 請求的回應時間。瀏覽器上的完整顯示時間還包含圖片、JavaScript、CSS 的載入、瀏覽器快取、外部服務處理時間等。最後確認時,請搭配瀏覽器開發者工具一起看。
Docker 預設的日誌驅動程式 json-file 若放著不管,日誌檔會無限制地膨脹。對於流量高的網站來說,這常常就是磁碟空間被吃光的原因。
步驟
docker-compose.yml,在變得很重的服務加上以下設定services:
web:
logging:
driver: json-file
options:
max-size: "10m"
max-file: "10"
docker compose up -d --force-recreate web
由於會重新建立既有容器,服務會暫時停止。web 請替換成實際的服務名稱。
max-file 是指定要保留的檔案數。因為這會讓你無法再往前查更早以前的日誌,所以使用這個設定時也要留意這個缺點。
很容易被忽略的一點,是分配給 Docker Desktop 本身的 CPU、記憶體設定。
步驟
C:\Users\使用者名稱\.wslconfig 中設定)docker stats 確認是否有改善即使是往增加配置的方向調整,如果壓迫到主機端的記憶體或 CPU,也可能讓包含 IDE 和瀏覽器在內的整體環境變慢。請小幅度調整,並用相同的測量方法比較效果。
我目前的環境是在 .wslconfig 裡這樣寫的。

如果把 Windows 主機上的專案直接做 bind mount,PHP、Node.js、Python 等在大量讀寫小檔案時,檔案存取可能會成為瓶頸。
步驟
C:\...)還是 WSL2 的 Linux 端(\\wsl$\...)從 Linux 端檔案系統做 bind mount,通常比從 Windows 端檔案系統做 bind mount 更容易得到較好的檔案存取效能。
尤其是大量使用 bind mount 的 Node.js 或 PHP 專案,通常更能感受到差異。
如果每次建置都變慢,很常見的情況是把 node_modules、.git 之類不必要的檔案也一起送進 Docker 了。
步驟
.dockerignore 檔案node_modules
.git
*.log
docker build .
如果建置時顯示的 Sending build context to Docker daemon 數值變小了,就代表有生效。
如果建置所需工具被原封不動地留在執行用映像檔裡,映像檔就會白白變大,容器啟動與重建也會變慢。
依照所使用的框架與語言,Dockerfile 的寫法會差很多。請先確認執行時需要的共用函式庫、設定檔、憑證、檔案權限後再套用。
步驟
FROM node:20 AS builder
WORKDIR /app
COPY . .
RUN npm ci && npm run build
FROM node:20-slim
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
CMD ["node", "dist/index.js"]
產物類型會依語言與框架而不同。以 PHP 來說,PHP 擴充、Composer 的 vendor、Apache 或 PHP-FPM 設定等,執行時需要的檔案都必須包含到最終階段。
docker images
不過,如果忘了複製產物或共用函式庫,啟動後就會出現錯誤,所以請確認啟動、資料庫連線、檔案上傳、圖片處理與主要頁面都正常。
如果 FROM 指定的基底映像檔太大,映像檔的 pull 與容器啟動就會花更多時間。
・Node.js:node:20 → node:20-slim
・PHP:php:8.2-apache → php:8.2-apache-slim,如果不需要 Apache,則可用 CLI 版 php:8.2-cli
・Python:python:3.12 → python:3.12-slim
步驟
-slim 或 -alpine)docker compose build
alpine 系列不是基於 glibc,而是 musl,因此有些相依套件可能無法運作。特別是用 apt 前提來建置 PHP 擴充功能時,要注意套件名稱在 alpine(apk)底下會改變。修改後請確認啟動、資料庫連線、登入、檔案上傳、圖片處理、主要頁面、批次處理都正常。
舊版 Docker builder 有時候只要原始碼稍微改一下,快取就不會命中,導致每次都得完整建置。
重點
・先只 COPY 相依性定義檔(例如 package.json、composer.json 等),先執行安裝,再 COPY 其餘原始碼;這樣在原始碼變更時,相依性層仍可保留快取並重複使用
COPY composer.json composer.lock ./
RUN composer install
COPY . .
在 BuildKit 中,也可以使用 cache mount,讓套件管理器的下載快取能在不同次建置之間重複利用。
# syntax=docker/dockerfile:1
RUN --mount=type=cache,target=/root/.npm npm ci
步驟
docker buildx ls
$env:DOCKER_BUILDKIT = "1"
docker compose build
在 Docker Desktop 裡有時本來就會預設使用 BuildKit,因此就算設定了環境變數,也不一定會有額外效果。
直接掛載主機目錄的 bind mount,尤其在 Windows 上,每次檔案存取都會牽涉跨越 OS 邊界的處理,因此效能很容易下降。像 DB 資料目錄這種頻繁讀寫的地方,改用 named volume 後,體感通常會有明顯差異。
步驟
docker compose exec -T db mysqldump -u root -p mydb > backup.sql
docker-compose.yml 的 volume 定義services:
db:
volumes:
- db-data:/var/lib/mysql
volumes:
db-data:
docker compose down
docker compose up -d --force-recreate db
docker compose exec -T db mysql -u root -p mydb < backup.sql
db、mydb、/var/lib/mysql 都只是範例,請替換成實際設定。若執行 docker compose down -v,named volume 也會被刪除,因此若要保留 DB 資料,請不要加 -v。
不只要確認備份有成功,也要確認能還原到另一個本機 volume 或工作副本。
・先做資源清理、找出瓶頸、設定日誌上限,絕對不吃虧。不過,docker system prune 請先確認刪除對象,並注意不要刪到 DB volume
・Docker Desktop 的資源配置與 WSL2 檔案共享方式,在資源不足或 bind mount 成為瓶頸時特別有效
・.dockerignore、多階段建置、基底映像輕量化、善用建置快取,主要是改善建置、拉取、容器重建與啟動時間
・切換成 named volume 適合 DB、快取等 I/O 成為瓶頸的情況。請務必先備份既有資料,並確認能成功還原
・請在修改前後測量 docker system df、docker stats 與頁面回應時間,並比較中位數與第 95 百分位
這次我介紹了自己實際做過的 Docker 環境輕量化、加速方法。
我自己的環境在這樣調整後,運作狀況明顯改善,建置速度與本機環境的頁面顯示也快了很多。
實際能不能感受到效果會依環境而異,因此如果照著這些方法做了還是沒改善,建議可以把 Docker 相關檔案交給 AI 讀取,問問看瓶頸到底在哪裡。
通常它會列出一長串改善建議,但如果不小心全部套用,環境也可能被破壞,所以哪些可以執行、哪些不行,還是得由你自己判斷。
先從安全的方法開始,一個一個試吧!
參考
・Docker 日誌設定:https://docs.docker.com/engine/logging/configure/
・Docker 資源限制:https://docs.docker.com/engine/containers/resource_constraints/
・Docker WSL2 最佳實務:https://docs.docker.com/desktop/features/wsl/best-practices/
・Docker Volume:https://docs.docker.com/engine/storage/volumes/
原文出處:https://qiita.com/nolanlover0527/items/24ee07122dd4d0523069