跳轉到

5.2 撰寫 docker-compose.yml#

用一份檔案宣告整個架構#

把 5.1 節那四行 docker run 改寫成 docker-compose.yml(放在專案根目錄):

services:
  api:
    build: .
    ports:
      - "8000:8000"
    environment:
      - DATABASE_URL=postgres://postgres:mysecret@db:5432/mydb
      - REDIS_URL=redis://cache:6379
    depends_on:
      - db
      - cache
    volumes:
      - .:/app
      - /app/node_modules

  db:
    image: postgres:16
    environment:
      - POSTGRES_PASSWORD=mysecret
    volumes:
      - pgdata:/var/lib/postgresql/data

  cache:
    image: redis:7

volumes:
  pgdata:

對照 docker run 的每個部分,各個配置的意義:

配置 對應 docker run 意義
image: 鏡像名稱 用現成 Image
build: docker build . 用這個目錄的 Dockerfile 建構(二選一)
ports: -p 埠對映,只有要對外暴露的服務才需要
volumes: -v 掛載(Bind Mount 或 volume)
environment: -e 環境變數
depends_on: (人工顧順序) 宣告啟動順序的相依
networks: --network 通常不用寫——Compose 自動建網路

幾個關鍵細節#

服務名稱就是主機名稱#

api 的 DATABASE_URL 中,host 寫 db——Compose 自動建立網路(複習 4.2 節),每個服務可以用服務名稱互相訪問。這是 Compose 最好用的地方:你完全不用管網路設定。

build 與 image 二選一(也可以同時有)#

  • image: postgres:16:直接用現成 Image
  • build: .:用本專案的 Dockerfile 建構
  • 兩個都寫:建構之後順便打上 image: 指定的標籤(方便推上倉庫)

depends_on 只管啟動順序#

depends_on 保證 db 先啟動,但不保證 Postgres 已經準備好接受連線(啟動到就緒有幾秒差距)。應用程式要有連線重試的邏輯,這是初學者最常踩的坑。

開發用 Bind Mount、資料用 volume#

  • db 的 pgdata 是具名 volume:資料庫資料要活得比容器久(複習 4.1 節)
  • api 的 .:/app 是 Bind Mount:開發時改主機程式碼、容器內即時生效
  • /app/node_modules 是一個「排除」技巧:避免主機的 node_modules 蓋掉容器內安裝的依賴

環境變數與機密#

正式的專案不該把密碼寫死在 YAML 裡。用 .env 檔搭配變數引用:

  db:
    image: postgres:16
    environment:
      - POSTGRES_PASSWORD=${DB_PASSWORD}

.env 檔(記得放進 .gitignore,不進版本控制):

DB_PASSWORD=mysecret

本章小結#

  • services 定義每個容器,image/build 二選一(可同時有)
  • 服務名稱就是主機名稱,網路 Compose 自動建,通常不用寫 networks
  • depends_on 只保證啟動順序,不保證服務已就緒——要有連線重試
  • 資料用具名 volume,開發程式碼用 Bind Mount
  • 機密用 .env + ${VAR} 引用,.env 不進 Git

想一想#

  1. 為什麼 Compose 通常不用寫 networks 配置?它背後自動做了什麼?
  2. depends_on 加了之後,為什麼應用程式還可能連不上資料庫?怎麼解?
  3. 為什麼 .env 不該進 Git?那要怎麼讓同事知道需要哪些環境變數?(提示:放一份 .env.example)