自架服務用 Docker Compose 很常見,一直以來半路出家摸索,大多沒遇到什麼問題——直到把 blinko、linkwarden 和 ollama 整合到同一個內網(ollama-network)之後,才發現之前的做法其實暗藏隱患。
問題描述
每次更新 blinko 就很容易出現資料庫連線錯誤。看了 log、問了 AI 之後總算搞懂原因:
我把所有服務都放在同一個 Docker 網路,目的是讓支援 AI 的服務都能透過 http://ollama:11434 連接。這個想法本身沒問題,但問題出在資料庫命名:
blinko的 Postgres 服務內有一個叫blinko的資料庫linkwarden的 Postgres 服務內也有一個叫postgres(或相同)的資料庫名稱
兩者放在不同網路時相安無事;一旦放進同一個網路,更新時 Docker 就可能解析到錯誤的資料庫容器,造成連線失敗。
解決方法很簡單:將各服務的 Postgres 容器名稱和資料庫名稱改為唯一、不重複的名稱即可。
1. PostgreSQL 連線字串格式說明
在 .env 或 docker-compose.yml 中常見這樣的連線字串:
DATABASE_URL=postgresql://blinko:Ui4VTuXgi0@blinko-postgres:5432/blinko
這是標準的 PostgreSQL 連線 URL,讓應用程式知道要去哪裡、用什麼身份連接資料庫。
1.1 格式結構
postgresql://<使用者名稱>:<密碼>@<主機名稱>:<埠號>/<資料庫名稱>
1.2 逐段說明
以 postgresql://blinko:Ui4VTuXgi0@blinko-postgres:5432/blinko 為例:
| 區塊 | 值 | 說明 |
|---|---|---|
| 協定 | postgresql:// | 表示使用 PostgreSQL 資料庫 |
| 使用者名稱 | blinko | 對應 POSTGRES_USER=blinko |
| 密碼 | Ui4VTuXgi0 | 對應 POSTGRES_PASSWORD,正式環境應改用 .env 變數,避免硬編碼在設定檔中 |
| 主機名稱 | blinko-postgres | Docker Compose 內部的服務名稱,不是 IP;同網路內的容器可直接用此名稱連線 |
| 埠號 | 5432 | PostgreSQL 預設埠號 |
| 資料庫名稱 | blinko | 對應 POSTGRES_DB=blinko |
1.3 實務補充
容器之間以服務名稱作為 DNS 是 Docker 的內建機制。 只要兩個容器在同一個 network 內,就可以直接用服務名稱互連,不需要指定 IP。
若要在本機(非 Docker 環境)測試,需將主機名稱改為 localhost:
postgresql://blinko:Ui4VTuXgi0@localhost:5432/blinko
2. Docker Compose 範例
2.1 單一服務的 Postgres 定義
services:
blinko-postgres: # 服務名稱 = 容器間連線的 DNS 名稱,必須唯一
image: postgres:14
container_name: blinko-postgres # 可自訂,不設定則由 Docker 隨機命名
restart: always
environment:
POSTGRES_DB: blinko # 資料庫名稱
POSTGRES_USER: blinko # 使用者帳號
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} # 密碼從 .env 讀取
TZ: Asia/Taipei
volumes:
- blinko-postgres-data:/var/lib/postgresql/data
networks:
- ollama-network
volumes:
blinko-postgres-data:
networks:
ollama-network:
external: true
2.2 多服務共用同一網路時的命名建議
當多個服務(如 blinko 和 linkwarden)都在同一個網路下,務必確保每個服務的 Postgres 容器名稱和資料庫名稱都不同,避免 Docker DNS 解析錯亂:
services:
# blinko 的資料庫
blinko-postgres:
image: postgres:14
container_name: blinko-postgres
environment:
POSTGRES_DB: blinko
POSTGRES_USER: blinko
POSTGRES_PASSWORD: ${BLINKO_POSTGRES_PASSWORD}
networks:
- ollama-network
# linkwarden 的資料庫
linkwarden-postgres:
image: postgres:14
container_name: linkwarden-postgres
environment:
POSTGRES_DB: linkwarden
POSTGRES_USER: linkwarden
POSTGRES_PASSWORD: ${LINKWARDEN_POSTGRES_PASSWORD}
networks:
- ollama-network
對應的連線字串:
# blinko
DATABASE_URL=postgresql://blinko:${BLINKO_POSTGRES_PASSWORD}@blinko-postgres:5432/blinko
# linkwarden
DATABASE_URL=postgresql://linkwarden:${LINKWARDEN_POSTGRES_PASSWORD}@linkwarden-postgres:5432/linkwarden
重點:服務名稱(
blinko-postgres、linkwarden-postgres)就是 Docker 內部的 DNS 名稱,名稱唯一才能確保每個應用程式連到正確的資料庫。
