Docker等待postgresql运行

Qub*_*uba 17 postgresql docker

我在我的项目中使用带有django的postgresql.我把它们放在不同的容器中,问题是我需要在运行django之前等待postgres.这时我正在使用sleep 5django容器的command.sh文件.我还发现netcat可以做到这一点,但我更喜欢没有额外包的方式.curl和wget不能这样做,因为他们不支持postgres协议.有办法吗?

小智 24

如果你有,psql你可以简单地将以下代码添加到.sh文件:

RETRIES=5

until psql -h $PG_HOST -U $PG_USER -d $PG_DATABASE -c "select 1" > /dev/null 2>&1 || [ $RETRIES -eq 0 ]; do
  echo "Waiting for postgres server, $((RETRIES--)) remaining attempts..."
  sleep 1
done
Run Code Online (Sandbox Code Playgroud)

  • 此解决方案假设您将 Postgres 客户端实用程序安装到 Web 容器中,这会使图像膨胀并增加攻击向量 (7认同)
  • 它稍作修改,添加了**RETRIES=$((RETRIES-=1))**。这是修改后的版本:`直到 psql -h $HOST -U $USER -d $DATABASE -c "select 1" > /dev/null 2>&1 || [ $RETRIES -eq 0 ]; do echo "正在等待 postgres 服务器启动,$((RETRIES)) 剩余尝试..." RETIES=$((RETRIES-=1)) sleep 1 done` (2认同)

Ali*_*ter 20

这将成功等待Postgres开始.(特别是第6行).只需替换npm startPostgres启动后你想要发生的任何命令.

services:
  practice_docker: 
    image: dockerhubusername/practice_docker
    ports: 
      - 80:3000
    command: bash -c 'while !</dev/tcp/db/5432; do sleep 1; done; npm start'
    depends_on:
      - db
    environment:
      - DATABASE_URL=postgres://postgres:password@db:5432/practicedocker
      - PORT=3000   
  db:
    image: postgres
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=password
      - POSTGRES_DB=practicedocker
Run Code Online (Sandbox Code Playgroud)

  • @ShubhamKhandare你可以在数据库上使用“restart:always” (2认同)

Nic*_*rdu 14

Dockerfile添加等待并更改启动命令以使用它:

ADD https://github.com/ufoscout/docker-compose-wait/releases/download/2.7.3/wait /wait
RUN chmod +x /wait

CMD /wait && npm start
Run Code Online (Sandbox Code Playgroud)

然后,在您的 api 服务中docker-compose.yml添加一个WAIT_HOSTS环境变量:

ADD https://github.com/ufoscout/docker-compose-wait/releases/download/2.7.3/wait /wait
RUN chmod +x /wait

CMD /wait && npm start
Run Code Online (Sandbox Code Playgroud)

这样做的好处是它支持等待多个服务:

services:
  api: 
    depends_on:
      - postgres
    environment:
      - WAIT_HOSTS: postgres:5432

  postgres:
    image: postgres
    ports:
      - "5432:5432"
Run Code Online (Sandbox Code Playgroud)

有关更多详细信息,请阅读他们的文档


gad*_*k.a 14

我已经通过向 docker-compose 定义添加运行状况检查来解决我的问题。

  db:
    image: postgres:latest
    ports:
      - 5432:5432
    healthcheck:
      test: "pg_isready --username=postgres && psql --username=postgres --list"
      timeout: 10s
      retries: 20
Run Code Online (Sandbox Code Playgroud)

然后在依赖的服务中可以检查健康状态:

  my-service:
    image: myApp:latest
    depends_on:
      kafka:
        condition: service_started
      db:
        condition: service_healthy
Run Code Online (Sandbox Code Playgroud)

来源:https ://docs.docker.com/compose/compose-file/compose-file-v2/#healthcheck


tiz*_*ano 10

为什么不卷曲?

像这样的东西:

while ! curl http://$POSTGRES_PORT_5432_TCP_ADDR:$POSTGRES_PORT_5432_TCP_PORT/ 2>&1 | grep '52'
do
  sleep 1
done
Run Code Online (Sandbox Code Playgroud)

这个对我有用。


Qub*_*uba 9

您的解决方案问题tiziano默认情况下没有安装curl,我想避免安装其他东西.无论如何,我做了bereal说的.如果有人需要它,这是脚本.

import socket
import time
import os

port = int(os.environ["DB_PORT"]) # 5432

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
while True:
    try:
        s.connect(('myproject-db', port))
        s.close()
        break
    except socket.error as ex:
        time.sleep(0.1)
Run Code Online (Sandbox Code Playgroud)

  • 您的解决方案的问题是默认情况下未安装 python,我想避免安装额外的东西。;) (6认同)
  • :-D 公平地说,OP 使用的是 Django,因此将安装 python (3认同)

Kur*_*eek 9

如果后端应用程序本身具有 PostgreSQL 客户端,则可以pg_isreadyuntil循环中使用该命令。例如,假设我们有以下项目目录结构,

.
??? backend
?   ??? Dockerfile
??? docker-compose.yml
Run Code Online (Sandbox Code Playgroud)

docker-compose.yml

version: "3"
services:
  postgres:
    image: postgres
  backend:
    build: ./backend
Run Code Online (Sandbox Code Playgroud)

和一个 backend/Dockerfile

FROM alpine
RUN apk update && apk add postgresql-client
CMD until pg_isready --username=postgres --host=postgres; do sleep 1; done \
    && psql --username=postgres --host=postgres --list
Run Code Online (Sandbox Code Playgroud)

其中“实际”命令仅psql --list用于说明。然后运行docker-compose builddocker-compose up将为您提供以下输出:

在此处输入图片说明

请注意psql --list命令的结果如何仅出现在所需的pg_isready日志之后postgres:5432 - accepting connections

相比之下,我发现这种nc -z方法并不一致。例如,如果我backend/Dockerfile

FROM alpine
RUN apk update && apk add postgresql-client
CMD until nc -z postgres 5432; do echo "Waiting for Postgres..." && sleep 1; done \
    && psql --username=postgres --host=postgres --list
Run Code Online (Sandbox Code Playgroud)

然后docker-compose build跟着docker-compose up给我以下结果:

在此处输入图片说明

也就是说,该psql命令会抛出一个FATAL错误,即the database system is starting up.

简而言之,使用until pg_isready循环(这里也推荐)是 IMO 的首选方法。


Nin*_*ham 9

正如其他答案提到的,有几个解决方案。

但不要让它变得复杂,只需让它与restart: on-failure. 您的服务将打开与数据库的连接,并且可能第一次会失败。就让它失败吧。Docker 将重新启动您的服务,直至其变绿。让您的服务简单并以业务为中心。

version: '3.7'

services:

  postgresdb:
    hostname: postgresdb
    image: postgres:12.2
    ports:
      - "5432:5432"
    environment:
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=secret
      - POSTGRES_DB=Ceo

  migrate:
    image: hanh/migration
    links:
      - postgresdb
    environment:
      - DATA_SOURCE=postgres://user:secret@postgresdb:5432/Ceo
    command: migrate sql --yes
    restart: on-failure # will restart until it's success
Run Code Online (Sandbox Code Playgroud)

查看重启政策


kol*_*bok 9

除了以下内容之外,其他解决方案均无效:

version : '3.8'
services :
  postgres :
    image : postgres:latest
    environment :
      - POSTGRES_DB=mydbname
      - POSTGRES_USER=myusername
      - POSTGRES_PASSWORD=mypassword
    healthcheck :
      test: [ "CMD", "pg_isready", "-q", "-d", "mydbname", "-U", "myusername" ]
      interval : 5s
      timeout : 5s
      retries : 5
  otherservice:
    image: otherserviceimage
    depends_on :
      postgres:
        condition: service_healthy
Run Code Online (Sandbox Code Playgroud)

感谢这个帖子:https ://github.com/peter-evans/docker-compose-healthcheck/issues/16


小智 8

我花了几个小时研究这个问题,然后找到了解决方案。DockerDepends_on只是考虑启动服务来运行另一个服务。发生这种情况的原因是,一旦数据库启动,service-app就会尝试连接到ur db,但它尚未准备好接收连接。因此,您可以在应用程序服务中检查数据库运行状况,以等待连接。这是我的解决方案,它解决了我的问题。:)重要:我正在使用docker-compose版本2.1。

version: '2.1'

services:
  my-app:
    build: .
    command: su -c "python manage.py runserver 0.0.0.0:8000"
    ports:
       - "8000:8000"
    depends_on:
      db:
        condition: service_healthy
    links:
      - db
    volumes:
      - .:/app_directory

  db:
    image: postgres:10.5
    ports:
      - "5432:5432"
    volumes:
      - database:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5

volumes:
  database:
Run Code Online (Sandbox Code Playgroud)

在这种情况下,无需创建.sh文件。我希望它对你们有帮助;)

  • 为了消除评论中的混乱,它现在可以工作了,因为 v2 和 v3 都已被[新规范](https://docs.docker.com/compose/compose-file/)取代 (13认同)
  • 很好,感谢“pg_isready”提示。整齐的命令。 (11认同)
  • 我可能是错的,但我正在使用 3.8 并且它对我有用。我什至把它拿出来测试一下,它又坏了。 (3认同)
  • [compose file v3 参考](https://docs.docker.com/compose/compose-file/compose-file-v3/#depends_on) 中不再记录“depends_on”的“condition”属性,但它记录在 [compose file v2 参考](https://docs.docker.com/compose/compose-file/compose-file-v2/#depends_on) 中,并且似乎两者都支持(我正在使用3.5 中)。我已经浏览了他们的问题跟踪器,他们谈论它破坏了 docker swarm,所以他们说他们只支持简短形式(没有“条件”),但这似乎还不是真的。 (3认同)
  • 从compose 3.x开始,depends_on不接受条件形式。 (2认同)
  • 现在已记录,该解决方案适用于 3.8 -&gt; https://docs.docker.com/compose/compose-file/#depends_on (2认同)
  • 为了在 2023 年解决问题,**docker 弃用了撰写 YAML 文件中的“version: X”语法**。极其令人困惑的部分是,Docker 通过“*宣布 Compose V2*”(意思是 Docker Compose 本身的版本 2,而不是 YML 版本的 V2)来宣布这一点。我认为这是他们的一个重大错误,因为大多数人会认为他们指的是 YML 版本 2……但版本不再是一个“东西”。请参阅:https://www.docker.com/blog/tag/compose-v2/ (2认同)

vee*_* yh 7

等待它的小型包装器脚本,您可以将其包含在应用程序的映像中,以轮询给定的主机和端口,直到它接受TCP连接为止。

可以通过以下命令在Dockerfile中克隆

RUN git clone https://github.com/vishnubob/wait-for-it.git
Run Code Online (Sandbox Code Playgroud)

docker-compose.yml

version: "2"
services:
   web:
     build: .
     ports:
       - "80:8000"
     depends_on:
       - "db"
     command: ["./wait-for-it/wait-for-it.sh", "db:5432", "--", "npm",  "start"]
   db:
     image: postgres
Run Code Online (Sandbox Code Playgroud)


Pif*_*ych 6

最简单的解决方案是简短的bash脚本:

while ! nc -z HOST PORT; do sleep 1; done;
./run-smth-else;
Run Code Online (Sandbox Code Playgroud)


jto*_*mpl 5

不幸的是,睡眠直到pg_isready返回 true 并不总是可靠的。如果您的 postgres 容器至少指定了一个 initdb 脚本,则postgres 在其引导过程期间启动后会重新启动,因此即使pg_isready已返回 true,它也可能尚未准备好。

相反,您可以做的是等待该实例的 docker 日志返回一个PostgreSQL init process complete; ready for start up.字符串,然后才继续进行pg_isready检查。

例子:

start_postgres() {
  docker-compose up -d --no-recreate postgres
}

wait_for_postgres() {
  until docker-compose logs | grep -q "PostgreSQL init process complete; ready for start up." \
    && docker-compose exec -T postgres sh -c "PGPASSWORD=\$POSTGRES_PASSWORD PGUSER=\$POSTGRES_USER pg_isready --dbname=\$POSTGRES_DB" > /dev/null 2>&1; do
    printf "\rWaiting for postgres container to be available ... "
    sleep 1
  done
  printf "\rWaiting for postgres container to be available ... done\n"
}

start_postgres
wait_for_postgres
Run Code Online (Sandbox Code Playgroud)