docker compose 啟動服務時,自動初始化(init)資料庫跟資料(seed) - PostgreSQL

就不需要每次重新部署時,都要手動進去資料庫執行腳本

Posted by Young on 2025-10-03
Estimated Reading Time 3 Minutes
Words 701 In Total

目的?

以往在用 docker compose 快速啟動服務,常常會遇到:

  • 在一台全新的機器或開發環境中,都需要手動進入docker內部db容器,手動執行初始化(init)資料庫結構的 SQL 腳本,接著再執行資料(seed)的 SQL 腳本來填充初始資料
  • 流程不僅繁瑣,而且容易出錯,特別是在多個環境(如開發、測試、生產)中部署時

比如在某專案中,固定會有個 products 的資料表,並且每次部署到新主機第一次啟動時,去打端點都一定會跳sqlalchemy.exc.ProgrammingError: (psycopg2.errors.UndefinedTable) relation "table_name" does not exist 資料表不存在的錯誤,都得透過手動執行 Python 腳本 create_db_data.py + SQLAlchemy 語法來建表與插入資料

PostgreSQL 官方 image docker-entrypoint-initdb.d/ 機制

PostgreSQL 官方 image 內建的 docker-entrypoint.sh 會在容器初始化(第一次建立資料 volume)時,自動執行 /docker-entrypoint-initdb.d/ 目錄底下的 .sql.sh 檔案。

只要是在 /docker-entrypoint-initdb.d/.sql.sh 檔案,Postgres 會在 第一次啟動資料庫 時自動執行

初始化 SQL 檔案

創建 init-scripts 資料夾並在底下建立了三個 SQL 檔

1
2
3
4
5
6
7
project/
├─ docker-compose.yaml
├─ init-scripts/
│ ├─ 001_enable_extensions.sql
│ ├─ 002_create_table.sql
│ └─ 003_seed_data.sql
...

001_enable_extensions.sql中,確保 pgcrypto 可用(因為用了 gen_random_uuid())產生 UUID

1
CREATE EXTENSION IF NOT EXISTS "pgcrypto";

002_create_table.sql,寫建立資料表的 SQL

1
2
3
4
5
6
7
8
9
CREATE TABLE IF NOT EXISTS products (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(100) UNIQUE NOT NULL,
description TEXT,
price NUMERIC(10,2) NOT NULL,
in_stock BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMP NOT NULL DEFAULT now(),
updated_at TIMESTAMP NOT NULL DEFAULT now()
);

插入固定資料

1
2
3
4
5
INSERT INTO products (name, description, price, in_stock)
VALUES
('iPhone 15', '最新款智慧型手機', 32900.00, TRUE),
('MacBook Air', '輕薄筆電', 42900.00, TRUE),
('AirPods Pro', '主動降噪無線耳機', 7990.00, FALSE);

在 docker-compose.yaml 中掛載 init-scripts

最後在 docker-compose.yaml 中掛載 init-scripts 目錄

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
services:
db:
image: postgres:16
container_name: my_postgres
restart: always
environment:
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mypassword
POSTGRES_DB: mydb
volumes:
- ./init-scripts:/docker-entrypoint-initdb.d
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"

volumes:
postgres_data:

這樣每次啟動時容器時,Postgres 容器就會:

  1. 執行 001_enable_extensions.sql → 啟用 UUID
  2. 執行 002_create_table.sql → 建立 products 資料表
  3. 執行 003_seed_data.sql → 插入三筆固定商品

結果

啟動後,我們可以進容器查詢資料:

1
docker exec -it my_postgres psql -U myuser -d mydb -c "SELECT * FROM products;"

應就能看到類似結果:

1
2
3
4
5
id        |    name     |     description     |  price  | in_stock |        created_at        |        updated_at        
--------------------------------------+-------------+---------------------+---------+----------+--------------------------
5a32cda3-28a4-4f4f-8df6-41d4a8e2f01d | iPhone 15 | 最新款智慧型手機 | 32900.0 | t | 2025-10-07 13:00:00+00 | 2025-10-07 13:00:00+00
182d9cb5-4e43-44c6-9e35-9a933b292c21 | MacBook Air | 輕薄筆電 | 42900.0 | t | 2025-10-07 13:00:00+00 | 2025-10-07 13:00:00+00
3efcd124-46b9-4c02-850b-0f991a216af8 | AirPods Pro | 主動降噪無線耳機 | 7990.0 | f | 2025-10-07 13:00:00+00 | 2025-10-07 13:00:00+00

若您覺得這篇文章對您有幫助,歡迎分享出去讓更多人看到⊂◉‿◉つ~


留言版