在 docker-compose 中运行 Django 应用程序:与 postgres db 的连接第一次拒绝但之后可以工作

Posted

技术标签:

【中文标题】在 docker-compose 中运行 Django 应用程序:与 postgres db 的连接第一次拒绝但之后可以工作【英文标题】:Running Django app in docker-compose: connection to postgres db refuses the first time but works afterwards 【发布时间】:2017-12-16 05:27:53 【问题描述】:

我有一个奇怪的问题,可以用 Docker 的simple tutorial 重现。

如果我完全按照教程进行操作,一切都会正常工作,即在docker-compose up 命令之后,Web 容器将运行并很好地连接到 db 容器。

但是,如果我选择在主机上创建相同的 Django 项目,请更改其 postgres 数据库的设置,并将其复制到其 Dockerfile 中的 Web 映像,而不是将主机目录安装到容器并执行这些操作如教程中所示(使用命令docker-compose run web django-admin.py startproject composeexample .,然后更改生成并位于主机上的挂载目录中的设置文件),我第一次运行docker-compose up时,Web容器连接到db,报错如下

web_1 | psycopg2.OperationalError:无法连接到服务器:连接被拒绝 web_1 |服务器是否在主机“db”(172.18.0.2)上运行并接受 web_1 |端口 5432 上的 TCP/IP 连接?

但是,如果我使用 docker-compose down 停止撰写,然后使用 docker-compose up 再次运行它,Web 容器将成功连接到数据库而没有问题。

“连接被拒绝”在这里似乎不是一个罕见的问题,但我已经检查并验证了所有设置是否正确以及常见的原因,例如端口号错误、端口未公开或将主机设置为“本地”而不是“数据库” ' 等不是这种情况下的问题。

注意:FWIW,我在主机中使用 CNTLM 作为系统代理,必须为 web 图像设置环境变量,它适用于其他场景。

编辑: 请在下面找到更多信息。

在主机目录中,我有以下文件和目录

composeexample(由另一个容器按照相同的教程生成并复制到此处) manage.py(由另一个容器生成并复制到这里) requirements.txt(与教程中的完全相同) Dockerfile(对教程中的文件稍作修改) docker-compose.yml(根据教程中的稍作修改)

composeexample/settings.py:

.........
DATABASES = 
    'default': 
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'postgres',
        'USER': 'postgres',
        'HOST': 'db',
        'PORT': 5432,
    

.........

Dockerfile(大体相同,添加了环境变量):

FROM python:3.5
ENV PYTHONUNBUFFERED 1
ENV http_proxy "http://172.17.0.1:3128"
ENV https_proxy "http://172.17.0.1:3128"
ENV HTTP_PROXY "http://172.17.0.1:3128"
ENV HTTPS_PROXY "http://172.17.0.1:3128"

RUN mkdir /code
WORKDIR /code
ADD requirements.txt /code/
RUN pip install -r requirements.txt
ADD . /code/

docker-compose(我删除了挂载的卷 .:/code,因为项目文件在构建时已经复制到 Web 映像中。我测试过将其保留在原始文件中,但没有任何区别):

version: '3'

services:
  db:
    image: postgres
  web:
    build: .
    command: python3 manage.py runserver 0.0.0.0:8000
    ports:
      - "8000:8000"
    depends_on:
      - db

【问题讨论】:

你能用你的 docker-compose、dockerfile 和设置 de django 更新帖子吗? 似乎你的 django 容器与你的 db 容器没有正确的连接 谢谢,我已经为数据库添加了 docker-compose、dockerfile 和 Django 设置。是的,我一次又一次地检查以确保与数据库的连接设置正确。事实上,正如问题中所说,如果我停止并再次启动撰写,它将毫无问题地连接到数据库。所以我假设连接设置一定是正确的? 为什么不尝试几秒钟来启动 django 容器,我不确定这是否有问题,但你试试command: /bin/bash -c "sleep 7; python3 manage.py runserver -h 0.0.0.0 -p 9000 -r -d" 非常感谢。将sleep 5 添加到 Web 容器的命令中已经解决了我的问题。我已经读过这个,但我从来没有想到这实际上是问题所在。不过,这对我来说还是有点奇怪,因为我希望一旦您设置了depends_on,Web 容器会等待数据库完全准备好后再尝试连接它? 【参考方案1】:

使用wait-for-it.sh 等待 Postgres 准备就绪:

下载这个众所周知的脚本:https://raw.githubusercontent.com/vishnubob/wait-for-it/master/wait-for-it.sh

version: '3'

services:
  db:
    image: postgres
  web:
    build: .
    command: /wait-for-it.sh db:5432 -- python3 manage.py runserver 0.0.0.0:8000
    volumes:
      - ./wait-for-it.sh:/wait-for-it.sh
    ports:
      - "8000:8000"
    depends_on:
      - db

它会一直等到db端口打开,不会再浪费了。

【讨论】:

谢谢,我接受了朱利安的回答,因为它确实帮助我快速而简单地解决了我的问题。我也做了一些研究,这个解决方案对我来说是一个更合适的解决方案,因此我赞成。【参考方案2】:

正如文档所说 depends_ondepends_on 它表达了容器之间的依赖关系,但这并不意味着一个容器将等待其他容器准备好,一个可能的解决方案是在docker-compose 中添加一点睡眠,例如这个:

command: /bin/bash -c "sleep 7; python3 manage.py runserver -h 0.0.0.0 -p 9000 -r -d"

【讨论】:

【参考方案3】:

您可以使用healthcheck。

示例来自:peter-evans/docker-compose-healthcheck: How to wait for container X before starting Y using docker-compose healthcheck

version: '3'

services:
  db:
    image: postgres
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 3s
      timeout: 30s
      retries: 3
  web:
    build: .
    command: python3 manage.py runserver 0.0.0.0:8000
    ports:
      - "8000:8000"
    depends_on:
      db:
        condition: service_healthy

【讨论】:

docker compose file version 3 或更高版本不再支持这种方式

以上是关于在 docker-compose 中运行 Django 应用程序:与 postgres db 的连接第一次拒绝但之后可以工作的主要内容,如果未能解决你的问题,请参考以下文章

在 gitlab ci 中查找在 docker-compose 中运行的容器的 url/ip

如何使用 pycharm 调试在 docker-compose 中运行的进程

如何在 AWS CodeBuild 上运行 docker-compose?

使用 docker 运行多个项目,每个项目都使用 docker-compose 运行

Docker-Compose

Django模板语言相关内容 Djan