使用Docker自動設定PostgreSQL

banq發表於2024-06-25

展示一些使用 Docker Compose 配置 PostgreSQL 本地開發環境的技巧。

從基本設定開始:

version: <font>"3"
services:
    postgres:
        image: postgres:15.1-alpine
        container_name: postgres
        environment:
            - POSTGRES_DB=postgres
            - POSTGRES_USER=postgres
            - POSTGRES_PASSWORD=Password12!
        ports:
            -
"5432:5432"

最基本的設定,此外還透過環境變數新增兩個額外的配置:

  • POSTGRES_DB - 指示 PostgreSQL 容器自動建立具有提供的名稱的預設資料庫,
  • POSTGRES_USER - 設定預設使用者名稱。
  • POSTGRES_PASSWORD - 為 PostgreSQL 使用者設定自定義密碼。

如果我們執行:
docker-compose up

然後,將建立一個新的 PostgreSQL 容器,並在localhost:5432上可用(因為我們也向主機公開了這個埠)。我們現在可以嘗試連線我們的應用程式。

如果我們想設定一個基本的資料庫結構,比如一組預定義的表、索引、資料等,該怎麼辦?

大多數關聯式資料庫都支援一個特殊的docker-entrypoint-initdb.d資料夾。此資料夾用於在首次建立容器時自動初始化資料庫。您可以將.sql或.sh指令碼放在那裡,Docker 會自動執行。這隻會在容器首次啟動時發生,而不會在後續重新啟動時發生。

讓我們嘗試一下並新增一個名為001-init.sql的基本指令碼:

BEGIN;

-- structure setup

CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    username VARCHAR(50) NOT NULL,
    email VARCHAR(100) NOT NULL
);

-- data setup

INSERT INTO users (username, email) 
VALUES ('user1', 'user1@example.com');

INSERT INTO users (username, email) 
VALUES ('user2', 'user2@example.com');

COMMIT;

這是一個簡單的指令碼,表明我們可以以事務方式執行指令碼(參見BEGIN和COMMIT)。我們還可以設定資料庫結構並插入一些資料。
我們可以將指令碼拆分為兩個檔案:001-init-structure.sql和002-init-data.sql。它們將按字母順序執行。

如何將它放入容器中呢?我們可以使用volumes卷來實現這一點:

卷可以儲存容器資料。您可以重新啟動容器,資料將保留。它還允許將主機作業系統檔案掛載/繫結到容器。它可以雙向進行,您可以將檔案傳送到容器,但您也可以在主機儲存中檢視從容器生成的檔案。

version: <font>"3"
services:
    postgres:
        image: postgres:15.1-alpine
        container_name: postgres
        environment:
            - POSTGRES_DB=postgres
            - POSTGRES_USER=postgres
            - POSTGRES_PASSWORD=Password12!
        ports:
            -
"5432:5432"
    # VOLUMES CONFIG
        volumes:
            - ./docker/postgres:/docker-entrypoint-initdb.d

其中./docker/postgres是相對於 Docker Compose 檔案的本地資料夾路徑。我們可以將我們的 init 檔案放在那裡,它們將在構建期間自動複製到 Docker 容器,然後在 Docker 容器例項首次執行時執行。這非常簡單且有用,不是嗎?


建立更多資料庫
預設的postgres資料庫外,還將建立blogs和auth兩個資料庫。

我們可以新增一個名為000-create-multiple-postgresql-databases.sh的新指令碼,並在其中放入以下指令碼:

#!/bin/bash

set -e
set -u

function create_database() {
    local database=$1
    echo <font>"  Creating Database '$database' for '$POSTGRES_USER'"
    psql -v ON_ERROR_STOP=1 --username
"$POSTGRES_USER" <<-EOSQL
        CREATE DATABASE $database;
        GRANT ALL PRIVILEGES ON DATABASE $database TO $POSTGRES_USER;
EOSQL
}

if [ -n
"$POSTGRES_MULTIPLE_DATABASES" ]; then
    echo
"Multiple database creation requested: $POSTGRES_MULTIPLE_DATABASES"
    for db in $(echo $POSTGRES_MULTIPLE_DATABASES | tr ',' ' '); do
        create_database $db
    done
    echo
"Multiple databases created"
fi

它檢查是否存在POSTGRES_MULTIPLE_DATABASES環境變數。如果有,它會透過用逗號分隔值來獲取資料庫名稱。然後,它會執行create_database函式,該函式會建立一個新資料庫並向透過POSTGRES_USER環境變數提供的使用者授予所有許可權。

現在,如果我們將此檔案放入./docker/postgres資料夾,它將在我們的指令碼之前自動執行。這是因為我們將此資料夾對映到卷,並且指令碼按字母順序執行。

我們需要將 POSTGRES_DB更改為POSTGRES_MULTIPLE_DATABASES:

version: <font>"3"
services:
    postgres:
        image: postgres:15.1-alpine
        container_name: postgres
        environment:
            # UPDATED TO MULTIPLE DATABASES
            - POSTGRES_MULTIPLE_DATABASES=
"postgres,blogs,auth"
            - POSTGRES_USER=postgres
            - POSTGRES_PASSWORD=Password12!
        ports:
            -
"5432:5432"
        volumes:
            - ./docker/postgres:/docker-entrypoint-initdb.d


使用Web IDE
我們有一個完全設定的資料庫,並可以從我們的應用程式連線到它,但是有一個 IDE 來檢視資料不是很好嗎?

PostgreSQL 有一個不錯的開源 Web IDE,名為pgAdmin。可以將其用作 Docker 映象。讓我們透過擴充套件配置來實現它!

version: <font>"3"
services:
    postgres:
        image: postgres:15.1-alpine
        container_name: postgres
        environment:
            - POSTGRES_MULTIPLE_DATABASES=
"postgres,blogs,auth"
            - POSTGRES_USER=postgres
            - POSTGRES_PASSWORD=Password12!
        ports:
            -
"5432:5432"
        volumes:
            - ./docker/postgres:/docker-entrypoint-initdb.d


    pgadmin:
        container_name: pgadmin_container
        image: dpage/pgadmin4
        environment:
            - PGADMIN_DEFAULT_EMAIL=${PGADMIN_DEFAULT_EMAIL:-pgadmin4@pgadmin.org}
            - PGADMIN_DEFAULT_PASSWORD=${PGADMIN_DEFAULT_PASSWORD:-postgres}
            - PGADMIN_CONFIG_SERVER_MODE=False
            - PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED=False
        ports:
            -
"${PGADMIN_PORT:-5050}:80"
        depends_on:
            - postgres


讓我們討論一下那些有點神秘的環境變數設定。

  • PGADMIN_DEFAULT_EMAIL和PGADMIN_DEFAULT_PASSWORD - 為 pgAdmin 使用者設定預設憑據。pgAdmin 也可以作為常規服務託管(例如在測試環境中)並具有更高階的使用者設定,但對於本地開發來說單個使用者就足夠了。
  • PGADMIN_CONFIG_SERVER_MODE - 確定 pgAdmin 是在伺服器模式(多使用者)還是桌面模式(單使用者)下執行。我們將其設定為 false,這樣就不會提示我們輸入登入憑據。這是一個令人討厭的廢紙,我們要把它去掉。
  • PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED - 控制是否需要主密碼才能訪問已儲存的伺服器定義和其他敏感資訊。透過將其設定為 false,我們可以跳過 pgAdmin 中伺服器詳細資訊的額外密碼保護層。

您可能注意到了一個奇怪的語法:${PGADMIN_DEFAULT_EMAIL: -pgadmin4@pgadmin.org }。此設定意味著,如果在主機環境中定義了PGADMIN_DEFAULT_EMAIL,則使用其值。否則,它將恢復為預設值(在我們的例子中為pgadmin4@pgadmin.org)。

您可以在 shell 中傳遞這樣的變數:

export PGADMIN_DEFAULT_EMAIL=myemail@example.com
export PGADMIN_DEFAULT_PASSWORD=securepassword
export PGADMIN_PORT=6666
docker-compose up

或者在與我們的docker-compose.yml檔案相同的資料夾中定義.env檔案,Docker將自動使用它。

PGADMIN_DEFAULT_EMAIL=myemail@example.com
PGADMIN_DEFAULT_PASSWORD=securepassword
PGADMIN_PORT=6666

它對於安全性和在不修改主指令碼的情況下更改變數非常有用。

回到我們的 Docker Compose 配置。如果我們現在啟動容器,我們還會在http://localhost:5050上執行 pgAdmin 。

但是我們不會自動看到任何資料庫

自動發現資料庫
pgAdmin 不會進行任何自動發現。我們可以手動設定連線,但每次清理卷時都需要重複此操作。如果我們想在新環境中清理測試資料(可以透過執行docker compose down -v來完成),這種情況經常發生。

讓我們改變它並自動設定我們的伺服器列表。讓我們首先在新的docker/pgAdmin資料夾中定義servers.json檔案。然後放在那裡:

{
    <font>"Servers": {
       
"1": {
           
"Group": "Servers",
           
"Name": "Docker",
           
"Host": "postgres",
           
"Port": 5432,
           
"MaintenanceDB": "postgres",
           
"Username": "postgres", "
           
"Password": "Password12!",
           
"SSLMode": "prefer",
           
"Favorite": true
        }
    }
}

如您所見,我們只是在配置資料庫。如果願意,我們可以透過新增“2”:{ }”等來定義更多內容。

如果你隨機生成密碼(例如在 CI/CD 中),那麼你也可以定義 passfile 並將“Password”: “Password12!“,替換為“PassFile”: “/pgpass”, 。然後你可以保持伺服器設定不變,但只需在passfile中定義資料庫密碼。我們可以將其放在docker/pgAdmin資料夾中並將其放入其中:

postgres:5432:*:postgres:Password12!

讓我們繼續這個較長的路徑來展示完整的設定。我們需要稍微調整一下我們的配置:

version: <font>"3"
services:
    postgres:
        image: postgres:15.1-alpine
        container_name: postgres
        environment:
            - POSTGRES_MULTIPLE_DATABASES=
"postgres,blogs,auth"
            - POSTGRES_USER=postgres
            - POSTGRES_PASSWORD=Password12!
        ports:
            -
"5432:5432"
        volumes:
            - ./docker/postgres:/docker-entrypoint-initdb.d


    pgadmin:
        container_name: pgadmin_container
        image: dpage/pgadmin4
        environment:
            - PGADMIN_DEFAULT_EMAIL=${PGADMIN_DEFAULT_EMAIL:-pgadmin4@pgadmin.org}
            - PGADMIN_DEFAULT_PASSWORD=${PGADMIN_DEFAULT_PASSWORD:-postgres}
            - PGADMIN_CONFIG_SERVER_MODE=False
            - PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED=False
        ports:
            -
"${PGADMIN_PORT:-5050}:80"
        depends_on:
            - postgres
        user: root
        entrypoint: /bin/sh -c
"chmod 600 /pgpass; /entrypoint.sh;"
        volumes:
            - ./docker/pgAdmin/pgpass:/pgpass
            - ./docker/pgAdmin/servers.json:/pgadmin4/servers.json

我們定義了我們的使用者需要是 root,因為我們需要能夠為 passfile 設定適當的許可權。我們可以透過更改入口點來進行這樣的設定:

/bin/sh -c "chmod 600 /pgpass; /entrypoint.sh;"

Entrypoint用於指定啟動容器時執行的命令。這裡我們指的是在執行常規啟動命令之前,執行一個額外的 shell 指令碼,使用chmod函式設定
適當的pgpass許可權。

我們還對映卷以將pgpass和servers.json放入適當的資料夾中。如您所見,我們還可以對映特定檔案,而不僅僅是目錄。

現在,如果我們執行:

docker compose up

我們應該獲得帶有預配置資料庫的 pgAdmin。太棒了!

相關文章