Flask无法使用docker-compose连接PostgreSQL

3

我使用docker-compose构建了一个带有postgresql的flask应用程序。 但是flask无法连接postgresql。 错误信息:

     conn = _connect(dsn, connection_factory=connection_factory, **kwasync)
sqlalchemy.exc.OperationalError: (psycopg2.OperationalError) could not connect to server: Connection refused
    Is the server running on host "postgres-db" (192.168.16.2) and accepting
    TCP/IP connections on port 5432?

当我进入Docker容器并ping postgres-db时,它可以正常工作。而且当我启动Flask app时,它也可以正常工作。仅当我使用docker-compose up时才无法连接。
我的docker-compose文件:
version: '3.7'

services:
  postgres-db:
    restart: always
    image: postgres:11.1-alpine   
    privileged: true  
    ports:
      - 5432:5432  
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: Aa123456 
      PGDATA: /var/lib/postgresql/data/pgdata 
    volumes:
      - ./db/postgres/data:/var/lib/postgresql/data/pgdata

  api:
    build:
      context: ./api
      dockerfile: Dockerfile
    volumes:
      - './api:/usr/src/app'
    ports:
      - 5002:5000
    environment:
      - FLASK_CONFIG=development
      - FLASK_ENV=development
      - APP_SETTINGS=project.config.DevelopmentConfig
      - DATABASE_URL=postgres://postgres:Aa123456@postgres-db:5432/cms
      - DATABASE_TEST_URL=postgres://postgres:Aa123456@postgres-db:5432/cms_test
      - SECRET_KEY=ZQbn05PDeA7v11
    depends_on:
      - postgres-db
    container_name: api
    links: 
      - postgres-db:postgres-db

  nginx:
    build:
      context: ./nginx
      dockerfile: Dockerfile
    restart: unless-stopped
    ports:
      - 8080:8080
    depends_on:
      - api
      - client

  client:
    build:
      context: ./client
      dockerfile: Dockerfile
    volumes:
      - './client:/usr/src/app'
      - '/usr/src/app/node_modules'
    ports:
      - 3008:3000
    environment:
      - NODE_ENV=development
      - REACT_APP_SERVICE_URL=http://localhost:8080
      - CHOKIDAR_USEPOLLING=true
    depends_on:
      - api

如果您等待一分钟并再次尝试 docker-compose up -d,它是否有效?(经常出现的问题是应用程序在数据库完全初始化之前启动。)links: 不是必需的,可能会导致复杂性,并建议将其删除。 - David Maze
1个回答

5
你遇到了一种竞争条件(race condition):应用程序在数据库可用之前尝试连接数据库。这并不是Docker独有的问题,如果你的应用程序依赖于外部资源(如数据库服务器),那么需要编写代码来处理这种情况。
一个简单的解决方案是阻塞启动应用程序直到数据库可用。运行像 SELECT 1 这样的循环,并等待它成功即可。下面是一个示例 shell 脚本:
while ! psql -h postgresb-db -c 'select 1'; do
  sleep 1
done

如果您在Flask应用程序中使用SQLAlchemy,您可以执行类似的操作:
from sqlalchemy import create_engine
from sqlalchemy import exc
import time


while 1:
    try:
        e = create_engine('postgres://lars@localhost/sandbox')
        e.execute('select 1')
    except exc.OperationalError:
        print('Waiting for database...')
        time.sleep(1)
    else:
        break

print('Connected!')

上述解决启动竞争条件的方法是很好的,但它无法处理数据库服务器在您的应用程序运行时重新启动的情况。在这种情况下,您需要优雅地处理断开连接/重连。这需要在代码设计方面进行更多工作,超出了本答案的范围。请参阅此答案来了解有关此主题的一些讨论。

我的docker-compose文件中有depends_on。这不意味着它将等待数据库准备就绪,然后运行docker吗?depends_on: - postgres-db - Yang Wang
Docker并不知道容器内运行的应用程序的任何信息。它无法判断数据库服务器是否准备好服务连接。depends_on只是意味着“等待指定的容器启动”,实际上几乎没有什么用处。 - larsks

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