You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Postgres Docker镜像偶发启动失败问题排查求助

Postgres容器偶尔启动失败:init.sql环境变量未替换的原因与解决方法

问题背景与配置

我正在开发一个用Docker Compose部署Postgres服务的Python项目,相关配置如下:

compose/postgresql/Dockerfile

FROM postgres:16.1-bullseye

COPY init.sql /docker-entrypoint-initdb.d/init.sql

compose/postgresql/init.sql(基础账号配置)

-- Create database & user
CREATE DATABASE ${POSTGRES_DB};
CREATE USER ${POSTGRES_USER} WITH ENCRYPTED PASSWORD ${POSTGRES_PASSWORD};

-- Grant privileges
GRANT ALL PRIVILEGES ON DATABASE ${POSTGRES_DB} TO ${POSTGRES_USER} WITH GRANT OPTION;
GRANT USAGE ON SCHEMA public TO ${POSTGRES_USER};
GRANT SELECT ON ALL TABLES IN SCHEMA public TO ${POSTGRES_USER};

-- Alter default privileges
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO ${POSTGRES_USER};
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO ${POSTGRES_USER};

docker-compose.yaml(Docker Compose V2)

services:
  postgres:
    build:
      context: compose/postgresql
    ports:
      - "5432:5432"
    environment:
      - POSTGRES_USER=app-user
      - POSTGRES_PASSWORD=S3cr3tP4ssw0rd
      - POSTGRES_DB=app-db

  python-service:
    [...]

问题现象

本地测试时,Python服务会在Postgres启动10秒后启动(Postgres本身启动仅需2-3秒),但偶尔(约每10次运行出现1次)Postgres容器启动失败,报错无法解析init.sql:

postgres-1        | /usr/local/bin/docker-entrypoint.sh: running /docker-entrypoint-initdb.d/init.sql
postgres-1        | psql:/docker-entrypoint-initdb.d/init.sql:2: ERROR:  syntax error at or near "$"
postgres-1        | LINE 1: CREATE DATABASE ${POSTGRES_DB};
postgres-1        |                         ^
postgres-1        | 2024-05-07 08:13:10.214 UTC [61] ERROR:  syntax error at or near "$" at character 17
postgres-1        | 2024-05-07 08:13:10.214 UTC [61] STATEMENT:  CREATE DATABASE ${POSTGRES_DB};
postgres-1 exited with code 3

原因分析

这个“随机失败”并非真正随机,是由Postgres镜像的初始化逻辑决定的:

  1. Postgres官方镜像的docker-entrypoint.sh仅在**数据库首次初始化(对应的数据卷为空)**时,才会执行/docker-entrypoint-initdb.d/目录下的初始化文件。
  2. 对于.sql格式的文件,entrypoint会直接调用psql执行,但psql无法识别shell风格的${VAR}变量,因此会抛出语法错误。
  3. 当数据卷已存在(之前成功初始化过数据库),entrypoint会跳过初始化步骤,容器正常启动;只有当数据卷被清空(比如首次启动、手动删除数据卷、docker-compose down -v清理卷),才会触发init.sql的执行,进而报错。

解决方法

有三种可行方案,推荐前两种以保持配置灵活性:

方案一:用shell脚本+模板文件实现变量替换

将初始化逻辑改为可执行的shell脚本,利用envsubst替换模板中的环境变量后执行:

  1. 重命名原init.sql为init.sql.tmpl(内容保持不变)
  2. 创建init.sh脚本:
#!/bin/sh
set -e

# 替换模板中的环境变量,然后传给psql执行
envsubst < /docker-entrypoint-initdb.d/init.sql.tmpl | psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "postgres"
  1. 修改Dockerfile,复制两个文件并给脚本加执行权限:
FROM postgres:16.1-bullseye

COPY init.sh /docker-entrypoint-initdb.d/init.sh
COPY init.sql.tmpl /docker-entrypoint-initdb.d/init.sql.tmpl
RUN chmod +x /docker-entrypoint-initdb.d/init.sh

方案二:直接在shell脚本中写SQL(无需模板)

直接在shell脚本中使用环境变量编写SQL逻辑:

  1. 创建init.sh脚本:
#!/bin/sh
set -e

# 直接通过shell变量注入SQL逻辑
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "postgres" <<-EOSQL
    CREATE DATABASE $POSTGRES_DB;
    CREATE USER $POSTGRES_USER WITH ENCRYPTED PASSWORD '$POSTGRES_PASSWORD';
    
    GRANT ALL PRIVILEGES ON DATABASE $POSTGRES_DB TO $POSTGRES_USER WITH GRANT OPTION;
    GRANT USAGE ON SCHEMA public TO $POSTGRES_USER;
    GRANT SELECT ON ALL TABLES IN SCHEMA public TO $POSTGRES_USER;
    
    ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO $POSTGRES_USER;
    ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO $POSTGRES_USER;
EOSQL
  1. 修改Dockerfile:
FROM postgres:16.1-bullseye

COPY init.sh /docker-entrypoint-initdb.d/init.sh
RUN chmod +x /docker-entrypoint-initdb.d/init.sh

方案三:利用镜像自带的自动初始化简化配置

Postgres官方镜像已经支持通过POSTGRES_DB、POSTGRES_USER、POSTGRES_PASSWORD自动创建对应的数据库和用户,无需手动编写这部分SQL:

  1. 简化init.sql,仅保留权限授予逻辑(注意替换为实际的用户名和库名):
-- Grant privileges
GRANT ALL PRIVILEGES ON DATABASE app-db TO app-user WITH GRANT OPTION;
GRANT USAGE ON SCHEMA public TO app-user;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO app-user;

-- Alter default privileges
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO app-user;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO app-user;
  1. Dockerfile保持原配置即可。

注:此方案灵活性较低,若后续修改环境变量需同步修改init.sql。

内容的提问来源于stack exchange,提问作者Adrien

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 10:55:54