[隨手筆記][SQLServer資料庫容器化] .NET與Docker Container微服務實務應用

[上課筆記][SQLServer資料庫容器化] .NET與Docker Container微服務實務應用

本文承接上一篇文章:[隨手筆記][ASP.NET MVC5 網站容器化] .NET與Docker Container微服務實務應用

將 本機電腦的 SQL Server  Express 的資料庫容器化

**匯入方式**:使用本機電腦的資料庫 `.bak` 完整備份檔,不複製進映像檔,改用 Volume 方式, bind mount 掛載本機 Backup 資料夾(`:ro` 唯讀)。

**資料持久化**:使用 Docker 命名 Volume `sqldata`,容器刪除重建後,資料庫的資料才不會消失。

**容器內的SQL Server Express實例名稱**:`MSSQLSERVER`(預設實例,自動監聽 port 1433,不需額外設定容器的 TCP/IP 登錄)。

**容器化後的資料庫連線方式**:同一份 .yml (多個容器相同網路)的容器彼此之間使用 SQL Authentication 連線登入和DNS(`Server=db`),不使用 Windows Authentication(跨容器需 AD + gMSA,太複雜)。
本機電腦的SSMS連線到容器內的SQL Server Express,則是(`Server=localhost,1433`)+ SQL Authentication 連線登入。

### 步驟 1:備份本機 Northwind 資料庫

**如何做?**:用 SSMS 將本機 Northwind 資料庫完整備份為 `.bak` 檔,並用 `RESTORE FILELISTONLY` 查出邏輯檔案名稱。

- 將 .bak 檔存到本機電腦路徑:`C:\Program Files\Microsoft SQL Server\MSSQL16.SQLEXPRESS\MSSQL\Backup\Northwind.bak`

- 手動進入 SSMS 用 RESTORE FILELISTONLY 的 SQL 語句查詢上述路徑的備份檔的邏輯檔案名稱(還原至容器內的 SQL Server 時需要),通常是 SQL Server 內部用的別名

RESTORE FILELISTONLY FROM DISK = N'C:\Program Files\Microsoft SQL Server\MSSQL16.SQLEXPRESS\MSSQL\Backup\Northwind.bak'      

- LogicalName:`Northwind`(資料檔 Type=D)、`Northwind_log`(日誌檔 Type=L)

**原因**:最一開始的容器內的 SQL Server 需要從 `.bak` 還原資料庫。`RESTORE FILELISTONLY` 取得的邏輯名稱用於 `WITH MOVE`,將資料檔指向容器內的 Volume 掛載點。

### 步驟 2:在.Net 方案目錄下建立 `docker/Dockerfile.db` — 映像檔腳本

**映像檔內容**:建立 SQL Server Express Windows 容器的 Dockerfile。

**原因**:Microsoft 官方只有 Linux 版 SQL Server 映像,而本範例的 Web 容器是 Windows 容器(Docker Desktop 同時只能跑一種),所以必須自建(基底映像檔為微軟的Windows Server 2022 + 為容器下載安裝 SQL Server Express,微軟的連結會自動導向下載最新版的SQL Server Express)。

關鍵設計:

- 基底映像:`mcr.microsoft.com/windows/servercore:ltsc2022`(與 Web 容器相同 Windows 版本)

- SQL Server實例名稱:`MSSQLSERVER`(預設實例,自動使用 port 1433)

- 啟用 SQL Authentication,SA 密碼在安裝時設定
本機開發環境的SQL登入帳號用sa,但實際的正式上線環境最好別使用sa帳號。

/SAPWD=YourStrong!Passw0rd  ← 在安裝時候就設定 sa 的密碼。

- .bak 不 COPY 進映像(避免映像檔肥大),由 docker-compose.yml 透過 Volumn 掛載

Dockerfile.db 內容如下

# 使用 Windows Server Core 2022 作為基底映像(與 Web 容器相同 Windows 版本)
FROM mcr.microsoft.com/windows/servercore:ltsc2022

# 使用 PowerShell 作為預設 Shell
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]

# 1) 下載 SQL Server Express 安裝引導程式(由微軟提供,實際版本依下載時間而定)
RUN Invoke-WebRequest -Uri 'https://go.microsoft.com/fwlink/p/?linkid=2216019&clcid=0x404&culture=zh-tw&country=tw' -OutFile 'C:\\SQLEXPR-SSEI.exe'

# 2) 用引導程式下載 SQL Server Express 安裝媒體
RUN Start-Process -FilePath 'C:\\SQLEXPR-SSEI.exe' \
    -ArgumentList '/Action=Download', '/MediaPath=C:\\SQLMedia', '/MediaType=Core', '/Quiet' \
    -Wait -NoNewWindow ; \
    Remove-Item 'C:\\SQLEXPR-SSEI.exe' -Force

# 3) 解壓並靜默安裝 SQL Server Express
RUN $setupExe = (Get-ChildItem 'C:\\SQLMedia\\*.exe' | Select-Object -First 1).FullName ; \
    Start-Process -FilePath $setupExe -ArgumentList '/x:C:\\SQLSetup', '/q' -Wait -NoNewWindow ; \
    Start-Process -FilePath 'C:\\SQLSetup\\setup.exe' -ArgumentList \
      '/Q', \
      '/IACCEPTSQLSERVERLICENSETERMS', \
      '/ACTION=Install', \
      '/FEATURES=SQLEngine', \
      '/INSTANCENAME=MSSQLSERVER', \
      '/SECURITYMODE=SQL', \
      '/SAPWD=YourStrong!Passw0rd', \
      '/SQLSVCSTARTUPTYPE=Automatic', \
      '/TCPENABLED=1', \
      '/UPDATEENABLED=False' \
    -Wait -NoNewWindow ; \
    Remove-Item -Recurse -Force 'C:\\SQLMedia', 'C:\\SQLSetup'

# 4) 建立備份檔與資料檔的目錄
RUN New-Item -ItemType Directory -Path 'C:\\backup' -Force ; \
    New-Item -ItemType Directory -Path 'C:\\SQLData' -Force

# 5) 複製啟動腳本到容器(檔案已移至 docker/ 資料夾,build context 為專案根目錄)
COPY docker/DbContainer-StartScript.ps1 C:/DbContainer-StartScript.ps1

# 宣告容器使用 port 1433
EXPOSE 1433

# 容器啟動時執行啟動腳本
ENTRYPOINT ["powershell", "-File", "C:\\DbContainer-StartScript.ps1"]

容器內沒有安裝 SQL Server Browser 服務(負責解析實例名稱),所以Web容器內的應用程式的資料庫連線字串要連線至 另一資料庫容器內的SQL Server不能用
Server=db\MSSQLSERVER,須直接使用 DNS 的方式(預設 1433 Port):Server=db,這樣才能跳過實例名稱的解析,連線至容器內的SQL Server。
 

### 步驟 3:在.Net 方案目錄下建立 `docker/DbContainer-StartScript.ps1` — 資料庫容器一啟動就要執行的 powershell 腳本,主要用途還原掛載資料庫的資料

**原因**:容器啟動時需要啟動 SQL Server 服務、等待完全就緒、還原or從Volumn掛載資料庫。
Dockerfile 的 `RUN` 只在建置映像檔時執行,啟動容器時的邏輯要放在 ENTRYPOINT 腳本裡。

腳本流程:

1. **設定區**(檔案最上方,集中宣告變數,方便未來修改):資料庫名稱、邏輯檔名、.bak 路徑、.mdf/.ldf 路徑

2. **讀取 SA 密碼**:從環境變數 `SA_PASSWORD` 讀取(docker-compose.yml 傳入),若未設定用預設值

3. **啟動 SQL Server 服務**:`Start-Service 'MSSQLSERVER'`

4. **等待SQL Server完全就緒**:用 sqlcmd 重試迴圈(最多 30 次,每次 2 秒)— 業界標準做法,SQL Server 沒有就緒通知機制

5. **三段式判斷資料庫狀態**

   - `sys.databases` 已有(容器僅重啟)→ 跳過還原or掛載資料庫。

   - `sys.databases` 沒有(master資料庫無資料),但 Volume 上有 .mdf檔。
(容器刪除重建導致 master資料庫的資料消失,但 Volume 的 .mdf 檔案還健在的情況)→ 使用 T-SQL 的 **FOR ATTACH**指令,掛載 Volumn 的 .mdf 資料庫。

   - `sys.databases` 和 Volumn 都沒有資料(最一開始的部署) → 從本機電腦的 `.bak` 完整備份檔還原資料庫至容器內的SQL Server。

6. **保持容器運行**:`Wait-Event`(無限等待)

 

還原 .bak 的關鍵:WITH MOVE 
- `WITH MOVE` 將資料檔案指向 Volume 掛載的 `C:\SQLData`

RESTORE DATABASE [Northwind]
FROM DISK = N'C:\backup\Northwind.bak'
WITH MOVE '邏輯資料檔名' TO 'C:\SQLData\Northwind.mdf',
     MOVE '邏輯日誌檔名' TO 'C:\SQLData\Northwind_log.ldf',
     REPLACE 

DbContainer-StartScript.ps1 內容

# 容器啟動後,要執行的 powershell 腳本
# 啟動 SQL Server → 等待 SQL Server完全就緒 → 還原 Northwind(如果尚未存在)→ 保持容器運行

# ============================================================
# 設定區(未來要修改請改這裡)
# ============================================================

# 要還原的資料庫名稱
$dbName = 'Northwind'
# .bak 備份檔中的資料檔邏輯名稱(用 RESTORE FILELISTONLY 查詢)
$dbLogicalData = 'Northwind'
# .bak 備份檔中的日誌檔邏輯名稱(用 RESTORE FILELISTONLY 查詢)
$dbLogicalLog = 'Northwind_log'
# .bak 備份檔在容器內的路徑
$bakPath = 'C:\backup\Northwind.bak'
# 容器內,資料庫還原後的資料檔存放路徑(對應 Volume 掛載點)
$mdfPath = 'C:\SQLData\Northwind.mdf'
# 容器內,資料庫還原後的日誌檔存放路徑(對應 Volume 掛載點)
$ldfPath = 'C:\SQLData\Northwind_log.ldf'

# ============================================================

# 從環境變數讀取 SA 密碼(若未設定則使用預設值)
$saPassword = $env:SA_PASSWORD
if (-not $saPassword) { $saPassword = 'YourStrong!Passw0rd' }

# 啟動 SQL Server 服務
Write-Host 'Starting SQL Server service...'
Start-Service 'MSSQLSERVER'
Write-Host 'SQL Server service started.'

# 等待 SQL Server 就緒(最多重試 30 次,每次間隔 2 秒)
Write-Host 'Waiting for SQL Server to become ready...'
$retries = 30
$ready = $false
for ($i = 0; $i -lt $retries; $i++) {
    try {
        $result = & sqlcmd -S 'localhost' -C -U sa -P $saPassword -Q "SELECT 1" -b 2>&1
        if ($LASTEXITCODE -eq 0) {
            Write-Host 'SQL Server is ready.'
            $ready = $true
            break
        }
    } catch { }
    Write-Host "  Attempt $($i+1)/$retries - waiting 2 seconds..."
    Start-Sleep -Seconds 2
}

if (-not $ready) {
    Write-Host 'ERROR: SQL Server did not start in time.' -ForegroundColor Red
    exit 1
}

# 可以透過 sqlcmd 連線 SQL Server 了

# 判斷資料庫狀態並決定動作:
# 1. sys.databases 已有 → 跳過(同一容器重啟的情況)
# 2. sys.databases 沒有,但 Volume 上有 .mdf → 掛載(容器刪除重建,但 Volume 資料還在)
# 3. 都沒有 → 從 .bak 還原(全新部署)
$dbCheck = & sqlcmd -S 'localhost' -C -U sa -P $saPassword -Q "SELECT name FROM sys.databases WHERE name = '$dbName'" -h -1 -W -b 2>&1
if ($dbCheck -match $dbName) {
    # 情況 1:資料庫已註冊在 master 中(容器只是重啟,沒有被刪除重建)
    Write-Host "$dbName database already exists. Skipping restore."
} elseif (Test-Path $mdfPath) {
    # 情況 2:master 不認識此資料庫(容器被刪除重建),但 Volume 上仍有資料檔
    Write-Host "Attaching $dbName database from existing Volume data..."
    & sqlcmd -S 'localhost' -C -U sa -P $saPassword -Q @"
CREATE DATABASE [$dbName] ON
(FILENAME = N'$mdfPath'),
(FILENAME = N'$ldfPath')
FOR ATTACH
"@ -b

    if ($LASTEXITCODE -eq 0) {
        Write-Host "$dbName database attached successfully."
    } else {
        Write-Host "ERROR: Failed to attach $dbName database." -ForegroundColor Red
    }
} else {
    # 情況 3:全新部署,Volume 上沒有資料檔
    Write-Host "Restoring $dbName database from backup..."

    # 確保資料目錄存在
    $dataDir = Split-Path $mdfPath -Parent
    if (-not (Test-Path $dataDir)) { New-Item -ItemType Directory -Path $dataDir | Out-Null }

    & sqlcmd -S 'localhost' -C -U sa -P $saPassword -Q @"
RESTORE DATABASE [$dbName]
FROM DISK = N'$bakPath'
WITH MOVE '$dbLogicalData' TO '$mdfPath',
     MOVE '$dbLogicalLog' TO '$ldfPath',
     REPLACE
"@ -b

    if ($LASTEXITCODE -eq 0) {
        Write-Host "$dbName database restored successfully."
    } else {
        Write-Host "ERROR: Failed to restore $dbName database." -ForegroundColor Red
    }
}

# 保持容器運行(無限等待)
Write-Host 'SQL Server is running.'
Wait-Event

### 步驟 4:在.Net 方案目錄下建立 `docker-compose.yml`

- 新增 `db` 服務(資料庫容器)

- 對外開放 port `1433:1433`(開發時可用 SSMS 從本機電腦連線至容器內的SQL Server)

- 設定資料庫容器的 `SA_PASSWORD` 環境變數

**目標**:讓資料庫容器 + Web 容器可以一鍵啟動,共用同一個 Docker 網路(服務名稱 = DNS 名稱)。
資料庫和網站的容器啟動設定都放在同一個 docker-compose.yml 的好處是:
兩個容器自動在同一個網路,Web 容器可以直接用 db 這個服務名稱(DNS)連到資料庫。

docker-compose.yml 的 結構(由上到下):

1. **volumes 宣告**:`sqldata` 命名 Volume,掛載到容器內的路徑 `C:/SQLData`(資料持久化)

2. **db 服務**:SQL Server 容器,bind mount Backup 資料夾(`:ro`)+ sqldata Volume

3. **web 服務**:ASP.NET 容器,`depends_on: db` 確保 db 先啟動

- **SA 密碼**:開發用先寫死在 docker-compose.yml,正式環境改用 `.env` 檔或 Docker secrets

容器間網路連線

- Docker Compose 自動建立網路,Web、DB 兩個服務在同一網路

- Web 容器用主機名稱 `db` 連線 SQL Server(服務名稱 = DNS 名稱),但本機電腦的 SSMS 使用 localhost,1433 連線 容器內的 SQL Server。

docker-compose.yml 內容

# 宣告命名 Volume(由 Docker 管理儲存位置,不需指定本機路徑)
# 用途:讓容器內的資料可以持久保存,即使容器被刪除重建,Volume 中的資料依然存在
# 查看:docker volume ls(列出所有 Volume)
# 刪除:docker-compose down -v(加 -v 才會刪除 Volume,不加則保留)
volumes:
  sqldata: # 存放 SQL Server 的資料檔(.mdf/.ldf),供下方 db 服務使用

# 定義要執行的容器服務
services:
  # ===== 資料庫容器 =====
  db: # 服務名稱,也是容器在 Docker 網路中的 DNS 名稱(其他容器可用 "db" 來連線此容器)
    image: webnet-db:v20260325          # 建置出來的映像檔名稱
    container_name: webnet-db-v20260325 # 在 Docker Desktop 裡看到的名字
    build:
      context: .                        # 建置的根目錄(專案根目錄)
      dockerfile: docker/Dockerfile.db  # 使用 docker 資料夾裡的 Dockerfile.db
    # port 對應(本機電腦主機:容器)
    ports:
      - "1433:1433" # 本機電腦的 1433 port 對應 容器內的 1433 port(可用 SSMS 從本機連線至容器內的 SQL Server)
    environment:
      - SA_PASSWORD=YourStrong!Passw0rd # SA 帳號密碼(啟動腳本會讀取此環境變數)
    volumes:
      # 掛載本機的 Backup 資料夾到容器內(Windows 容器不支援掛載單一檔案,只能掛載整個資料夾)
      # :ro 表示容器只能讀取,不能修改本機的檔案
      - "C:/Program Files/Microsoft SQL Server/MSSQL16.SQLEXPRESS/MSSQL/Backup:C:/backup:ro"
      # 使用上方宣告的 sqldata Volume,掛載到容器內的 C:/SQLData
      - sqldata:C:/SQLData
    restart: unless-stopped # 自動重啟,但手動停止後不會再自動重啟

  # ===== 網站容器 =====
  web: # 服務名稱,也是容器在 Docker 網路中的 DNS 名稱(其他容器可用 "web" 來連線此容器)
    image: webnet:v20260325             # 建置出來的映像檔名稱
    # 容器的名稱(在 Docker Desktop 裡看到的名字)
    container_name: webnet-v20260325    # 容器名稱不能用冒號,改用減號
    # 建置映像檔的設定
    build:
      context: .                         # 建置的根目錄(專案根目錄)
      dockerfile: docker/Dockerfile.web  # 使用 docker 資料夾裡的 Dockerfile.web
    # port 對應(本機電腦主機:容器)
    ports:
      - "8443:443" # 本機電腦的 8443 port 對應 容器內的 443 port
    depends_on:
      - db         # 一鍵啟動時,自動先啟動 db 容器,再啟動 web 容器
    restart: unless-stopped # 自動重啟,但手動停止後不會再自動重啟

### 步驟 5:改寫網站應用程式的資料庫連線字串

**目標**:讓 Web 容器內的網站能透過 Docker 網路連線到 DB 容器的 SQL Server 存取資料。

修改的檔案:

- `網站根目錄下的Web.config`:確認 `connectionStrings`區段的資料庫連線字串為: `Server=db;Database=Northwind;User Id=sa;Password=YourStrong!Passw0rd;TrustServerCertificate=True;`

 

### 步驟 6:建置與測試

**如何做?**:`docker-compose build` → `docker-compose up -d` 建立並啟動容器→ 驗證所有功能(查詢資料庫、瀏覽網站)。

在 docker-compose.yml 所在目錄(通常是.Net 方案目錄)開啟 cmd 終端機 ,執行  docker-compose build,建置所有映像檔。     

- **建置映像檔時間**:db 映像第一次建置大約需 20-40 分鐘(因為要下載安裝 SQL Server Express),之後在Docker有快取,再次docker-compose build速度會快很多

 建置過程中會看到一堆輸出,耐心等待,看到 Successfully built 就代表成功。映像檔全部建置成功後,再執行 cmd指令 docker-compose up -d (建立並啟動所有容器)

 ┌──────┬────────────────────────────────────┐
 │ 參數 │                意思                │
 ├──────┼────────────────────────────────────┤
 │ up   │ 建立並啟動所有容器                     │
 ├──────┼────────────────────────────────────┤
 │ -d   │ 背景執行(detach),不會佔住終端機 │
 └──────┴────────────────────────────────────┘

 因為有 depends_on,Docker 會自動先啟動 db,再啟動 web。
 

查看容器狀態的指令:docker-compose ps,應該會看到兩個容器都是 Up 狀態。

查看 db 容器的啟動日誌: docker-compose logs db

 確認看到以下關鍵訊息:
 - SQL Server is ready.
-  顯示 SQL Server 啟動成功
 - Northwind database restored successfully. 

- 用 SSMS 連線 `localhost,1433`(帳號 `sa`、密碼YourStrong!Passw0rd)連線容器 SQL Server 確認資料庫與資料表都能正常存取。

- 測試 資料庫容器的 Volumn 資料持久化:docker-compose down 全部容器下架刪除→ docker-compose up -d 重建啟動容器,再用 SSMS 連線查詢 DB容器的資料庫並確認資料仍然保留。 

  
開啟瀏覽器並瀏覽網站: https://localhost:8443
(因為是自簽憑證,瀏覽器會警告不安全,點「繼續前往」即可)

踩過的地雷坑

### 地雷 1:微軟基底映像檔的下載連結自動升級 SQL Server 版本

**現象**:Dockerfile 中用微軟官方下載連結下載 SQL Server Express,預期下載 2022 版(版本 16),實際下載到 2025 版(版本 17)。導致登錄路徑 `MSSQL16.SQLEXPRESS` 不存在,`docker-compose build` 失敗。

**原因**:微軟的 `go.microsoft.com/fwlink` 連結是動態的,會指向最新版本。

**解法**:最終改用**預設實例 `MSSQLSERVER`**(`/INSTANCENAME=MSSQLSERVER`),搭配 `/TCPENABLED=1` 安裝參數,預設實例自動監聽 port 1433,完全不需要手動修改登錄檔設定 TCP/IP,從根本避開了版本號問題。


 

### 地雷 2:Windows 容器不支援掛載單一檔案

**現象**:docker-compose.yml 中嘗試 bind mount 單一 `.bak` 檔案,`docker-compose up -d` 報錯 `source path must be a directory`。

**原因**:Windows 容器只支援掛載**整個資料夾**,不支援掛載單一檔案(Linux 容器可以)。

**解法**:改為掛載 `.bak` 所在的整個 `Backup` 資料夾:

```yaml

- "C:/Program Files/Microsoft SQL Server/MSSQL16.SQLEXPRESS/MSSQL/Backup:C:/backup:ro"

```

### 地雷 3:ODBC Driver 18 預設要求加密連線(SSL 憑證信任錯誤)

**現象**:資料庫容器啟動後,`sqlcmd` 連線 SQL Server 失敗,錯誤訊息 `The certificate chain was issued by an authority that is not trusted`。所有 30 次重試都失敗,資料庫無法還原。

**原因**:下載到的 SQL Server 2025 內含 ODBC Driver 18,預設要求加密連線並驗證 SSL 憑證。容器內的 SQL Server 使用自簽憑證,不被信任。

**解法**

- `DbContainer-StartScript.ps1`:所有 `sqlcmd` 呼叫加上 **`-C`** 旗標(trust server certificate)

- `網站應用程式根目錄下的Web.config`:連線字串加上 **`TrustServerCertificate=True;`**

### 地雷 4:容器刪除重建後資料庫被重新還原(Volume 持久化失效)

**現象**:`docker-compose down` → `up -d` 後,日誌顯示資料庫又從 `.bak` 重新還原,而不是使用 Volume 上已有的資料。

**原因**:啟動腳本透過查詢 `sys.databases` 判斷資料庫是否存在。但 `sys.databases` 存在 **master 資料庫**中,而 master 不在 Volume 上(它在容器檔案系統中)。容器被刪除重建時 master 是全新的,不認識 Northwind,所以導致每次都從本機電腦重新還原資料庫。

**解法**:改為**三段式判斷**

1. `sys.databases` 有 → 跳過(容器僅重啟)

2. `sys.databases` 沒有,但 `.mdf` 檔案存在(Volume 上)→ `CREATE DATABASE ... FOR ATTACH`(快速掛載)

3. 都沒有 → 從 `.bak` 還原(全新部署)


 

### 地雷 5:PowerShell 字串內嵌在 Dockerfile RUN 中的展開問題

**現象**:嘗試在資庫料容器的映像檔腳本 Dockerfile 的 `RUN` 指令中使用 PowerShell 動態查詢 SQLServer Express 具名實例的登錄路徑,變數展開和字串跳脫出錯,`docker-compose build` 失敗。

**原因**:Dockerfile 的 `RUN` 指令中的 PowerShell 語法受到多層跳脫影響(Docker 解析 → PowerShell 解析),複雜的字串操作容易出問題。

**解法**:改用預設實例 `MSSQLSERVER`,從根本消除了需要動態查詢SQL Server具名實例的登錄路徑並指派 Port 1433 的必要性。不需要額外PowerShell 修改登錄檔設定 TCP/IP,從根本避開了資料庫版本號問題。  

## 常用指令速查

| 指令 | 用途 |

|------|------|

| `docker-compose build` | 建置映像檔(首次約 20-40 分鐘) |

| `docker-compose up -d` | 建立&啟動所有容器(背景執行,不佔用終端機) |

| `docker-compose down` | 停止並刪除所有容器(Volume 資料保留不刪除) |

| `docker-compose down -v` | 停止並刪除所有容器 + `-v` 旗標表示Volume資料也刪除) |

| `docker-compose logs db` | 查看 DB 容器日誌 |

| `docker-compose restart db` | 重啟 DB 容器 |

| `docker-compose rm -sf db` | 只刪除 DB 容器(不影響 web 容器) |

| `docker-compose ps` | 查看容器狀態 |