【问题标题】:Allow docker container to connect to a local/host postgres database允许 docker 容器连接到本地/主机 postgres 数据库
【发布时间】:2015-09-23 19:01:19
【问题描述】:

我最近一直在玩 Docker 和 QGIS,并按照this tutorial 中的说明安装了一个容器。

一切都很好,虽然我无法连接到包含我所有 GIS 数据的本地主机 postgres 数据库。我认为这是因为我的 postgres 数据库未配置为接受远程连接,并且一直在编辑 postgres conf 文件以允许使用this article 中的说明进行远程连接。

当我尝试连接到在 Docker 中运行 QGIS 的数据库时仍然收到错误消息:无法连接到服务器:Connection refused Is the server running on host "localhost" (::1) and accepting TCP/IP connections to port 5433? postgres 服务器正在运行,我编辑了我的 pg_hba.conf 文件以允许来自一系列 IP 地址 (172.17.0.0/32) 的连接。我之前使用docker ps查询了docker容器的IP地址,虽然IP地址发生了变化,但到目前为止它一直在172.17.0.x范围内

任何想法为什么我不能连接到这个数据库?可能是我想的非常简单的事情!

我正在运行 Ubuntu 14.04; Postgres 9.3

【问题讨论】:

    标签: ubuntu docker postgresql-9.3 qgis


    【解决方案1】:

    您可以添加多个监听地址以获得更好的安全性。

    listen_addresses = 'localhost,172.17.0.1'
    

    添加 listen_addresses = '*' 不是一个好选择,这非常危险,并且会将您的 postgresql 数据库暴露在狂野的西部。

    【讨论】:

      【解决方案2】:

      简单的解决方案

      最新版本的 docker (18.03) 提供了内置的端口转发解决方案。在您的 docker 容器中,只需将 db 主机设置为 host.docker.internal。这将被转发到运行 docker 容器的主机。

      文档在这里:https://docs.docker.com/docker-for-mac/networking/#i-want-to-connect-from-a-container-to-a-service-on-the-host

      【讨论】:

      • 这是迄今为止最好的答案!更容易,应该如何。
      • host.docker.internal 仅限于 mac 吗?
      • @Dragas 根据文档,它“不能在 Mac 之外工作”,但是在 Docker for Windows 的文档中提到了相同的 DNS 名称,所以我认为它仅限于“Docker for. ……”。无论哪种方式,它都是仅用于开发的:您不打算发布使用它的 docker 映像。
      • 仅适用于 windows 和 mac。在我的情况下,ubuntu 无法正常工作
      【解决方案3】:

      让我试着解释一下我做了什么。

      Postgresql

      首先,我进行了必要的配置,以确保我的 Postgres 数据库接受来自外部的连接。

      打开pg_hba.conf并在最后添加以下行:

      host    all             all             0.0.0.0/0               md5
      

      打开 postgresql.conf 并查找 listen_addresses 并在此处进行如下修改:

      listen_addresses = '*'
      

      确保上面的行没有用#注释

      -> 重启你的数据库

      OBS:这不是生产环境的推荐配置

      接下来,我查找了主持人的ip。我使用的是 localhosts ip 127.0.0.1,但容器看不到它,因此在运行容器时会显示有问题的 Connection Refused 消息。在网上对此进行了长时间搜索后,我读到该容器从您的本地网络中看到了内部 IP(您的路由器将其归属于连接到它的每个设备,我不是在谈论让您访问的 IP互联网)。也就是说,我打开了一个终端并执行了以下操作:

      寻找本地网络ip

      打开终端或 CMD

      (MacOS/Linux)

      $ ifconfig
      

      (Windows)

      $ ipconfig
      

      此命令将显示您的网络配置信息。 看起来像这样:

      en4: 
          ether d0:37:45:da:1b:6e 
          inet6 fe80::188d:ddbe:9796:8411%en4 prefixlen 64 secured scopeid 0x7 
          inet 192.168.0.103 netmask 0xffffff00 broadcast 192.168.0.255
          nd6 options=201<PERFORMNUD,DAD>
          media: autoselect (100baseTX <full-duplex>)
          status: active
      

      寻找激活的那个。

      就我而言,我的本地网络 IP 是 192.168.0.103

      完成后,我运行了容器。

      码头工人

      使用--add-host 参数运行容器,如下所示:

      $ docker run --add-host=aNameForYourDataBaseHost:yourLocalNetWorkIp --name containerName -di -p HostsportToBind:containerPort imageNameOrId
      

      就我而言,我做到了:

      $ docker run --add-host=db:192.168.0.103 --name myCon -di -p 8000:8000 myImage
      

      我使用的是Django,所以8000端口是默认的。

      Django 应用程序

      访问数据库的配置是:

      settings.py

      DATABASES = {
          'default': {
                  'ENGINE': 'django.db.backends.postgresql',
                  'NAME': ‘myDataBaseName',
                  'USER': ‘username',
                  'PASSWORD': '123456',
                  'HOST': '192.168.0.103',
                  'PORT': 5432,
          }
      }
      

      参考文献

      关于-p标志: Connect using network port mapping

      关于docker runDocker run documentation

      有趣的文章: Docker Tip #35: Connect to a Database Running on Your Docker Host

      【讨论】:

        【解决方案4】:

        此处发布的解决方案对我不起作用。因此,我发布此答案是为了帮助面临类似问题的人。

        注意:此解决方案也适用于 Windows 10,请查看下方评论。

        操作系统:Ubuntu 18
        PostgreSQL:9.5(托管在 Ubuntu 上)
        Docker:服务器应用程序(连接到 PostgreSQL)

        我正在使用 docker-compose.yml 构建应用程序。

        第一步:请添加host.docker.internal:&lt;docker0 IP&gt;

        version: '3'
        services:
          bank-server:
            ...
            depends_on:
              ....
            restart: on-failure
            ports:
              - 9090:9090
            extra_hosts:
              - "host.docker.internal:172.17.0.1"
        

        要查找 docker i.e. 172.17.0.1 (in my case) 的 IP,您可以使用:

        $> ifconfig docker0
        docker0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
                inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
        

        $> ip a
        1: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
            inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
               valid_lft forever preferred_lft forever
        

        第 2 步: 在 postgresql.conf 中,将 listen_addresses 更改为 listen_addresses = '*'

        第 3 步: 在 pg_hba.conf 中,添加这一行

        host    all             all             0.0.0.0/0               md5
        

        第 4 步: 现在使用 sudo service postgresql restart 重新启动 postgresql 服务

        第 5 步:请使用 host.docker.internal 主机名从服务器应用程序连接数据库。
        例如:jdbc:postgresql://host.docker.internal:5432/bankDB

        享受!!

        【讨论】:

        • sudo nano /etc/postgresql/&lt;your_version&gt;/main/postgresql.conf 对于那些想要编辑 postgres conf 的人
        • 我已经在 Windows 10 和 Docker 中验证了这些步骤。这运作良好。只需要注意:在步骤 1 和步骤 4 中使用 ipconfig 重新启动 postgresql 服务转到控制面板 -> 管理工具 -> 查看本地服务,然后搜索 Postgresql 服务并重新启动它们。
        • 谢谢,第 2 步和第 3 步解决了我的问题。就我而言,我将新的pg_hba 条目的method 设置为password。此外,您不必打开对所有 IP (0.0.0.0/0) 的访问权限,而是可以使用 docker 网桥 IP(使用 ip -h -c a 查找该 IP),在我的情况下是 172.17.0.1/16
        【解决方案5】:

        以防万一,上述解决方案不适用于任何人。使用以下语句从 docker 连接到主机 postgres(在 mac 上):

        psql --host docker.for.mac.host.internal -U postgres
        

        【讨论】:

          【解决方案6】:

          TL;DR

          1. 使用172.17.0.0/16 作为IP 地址范围,而不是172.17.0.0/32
          2. 不要使用localhost 连接到您主机上的PostgreSQL 数据库,而是使用主机的IP。为了保持容器的可移植性,请使用 --add-host=database:&lt;host-ip&gt; 标志启动容器,并使用 database 作为主机名以连接到 PostgreSQL。
          3. 确保将 PostgreSQL 配置为侦听所有 IP 地址上的连接,而不仅仅是 localhost。在 PostgreSQL 的配置文件中查找设置 listen_addresses,通常在 /etc/postgresql/9.3/main/postgresql.conf 中找到(感谢 @DazmoNorton)。

          加长版

          172.17.0.0/32 不是 IP 地址的范围,而是单个地址(名称为 172.17.0.0)。任何 Docker 容器都不会分配该地址,因为它是 Docker 网桥 (docker0) 接口的网络地址。

          Docker启动时,会创建一个新的桥接网络接口,调用ip a时可以很容易看到:

          $ ip a
          ...
          3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN 
              link/ether 56:84:7a:fe:97:99 brd ff:ff:ff:ff:ff:ff
              inet 172.17.42.1/16 scope global docker0
                 valid_lft forever preferred_lft forever
          

          如您所见,在我的例子中,docker0 接口的 IP 地址为172.17.42.1,网络掩码为/16(或255.255.0.0)。这意味着网络地址是172.17.0.0/16

          IP 地址是随机分配的,但无需任何额外配置,它始终在172.17.0.0/16 网络中。对于每个 Docker 容器,将分配该范围内的随机地址。

          这意味着,如果您想授予所有可能的容器对您的数据库的访问权限,请使用172.17.0.0/16

          【讨论】:

          • 嘿,感谢您的 cmets。我已将pg_hba.conf 更改为您建议的地址,但在停止并重新启动 postgres 服务后仍然收到相同的连接错误消息。我已经在我的 ipv4 连接下添加了该行 - 我应该在其他地方添加您建议的地址吗?或者,在我在 Docker 中运行的 QGIS 应用程序中,我是否需要更改 postgres 连接信息?例如,如果我从 docker 容器内连接,主机是否仍然是“localhost”?
          • 啊,这很重要。不,localhost 不是 Docker 容器内的主机系统。尝试连接到主机系统的公共 IP 地址。为了保持容器的可移植性,您还可以使用--add-host=database:&lt;host-ip&gt; 启动容器,并简单地使用database 作为主机名从Docker 容器内连接到您的PostgreSQL 主机。
          • 我还需要一件。我还必须编辑/etc/postgresql/9.3/main/postgresql.conf 并将我的服务器的eth0 IP 地址添加到listen_addresses。默认情况下,listen_addresses 的 postgres 仅绑定到 localhost
          • @DzamoNorton,感谢您的提示!我相应地更新了我的答案。
          • @helmbert host-ip 是虚拟机或docker容器的ip地址?
          【解决方案7】:

          为了设置一些简单的东西,允许 Postgresql 连接 docker 容器到我的本地主机,我在 postgresql.conf 中使用了这个:

          listen_addresses = '*'
          

          并添加了这个 pg_hba.conf:

          host    all             all             172.17.0.0/16           password
          

          然后重新启动。我的客户端 来自 docker 容器(位于 172.17.0.2)然后可以使用主机:密码、数据库、用户名和密码连接到在我的本地主机上运行的 Postgresql。

          【讨论】:

            【解决方案8】:

            对于 docker-compose,您可以尝试添加

            network_mode: "host"
            

            例子:

            version: '2'
            services:
              feedx:
                build: web
                ports:
                - "127.0.0.1:8000:8000"
                network_mode: "host"
            

            https://docs.docker.com/compose/compose-file/#network_mode

            【讨论】:

            • 添加networkd_mode到主机断开另一个容器之间的连接
            【解决方案9】:

            在 Ubuntu 中:

            首先,您必须通过以下命令检查系统中的 Docker 数据库端口是否可用 -

            sudo iptables -L -n
            

            样本输出:

            Chain DOCKER (1 references)
            target     prot opt source               destination         
            ACCEPT     tcp  --  0.0.0.0/0            172.17.0.2           tcp dpt:3306
            ACCEPT     tcp  --  0.0.0.0/0            172.17.0.3           tcp dpt:80
            ACCEPT     tcp  --  0.0.0.0/0            172.17.0.3           tcp dpt:22
            

            这里3306用作172.17.0.2 IP上的Docker数据库端口,如果此端口不可用运行以下命令-

            sudo iptables -A INPUT -p tcp --dport 3306 -j ACCEPT
            

            现在,您可以通过以下配置轻松地从本地系统访问 Docker 数据库

              host: 172.17.0.2 
              adapter: mysql
              database: DATABASE_NAME
              port: 3307
              username: DATABASE_USER
              password: DATABASE_PASSWORD
              encoding: utf8
            

            在 CentOS 中:

            首先,您必须通过以下命令检查防火墙中的 Docker 数据库端口是否可用 -

            sudo firewall-cmd --list-all
            

            样本输出:

              target: default
              icmp-block-inversion: no
              interfaces: eno79841677
              sources: 
              services: dhcpv6-client ssh
              **ports: 3307/tcp**
              protocols: 
              masquerade: no
              forward-ports: 
              sourceports: 
              icmp-blocks: 
              rich rules:
            

            这里3307用作172.17.0.2 IP上的Docker数据库端口,如果此端口不可用运行以下命令-

            sudo firewall-cmd --zone=public --add-port=3307/tcp
            

            在服务器中,您可以永久添加端口

            sudo firewall-cmd --permanent --add-port=3307/tcp
            sudo firewall-cmd --reload
            

            现在,您可以通过上述配置轻松地从本地系统访问 Docker 数据库。

            【讨论】:

            • 我知道这已经过时了,但是现在,您可以通过以下配置轻松地从本地系统访问 Docker 数据库 - 这是错误的做法。他有一个本地数据库,还有一个 docker 应用程序试图连接到该本地数据库,而不是相反
            【解决方案10】:

            简单的解决方案

            只需将--network=host 添加到docker run。就这样!

            这样容器将使用主机的网络,因此localhost127.0.0.1 将指向主机(默认情况下它们指向容器)。示例:

            docker run -d --network=host \
              -e "DB_DBNAME=your_db" \
              -e "DB_PORT=5432" \
              -e "DB_USER=your_db_user" \
              -e "DB_PASS=your_db_password" \
              -e "DB_HOST=127.0.0.1" \
              --name foobar foo/bar
            

            【讨论】:

            • 小心!这个解决方案在我看来是正确的,但不适用于 macOS。不要浪费时间试图弄清楚它为什么不起作用。看看:github.com/docker/for-mac/issues/2716
            • 在 Debian 上工作。尝试更改 postgresql.conf 和 pg_hba.conf 但这更简单更快。
            • 另外,不能发布端口?
            【解决方案11】:

            Docker for Mac 解决方案

            17.06 起

            感谢@Birchlabs 的评论,现在有了这个special Mac-only DNS name available 就容易多了:

            docker run -e DB_PORT=5432 -e DB_HOST=docker.for.mac.host.internal
            

            从 17.12.0-cd-mac46 开始,应使用 docker.for.mac.host.internal 而不是 docker.for.mac.localhost。详情请见release note

            旧版本

            @helmbert 的回答很好地解释了这个问题。但是Docker for Mac does not expose the bridge network,所以我不得不做这个技巧来解决这个限制:

            $ sudo ifconfig lo0 alias 10.200.10.1/24
            

            打开/usr/local/var/postgres/pg_hba.conf并添加这一行:

            host    all             all             10.200.10.1/24            trust
            

            打开/usr/local/var/postgres/postgresql.conf并编辑更改listen_addresses

            listen_addresses = '*'
            

            重新加载服务并启动您的容器:

            $ PGDATA=/usr/local/var/postgres pg_ctl reload
            $ docker run -e DB_PORT=5432 -e DB_HOST=10.200.10.1 my_app 
            

            此解决方法的作用与@helmbert 的答案基本相同,但使用附加到lo0 而不是docker0 网络接口的IP 地址。

            【讨论】:

            • 截至 2017 年 4 月 4 日,这仍然是最新的吗?
            • 我喜欢这种不会暴露数据库的方式。顺便说一句,我可以在 CentOS 上使用它吗?当我尝试使用您提供的别名命令时出现错误:别名:未知主机。
            • 从 Docker 17.06.0-rc1-ce-mac13(2017 年 6 月 1 日)开始,macOS 上有更好的方法。容器识别主机docker.for.mac.localhost。这是您的主机的 IP。在容器的主机数据库中查找其条目,如下所示:docker run alpine /bin/sh -c 'getent hosts docker.for.mac.localhost'
            • @Birchlabs 这太棒了!
            • 似乎从 18.03 起已更改为 host.docker.internal,其他选项仍然可用但已弃用 (Source)。
            【解决方案12】:

            另一种解决方案是服务卷,您可以定义一个服务卷并将主机的 PostgreSQL 数据目录挂载在该卷中。查看给定的撰写文件以了解详细信息。

            version: '2'
            services:
              db:   
                image: postgres:9.6.1
                volumes:
                  - "/var/lib/postgresql/data:/var/lib/postgresql/data" 
                ports:
                  - "5432:5432"
            

            通过这样做,另一个 PostgreSQL 服务将在容器下运行,但使用与宿主 PostgreSQL 服务相同的数据目录。

            【讨论】:

            • 这可能会导致与正在运行的主机 PostgreSQL 服务发生写入冲突
            • 我认为在这种情况下停止主机服务将解决问题。
            【解决方案13】:

            我的设置需要做的另一件事是添加

            172.17.0.1  localhost
            

            /etc/hosts

            这样 Docker 将指向 172.17.0.1 作为数据库主机名,而不是依赖于不断变化的外部 ip 来查找数据库。希望这可以帮助其他人解决这个问题!

            【讨论】:

            • 这是一个糟糕的解决方案。本地主机通常应指向 127.0.0.1。更改它可能会产生不良后果,即使在这种特殊情况下它有效。
            • 更好的方法是在运行容器时使用--add-host=database:172.17.0.1 设置database 主机。然后将您的应用程序指向该主机。这样可以避免在容器内硬编码 IP 地址。
            • --add-host=database:172.17.0.1 更可取
            猜你喜欢
            • 2019-03-25
            • 2022-11-20
            • 2023-04-02
            • 2015-11-28
            • 2020-01-20
            • 2018-11-28
            • 1970-01-01
            • 2021-05-22
            • 1970-01-01
            相关资源
            最近更新 更多