目錄

Docker Compose 是 Docker 官方提供的多容器應用編排工具,主要用來解決「單一應用需要同時啟動與管理多個容器服務」的問題。透過單一的 docker-compose.yml(或新版 compose.yaml)設定檔,開發者可以以結構化方式定義各個服務(Services)、容器之間的網路(Networks)以及資料持久化所需的 Volume,並以一行指令完成整個應用環境的啟動、停止與重建。
相較於逐一使用 docker run 管理容器,Docker Compose 將所有設定集中於檔案中,讓部署流程具備可讀性、可重現性與版本控管能力,特別適合需要多個元件協同運作的應用情境,例如 Web 應用架構(Nginx / Apache + 應用程式 + 資料庫)、API 服務、背景工作(Worker)、快取系統(Redis)等。
在實務上,Docker Compose 廣泛應用於本地開發環境、測試環境,以及中小型正式環境部署,能在不引入 Kubernetes 複雜度的前提下,提供足夠穩定且可維運的多容器部署方案,因此被視為現代 Docker 開發流程中不可或缺的核心工具之一。
本篇將完整說明:
- Docker Compose 解決了什麼問題
- 與單一
docker run、Kubernetes 的差異 - 完整 Nginx + App + Volume 實戰
- 開發環境 vs 正式環境的設計差異
- 可直接套用的最佳實務(Best Practices)
一、Docker Compose 是什麼?(Why Docker Compose Exists)
Docker Compose 是 Docker 官方提供的 多容器應用部署與管理工具,其設計目的在於解決「單一應用由多個容器共同組成時,部署與管理變得複雜且難以維護」的問題。
在現代應用架構中,一個完整的系統通常不再只是單一程式,而是由多個相互依賴的服務所構成,例如:
Web Server(Nginx / Apache)
應用程式(PHP、Node.js、Python 等)
資料庫(MySQL、PostgreSQL)
快取服務(Redis、Memcached)
背景任務或排程服務(Worker、Queue)
如果僅使用 docker run 逐一啟動這些容器,會面臨以下實務問題:
- 啟動指令冗長,參數難以閱讀與維護
- 容器之間的相依關係不易管理
- 網路與 Volume 設定分散,容易出錯
- 環境無法被完整重現,不利於團隊協作與版本控管
Docker Compose 的核心價值,正是透過 宣告式(Declarative)設定檔,將上述複雜度集中管理。
Docker Compose 的運作核心概念
Docker Compose 透過一個 docker-compose.yml(或新版 compose.yaml)檔案,明確定義整個應用所需的所有組成元件,包括:
- Services:每一個服務對應一個容器(例如 Web、App、DB)
- Networks:定義服務之間如何互相連線與通訊
- Volumes:處理資料持久化,避免容器重建造成資料遺失
這種設計讓部署流程從「執行指令」轉變為「描述架構」,使整個系統具備以下特性:
- 可讀性:架構一眼即可理解
- 可重現性:任何人都能用同一份設定啟動相同環境
- 可維護性:設定集中管理,降低人為錯誤風險
為什麼 Docker Compose 會成為主流部署工具?
Docker Compose 的定位,介於「單一容器操作」與「完整容器編排平台(如 Kubernetes)」之間,提供一個低門檻但實用性極高的解決方案。
在實務上,Docker Compose 特別適合以下情境:
- 本地開發環境(Development)
- 測試與預備環境(Staging)
- 中小型正式環境(Production on single host)
- 教學、示範與 PoC 專案
對多數中小型專案而言,Docker Compose 已能滿足部署、維運與穩定性的需求,且不需承擔 Kubernetes 帶來的學習與管理成本。
簡而言之,Docker Compose 是一種用檔案描述多容器應用架構,並以單一指令管理整個應用生命週期的工具。
它讓多容器部署從「複雜且不可控」,轉變為「結構化、可重現且可長期維運」,因此成為現代 Docker 開發與部署流程中不可或缺的核心工具。
二、為什麼單用 docker run 不夠?(問題背景)
單容器還行,多容器就失控
在 Docker 的學習初期,docker run 幾乎是每個人接觸的第一個指令。
當你只是啟動一個單純的服務(例如測試用的 Nginx、一次性的工具容器),docker run 的確快速、直覺且有效。
然而,一旦進入實際應用部署場景,情況會迅速改變。
在真實的系統中,一個「應用」通常不是單一容器,而是由多個角色明確、彼此依賴的服務所組成,例如:
- Web Server:Nginx / Apache,負責對外請求與反向代理
- Application:PHP、Node.js、Python 等應用程式本體
- Database:MySQL、PostgreSQL,負責資料儲存
- Cache / Queue:Redis、RabbitMQ,用於效能與非同步處理
這些容器必須同時存在、能互相通訊,並且在不同環境(開發、測試、正式)中保持一致行為。
如果在這種情況下仍然只使用 docker run,很快就會遇到以下結構性問題。
使用 docker run 管理多容器的實務困境
1. 啟動指令過長且不可讀,設定分散且難以維護
每一個 docker run 指令,實際上都承載了大量部署設定,包括:
- Port 對應
- Volume 掛載
- 環境變數
- Network 指定
- Restart policy
- 資源限制
當這些設定全部藏在 CLI 參數中時,會產生幾個直接後果:
- 設定無法被版本控管(Git 無從追蹤)
- 團隊成員無法 code review 部署規格
- 任何微小調整都必須重新手動輸入整串指令
- 容易因遺漏參數而導致環境不一致或事故
換言之,部署行為變成「人腦記憶」而不是「文件規格」。
2. 容器間網路與服務發現需要人工處理,錯誤風險高
多容器之間必須透過 Docker network 溝通。使用 docker run 時,你通常需要:
- 手動建立 network
- 確保每個容器加入正確的 network
- 自行規範容器命名與連線方式
一旦有容器重建、名稱改變或 network 指定錯誤,就會出現:
- 服務啟動了,但彼此連不上
- 需要硬編 IP(極不穩定)
- 不同環境使用不同連線方式,難以排錯
這些問題在小規模時尚可人工補救,但隨著服務數量增加,維運成本會急遽上升。
3. 環境重建成本極高,無法保證一致性
當你需要在另一台機器、另一個環境重新部署同一套系統時,使用 docker run 通常代表:
- 必須重新打出所有啟動指令
- 仰賴文件、聊天紀錄或個人記憶
- 極容易出現「少一個參數」或「順序不同」的問題
這也是為什麼實務中常出現經典狀況:
「我這台可以跑,為什麼你那台不行?」
本質原因在於:部署過程本身不可被完整重現。
Docker Run vs Docker Compose 比較表(升級版)
項目 | docker run | Docker Compose |
|---|---|---|
適用場景 | 單一容器、臨時測試 | 多容器應用、長期運行 |
容器數量管理 | 手動逐一啟動 | 集中定義、一次管理 |
設定方式 | CLI 參數,難追蹤 | YAML 檔案,可版控 |
可讀性 | 低,需解析指令 | 高,一眼理解架構 |
網路與服務發現 | 人工處理,易出錯 | 內建 network 與服務名稱 |
環境重建 | 成本高、易失誤 | 一行指令即可重建 |
團隊協作 | 幾乎不可行 | 非常適合 |
正式環境適用性 | 不建議 | 廣泛使用於中小型正式環境 |
docker run適合單一容器與短期測試,但在多容器應用中,設定分散、難以重現且維運成本高;Docker Compose 則透過宣告式設定集中管理服務、網路與資料,成為從個人測試邁向團隊與正式環境部署的關鍵工具。
三、Docker Compose 的核心組成(一定要懂)
Docker Compose 的本質是「用一份宣告式 YAML 檔描述整個多容器應用」。你在 docker-compose.yml(或新版 compose.yaml)裡寫的內容,並不是「啟動指令」,而是「部署規格」。
這份規格通常由三個核心元件構成:services(服務)、networks(網路)、volumes(儲存)。只要你能把這三個概念掌握,基本上就能正確設計大部分的多容器部署。
3.1 services(服務)—每個服務就是一個「可被管理的容器角色」
在 Compose 中,services 用來定義你的應用包含哪些服務。每個 service 大多對應「一類容器角色」,例如:
nginx:對外入口(Reverse Proxy / Static files)app:應用本體(PHP-FPM / Node / Python)db:資料庫(MySQL / PostgreSQL)redis:快取或佇列
services 能定義什麼?
services 的關鍵價值是:把容器啟動參數集中化與結構化。常見欄位包含:
image:使用現成映像檔build:使用 Dockerfile 自行建置映像檔ports:對外開放端口(host:container)environment:環境變數(建議搭配.env)volumes:掛載資料夾或命名 Volumedepends_on:啟動相依(先啟動 DB 再啟動 App)restart:正式環境自動重啟策略healthcheck:健康檢查(讓系統知道服務是否 ready)command/entrypoint:覆寫預設啟動命令
services 範例:同時包含 image、ports、environment、restart
services:
web:
image: nginx:alpine
ports:
- "8080:80"
restart: unless-stopped
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: "change_me"
restart: unless-stopped
實務重點:服務名稱就是「內部 DNS 名稱」
在 Compose 自建的 network 裡,服務名稱可直接當作主機名使用。
例如 web 需要連 db,連線字串通常寫成:
db:5432(PostgreSQL)redis:6379
這是 Compose 的「服務發現(service discovery)」能力,也是多容器協作能簡化的重要原因。
3.2 networks(網路)—把容器「隔離」並讓它們「可控互通」
networks 用來定義容器彼此如何通訊。很多人忽略 networks,因為 Compose 預設會自動建立一個 network(通常名為 <project>_default)。但在正式環境與多專案同機時,明確定義 network 能帶來:
- 隔離性:不同專案的容器不應互通
- 安全性:避免服務誤暴露或被其他容器掃描
- 可維運性:網路規格清楚,不靠預設行為猜測
networks 範例:自建 app_net 並指定到服務
services:
web:
image: nginx:alpine
networks:
- app_net
app:
image: node:20-alpine
networks:
- app_net
networks:
app_net:
driver: bridge
實務重點 1:不要把所有服務都丟到同一個 network(能分就分)
常見安全做法是至少分兩層:
frontend_net:對外入口(web / reverse proxy)backend_net:內網服務(db / redis / internal api)
例如:
services:
web:
image: nginx:alpine
networks:
- frontend_net
- backend_net
db:
image: postgres:16
networks:
- backend_net
networks:
frontend_net:
backend_net:
這樣 db 不在前端網路上,降低暴露面。
實務重點 2:ports 是「對外」,expose 是「對內」
ports:把容器端口映射到主機,對外可連expose:只在 Compose network 內可見,不映射到主機
(大多情況你不需要 expose,因為同網路內本來就可互通;但概念要懂。)
3.3 volumes(儲存)—讓資料「不隨容器消失」,是正式環境必修
容器的檔案系統本質上是「可丟棄的」。容器刪掉或重建後,容器內的資料就會消失。
因此只要你的服務涉及:
- 資料庫資料(DB data)
- 上傳檔(uploads)
- 產出檔(reports/export)
- TLS 憑證、設定、快取
你就必須使用 volumes 做持久化。
兩種常見 volume 型態
A) Bind Mount(綁定主機資料夾)
適合開發環境(即時修改、立刻生效)
volumes:
- ./html:/usr/share/nginx/html:ro
B) Named Volume(命名 Volume)
適合正式環境(穩定、可搬遷、避免路徑耦合)
services:
db:
image: postgres:16
volumes:
- db_data:/var/lib/postgresql/data
volumes:
db_data:
實務重點 1:資料庫務必使用 Named Volume
原因是:
不依賴主機特定目錄
便於備份與搬遷
權限/SELinux 問題相對少(仍需注意)
實務重點 2:能 :ro 就 :ro
像 Nginx 設定或靜態檔,如果不需要容器寫入,建議使用唯讀掛載:
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
這是最簡單也最有效的安全加分手段之一。
3.4(加值但建議必懂)depends_on、restart、healthcheck:把「可跑」變成「可維運」
雖然你問的核心是 services/networks/volumes,但若你的目標是「正式環境」,以下三個欄位幾乎是必修:
depends_on:描述啟動相依
services:
app:
image: node:20-alpine
depends_on:
- db
注意:depends_on 多數情況只保證「啟動順序」,不保證 DB 已 ready。要更可靠需搭配 healthcheck 或應用端 retry。
restart:正式環境建議 unless-stopped
restart: unless-stopped
healthcheck:讓 Compose 知道服務是否健康
services:
db:
image: postgres:16
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
- services:定義「有哪些容器服務」以及它們的啟動規格
- networks:定義「服務之間如何互通」並提供隔離與安全邊界
- volumes:定義「哪些資料需要持久化」以符合正式環境的可靠性需求
掌握 services / networks / volumes,你就能用 Docker Compose 把多容器應用從「可跑」提升到「可控、可重現、可維運」。
四、Docker Compose 基本指令
Docker Compose 的指令看似不多,但在正式環境(Production)中,你需要的不只是「會用」,而是要清楚每個指令會影響哪些資源(容器、網路、Volume)、何時該用、用錯會發生什麼事,以及如何搭配日常維運流程(更新、回滾、排錯、擴容)。本節會用「維運視角」把你必背的指令講到可上線的程度。
4.1 Docker Compose v2 指令慣用法(先釐清)
目前 Docker 官方主流是 Compose v2,指令形式為:
- ✅
docker compose ...(建議使用) - ⚠️
docker-compose ...(舊版獨立二進位,很多環境仍可用)
兩者功能接近,但在文件與新功能支援上,以 docker compose 為準。本篇以下示範也以 docker compose 為主。
4.2 一張表搞懂:最常用指令、用途、影響範圍與風險
指令 | 主要用途 | 影響範圍 | 常見使用時機 | 注意事項 / 風險 |
|---|---|---|---|---|
docker compose up -d | 建立並啟動服務(背景) | 建容器、建網路、建 Volume(若定義) | 首次部署、變更設定後重啟 | 若 image 更新,未必會自動拉新(視情況需 pull) |
docker compose up -d –build | 重新 build 並啟動 | 會重建使用 build: 的服務 | 修改 Dockerfile / 依賴後 | 正式環境建議在 CI build image,不在機器上 build |
docker compose down | 停止並移除容器與預設網路 | 移除容器、移除 network | 停用整套服務、清理環境 | 不會移除 named volume(資料通常保留) |
docker compose down -v | 停止並移除,並刪除 volumes | 容器、網路、Volume | 測試環境重置、確認要清空資料 | 會刪資料(DB volume 會被清空) |
docker compose ps | 查看服務狀態 | 無(僅查詢) | 佈署後確認、故障排查 | 可搭配 –all 看停止的容器 |
docker compose logs -f –tail=200 | 追蹤 logs(串流) | 無(僅讀取) | 線上排錯、看啟動失敗原因 | -f 會持續佔用終端,正式排障常用 |
docker compose exec | 進入容器內執行命令 | 容器內部 | 檢查設定、測試連線、跑 migrations | exec 需要容器在跑;shell 可能是 sh 非 bash |
docker compose restart | 重啟服務(不中斷其他服務) | 指定容器 | 單一服務異常、更新設定後 | 不會重新建立容器(與 up -d 不同) |
docker compose stop | 停止服務但不移除 | 指定容器 | 暫停服務、維護 | 下次可用 start 恢復 |
docker compose start | 啟動已存在容器 | 指定容器 | stop 後恢復 | 不會重建容器 |
docker compose pull | 拉最新 image | image 層 | 版本更新、上線前預拉 | 通常與 up -d 搭配 |
docker compose config | 展開並驗證配置 | 無(輸出內容) | 上線前檢查 YAML、debug 變數 | 非常適合用來確認 .env 是否套用 |
docker compose top | 查看容器內程序 | 無(查詢) | 排查 CPU/記憶體異常 | 需要宿主機權限允許 |
docker compose down –remove-orphans | 清除未在 YAML 中的孤兒容器 | 移除 orphan containers | 改服務名或移除服務後清理 | 避免舊容器殘留占資源 |
4.3 正式環境「標準維運流程」該怎麼用這些指令?
流程 A:第一次部署
1. 驗證設定:
docker compose config
2. 啟動服務:
docker compose up -d
3. 確認狀態:
docker compose ps
4. 追 logs 確認無錯:
docker compose logs -f --tail=200
流程 B:更新版本(安全、可控)
1. 拉新 image(避免 up 時拉到一半卡住):
docker compose pull
2. 以新 image 重建並啟動:
docker compose up -d
3. 確認狀態 + logs:
docker compose ps
docker compose logs -f --tail=200
補充:如果你固定用 tag(如
:1.2.3),pull+up -d是標準流程;如果你用latest,就更需要流程化,否則很難追溯版本。
流程 C:故障排查(線上最常用)
1. 先看狀態:
docker compose ps
2. 看 logs(鎖定錯誤服務):
docker compose logs -f --tail=300
3. 進容器檢查連線、設定、健康狀態:
docker compose exec sh
4. 必要時只重啟單一服務:
docker compose restart
4.4 up / restart / down 的差異(很多人會用錯)
在實務中,docker compose up、restart、down 是最常被使用、也是最常被誤用的三個指令。
問題並不在於指令本身複雜,而是使用者對「容器生命週期」與「設定是否會被重新套用」的理解不完整。
以下從四個常見原因,拆解為什麼錯誤會反覆發生。
原因一:把 Docker Compose 當成「服務啟動工具」,而不是「部署規格管理工具」
很多人潛意識裡,仍然把 Docker Compose 當成:
「比較方便的 docker run」
因此會直覺認為:
- 服務怪怪的 →
restart - 改了設定 →
restart - 更新 image →
restart
但實際上,Docker Compose 的核心角色是:
根據 YAML 規格,確保「目前運行狀態」符合「宣告的設定狀態」
只有 docker compose up 會重新比對「設定檔 vs 現在容器狀態」,必要時重建容器;restart 只是在不檢查設定的前提下,重啟既有容器。
錯誤根源:使用者不知道「設定是否會被重新套用」,才是指令選擇的關鍵。
原因二:直覺以為「重啟 = 套用新設定」(這是最常見的誤解)
這是最多人踩的坑。
常見錯誤情境
使用者做了以下任一件事:
- 修改
environment - 修改
ports - 修改
volumes - 修改
depends_on - 修改
.env內容
然後執行:
docker compose restart
結果發現:
- 設定沒有生效
- 行為跟預期不同
- 甚至以為 Compose 沒作用
真正原因
restart 不會:
- 重新建立容器
- 重新套用 YAML 設定
- 重新掛載 Volume
- 重新映射 Port
它只做一件事:
把「同一個容器」關掉再打開
錯誤根源:把「服務層級的重啟」誤認為「部署層級的更新」。
原因三:down 和 down -v 的風險被低估(但後果極嚴重)
docker compose down 本身是安全的:
- 移除容器
- 移除 network
- 保留 named volume(資料還在)
但很多人並沒有真正理解:
docker compose down -v
這代表:
- 資料庫 Volume 會被刪除
- 所有持久化資料會消失
- 幾乎等同於「整個環境重置」
為什麼會誤用?
常見原因包括:
- 在測試環境用習慣了
-v - 以為只是「清乾淨一點」
- 不知道 Volume 與容器是不同生命週期
錯誤根源:沒有建立「資料 ≠ 容器」的觀念。
原因四:缺乏「標準部署流程」,導致指令被隨意混用
在沒有明確 SOP 的情況下,操作通常會變成:
- 這次用
restart - 下次用
down再up - 有時
pull,有時沒有 - 有人覺得「只要跑起來就好」
這會導致:
- 同一份設定,不同人用不同方式上線
- 問題難以重現
- 回滾與排錯成本暴增
Docker Compose 設計這些指令,本來就是為了讓你建立一致的操作模式,例如:
- 設定變更 →
up -d - 短暫異常 →
restart - 整套下線 →
down
錯誤根源:把部署當成「臨時操作」,而不是「流程」。
搭配理解用的對照表
指令 | 是否比對設定 | 是否重建容器 | 是否影響資料 | 正確使用時機 |
|---|---|---|---|---|
up -d | ✅ 會 | 視需要 | ❌ 不會 | 設定變更、更新版本 |
restart | ❌ 不會 | ❌ 不會 | ❌ 不會 | 短暫異常、程序卡住 |
down | N/A | ✅ 會移除 | ❌ 不會 | 整套服務下線 |
down -v | N/A | ✅ 會移除 | ✅ 會刪資料 | 僅限測試環境 |
因為它們看起來都像「讓服務重新跑起來」,但實際上作用在不同的層級(設定、容器、資料)。
只要你有改設定,就不要用restart;
只要你不確定資料能不能刪,就不要用down -v。
4.5 正式環境建議的「最低指令熟練度清單」
以下列出的是在正式環境中一定要熟悉的最低限度 Docker Compose 指令,足以完成部署、更新與基本排錯,避免因指令誤用而影響服務穩定性或資料安全。
如果你只想記最少但足夠上線的指令,請至少熟:
docker compose up -ddocker compose psdocker compose logs -f --tail=200docker compose pulldocker compose exec <svc> shdocker compose down(知道-v會刪資料)
4.6 常見誤區(避免踩雷)
在正式環境用
down -v
這通常等於「把 DB 資料刪掉」,除非你就是要重置。
只用
restart以為等於套用設定
- 你改了
ports/volumes/environment,restart不會重建容器,可能根本沒套到新設定。 - 改配置後應優先用:
docker compose up -d
不先
pull就up
- 會造成部署時間不可控,尤其在尖峰時段可能拉 image 很慢。
- 正式環境習慣先
pull,再up -d。
五、完整實戰:Nginx + App + Volume(新手到正式環境)
5.1 專案目錄結構(非常重要)
compose-demo/
├─ docker-compose.yml
├─ nginx/
│ └─ default.conf
└─ html/
└─ index.html
5.2 docker-compose.yml(完整可用)
version: "3.9"
services:
nginx:
image: nginx:alpine
container_name: demo-nginx
ports:
- "8080:80"
volumes:
- ./html:/usr/share/nginx/html:ro
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
networks:
- app_net
restart: unless-stopped
networks:
app_net:
driver: bridge
5.3 Nginx 設定檔(nginx/default.conf)
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
5.4 測試 HTML(html/index.html)
Docker Compose Hello World
Hello Docker Compose
Multi-container deployment is working.
5.5 啟動服務
docker compose up -d
瀏覽:
http://YOUR_SERVER_IP:8080
六、逐行解析 docker-compose.yml
先看我們在前面實戰用到的 docker-compose.yml(此處維持原範例,方便你對照):
version: "3.9"
services:
nginx:
image: nginx:alpine
container_name: demo-nginx
ports:
- "8080:80"
volumes:
- ./html:/usr/share/nginx/html:ro
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
networks:
- app_net
restart: unless-stopped
networks:
app_net:
driver: bridge
接下來逐行拆解。
6.1 version: "3.9" 代表什麼?要不要寫?
version: "3.9"
- 這是 Compose 檔案格式版本標記,在早期 Compose 與 Docker Engine 相容性差異較大時很重要。
- 在新版 Docker Compose v2 中,許多情況下
version欄位已不是必需,但保留也不會造成問題。
實務建議
- 若你想讓文件更清楚、也避免舊環境解析差異:保留
version沒問題。 - 若你團隊全部使用新版 Docker Compose:可以省略,但要確保一致。
6.2 services: 為什麼是 Compose 的核心?
services:
services是整個 Compose 的核心區塊:你在這裡定義「有哪些容器服務」。- 每個 service 名稱(這裡是
nginx)同時也是:
Compose 內部辨識名稱
同 network 內的 DNS 主機名(例如其他服務可用
nginx:80連它)
6.3 nginx: 這個名稱怎麼取才對?
nginx:
nginx是服務名稱(service name)。- 取名原則:反映角色(web、app、db、redis)而不是反映版本或主機名稱。
- 因為這個名稱會被你用在:
docker compose logs nginxdocker compose restart nginx內部連線(其他容器連
nginx)
常見錯誤
用
nginx1、nginx-prod、nginx-2026等帶環境字樣的名稱,會讓檔案在跨環境與後續維護時變得混亂。環境差異應該用.env或 override file 管理。
6.4 image: nginx:alpine 為什麼要指定 tag?
image: nginx:alpine
image表示這個服務要用哪個映像檔。nginx:alpine是常見的輕量版(體積小、啟動快)。
正式環境建議(非常重要)
不要用
latest(或任何會漂移的 tag),因為它會造成:每次部署結果不一致
難以回溯問題版本
建議改成明確版本,例如:
nginx:1.27-alpine(舉例)
原則:正式環境要「可重現」,因此 image tag 要「可鎖定」。
6.5 container_name: 要不要寫?
container_name: demo-nginx
container_name是用來指定容器在 Docker 裡的實際名稱。- 不寫也可以,Compose 會自動命名(通常是
<project>-<service>-1)。
實務建議
- 開發/教學可以寫,方便新手辨識。
- 正式環境通常不建議硬指定,原因是:
未來要 scale(擴容)時會衝突(同名無法多份)
多環境同機部署可能撞名
如果你有明確需求(監控、既有腳本依賴容器名),才保留
container_name。
6.6 ports: 對外開放端口,最容易寫錯
ports:
- "8080:80"
這代表:
- 主機(host)的
8080對應到容器(container)的80 - 對外訪問
http://host:8080會進到容器的 80
常見錯誤與排查
- 左右寫反:寫成
"80:8080"會導致對外端口不如預期 - 端口已被占用:主機 8080 被別的服務用,容器就起不來
- 想內部互通卻開 ports:容器間互通只要同 network,不一定需要
ports
正式環境建議
- 對外入口(reverse proxy)才用
ports - DB/Redis 這種內部服務通常不要映射到 host
6.7 volumes: 這裡不只是掛資料夾,更牽涉安全性
volumes:
- ./html:/usr/share/nginx/html:ro
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
這兩行代表:
- 把主機的
./html掛到容器的網站根目錄 - 把主機的 Nginx 設定檔掛到容器的 conf 位置
:ro表示 唯讀(read-only)
為什麼 :ro 很重要?
- 讓容器無法改寫主機檔案
- 降低被入侵後「反向寫入」的風險
- 在正式環境中屬於低成本高收益的安全措施
開發 vs 正式差異
- 開發環境:常用 bind mount(像上面這樣),改檔即生效
- 正式環境:通常建議把設定與靜態檔打進 image,或用受控的 config 管理方式(避免主機檔案被誤改)
6.8 networks: 為什麼要寫?不是預設就有嗎?
networks:
- app_net
- Compose 預設會給你一個
defaultnetwork,確實可用。 - 但明確寫出 network 的好處是:
多專案同機時避免互通
方便後續分成 frontend/backend 網段
讓架構更清晰,利於維運與安全稽核
6.9 restart: unless-stopped 為何是正式環境必加?
restart: unless-stopped
這代表:
- 容器如果因為異常退出、或主機重開機,Docker 會自動把它拉起來
- 除非你手動
stop,否則它會保持自動恢復
常見選項差異
no:不自動重啟(預設)always:永遠重啟(即使你 stop,它也可能在 daemon 重啟後回來)unless-stopped:最適合大部分正式環境on-failure:只在非 0 exit code 時重啟(較偏批次任務)
6.10 networks: 定義區塊(底部)在做什麼?
networks:
app_net:
driver: bridge
- 定義一個名為
app_net的 network,使用bridge驅動(單機最常見) - 這個 network 會在
docker compose up時自動建立 - 所有加入
app_net的服務都會在同一個隔離網段內互通
正式環境加分做法(可選)
若你要更嚴謹的分層:建立
frontend_net、backend_net,讓 DB/Redis 不加入前端網路。
6.11 這份 Compose 檔案「做對了什麼」?
- 用
services集中描述容器規格 ports只暴露入口服務volumes使用:ro降低風險- 自建
network讓架構更清晰 restart: unless-stopped具備正式環境自復原能力
6.12 常見改造:把「教學檔」升級成「正式環境檔」的 3 個關鍵
- 鎖定 image 版本(避免漂移)
- 移除或避免
container_name(為擴容與多環境預留) - 加入
healthcheck(讓監控與依賴更可靠)
七、Docker Compose vs Kubernetes(何時該升級?)
在多容器部署的世界裡,Docker Compose 與 Kubernetes 並不是「互相取代」的關係,而是解決不同規模與複雜度問題的工具。
許多使用者之所以會在兩者之間感到困惑,往往是因為在還不需要 Kubernetes 的時候,就過早引入了它的複雜度。
因此,這一節的重點不在於「哪個比較強」,而在於:
你的應用規模與維運需求,是否已經超出 Docker Compose 能合理負擔的範圍?
Docker Compose 的定位:單機、多容器、可控的部署工具
Docker Compose 的設計核心,是讓你能在**單一主機(Single Host)**上,以宣告式方式管理多個容器服務。
它非常擅長處理以下問題:
- 一個應用由多個服務組成(Web / App / DB / Cache)
- 需要快速部署、重建與回滾
- 環境結構相對單純
- 重視可讀性與低維運成本
在實務上,大量情境其實都落在 Compose 的甜蜜點,例如:
- 公司內部系統
- 中小型網站與 API
- 單台或少量主機的正式環境
- 教學、測試、PoC 專案
在這些場景中,Docker Compose 提供的功能已經足夠穩定且可長期維運。
Kubernetes 的定位:多節點、叢集化、自動化的平台
Kubernetes 則是為了解決另一個層級的問題而存在:
- 應用必須分散在多台主機(Multi-Node Cluster)
- 需要高可用(HA)、自動調度與自動修復
- 流量與負載高度動態
- 需要更進階的資源與存取控制
Kubernetes 並不是單純的「部署工具」,而是一個完整的容器編排平台,涵蓋:
- 節點管理
- Pod 調度
- Service / Ingress
- 自動擴縮(HPA)
- 滾動更新與回滾
- RBAC 與多租戶
這也意味著:你不只是在學一個指令集,而是在導入一個平台級系統。
為什麼「太早升級 Kubernetes」反而是負擔?
許多團隊在容器化後,很快就問:
「我們是不是該用 Kubernetes?」
但實務上,常見的情況是:
- 應用還只跑在一台主機
- 流量穩定、沒有自動擴縮需求
- 維運人力有限
- 問題主要在「部署一致性」,而不是「叢集調度」
在這種情況下導入 Kubernetes,往往會帶來:
- 額外的學習成本
- 更多 YAML 與抽象層
- 更複雜的排錯流程
- 維運負擔大於實際收益
這也是為什麼許多經驗豐富的團隊會建議:
先把 Docker Compose 用好,再談 Kubernetes。
Docker Compose vs Kubernetes 比較表(決策導向版)
項目 | Docker Compose | Kubernetes |
|---|---|---|
設計目標 | 單機多容器管理 | 多節點容器編排 |
適用規模 | 小~中型應用 | 中~大型系統 |
部署複雜度 | 低 | 高 |
學習曲線 | 平緩 | 陡峭 |
是否需要叢集 | 不需要 | 需要 |
自動擴縮 | 不支援 | 原生支援 |
高可用(HA) | 需自行設計 | 內建 |
滾動更新 | 基本(需設計) | 原生支援 |
維運成本 | 低 | 高 |
適合對象 | 開發者、小型團隊 | 專職 DevOps 團隊 |
什麼時候該「升級」到 Kubernetes?
你可以用以下幾個問題自我檢查:
- 是否需要同時管理多台主機?
- 是否需要服務自動擴縮?
- 是否需要無中斷更新與高可用?
- 是否已有足夠的維運與 DevOps 能力?
- Docker Compose 是否已成為瓶頸,而非人為操作問題?
如果這些問題中,大多數答案是「是」,那麼 Kubernetes 才會真正帶來價值。
常見實務路線
在實務上,最健康、風險最低的路線通常是:
- Docker Compose:建立容器化基礎、部署一致性與維運流程
- (必要時)多主機 + 反向代理 / 負載平衡:延伸 Compose 的使用壽命
- Kubernetes:當規模與需求明確超出單機能力時再導入
這條路線能確保你在每個階段,都使用「剛剛好、不過度設計」的工具。
Docker Compose 與 Kubernetes 並非競爭關係,而是解決不同規模問題的工具;在單機與中小型部署情境下,Docker Compose 具備最佳的簡潔性與維運效率,而 Kubernetes 則適合在多節點、高可用與自動化需求明確時再導入。
八、正式環境 Docker Compose 最佳實務(必看)
本節說明 Docker Compose 在正式環境部署時的核心最佳實務,包括設定管理、安全性與維運考量,幫助避免常見誤用並提升整體部署可靠度。
8.1 使用 .env
APP_PORT=8080
ports:
- "${APP_PORT}:80"
8.2 不要把密碼寫進 YAML
- 用
.env - 或 Docker secrets(進階)
8.3 每個專案一個 network
避免容器誤連、提升安全性。
九、常見錯誤與排查(FAQ)
Docker Compose 是 Docker 官方提供的多容器管理工具,透過單一 docker-compose.yml 檔案定義多個服務、網路與 Volume,並以一行指令啟動完整應用環境。
docker run 適合單一容器測試,而 Docker Compose 專為多容器應用設計,能集中管理設定、版本控管與服務相依關係,較適合正式環境部署。
可以。Docker Compose 常用於中小型正式環境,只要搭配版本鎖定、restart policy、獨立 network 與安全設定,即可穩定長期運行。
在新版 Docker Compose(v2)中,version 欄位已非必要,但保留並不會影響執行,對舊環境相容性較佳。
Docker Compose 適合單機與中小型部署,設定簡單、維護成本低;Kubernetes 適合多節點叢集與高可用需求,但學習與維運成本較高。
- 檢查 ports
- 檢查 firewall
- 檢查 Nginx listen
- 確認 UID/GID
- 避免 root 寫入 host volume
十、結論:Docker Compose 在現代部署中的角色
Docker Compose 之所以在容器化技術快速演進的今天仍然長期被大量採用,關鍵不在於它「功能最強」,而在於它剛好落在多數團隊最需要的甜蜜點:以最低的心智負擔,提供足夠可靠的多容器部署能力。對多數中小型系統而言,部署的核心問題往往不是「缺少更複雜的編排平台」,而是「缺少一個可以被標準化、可重現、可維運的部署規格」。Docker Compose 正是用來補上這個缺口。
1) Docker Compose 的核心價值:把部署從「指令」變成「規格」
在沒有 Compose 的情境下,多容器部署通常依賴:
- 一串又一串
docker run指令 - 零散的 shell scripts
- 口耳相傳的部署步驟
- 無法審查、無法追溯的配置差異
這會直接導致三個典型問題:
- 不可重現:同一套服務在不同主機、不同人手上跑出不同結果
- 不可審查:部署配置不在 Git 中,無法 code review
- 不可維運:出問題時很難快速對照差異與回溯變更
Docker Compose 的本質,是把「服務、網路、資料」用 YAML 集中描述,讓部署從「臨時操作」進化成「可被管理的規格」。一旦部署規格可被版本控管,就能建立穩定的協作方式:誰改了什麼、何時改、為什麼改,都能在版本歷史裡被追溯。
2) Docker Compose 在部署版圖中的位置:介於單容器與叢集平台之間
從部署工具的演進脈絡看,Docker Compose 的定位非常清楚:
- 比單純
docker run更可靠、更可維運 - 比 Kubernetes 更輕量、更符合中小型需求
也因此,Compose 通常被用在以下典型場景:
- 單台主機或少量主機的正式環境(例如中小企業網站、內部系統、API 服務)
- 開發 / 測試 / Staging 環境(與正式環境保持高一致性)
- PoC 與快速交付(在可控成本下完成多服務協作)
它提供的能力剛好足以支撐大多數實際需求:多服務、可重建、可觀察、可更新、可回滾,且維運操作能被標準化。
3) 「可維運」比「能跑」更重要:Compose 的長期價值在於降低維運成本
很多團隊在容器化初期,最常遇到的不是技術瓶頸,而是維運落差:
- 服務偶發性故障,卻沒有一致的排查流程
- 更新時沒有標準流程,造成環境漂移或部署不可控
- 因為缺乏規格化配置,導致「你那台可以、我這台不行」反覆出現
Docker Compose 的長期價值,在於讓你建立一套可複製的維運模型:
- 用固定指令完成部署(
pull→up -d→ps→logs) - 用明確規格界定責任(services / networks / volumes)
- 用一致的設定管理方式避免漂移(
.env、版本鎖定、重啟策略)
當維運變成流程,而不是臨場反應,系統可靠度會顯著提升。
4) 什麼時候 Compose 會成為瓶頸?(也是你該升級的訊號)
Docker Compose 並非萬能。它的限制並不是「功能缺少」,而是「設計假設」:
- 假設你主要在單機或單節點上運行
- 假設你不需要叢集級別的調度與自動擴縮
- 假設你的 HA 與故障轉移不依賴平台自動化
因此,當你明確出現以下需求時,Compose 才會真正開始不足:
- 需要多節點叢集、跨主機調度
- 需要原生 HPA 自動擴縮與流量調度
- 需要更完整的多租戶與權限控管(RBAC)
- 需要平台級的 HA、滾動更新、節點故障自動接手
在這些情境下,Kubernetes 的複雜度才會被其能力「抵消」,並帶來實質收益。否則過早升級往往只會讓維運成本暴增。
5) 建議的實務落地策略:先用 Compose 建立可維運基礎,再談升級
更務實的做法是把部署能力分階段建立:
用 Docker Compose 建立穩定的部署規格與 SOP
先把「服務、網路、資料、更新流程」做成可重現的標準補齊正式環境最佳實務
版本鎖定、.env管理、restart policy、healthcheck、分網段、最小權限當需求明確超出單機能力時,再導入 Kubernetes
以「瓶頸驅動」而不是「潮流驅動」做技術升級
這樣你不會因為過度設計而付出高昂成本,也能確保每一步升級都有清楚的收益來源。
Docker Compose 在現代部署中的角色,是以宣告式配置把多容器應用轉為可重現、可維運的部署規格;它以低複雜度覆蓋大量真實需求,是從單容器走向正式環境的關鍵階段工具,而是否升級到 Kubernetes,應以多節點、高可用與自動化需求是否明確為判斷基準。
延伸閱讀
- Docker 是什麼?完整解析容器化核心概念、實務操作與常見問題(新手到實戰)
- Docker Image 與 Dockerfile 實戰教學(最佳實務與效能優化)
- Docker Compose 是什麼?多容器應用完整部署指南
- Docker Volume 與資料持久化完整解析(正式環境必讀)
- Docker Network 架構說明:容器如何安全互通?
- Docker 正式部署怎麼選主機?效能、穩定性與擴充性分析
- Docker 進階實務:安全性(Security)與映像檔最佳化完整指南
