从Docker容器访问主机数据库

224
如果我有一个运行在某个主机上的 MySQL 数据库,并且该主机还运行着一个 Docker 容器:那么我该如何从运行在主机上的 Docker 容器中访问 MySQL 数据库呢?
例如,是否有一种方法将主机的端口发布到容器中(相当于 docker run -p 的反向操作)?

1
如果MySQL服务器正在监听某个端口,容器难道不可以像其他互联网连接一样连接到该端口的主机吗? - jwodder
6个回答

193

从 18.03 的文档中:

我想从容器连接到主机上的服务

主机具有不断变化的 IP 地址(如果您没有网络访问,则为无)。从 18.03 开始,我们建议使用特殊的 DNS 名称 host.docker.internal 连接,该名称解析为主机使用的内部 IP 地址。

网关也可以通过 gateway.docker.internal 访问。

例如: 这是我在容器中使用的 MySQL 连接字符串,用于访问我的主机上的 MySQL 实例:

mysql://host.docker.internal:3306/my_awesome_database

52
提示:目前此功能仅适用于Mac和Win操作系统。 - flp
7
点赞。不只是因为该答案,而是因为你应该知道如何使用它并给出一个例子。 - granadaCoder
1
我花了将近2个小时才达到这个目标,非常感谢。 - Ashish Bainade
5
仍然没有Linux支持,正如@flp所写,对于那些好奇的人。这是GitHub问题的链接。 - GAltelino
这是我的.NET连接字符串,它无法从Docker中找到数据库:“server=gateway.docker.internal;user=root;database=mydb;port=3306;password=”尝试使用“host.docker.internal”也不行。 - Varun Setia
仍然无法在Linux主机上运行! - tyro

101

关于如何以一致、易懂和可移植的方式完成这个任务,有几个长期的讨论。虽然没有完全解决方案,但我会在下面链接到这些讨论。

无论如何,您可以尝试使用 --add-host 选项将主机的 IP 地址添加到容器的 /etc/hosts 文件中。从那里,连接到所需端口的主机就非常简单了:

向容器的 hosts 文件添加条目

您可以使用一个或多个 --add-host 标志将其他主机添加到容器的 /etc/hosts 文件中。此示例为名为 docker 的主机添加了一个静态地址:

 $ docker run --add-host=docker:10.180.0.1 --rm -it debian
    $$ ping docker
    PING docker (10.180.0.1): 48 data bytes
    56 bytes from 10.180.0.1: icmp_seq=0 ttl=254 time=7.600 ms
    56 bytes from 10.180.0.1: icmp_seq=1 ttl=254 time=30.705 ms
    ^C--- docker ping statistics ---
    2 packets transmitted, 2 packets received, 0% packet loss
    round-trip min/avg/max/stddev = 7.600/19.152/30.705/11.553 ms

注意:有时您需要连接到Docker主机,这意味着获取主机的IP地址。您可以使用以下shell命令简化此过程:

 $ alias hostip="ip route show 0.0.0.0/0 | grep -Eo 'via \S+' | awk '{ print $2 }'"
 $ docker run  --add-host=docker:$(hostip) --rm -it debian

文档:

https://docs.docker.com/engine/reference/commandline/run/

有关从容器访问主机的讨论:

https://github.com/docker/docker/issues/1143

https://github.com/docker/docker/issues/10023


它能在NGINX反向代理后面正常工作吗? - kta

47

从Docker 18.03起,请使用host.docker.internal


4
怎么样?你能举个例子说明如何使用它吗? - danfromisrael
为什么不运行一个Docker化的MySQL? - Igor De Oliveira Sá
在我的 Docker version 18.03.0-ce, build 0520e24 上无法工作。 - scythargon
@IgorDeOliveiraSá 想象一下以下情景,在开发环境中,您有一些数据库侦听127.0.0.1:3306,在生产环境中,您有一个侦听127.0.0.1:3306的数据库,本地是本地数据库,生产是云代理,您必须将mysql本地docker化并为生产找到另一种解决方案,或者您可以从docker连接到端口,这对两种情况都有效。 - Drachenfels
1
docker.for.mac.localhost18.03.1-ce-mac65 上对我有效。 - cdignam
2
用 host.docker.internal 替换 localhost - ifelse.codes

43

3
从 Docker 17.12.0 ce 版本开始,docker.for.mac.host.internal 可用。详见 https://docs.docker.com/docker-for-mac/release-notes/#docker-community-edition-17120-ce-mac46-2018-01-09-stable。 - Zangetsu
docker.for.mac.localhost works on 18.03.1-ce-mac65 - cdignam

35

其他答案对我没有很好的帮助。我的容器无法使用host.docker.internal解析主机IP。有两种方法:

  1. 共享主机网络 --net=host:

docker run -it --net=host  myimage
使用 Docker 的 IP 地址,通常为172.17.0.1。您可以通过调用 ifconfig 命令并获取 Docker 接口的 inet addr 来检查它。
user@ubuntu:~$ ifconfig
docker0   Link encap:Ethernet  HWaddr 02:42:a4:a2:b2:f1  
  inet addr:172.17.0.1  Bcast:0.0.0.0  Mask:255.255.0.0
  inet6 addr: fe80::42:a4ff:fea2:b2f1/64 Scope:Link
一旦你获得了这个IP地址,你可以将它作为参数传递给docker run,然后再传递给应用程序,或者像我一样,通过卷将jdbc.properties的位置映射到主机上的目录中,这样你就可以在外部管理该文件。

一旦你获得了这个IP地址,你可以将它作为参数传递给docker run,然后再传递给应用程序,或者像我一样,通过卷将jdbc.properties的位置映射到主机上的目录中,这样你就可以在外部管理该文件。

  docker run -it -v /host_dir/docker_jdbc_config:${jetty_base}/var/config myimage

注意:您的数据库可能不允许外部连接。对于postgresql,您需要编辑2个文件,如下所述这里这里

  1. 编辑postgresql.conf,使其监听所有地址。默认情况下,它将指向localhost。

listen_addresses = '*'
  • 编辑pg_hba.conf文件以允许来自所有地址的连接。在最后一行添加:

  • host all all 0.0.0.0/0 md5
  • host     all             all             0.0.0.0/0               md5
    

    重要提示:更新数据库访问的最后一步不建议在生产环境中使用,除非您确实知道自己在做什么。


    --net=host 在我的 CI 环境中起作用了,非常感谢! - Samy

    2

    仅适用于Linux
    情况1:Mysqldb在本地主机上运行,而不是在Docker上
    您可以将主机IP地址固定为连接,并连接到该IP地址,而不是localhost。
    对于Linux dockerhost,它是固定的,即172.17.0.1。因此,请连接到172.17.0.1而不是localhost。
    情况2:如果mysqldb也在Docker上运行,则最佳做法是使用Docker容器名称连接,而不是IP地址。
    因此,docker-compose文件应如下所示

    version: '3.8'
    services:
      mysqldb:
        image: mysql:5.7
        restart: unless-stopped
        env_file: ./.env
        environment:
          - MYSQL_ROOT_PASSWORD=$MYSQLDB_ROOT_PASSWORD
          - MYSQL_DATABASE=$MYSQLDB_DATABASE
        ports:
          - $MYSQLDB_LOCAL_PORT:$MYSQLDB_DOCKER_PORT
        volumes:
          - db:/var/lib/mysql
    producer:
         build: .
         ports:
          - '$LOCAL_PORT:$DOCKER_PORT' 
         depends_on:
             - mysqlsb
         volumes:
             - ./:/usr/src/app
    

    现在不需要使用IP地址或本地地址连接到MySQLSB数据库。
    var mysql = require('mysql');
    
    var con = mysql.createConnection({
      host: mysqldb,
      user: "yourusername",
      password: "yourpassword"
    });
    
    con.connect(function(err) {
      if (err) throw err;
      console.log("Connected!");
    });
    

    1
    使用Ubuntu 20.04。将主机更改为172.17.0.1确实解决了我的问题。容器可以从主机IP连接到数据库。 - Rahmat Nazali Salimi
    对于我和我的Docker Compose文件 - 指定我想要用作DB主机的服务起作用了。 - Justin
    非常感谢,根据您所描述的情况1,使用 Docker IP 172.17.0.1 替代 localhost 的解决方案解决了我的问题! - Kim Tang

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