把 ch09 打包好的 Spring Boot 映像,和你已經在用的 SQL Server 組成一整套環境——
新同事 clone 專案後只要一句 docker compose up,五分鐘內就有完整可跑的開發環境。
這一章給的是一份可以直接抄回專案用的完整設定,但重點在中間那個很多教學會講錯的地方:SQL Server 的官方映像沒有 docker-entrypoint-initdb.d 這個機制——
那是 PostgreSQL 和 MySQL 才有的。所以「怎麼自動建資料表」在 SQL Server 上要另外想辦法。
你的電腦(宿主機)
┌──────────────────────────────────────────┐
│ localhost:8080 ──┐ localhost:1433 ──┐ │
└───────────────────┼────────────────────┼─┘
│ port mapping │
┌───────────────────┼────────────────────┼─┐
│ myapp_default 網路(Compose 自動建立) │
│ ┌──────────┐ ┌────────────┐ ┌──────┐│
│ │ app │──→│ sqlserver │ │ redis││
│ │ (Spring) │──────────────────→│ ││
│ └──────────┘ └─────┬──────┘ └───┬──┘│
└────────────────────────┼─────────────┼───┘
mssql-data redis-data (volume)sqlserver:1433(服務名 + 內部埠,ch07)。localhost:1433(走 port mapping)。down 之後還在(ch06)。myapp/
├── docker-compose.yml # 共通定義(進版控)
├── docker-compose.override.yml # 本機開發覆蓋(自動載入)
├── .env # 秘密與可變參數(gitignore!)
├── .env.example # 範本(進版控,給同事參考)
├── .dockerignore
├── Dockerfile
├── pom.xml
└── src/.env 一定要加進 .gitignore,同時提供一份 .env.example(只有變數名沒有真值)。這是團隊專案的標準做法——新人知道要設定哪些變數,但 repo 裡沒有任何真實密碼。name: myapp
services:
# ========== 資料庫 ==========
sqlserver:
image: mcr.microsoft.com/mssql/server:2022-latest
environment:
ACCEPT_EULA: 'Y'
MSSQL_SA_PASSWORD: ${MSSQL_SA_PASSWORD:?請在 .env 設定 MSSQL_SA_PASSWORD}
MSSQL_PID: Developer # 開發版授權(免費)
ports:
- '127.0.0.1:1433:1433' # 只綁本機,同網段的人連不到(ch07)
volumes:
- mssql-data:/var/opt/mssql
healthcheck:
test:
- CMD-SHELL
- /opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P "$$MSSQL_SA_PASSWORD" -C -Q "SELECT 1" || exit 1
interval: 10s
timeout: 5s
retries: 12
start_period: 30s
deploy:
resources:
limits:
memory: 2G # SQL Server 官方最低需求 2 GB
restart: unless-stopped
# ========== 快取 ==========
redis:
image: redis:7-alpine
ports:
- '127.0.0.1:6379:6379'
volumes:
- redis-data:/data
command: redis-server --appendonly yes
healthcheck:
test: ['CMD', 'redis-cli', 'ping']
interval: 10s
timeout: 3s
retries: 5
restart: unless-stopped
# ========== 資料庫結構初始化(一次性任務)==========
db-init:
image: mcr.microsoft.com/mssql-tools
depends_on:
sqlserver:
condition: service_healthy # 等 DB 真的可以連了才跑
volumes:
- ./db/init:/scripts:ro
entrypoint:
- /bin/bash
- -c
- /opt/mssql-tools/bin/sqlcmd -S sqlserver -U sa -P "$$MSSQL_SA_PASSWORD" -i /scripts/01-create-db.sql
environment:
MSSQL_SA_PASSWORD: ${MSSQL_SA_PASSWORD}
restart: 'no' # 跑完就結束,不要重啟
# ========== 應用程式 ==========
app:
build:
context: .
dockerfile: Dockerfile
image: myapp:local
ports:
- '8080:8080'
environment:
SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-dev}
SPRING_DATASOURCE_URL: 'jdbc:sqlserver://sqlserver:1433;databaseName=mydb;encrypt=true;trustServerCertificate=true'
SPRING_DATASOURCE_USERNAME: sa
SPRING_DATASOURCE_PASSWORD: ${MSSQL_SA_PASSWORD}
SPRING_DATA_REDIS_HOST: redis
SPRING_DATA_REDIS_PORT: '6379'
TZ: Asia/Taipei
depends_on:
sqlserver:
condition: service_healthy
redis:
condition: service_healthy
db-init:
condition: service_completed_successfully # 等初始化跑完
healthcheck:
test: ['CMD', 'wget', '-qO-', 'http://localhost:8080/actuator/health']
interval: 30s
timeout: 5s
retries: 3
start_period: 60s
deploy:
resources:
limits:
memory: 1G
restart: unless-stopped
volumes:
mssql-data:
redis-data:${MSSQL_SA_PASSWORD:?...} —— 沒設定就直接報錯中止,比啟動後才神秘失敗好得多(ch11)。127.0.0.1:1433:1433 —— 只綁本機,咖啡廳 Wi-Fi 上不會被人連(ch07)。trustServerCertificate=true —— SQL Server 2022 的 JDBC driver 預設要求加密連線,開發環境用自簽憑證必須加這個,否則會噴 SSL 握手失敗(極常見)。如果你查過 PostgreSQL 或 MySQL 的教學,會看到這種寫法:
# PostgreSQL / MySQL 可以這樣(把 .sql 丟進去就自動執行)
volumes:
- ./init:/docker-entrypoint-initdb.d.sql 檔掛到那個路徑,它會被安靜地忽略——沒有錯誤訊息,只是什麼都不會發生。很多人在這裡卡很久,因為找不到任何線索。mssql-tools 映像跑 sqlcmd 執行你的 SQL 腳本。depends_on: condition: service_healthy 確保資料庫已就緒,再靠 service_completed_successfully 讓 app 等它跑完。up 都會跑一次——你的 SQL 必須寫成可重複執行的(idempotent):IF DB_ID('mydb') IS NULL
CREATE DATABASE mydb;
GO
IF OBJECT_ID('dbo.users') IS NULL
CREATE TABLE dbo.users (...);
GOFROM mcr.microsoft.com/mssql/server:2022-latest
USER root
COPY init.sql /init/
COPY entrypoint.sh /
RUN chmod +x /entrypoint.sh
USER mssql
ENTRYPOINT ["/entrypoint.sh"]entrypoint.sh 先在背景啟動 SQL Server,輪詢等它就緒後執行腳本,最後 wait。缺點是要自己維護一個映像,而且容易寫錯 PID 1 的處理(ch05)。
<!-- pom.xml -->
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-sqlserver</artifactId>
</dependency>
# src/main/resources/db/migration/V1__create_users.sql
# Spring Boot 啟動時自動執行未套用的遷移docker-compose.override.yml 會被自動載入並合併(ch11),拿來放只有本機開發才需要的東西最合適。
# docker-compose.override.yml —— 本機開發專用
services:
app:
build:
target: dev # 用 Dockerfile 裡的 dev 階段(有 shell 好除錯)
environment:
SPRING_PROFILES_ACTIVE: dev
SPRING_DEVTOOLS_RESTART_ENABLED: 'true'
JAVA_TOOL_OPTIONS: '-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005'
ports:
- '5005:5005' # 遠端除錯埠
volumes:
- ./target/classes:/app/classes # 編譯輸出掛進去,配合 DevTools 熱重載
# 開發時才需要的資料庫管理介面
adminer:
image: adminer
ports:
- '8081:8080'
profiles: ['tools'] # 預設不啟動,要用才 --profile toolslocalhost, Port: 5005。suspend=n 表示不等除錯器連上就啟動;改成 y 則會卡住等你連(要除錯啟動流程時用)。mvn package -DskipTests && docker compose up -d --build --no-deps app約 30~60 秒。行為和正式環境完全一致,不會有「本機好好的、映像裡卻不對」的問題。target/classes + Spring Boot DevTools,IDE 一編譯容器內就自動重啟,約 3~5 秒。docker compose up -d # 啟動全部(背景)
docker compose up -d --build # 重新建置後啟動
docker compose --profile tools up -d # 連 adminer 一起啟動
docker compose stop # 只停止,容器保留
docker compose down # 停止並移除容器/網路(資料保留)
docker compose down -v # 💣 連資料一起重置docker compose up -d --build --no-deps app # 只重建重啟 app,不動 DB ⭐
docker compose logs -f app # 跟隨 app 日誌
docker compose logs -f --tail 100 # 全部服務,只看最近 100 行
docker compose restart app # 單純重啟(不重建)
docker compose ps # 看誰活著、誰 healthy# 進 app 容器
docker compose exec app bash
# 直接對資料庫下 SQL(不用裝任何客戶端工具)
docker compose exec sqlserver /opt/mssql-tools18/bin/sqlcmd \
-S localhost -U sa -P "$MSSQL_SA_PASSWORD" -C -Q "SELECT name FROM sys.databases"
# 進 Redis
docker compose exec redis redis-cli# 完全重來:刪容器、網路、資料,重新建置
docker compose down -v && docker compose up -d --build
# 只重置資料庫,保留其他服務
docker compose stop sqlserver
docker compose rm -f sqlserver
docker volume rm myapp_mssql-data
docker compose up -d sqlserverdocker compose config # 看變數展開與合併後的最終設定 ⭐
docker compose ps -a # 含已結束的(看 db-init 有沒有成功)
docker compose logs db-init # 初始化任務的輸出
docker compose top # 各容器內的 process(驗證 PID 1)
docker stats # 即時資源用量make up # docker compose up -d --build
make logs # docker compose logs -f app
make reset # docker compose down -v && make up
make db # 進 sqlcmd你現在有一套很好用的開發環境。但直接把它搬到正式環境會出事——先看清楚差異在哪。
.env 檔換成雲端的秘密管理服務(AWS Secrets Manager、K8s Secret)。ports 整段拿掉,只讓內部網路連得到。build: 改成 image: 指向 registry 上有明確版本(甚至 digest)的映像(ch04)——正式環境不該在機器上現場編譯。restart: unless-stopped 只能重啟同一台機器上的容器。機器本身故障、或需要把工作負載搬到另一台,Compose 做不到。docker compose up 更新服務時會有停機空窗。沒有「先起新的、確認健康、再關舊的」這種零停機機制。| 做法 | 怎麼做 | 優點 | 缺點 |
|---|---|---|---|
| ❌ initdb.d 掛載 | 掛 .sql 到 /docker-entrypoint-initdb.d | — | SQL Server 官方映像根本沒這機制,會被安靜忽略 |
| A. 一次性 init 容器 | mssql-tools 映像跑 sqlcmd + service_healthy/completed_successfully | 純 Compose 可達成,職責清楚 | 每次 up 都會跑,SQL 必須寫成冪等 |
| B. 自訂映像 entrypoint | 背景啟動 DB、輪詢就緒、執行腳本、wait | 只需要一個服務 | 要自己維護映像,容易寫錯 PID 1 處理 |
| C. Flyway/Liquibase | 應用啟動時自動執行版本化遷移 | 版本控管、環境一致、不依賴容器機制、天生冪等 | 需要引入依賴(但幾乎都值得) |
| 項目 | 開發(Compose) | 正式環境 |
|---|---|---|
| 密碼 | .env 檔(gitignore) | Secrets Manager/K8s Secret |
| 資料庫 | 容器 + volume(方便重置) | 受管服務(RDS、Azure SQL) |
| 映像來源 | build: 本機建置 | image: registry 上的明確版本/digest |
| 資料庫對外埠 | 127.0.0.1:1433(方便用 SSMS) | 整段拿掉,只走內部網路 |
| 日誌 | docker compose logs | 集中式日誌系統 |
| 資源限制 | 可選 | 必設 |
| 更新方式 | up -d(有停機空窗) | 滾動更新(零停機)→ 需要編排系統 |
點擊卡片翻面查看答案,共 11 張。