PostgreSQL Docker容器自动导入dump失败,手动导入正常原因排查
问题
我有一份PostgreSQL数据库dump文件,想创建Docker容器并基于该dump初始化数据库,让容器每次启动时加载该dump。
我的Dockerfile内容如下:
FROM postgres:11-alpine ENV POSTGRES_USER=postgres ENV POSTGRES_PASSWORD=postgres ENV PGDATA=/data COPY ./dump.sql /docker-entrypoint-initdb.d/
Dockerfile构建成功,但启动容器时出现错误:
psql:/docker-entrypoint-initdb.d/dump.sql:19: ERROR: relation "public.max_designation" does not exist
奇怪的是,用以下脚本启动容器后手动导入dump却能正常运行:
docker run --name PG_LOCAL -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=postgres -d postgres cat ./dump.sql| docker exec -i PG_LOCAL psql -U postgres
dump文件的开头内容如下:
-- -- PostgreSQL database dump -- -- Dumped from database version 11.17 -- Dumped by pg_dump version 11.17 SET statement_timeout = 0; SET lock_timeout = 0; SET idle_in_transaction_session_timeout = 0; SET client_encoding = 'UTF8'; SET standard_conforming_strings = on; SELECT pg_catalog.set_config('search_path', '', false); SET check_function_bodies = false; SET xmloption = content; SET client_min_messages = warning; SET row_security = off; ALTER TABLE ONLY public.max_designation DROP CONSTRAINT max_designation_transportnoticearticle_id_fkey; [...]
请问为何容器自动导入dump失败,但手动导入却正常?
原因与解决方法
核心原因:psql执行错误的终止策略不同
你的dump文件包含**清理现有数据库对象(删除约束、表)**的语句(应该是导出时用了--clean参数),在空数据库中执行这些语句会报错,但两种导入方式的错误处理逻辑不一样:
- 容器初始化时,
docker-entrypoint.sh用psql -f执行dump文件,该命令默认遇到第一个错误就终止执行,所以开头删除不存在约束的语句报错后,后续创建表、导入数据的代码根本没机会运行,导致报错。 - 手动导入时,通过管道传递sql到
psql交互式执行,默认遇到错误不终止,会继续执行后面的创建表、导入数据的语句,最终数据库能正常初始化。
另外,你设置的PGDATA=/data只是指定了数据库数据目录,和本次报错无关。
解决方法
方法1:重新导出不含清理语句的dump
去掉--clean参数重新导出,这样dump文件只会包含创建表、导入数据、添加约束的内容,在空数据库中执行不会报错:
pg_dump -U postgres your_source_database > dump.sql
方法2:修改容器执行dump的方式,忽略错误
把dump的执行逻辑改成shell脚本,让psql遇到错误继续执行:
- 创建
init.sh脚本:#!/bin/bash psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -f /docker-entrypoint-initdb.d/dump.sql --set ON_ERROR_CONTINUE=on - 修改Dockerfile,复制脚本并添加执行权限:
FROM postgres:11-alpine ENV POSTGRES_USER=postgres ENV POSTGRES_PASSWORD=postgres ENV PGDATA=/data COPY ./dump.sql /docker-entrypoint-initdb.d/ COPY ./init.sh /docker-entrypoint-initdb.d/ RUN chmod +x /docker-entrypoint-initdb.d/init.sh
方法3:手动编辑dump文件
删除dump开头所有ALTER TABLE ... DROP CONSTRAINT、DROP TABLE等清理类语句,只保留创建对象和导入数据的部分,再重新构建容器。
内容的提问来源于stack exchange,提问作者Jakob Gerhardt
相关产品推荐
相关产品推荐

