【问题标题】:How to block until initdb has finished running in a Docker Postgres container?如何阻塞直到 initdb 在 Docker Postgres 容器中完成运行?
【发布时间】:2018-02-06 01:14:55
【问题描述】:

我想使用 Docker 将 .sql 文件加载到 PostgreSQL 数据库中并提供服务。

以下命令快速返回(在数据库完全填充之前 - 这似乎在后台异步发生)。

docker run \
    --name myContainer \
    -p 5432:5432 \
    -v mySqlFile.sql:/docker-entrypoint-initdb.d/dump.sql \
    -d postgres:alpine

由于此命令将成为脚本的一部分,因此我希望该命令在数据库使用 mySqlFile.sql 完全初始化之前一直阻塞

在加载mySqlFile.sql 时,我将如何阻止命令或其他一些暂停方式?

为清楚起见:我运行上述命令,然后运行以下命令:

echo "select count(*) as \"Posts imported\" from posts;" | docker exec -i myContainer psql -U postgres
 Posts imported
----------------
              0
(1 row)

等待几秒钟,我明白了:

echo "select count(*) as \"Posts imported\" from posts;" | docker exec -i myContainer psql -U postgres
 Posts imported
----------------
          51103
(1 row)

这是一个问题,因为我希望构建一个自动化的解决方案。我希望 docker run 命令阻塞,直到所有记录都导入 Postgres。然后我的数据库处于准备好下一个命令的状态。

【问题讨论】:

  • 您的 docker 将在挂载完成后运行。然后PostgreSQL服务会自动阻塞,直到/docker-entrypoint-initdb.d中的所有脚本都被执行完
  • 如何运行 bash 脚本而不是 psql ... 在 sleep 中运行 15 秒然后执行 psql 命令?
  • 感谢@Urban48,但我不想使用 sleep 语句,因为我无法预测 sql 转储的大小。 15 秒可能太长了(这还可以)......但它可能太短了(然后我的脚本会中断)

标签: postgresql docker


【解决方案1】:

想法:创建一个自定义包装器,等待在填充脚本末尾为 docker 插入表中的特定记录。

使用docker-compose,您可以使用自定义包装脚本:

在 docker-compose.yml 中

version: "2"
services:
  app:
     build: app/
     depends_on:
       - "db"
     command: ["./wait-for-postgres.sh", "db", "yourcommand", "with", "parameters"]
  db:
    build: db/

在wait-for-postgres.sh中

#!/bin/bash
# wait-for-postgres.sh

set -e

host="$1"
shift
cmd="$@"
#wait postgres up
until psql -h "$host" -U "postgres" -c '\q'; do
  >&2 echo "Postgres is unavailable - sleeping"
  sleep 1
done

#wait populate script
while ! psql -h "$host" -U "postgres" -t -c "select * from docker_ready" | egrep .
do
  sleep 1
done
>&2 echo "Postgres is populated - executing command"
exec $cmd

您可以在https://docs.docker.com/compose/startup-order/ 上找到一些提示, https://hub.docker.com/_/postgres/

【讨论】:

  • 感谢您的回答。不幸的是,数据库在 initdb 阶段启动(和关闭),因此此检查无法区分 initdb 进程的结束
【解决方案2】:

我找到了解决办法。我认为您可以使用这种方式。

当您运行 postgres 图像时,ENTRYPOINT 是一个 shell。

如果你运行ps aux,你会看到PID 1bash

但是当所有初始化完成后,postgres就成为exec "$@"的主进程。那个时候PID 1postgres

你可以使用这个技巧。你不知道你的初始化什么时候完成。但是您可以确定,当您的初始化完成后,您的 PID 1 将是 postgres

$ docker exec -i myContainer ps -p 1 -o comm=

这将在初始化运行时返回bash。并在一切完成后更改为postgres

当然,你需要循环。但是这样一来,您就可以确定什么时候初始化完全完成,无需任何猜测。

更新

当您使用 alpine 图像时,它具有 ps 的剪切版本。你需要在你的 alpine 镜像中安装 uncut ps。 (堆栈溢出question

先运行以下命令。

$ docker exec -i myContainer apk add --no-cache procps

现在ps -p 1 -o comm= 可以工作了

希望这会有所帮助

【讨论】:

  • 感谢您的回答。不幸的是,特定的 ps 命令在我的环境中不起作用。不过,我很欣赏您的解决方案的聪明之处,并将看看我是否可以将其作为起点。
  • 是的。我错过了。你正在使用高山。
  • @ChrisBeach,你现在可以检查一下吗?
  • 嘿@aerokite,你有一个示例,我可以如何将你的建议变成一个循环,以便我可以暂停我的脚本,直到它返回 postgres?
猜你喜欢
  • 2016-12-26
  • 2021-07-30
  • 2010-12-04
  • 2019-11-19
  • 1970-01-01
  • 2010-09-20
  • 1970-01-01
  • 2015-05-15
  • 1970-01-01
相关资源
最近更新 更多