我有以下
compose.yaml
:
services:
pg-local:
image: postgres:14.7
volumes:
- pgdata:/var/lib/postgresql/data
env_file:
- .env.local_postgres_docker_env
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 30s
timeout: 5s
retries: 3
configuration-manager:
build:
context: ..
ports:
- "5000:8080"
volumes:
- ~/.aws:/root/.aws
depends_on:
db-replicator:
condition: service_started
env_file:
- .env.config_manager_docker_env
db-migrator:
build:
context: ..
volumes:
- .:/migrations
depends_on:
pg-local:
condition: service_healthy
env_file:
- .env.config_manager_docker_env
command: bash -c 'poetry run flask db upgrade && echo "Migration completed successfully"'
db-replicator:
build:
context: ./postgres
depends_on:
db-migrator:
condition: service_completed_successfully
env_file:
- .env.local_postgres_docker_env
- .env.production_postgres_secrets
command: >
bash -c '
PGPASSWORD=${PROD_POSTGRES_PASSWORD} pg_dump -v --host=${PROD_HOST} --username=${PROD_USER} --dbname=${PROD_DB_NAME} --clean --create --no-password --file=/tmp/dump.sql &&
PGPASSWORD=${POSTGRES_PASSWORD} psql --host=pg-local --username=${POSTGRES_USER} < /tmp/globals.sql' &&
PGPASSWORD=${POSTGRES_PASSWORD} psql --host=pg-local --username=${POSTGRES_USER} < /tmp/dump.sql'
volumes:
pgdata:
pg-local
和 db-migrator
服务运作良好!
但是,执行 db-replicator
时,pg_dump
服务失败,并出现以下错误:
pg_dump: error: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed: No such file or directory
所有服务的所有环境变量都已明确定义(使用 `docker compose config 检查)
network_mode: host
docker run ...
(而不是 docker compose
)运行相关图像,并且成功了。Compose 中的环境变量处理可能有点复杂。 它分两个阶段发生:
$VARIABLE
文件,在文件中的任何位置使用 替换 .env
引用,并忽略任何特定于容器的设置。
environment:
和 env_file:
设置,它为主容器进程构建环境,该进程可能是一个 shell。在您的设置中,如果您正在运行
command: bash -c '... $VARIABLE ...'
,Compose 会替换 first步骤中的
$VARIABLE
引用;外壳看不到它。 如果 $PROD_HOST
可能仅在 .env.local_postgres_docker_env
文件中设置,那么当 Compose 根据主机环境进行替换时,它不会出现,您将得到一个空字符串。
这里的直接答案是通过在需要将文字
$$
字符传递到容器中的每个地方写入 $
来转义美元符号。
command: >
bash -c '
PGPASSWORD=$${PROD_POSTGRES_PASSWORD} pg_dump -v --host=$${PROD_HOST} --username=$${PROD_USER} --dbname=$${PROD_DB_NAME} --clean --create --no-password --file=/tmp/dump.sql &&
PGPASSWORD=$${POSTGRES_PASSWORD} psql --host=pg-local --username=$${POSTGRES_USER} < /tmp/globals.sql' &&
PGPASSWORD=$${POSTGRES_PASSWORD} psql --host=pg-local --username=$${POSTGRES_USER} < /tmp/dump.sql'
更实际的是,我可能会避免直接在 Compose 文件中编写这样的复杂脚本。 由于您似乎要为此步骤构建自定义图像,因此我会将其编写为 shell 脚本(将这些命令移至
postgres/replicate.sh
文件中)并使其成为图像的默认 CMD
# postgres/Dockerfile
...
COPY replicate.sh /usr/local/bin/replicate
CMD ["replicate"]
使用专用脚本中的命令并且该脚本是 Dockerfile 默认值
CMD
,您不会有特殊的转义问题,并且不需要 Compose command:
覆盖。
我还可能考虑完全删除这些单独的容器,并将这些数据库维护步骤移至主应用程序容器的入口点包装脚本中;例如,参见这个答案。