開發者必學 Docker:映像、容器與 Compose

DevOps2026-10-12TryQuickToolBox

你克隆了一個專案,照著安裝說明一步步操作,結果卻卡住了:缺少系統函式庫、資料庫啟動不了,或是 Node 版本不符。Docker 的存在就是為了消除這類問題。與其直接把服務安裝在你的機器上,不如用一次描述、到處都能以相同方式運行的隔離容器來執行。

本指南將解釋你真正需要掌握的三個核心概念:映像(image)、容器(container)與 Compose。內容著重實用,並提供你可以邊讀邊執行的指令。

映像與容器:心智模型

映像是一個唯讀範本,內含檔案系統快照,以及預設指令、對外連接埠等中繼資料。容器則是該映像的執行中(或已停止)實例,上頭多了一層薄薄的可寫層。

如果你學過物件導向程式設計,這個類比很貼切:映像就像類別(class),容器就像物件(object)。你可以從一個映像啟動多個容器,而且容器內的變更不會修改到映像本身。

概念是什麼常用指令
映像不可變範本docker build、docker pull
容器執行中的實例docker run、docker ps
磁碟區持久化儲存docker volume create
Compose多容器定義docker compose up

用 Dockerfile 建立你的第一個映像

Dockerfile 是一份食譜。每一道指令都會建立一層,而 Docker 會快取這些層,所以在沒有變更的情況下重新建置會非常快。以下是一個簡單的 Node.js 範例:

FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]

這裡有兩個習慣很重要。第一,先複製相依性清單,再複製其餘原始碼,這樣當只有應用程式碼變更時,npm ci 這一層就能被快取。第二,盡量選用像 alpine 這類精簡的基礎映像,以縮小體積並減少攻擊面。

建置並執行它:

docker build -t myapp:1.0 .
docker run --rm -p 3000:3000 myapp:1.0

-p 3000:3000 這個旗標會將主機的 3000 埠對應到容器的 3000 埠。--rm 會在容器結束時將其刪除,讓你的機器在開發過程中保持整潔。

日常容器指令

以下這些涵蓋了大部分日常工作:

當容器一啟動就結束時,請先讀取日誌。最常見的原因包括 CMD 寫錯、缺少環境變數,或是行程在容器內綁定到 127.0.0.1 而非 0.0.0.0。

為什麼需要 Compose

真實的應用程式通常需要不只一個行程:一個 API、一個資料庫,或許再加一個快取。用三個 docker run 指令搭配一長串旗標來執行,很容易出錯。Docker Compose 讓你在單一 YAML 檔案中宣告所有服務,並一次啟動它們。

services:
  api:
    build: .
    ports:
      - "3000:3000"
    environment:
      DATABASE_URL: postgres://app:secret@db:5432/app
    depends_on:
      - db
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: app
    volumes:
      - db_data:/var/lib/postgresql/data
volumes:
  db_data:

請注意,API 連線的是 db,而不是 localhost。Compose 會建立一個私有網路,每個服務都能透過其服務名稱互相存取。這是新手最容易混淆的地方。

用 docker compose up 啟動所有服務,或加上 -d 在背景執行。程式碼變更後用 docker compose up --build 重新建置。用 docker compose down 拆除環境,若想同時刪除具名磁碟區,再加上 -v。

磁碟區、綁定掛載與即時重載

容器是短暫的;裡面的資料在容器被移除後就會消失。有兩種機制可以解決這個問題:

  1. 具名磁碟區(Named volumes) — 由 Docker 管理,非常適合存放資料庫檔案。它們在容器重新啟動後依然存在,並在 volumes: 區段中宣告。
  2. 綁定掛載(Bind mounts) — 將主機目錄對應到容器內,非常適合在開發期間存放原始碼。
services:
  api:
    build: .
    volumes:
      - ./src:/app/src

使用綁定掛載時,你在主機上編輯檔案會即時反映到容器內。搭配檔案監看工具(nodemon、uvicorn --reload、air),就能在不重新建置映像的情況下實現即時重載。

實用的開發流程

  1. 為你的應用程式撰寫 Dockerfile,並保持精簡。
  2. 新增一個 docker-compose.yml,包含應用程式及其相依服務。
  3. 原始碼使用綁定掛載,資料使用具名磁碟區。
  4. 不要把機密資訊寫進檔案;改用 .env 檔案,並以 ${VAR} 引用變數。
  5. 新增 .dockerignore,內容包含 node_modules、.git 與建置產物,以加快建置速度。
  6. 執行 docker compose up --build 並反覆迭代。

如果你的服務會產生需要日後檢視的日誌,請記得容器日誌就只是純文字。能解析伺服器日誌的工具,例如 nginx 日誌分析器,與容器搭配得很好,因為你可以將日誌導出或複製出來,而不必在主機上安裝任何東西。

常見陷阱

常見問題

映像與容器有什麼差別?

映像是一個不可變的範本,內含檔案系統與中繼資料。容器則是該映像的執行中實例,額外加上一層可寫層。你可以從單一映像啟動多個容器。

單一服務也需要 Docker Compose 嗎?

不需要。單一容器用一行 docker run 指令就夠了。當你有多個服務、共享網路,或想集中宣告環境變數時,Compose 才會顯現價值。

容器之間如何互相通訊?

Compose 會將所有服務放在同一個共享網路上,每個服務都能透過其服務名稱互相存取。請用該名稱作為主機名稱,例如 db:5432,而不是 localhost。

Docker 的前期投資很小,回報卻很大。一旦你搞懂映像、容器與 Compose,要讓隊友上手或換一台機器,就不再是繁瑣的安裝儀式,而是一行指令就能搞定的事。