Docker-检查postgres是否就绪

33
我有一个Kong API Gateway容器和一个postgres容器,我需要在运行迁移之前从Kong容器中检查postgres是否已启动并准备好。我想使用RUN yum install postgresql -y && yum clean all 在我的Dockerfile中安装基于官方Kong镜像的自定义镜像中的postgres客户端工具,并使用psqlpg_isready来实现这一目标。我创建了一个名为polling的postgres用户,密码为空,专门用于通过这两个工具检查服务器状态。但是它们都无法正常工作。
我尝试从自定义Kong镜像中执行这些命令:
  1. psql. 命令psql -h postgres -U polling -w -c '\l'失败,出现错误psql: fe_sendauth: no password supplied。但是用户没有密码。我做错了什么?完整的使用psql检查服务器是否就绪的shell脚本在此处描述。

  2. pg_isready. 我不知道如何将此实用程序单独安装到基于官方Kong映像的自定义映像中,而官方Kong映像又基于centos:7映像,postgresql软件包不包括pg_isready。只有这些实用程序已安装并可在/usr/bin中找到:pg_configpg_dumppg_dumpallpg_restorepsql。如何安装pg_isready?我不想在Kong映像中安装完整的服务器。


关于#1,您是否调整了pg_hba.conf?请参见https://dba.stackexchange.com/questions/83164/remove-password-requirement-for-user-postgres - bluescores
@bluescores 对,我需要更改这个配置或在客户端添加 .pgpass。后者解决了这个问题。我应该注意到,我必须为我的轮询用户设置密码,否则 psql 会对我大喊大叫,即使 .pgpass 中没有密码。 - super.t
5个回答

68

以下是使用PostgreSQL提供的pg_isready工具的shell一行命令。

要从docker外部调用:

DOCKER_CONTAINER_NAME="mypgcontainer"
timeout 90s bash -c "until docker exec $DOCKER_CONTAINER_NAME pg_isready ; do sleep 5 ; done"

基于Jeroma Belleman的一篇文章


1
我很惊讶这个答案没有得到更多的赞。 我得出了同样的结论,它确实在使用pg_isready时有效,而在使用“wait for-it.sh”Bash脚本时无效,因为TCP端口已准备就绪,而Postgres尚未真正准备开始“工作”。 否则,您可能会遇到像这样的错误: An exception occurred in driver: SQLSTATE[08006] [7] server closed the connection unexpectedly - COil
3
这是一个新答案。 - Mikko Ohtamaa

23

我尝试了 wait-for-it,但在 Docker 容器中运行可能会有问题,因为 Docker 镜像中可能没有安装 nc(有时甚至没有 ping、telnet、curl 等)。因此,为了检查数据库是否正在运行,我在 docker-compose 文件中使用了 HealthCheck,并检查 pg_isready 的返回值,它是 postgres 数据库的一部分,因此您不需要将任何内容安装到 Docker 镜像中:

version: '2.3'
services:
  postgres-db:
    image: postgresImage
    healthcheck:
      test: /usr/bin/pg_isready
      interval: 5s
      timeout: 10s
      retries: 120
    ports:
      - '5432:5432'

  aplication:
    image: applicationImage
    depends_on:
      postgres-db:
        condition: service_healthy

6
compose v3 中已删除 depends_on。 - super.t
1
@super.t depends_on 在 v3 版本中 仍然存在 - Mike Branski
6
为了增加混淆,Docker-Compose v3.9 重新添加了 depends_oncondition。现在你可以通过 service_startedservice_healthyservice_completed_successfully 条件来控制启动。 - dvdgsng

8
我们通过在5432端口上进行简单的TCP检查来解决这个问题,而不需要任何PG工具。我们只需使用wait-for-it.sh即可。Postgres只有在服务器实际准备好提供服务时才会打开该端口,所以这显然是可以接受的。
示例Dockerfile:https://github.com/apim-haufe-io/wicked.kong/blob/master/Dockerfile 相应的启动脚本(只有最后一行与此特定问题相关):https://github.com/apim-haufe-io/wicked.kong/blob/master/startup.sh 代码片段:
wait-for-it.sh -h $KONG_PG_HOST -p 5432 -t 30 -- kong start --run-migrations

等一下: https://github.com/vishnubob/wait-for-it


(该网址为it技术相关内容,具体内容需要查看链接)

4
第一次在容器中启动PostgreSQL时,这种方法行不通,因为服务器还没有准备好并尝试运行启动脚本。遗憾的是,我曾经有过向服务器发送请求导致初始化脚本崩溃的情况。 - jemiloii
2
同意@jemiloii的观点。端口可用的时间比Postgres准备运行脚本的时间要早。 - ka3ak
我也偶尔见过这种情况,但并不常见。在某些地方,我也看到了在 wait-for-it 返回后加上 sleep 5,通常这已经足够了。如果想要更保险的话,可以运行 wait-for-it、sleep、wait-for-it。对我来说,通常已经足够好了。 - donmartin
wait-for-it.sh 在 Mac 上超时无效。 - Ezward

7
我的解决方案是基于官方的kong镜像创建一个新的镜像,并像这样覆盖入口点:
#!/usr/bin/env bash
set -e

# Disabling nginx daemon mode
export KONG_NGINX_DAEMON="off"

# Setting default prefix (override any existing variable)
export KONG_PREFIX="/usr/local/kong"

# Prepare Kong prefix
if [ "$1" = "/usr/local/openresty/nginx/sbin/nginx" ]; then
    kong prepare -p "/usr/local/kong"
fi

#waiting for postgres
until psql --host=$KONG_PG_HOST --username=$POLLING_USER $POLLING_DATABASE -w &>/dev/null
do
  echo "Waiting for PostgreSQL..."
  sleep 1
done

echo "Postgres is ready, running the migrations..."

kong migrations up

echo "READY TO START UP KONG USING CMD" $@;

exec "$@"

0
如几个答案所提到的,使用pg_isready是一个很好的方法来做到这一点。
然而,如果您的镜像可能通过/docker-entrypoint-initdb.d中的脚本加载数据或进行其他初始化工作,那么在这个阶段,即使数据库实际上还没有"准备好",这个测试也会成功。
为了避免这种情况,将-h localhost添加到pg_isready的调用中。在初始化过程中,Postgres只能通过UNIX套接字访问,所以在服务器关闭并以包括TCP访问的"生产"配置重新启动之前,测试将失败。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接