在開發環境中啟動時預先載入資料庫綱要與資料

在本地開發過程中,使用基礎資料和結構預載 (Pre-seeding) 資料庫是一種常見的作法,旨在改善開發與測試工作流程。透過模擬真實場景,這種做法有助於及早發現前端問題、確保資料庫管理員與軟體工程師之間的同步,並促進更順暢的協作。預載提供了諸如信心部署、跨環境的一致性以及及早檢測問題等優點,最終改善了整體開發流程。

在本指南中,您將學習如何:

  • 使用 Docker 啟動一個 Postgres 容器
  • 使用 SQL 指令碼預載 Postgres
  • 透過將 SQL 檔案複製到 Docker 映像檔中來預載 Postgres
  • 使用 JavaScript 程式碼預載 Postgres

在 Docker 中使用 Postgres

官方 Postgres Docker 映像檔提供了一種在開發機器上執行 Postgres 資料庫的便捷方式。Postgres Docker 映像檔是一個預先配置的環境,封裝了 PostgreSQL 資料庫系統。這是一個即開即用的獨立單元,可以在 Docker 容器中運作。透過使用此映像檔,您可以輕鬆快速地建立 Postgres 實例,而無需手動配置。

先決條件

若要跟隨本操作指南,需要具備以下先決條件:

啟動 Postgres

請按照以下步驟快速啟動 Postgres 示範

  1. 打開終端機並執行以下指令來啟動 Postgres 容器。

    此範例將啟動一個 Postgres 容器,並將連接埠 5432 暴露給主機,讓本機執行的應用程式能以密碼 mysecretpassword 連接到它。

    $ docker run -d --name postgres -p 5432:5432 -e POSTGRES_PASSWORD=mysecretpassword postgres
    
  2. 選擇該容器並在 Docker Dashboard 上檢查日誌,以確認 Postgres 已啟動並執行中。

    PostgreSQL Database directory appears to contain a database; Skipping initialization
    
    2024-09-08 09:09:47.136 UTC [1] LOG:  starting PostgreSQL 16.4 (Debian 16.4-1.pgdg120+1) on aarch64-unknown-linux-gnu, compiled by gcc (Debian 12.2.0-14) 12.2.0, 64-bit
    2024-09-08 09:09:47.137 UTC [1] LOG:  listening on IPv4 address "0.0.0.0", port 5432
    2024-09-08 09:09:47.137 UTC [1] LOG:  listening on IPv6 address "::", port 5432
    2024-09-08 09:09:47.139 UTC [1] LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
    2024-09-08 09:09:47.142 UTC [29] LOG:  database system was shut down at 2024-09-08 09:07:09 UTC
    2024-09-08 09:09:47.148 UTC [1] LOG:  database system is ready to accept connections
  3. 從本地系統連接到 Postgres。

    psql 是 PostgreSQL 的互動式 shell,用於連接到 Postgres 資料庫並開始執行 SQL 指令。假設您的本地系統已經安裝了 psql 工具,現在可以連接到 Postgres 資料庫。在您的本地終端機執行以下指令:

    $ docker exec -it postgres psql -h localhost -U postgres
    

    現在,您可以在 psql 提示字元中執行任何需要的 SQL 查詢或指令。

    使用 \q\quit 退出 Postgres 互動式 shell。

使用 SQL 指令碼預載 Postgres 資料庫

現在您已經熟悉了 Postgres,是時候了解如何使用範例資料進行預載了。在此示範中,您將首先建立一個包含 SQL 指令的指令碼。該指令碼定義了資料庫、資料表結構並插入範例資料。隨後,您將連接到資料庫以驗證資料。

假設您已經有一個執行中的 Postgres 資料庫實例,請按照以下步驟來進行資料庫預載。

  1. 建立一個名為 seed.sql 的空白檔案,並加入以下內容。

    CREATE DATABASE sampledb;
    
    \c sampledb
    
    CREATE TABLE users (
      id SERIAL PRIMARY KEY,
      name VARCHAR(50),
      email VARCHAR(100) UNIQUE
    );
    
    INSERT INTO users (name, email) VALUES
      ('Alpha', 'alpha@example.com'),
      ('Beta', 'beta@example.com'),
      ('Gamma', 'gamma@example.com');  

    此 SQL 指令碼會建立一個名為 sampledb 的新資料庫,連接到它,並建立一個 users 資料表。該資料表包含一個作為主鍵的自動遞增 id、一個長度上限為 50 個字元的 name 欄位,以及一個長度上限為 100 個字元且唯一的 email 欄位。

    建立資料表後,INSERT 指令會將三名使用者插入到 users 資料表中,並附帶其姓名與電子郵件。此設置形成了用於儲存具有唯一電子郵件地址之使用者資訊的基本資料庫結構。

  2. 預載資料庫。

    現在是時候使用 < 運算子將 seed.sql 的內容直接匯入到資料庫中。此指令用於針對名為 sampledb 的 Postgres 資料庫執行名為 seed.sql 的 SQL 指令碼。

    $ cat seed.sql | docker exec -i postgres psql -h localhost -U postgres -f-
    

    查詢執行後,您將看到以下結果

    CREATE DATABASE
    You are now connected to database "sampledb" as user "postgres".
    CREATE TABLE
    INSERT 0 3
  3. 執行以下 psql 指令來驗證名為 users 的資料表是否已成功填入 sampledb 資料庫中。

    $ docker exec -it postgres psql -h localhost -U postgres sampledb
    

    您現在可以在 psql shell 中執行 \l 來列出 Postgres 伺服器上的所有資料庫。

    sampledb=# \l
                                                 List of databases
    Name    |  Owner   | Encoding |  Collate   |   Ctype    | ICU Locale | Locale Provider |   Access privileges
    -----------+----------+----------+------------+------------+------------+-----------------+-----------------------
    postgres  | postgres | UTF8     | en_US.utf8 | en_US.utf8 |            | libc            |
    sampledb  | postgres | UTF8     | en_US.utf8 | en_US.utf8 |            | libc            |
    template0 | postgres | UTF8     | en_US.utf8 | en_US.utf8 |            | libc            | =c/postgres          +
              |          |          |            |            |            |                 | postgres=CTc/postgres
    template1 | postgres | UTF8     | en_US.utf8 | en_US.utf8 |            | libc            | =c/postgres          +
              |          |          |            |            |            |                 | postgres=CTc/postgres
    (4 rows)
    

    若要從 users 資料表中檢索所有資料,請輸入以下查詢

    sampledb=# SELECT * FROM users;
    id | name  |       email
    ----+-------+-------------------
     1 | Alpha | alpha@example.com
     2 | Beta  | beta@example.com
     3 | Gamma | gamma@example.com
    (3 rows)
    

    使用 \q\quit 退出 Postgres 互動式 shell。

透過綁定掛載 (bind-mounting) SQL 指令碼預載資料庫

在 Docker 中,掛載是指讓主機系統的檔案或目錄可以在容器內存取。這讓您能夠在主機與容器之間共享資料或設定檔,從而實現更大的靈活性與持久性。

現在您已經學會如何啟動 Postgres 並使用 SQL 指令碼預載資料庫,是時候學習如何將 SQL 檔案直接掛載到 Postgres 容器的初始化目錄 (/docker-entrypoint-initdb.d) 中了。/docker-entrypoint-initdb.d 是 PostgreSQL Docker 容器中的一個特殊目錄,用於在容器首次啟動時進行資料庫初始化。

在執行這些步驟前,請確保停止任何執行中的 Postgres 容器(連同磁碟區),以避免連接埠衝突。

$ docker container stop postgres
  1. 使用以下項目修改 seed.sql

    CREATE TABLE IF NOT EXISTS users (
     id SERIAL PRIMARY KEY,
     name VARCHAR(50),
     email VARCHAR(100) UNIQUE
    );
    
    INSERT INTO users (name, email) VALUES
     ('Alpha', 'alpha@example.com'),
     ('Beta', 'beta@example.com'),
     ('Gamma', 'gamma@example.com')
    ON CONFLICT (email) DO NOTHING;
  2. 建立一個名為 Dockerfile 的文字檔案並複製以下內容。

    # syntax=docker/dockerfile:1
    FROM postgres:latest
    COPY seed.sql /docker-entrypoint-initdb.d/

    此 Dockerfile 會將 seed.sql 指令碼直接複製到 PostgreSQL 容器的初始化目錄中。

  3. 使用 Docker Compose。

    使用 Docker Compose 可以更輕鬆地管理和部署帶有預載資料庫的 PostgreSQL 容器。此 compose.yml 檔案定義了一個名為 db 的 Postgres 服務,使用最新的 Postgres 映像檔,設定了一個名為 sampledb 的資料庫,以及一個使用者 postgres 和密碼 mysecretpassword

    services:
      db:
        build:
          context: .
          dockerfile: Dockerfile
        container_name: my_postgres_db
        environment:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: mysecretpassword
          POSTGRES_DB: sampledb
        ports:
          - "5432:5432"
        volumes:
          - data_sql:/var/lib/postgresql/data   # Persistent data storage
    
    volumes:
      data_sql:

    它將主機上的 5432 連接埠映射到容器的 5432,讓您可以從容器外部存取 Postgres 資料庫。它還定義了 data_sql 以實現資料持久化,確保容器停止時資料不會遺失。

    請注意,僅在您需要從非容器化的程式連接到資料庫時,才需要進行連接埠映射。如果您將連接到資料庫的服務也容器化,則應該透過自定義橋接網路 (custom bridge network) 連接至資料庫。

  4. 啟動 Compose 服務。

    假設您已經將 seed.sql 檔案放置在與 Dockerfile 相同的目錄中,請執行以下指令

    $ docker compose up -d --build
    
  5. 現在是時候驗證 users 資料表是否已填入資料了。

    $ docker exec -it my_postgres_db psql -h localhost -U postgres sampledb
    
    sampledb=# SELECT * FROM users;
      id | name  |       email
    ----+-------+-------------------
       1 | Alpha | alpha@example.com
       2 | Beta  | beta@example.com
       3 | Gamma | gamma@example.com
     (3 rows)
    
    sampledb=#

使用 JavaScript 程式碼預載資料庫

既然您已經學會了如何透過 SQL 指令碼、掛載磁碟區等多種方法預載資料庫,現在是時候嘗試使用 JavaScript 程式碼來達成目標。

  1. 建立一個包含以下內容的 .env 檔案

    POSTGRES_USER=postgres
    POSTGRES_DB_HOST=localhost
    POSTGRES_DB=sampledb
    POSTGRES_PASSWORD=mysecretpassword
    POSTGRES_PORT=5432
  2. 建立一個名為 seed.js 的新 JavaScript 檔案,並包含以下內容

    以下 JavaScript 程式碼匯入了 dotenv 套件,用於從 .env 檔案載入環境變數。.config() 方法會讀取 .env 檔案並將環境變數設定為 process.env 物件的屬性。這讓您能夠將資料庫憑證等敏感資訊安全地儲存在程式碼之外。

    接著,它從 pg 函式庫建立了一個新的 Pool 實例,該實例提供了用於高效資料庫互動的連接池。定義了 seedData 函式以執行資料庫預載操作。在指令碼末尾呼叫它以啟動預載流程。try...catch...finally 區塊則用於錯誤處理。

    require('dotenv').config();  // Load environment variables from .env file
    const { Pool } = require('pg');
    
    // Create a new pool using environment variables
    const pool = new Pool({
      user: process.env.POSTGRES_USER,
      host: process.env.POSTGRES_DB_HOST,
      database: process.env.POSTGRES_DB,
      port: process.env.POSTGRES_PORT,
      password: process.env.POSTGRES_PASSWORD,
    });
    
    const seedData = async () => {
      try {
         // Drop the table if it already exists (optional)
         await pool.query(`DROP TABLE IF EXISTS todos;`);
    
         // Create the table with the correct structure
         await pool.query(`
           CREATE TABLE todos (
             id SERIAL PRIMARY KEY,
             task VARCHAR(255) NOT NULL,
             completed BOOLEAN DEFAULT false
               );
         `   );
    
         // Insert seed data
         await pool.query(`
           INSERT INTO todos (task, completed) VALUES
           ('Watch netflix', false),
           ('Finish podcast', false),
           ('Pick up kid', false);
           `);
           console.log('Database seeded successfully!');
         } catch (err) {
           console.error('Error seeding the database', err);
         } finally {
           pool.end();
        }
      };
    
      // Call the seedData function to run the script
      seedData();
  3. 啟動預載流程

    $ node seed.js
    

    您應該會看到以下指令

    Database seeded successfully!
  4. 驗證資料庫是否已正確預載

    $ docker exec -it postgres psql -h localhost -U postgres sampledb
    
    sampledb=# SELECT * FROM todos;
    id |      task      | completed
    ----+----------------+-----------
    1 | Watch netflix  | f
    2 | Finish podcast | f
    3 | Pick up kid    | f
    (3 rows)  
    

重點回顧

在啟動時使用結構與資料預載資料庫,對於建立一致且真實的測試環境至關重要,這有助於在開發初期識別問題,並整合前後端工作。本指南已讓您具備相關知識與實作步驟,透過包括 SQL 指令碼、Docker 整合與 JavaScript 程式碼等多種方法達成預載目標。

© . This site is unofficial and not affiliated with Kubernetes or Docker Inc.