首页 文章

从Docker容器内部,如何连接到本机的本地主机?

提问于
浏览
758

所以我在Docker容器中运行了一个Nginx,我有一个在localhost上运行的mysql,我想从我的Nginx中连接到MySql . MySql在localhost上运行,而不是将端口暴露给外部世界,所以它绑定在localhost上,而不是绑定在机器的ip地址上 .

有没有办法从这个docker容器中连接到这个MySql或localhost上的任何其他程序?

17 回答

  • 2

    Simplest solution for Mac OSX

    只需使用Mac的IP地址即可 . 在Mac上运行此命令以获取IP地址并在容器内使用它:

    $ ifconfig | grep 'inet 192'| awk '{ print $2}'
    

    只要在Mac上或其他docker容器中本地运行的服务器正在侦听0.0.0.0,docker容器就可以到达那个地址 .

    如果您只想访问另一个正在侦听0.0.0.0的docker容器,则可以使用172.17.0.1

  • 229

    在Windows 10 Home上使用Docker Toolbox时,没有一个答案适用于我,但 10.0.2.2 确实如此,因为它使用VirtualBox将主机暴露给此地址上的VM .

  • 1

    Linux解决方案(内核> = 3.6) .

    好的,您的localhost服务器具有默认的docker接口 docker0 ,其IP地址为 172.17.0.1 . 您的容器以默认网络设置 --net="bridge" 启动 .

    • 为docker0接口启用route_localnet:
      $ sysctl -w net.ipv4.conf.docker0.route_localnet=1

    • 将此规则添加到iptables:
      $ iptables -t nat -I PREROUTING -i docker0 -d 172.17.0.1 -p tcp --dport 3306 -j DNAT --to 127.0.0.1:3306
      $ iptables -t filter -I INPUT -i docker0 -d 127.0.0.1 -p tcp --dport 3306 -j ACCEPT

    • 创建具有'%'访问权限的mysql用户 - 意思是 - 来自任何人,不包括localhost:
      CREATE USER 'user'@'%' IDENTIFIED BY 'password';

    • 将脚本中的mysql-server地址更改为172.17.0.1

    来自kernel documentation

    route_localnet - BOOLEAN:路由时不要将环回地址视为火星源或目的地 . 这使得127/8可用于本地路由目的(默认为FALSE) .

  • 7

    The CGroups and Namespaces are playing major role in the Container Ecosystem.

    命名空间提供了一层隔离 . 每个容器都在一个单独的命名空间中运行,其访问权限仅限于该命名空间Cgroup控制每个容器的资源利用率,而Namespace控制进程可以查看和访问相应资源的内容 .

    以下是您可以遵循的解决方案的基本理解,

    Use Network Namespace

    当容器从图像中生成时,将定义并创建网络接口 . 这为容器提供了唯一的IP地址和接口 .

    $ docker run -it alpine ifconfig
    

    通过将命名空间更改为主机,cotainers网络不会与其接口保持隔离,该进程将可以访问主机网络接口 .

    $ docker run -it --net=host alpine ifconfig
    

    如果进程侦听端口,它们将在主机接口上被侦听并映射到容器 .

    Use PID Namespace 通过更改Pid命名空间,允许容器与其正常范围之外的其他进程交互 .

    此容器将在其自己的命名空间中运行 .

    $ docker run -it alpine ps aux
    

    通过将命名空间更改为主机,容器还可以查看系统上运行的所有其他进程 .

    $ docker run -it --pid=host alpine ps aux
    

    Sharing Namespace

    在 生产环境 中执行此操作是一种不好的做法,因为您正在打破容器安全模型,该模型可能会泄漏漏洞并轻松访问窃听者 . 这仅适用于调试工具,并且低估了容器安全性的漏洞 .

    第一个容器是nginx服务器 . 这将创建一个新的网络和进程命名空间 . 此容器将自身绑定到新创建的网络接口的端口80 .

    $ docker run -d --name http nginx:alpine
    

    另一个容器现在可以重用此命名空间

    $ docker run --net=container:http mohan08p/curl curl -s localhost
    

    此外,此容器可以在共享容器中查看与进程的接口 .

    $ docker run --pid=container:http alpine ps aux
    

    这将允许您在不更改或重新启动应用程序的情况下为容器提供更多权限 . 以类似的方式,您可以连接到主机上的mysql,运行和调试您的应用程序 . 但是,不建议这样做 . 希望能帮助到你 .

  • 8

    Edit: 如果您使用Docker-for-macDocker-for-Windows 18.03,只需使用主机 host.docker.internal 连接到您的mysql服务 .

    As of Docker 18.04, this does not work on Docker-for-Linux.


    TLDR

    docker run 命令中使用 --network="host" ,然后docker容器中的 127.0.0.1 将指向您的泊坞主机 .


    关于docker容器网络模式的注意事项

    Docker在运行容器时提供different networking modes . 根据您选择的模式,您将以不同方式连接到在docker主机上运行的MySQL数据库 .

    docker run --network =“bridge”(默认)

    Docker默认创建一个名为 docker0 的网桥 . docker主机和docker容器都在该网桥上有一个IP地址 .

    在Docker主机上,输入 sudo ip addr show docker0 ,您将看到如下输出:

    [vagrant@docker:~] $ sudo ip addr show docker0
    4: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
        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
        inet6 fe80::5484:7aff:fefe:9799/64 scope link
           valid_lft forever preferred_lft forever
    

    所以这里我的docker主机在 docker0 网络接口上有IP地址 172.17.42.1 .

    现在启动一个新容器并在其上获取一个shell: docker run --rm -it ubuntu:trusty bash 并在容器类型 ip addr show eth0 中发现它的主网络接口是如何设置的:

    root@e77f6a1b3740:/# ip addr show eth0
    863: eth0: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
        link/ether 66:32:13:f0:f1:e3 brd ff:ff:ff:ff:ff:ff
        inet 172.17.1.192/16 scope global eth0
           valid_lft forever preferred_lft forever
        inet6 fe80::6432:13ff:fef0:f1e3/64 scope link
           valid_lft forever preferred_lft forever
    

    这里我的容器的IP地址为 172.17.1.192 . 现在看一下路由表:

    root@e77f6a1b3740:/# route
    Kernel IP routing table
    Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
    default         172.17.42.1     0.0.0.0         UG    0      0        0 eth0
    172.17.0.0      *               255.255.0.0     U     0      0        0 eth0
    

    因此,docker主机 172.17.42.1 的IP地址被设置为默认路由,可以从容器中访问 .

    root@e77f6a1b3740:/# ping 172.17.42.1
    PING 172.17.42.1 (172.17.42.1) 56(84) bytes of data.
    64 bytes from 172.17.42.1: icmp_seq=1 ttl=64 time=0.070 ms
    64 bytes from 172.17.42.1: icmp_seq=2 ttl=64 time=0.201 ms
    64 bytes from 172.17.42.1: icmp_seq=3 ttl=64 time=0.116 ms
    

    docker run --network =“host”

    或者,您可以使用network settings set to host运行docker容器 . 这样的容器将与docker主机共享网络堆栈,从容器的角度来看, localhost (或 127.0.0.1 )将引用docker主机 .

    请注意,Docker容器中打开的任何端口都将在docker主机上打开 . 这不需要-p or -P docker run option .

    我的docker主机上的IP配置:

    [vagrant@docker:~] $ ip addr show eth0
    2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
        link/ether 08:00:27:98:dc:aa brd ff:ff:ff:ff:ff:ff
        inet 10.0.2.15/24 brd 10.0.2.255 scope global eth0
           valid_lft forever preferred_lft forever
        inet6 fe80::a00:27ff:fe98:dcaa/64 scope link
           valid_lft forever preferred_lft forever
    

    并在 host 模式下从docker容器中:

    [vagrant@docker:~] $ docker run --rm -it --network=host ubuntu:trusty ip addr show eth0
    2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
        link/ether 08:00:27:98:dc:aa brd ff:ff:ff:ff:ff:ff
        inet 10.0.2.15/24 brd 10.0.2.255 scope global eth0
           valid_lft forever preferred_lft forever
        inet6 fe80::a00:27ff:fe98:dcaa/64 scope link
           valid_lft forever preferred_lft forever
    

    正如您所看到的,docker主机和docker容器共享完全相同的网络接口,因此具有相同的IP地址 .


    从容器连接到MySQL

    桥接模式

    要在桥接模式下从容器访问在docker主机上运行的MySQL,您需要确保MySQL服务正在侦听 172.17.42.1 IP地址上的连接 .

    为此,请确保在MySQL配置文件(my.cnf)中有 bind-address = 172.17.42.1bind-address = 0.0.0.0 .

    如果需要使用网关的IP地址设置环境变量,则可以在容器中运行以下代码:

    export DOCKER_HOST_IP=$(route -n | awk '/UG[ \t]/{print $2}')
    

    然后在您的应用程序中,使用 DOCKER_HOST_IP 环境变量打开与MySQL的连接 .

    Note: 如果您使用 bind-address = 0.0.0.0 您的MySQL服务器将侦听所有网络接口上的连接 . 这意味着您的MySQL服务器可以从Internet访问;确保相应地设置防火墙规则 .

    Note 2: 如果您使用 bind-address = 172.17.42.1 ,您的MySQL服务器将不会侦听对 127.0.0.1 的连接 . 在希望连接到MySQL的docker主机上运行的进程必须使用 172.17.42.1 IP地址 .

    主机模式

    要在主机模式下从容器访问在docker主机上运行的MySQL,您可以在MySQL配置中保留 bind-address = 127.0.0.1 ,您只需从容器连接到 127.0.0.1

    [vagrant@docker:~] $ docker run --rm -it --network=host mysql mysql -h 127.0.0.1 -uroot -p
    Enter password:
    Welcome to the MySQL monitor.  Commands end with ; or \g.
    Your MySQL connection id is 36
    Server version: 5.5.41-0ubuntu0.14.04.1 (Ubuntu)
    
    Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.
    
    Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners.
    
    Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
    
    mysql>
    

    note: 请使用 mysql -h 127.0.0.1 而不是 mysql -h localhost ;否则MySQL客户端将尝试使用unix套接字进行连接 .

  • 31

    对于macOS和Windows

    Docker v 18.03 and above (since March 21st 2018)

    使用您的内部IP地址或连接到特殊的DNS名称 host.docker.internal ,它将解析为主机使用的内部IP地址 .

    Linux支持挂起https://github.com/docker/for-linux/issues/264

    MacOS与早期版本的Docker

    Docker for Mac v 17.12 to v 18.02

    与上面相同但使用 docker.for.mac.host.internal .

    Docker for Mac v 17.06 to v 17.11

    与上面相同但是使用 docker.for.mac.localhost .

    Docker for Mac 17.05 and below

    要从docker容器访问主机,必须将IP别名附加到网络接口 . 你可以绑定你想要的任何IP,只要确保你没有将它用于其他任何东西 .

    sudo ifconfig lo0 alias 123.123.123.123/24

    然后确保您的服务器正在侦听上述IP或 0.0.0.0 . 如果它正在侦听localhost 127.0.0.1 ,它将不接受连接 .

    然后只需将您的docker容器指向此IP,即可访问主机!

    要测试,您可以在容器中运行类似 curl -X GET 123.123.123.123:3000 的内容 .

    别名将在每次重新启动时重置,因此如有必要,请创建启动脚本 .

    解决方案和更多文档:https://docs.docker.com/docker-for-mac/networking/#use-cases-and-workarounds

  • 2

    For Windows Machine :-

    在构建期间随机运行以下命令到Expose docker port

    $docker run -d --name MyWebServer -P mediawiki
    

    enter image description here

    enter image description here

    在上面的容器列表中,您可以看到分配为32768的端口 . 尝试访问

    localhost:32768
    

    您可以看到mediawiki页面

  • 2

    您可以使用ngrok创建到localhost计算机的安全隧道,然后将该隧道公开到docker容器 .

    ngrok可于2017年5月22日免费使用 .

    脚步:

    1)去ngrok

    2)download ngrok客户端并按照安装说明进行操作

    3)SIGN UP为一个帐户,他们将提供身份验证令牌 . 注册是必需的,因为ngrok只在注册后为您提供tcp端口隧道 . 注册无需任何费用或信用卡 .

    4)在你的终端做 ngrok tcp 3306 . 3306 是mysql在我的本地运行的端口,你也可以使用任何其他端口 .

    5)您将收到步骤 4 的地址,例如: tcp://0.tcp.ngrok.io:10117 . 这是与本地计算机的隧道连接 . 0.tcp.ngrok.io 映射到 localhost ,端口 10117 映射到本地端口 3306 . 现在,您可以使用此地址从任何位置访问localhost端口3306,包括在此计算机上运行的任何docker容器 . 在您的docker容器(无论它在哪里),假设您已经安装了mysql客户端,请执行以下操作:

    mysql --host 0.tcp.ngrok.io --port 10117 -u root

    您将能够从docker容器内登录本地计算机的 root 帐户!

    我发表了关于此解决方案的博客,请参阅更多详情here

  • 0

    您可以使用alpine图像获取主机IP

    docker run --rm alpine ip route | awk 'NR==1 {print $3}'
    

    这将更加一致,因为您总是使用alpine来运行命令 .

    与Mariano的答案类似,您可以使用相同的命令来设置环境变量

    DOCKER_HOST=$(docker run --rm alpine ip route | awk 'NR==1 {print $3}') docker-compose up
    
  • 53

    直到host.docker.internal为每个平台工作,你可以使用我的容器充当NAT网关而无需任何手动设置https://github.com/qoomon/docker-host

  • 3

    我不同意Thomasleveil的回答 .

    使mysql绑定到172.17.42.1将阻止使用主机上的数据库的其他程序到达它 . 这仅在所有数据库用户都已停靠时才有效 .

    将mysql绑定到0.0.0.0会将db打开到外部世界,这不仅是一件非常糟糕的事情,而且与原作者想要做的事情相反 . 他明确地说“MySql在localhost上运行,而不是将端口暴露给外部世界,所以它绑定在localhost上”

    回答ivant的评论

    “为什么不将mysql绑定到docker0呢?”

    这是不可能的 . mysql / mariadb文档明确表示无法绑定到多个接口 . 您只能绑定到0,1或所有接口 .

    作为结论,我还没有找到任何方法从docker容器到达主机上的(仅限localhost)数据库 . 这绝对是一个非常常见的模式,但我不知道该怎么做 .

  • -13

    Solution for Windows 10

    Docker Community Edition 17.06.0-ce-win18 2017-06-28 (stable)

    您可以使用主机 docker.for.win.localhost 的DNS名称来解析内部IP . (警告某些消息来源提到 windows 但它应该是 win

    Overview 我需要做类似的事情,即从我的Docker容器连接到我的localhost,它运行 Azure Storage EmulatorCosmosDB Emulator .

    Azure Storage Emulator 默认侦听127.0.0.1,而你也可以更改它的绑定,我正在寻找一个可以使用默认设置的解决方案 .

    这也适用于从我的Docker容器连接到 SQL ServerIIS ,两者都在我的主机上本地运行,具有默认端口设置 .

  • 2

    Edit: I ended up prototyping out the concept on GitHub. Check out: https://github.com/sivabudh/system-in-a-box


    首先,我的答案是面向两组人:使用Mac的人和使用Linux的人 .

    host 网络模式在Mac上不起作用 . 您必须使用IP别名,请参阅:https://stackoverflow.com/a/43541681/2713729

    什么是主机网络模式?见:https://docs.docker.com/engine/reference/run/#/network-settings

    其次,对于那些使用Linux的人(我的直接经验是使用Ubuntu 14.04 LTS并且我即将升级到16.04 LTS), yes ,您可以使服务在Docker容器内运行连接到运行的 localhost 服务Docker主机(例如您的笔记本电脑) .

    怎么样?

    关键是当您运行Docker容器时,必须使用 host 模式运行它 . 该命令如下所示:

    docker run --network="host" -id <Docker image ID>

    当您在容器内执行 ifconfig (您需要 apt-get install net-tools 容器 ifconfig 可以调用)时,您将看到网络接口与Docker主机(例如您的笔记本电脑)上的网络接口相同 .

    重要的是要注意我是Mac用户,但我在Parallels下运行Ubuntu,因此使用Mac并不是一个缺点 . ;-)

    这就是你如何将NGINX容器连接到 localhost 上运行的MySQL .

  • 0

    我做了一个类似上面的帖子的hack获取本地IP映射到容器中的别名(DNS) . 主要问题是使用一个在 Linux and OSX the host IP address 中都能运行的简单脚本动态获取 . 我做了这个在两种环境下都能运行的脚本(即使在配置了 "$LANG" != "en_*" 的Linux发行版中):

    ifconfig | grep -E "([0-9]{1,3}\.){3}[0-9]{1,3}" | grep -v 127.0.0.1 | awk '{ print $2 }' | cut -f2 -d: | head -n1
    

    因此,使用Docker Compose,完整配置将是:

    Startup script (docker-run.sh)

    export DOCKERHOST=$(ifconfig | grep -E "([0-9]{1,3}\.){3}[0-9]{1,3}" | grep -v 127.0.0.1 | awk '{ print $2 }' | cut -f2 -d: | head -n1)
    docker-compose -f docker-compose.yml up
    

    docker-compose.yml

    myapp:
      build: .
      ports:
        - "80:80"
      extra_hosts:
        - "dockerhost:$DOCKERHOST"
    

    然后在代码中将 http://localhost 更改为 http://dockerhost .

    有关如何自定义 DOCKERHOST 脚本的更高级指南,请查看this post并解释其工作原理 .

  • 10

    对于那些在Windows上,假设您正在使用桥接网络驱动程序,您将需要专门将MySQL绑定到hyper-v网络接口的IP地址 .

    这是通过通常隐藏的C:\ ProgramData \ MySQL文件夹下的配置文件完成的 .

    绑定到0.0.0.0将不起作用 . 所需的地址也显示在docker配置中,在我的例子中是10.0.75.1 .

  • 9

    这是我的解决方案:它适用于我的情况

    • 通过/etc/mysql/mysql.conf.d中的注释 #bind-address = 127.0.0.1 将本地mysql服务器设置为公共访问

    • 重启mysql服务器 sudo /etc/init.d/mysql restart

    • 运行以下命令打开用户root访问任何主机 mysql -uroot -proot GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'root' WITH GRANT OPTION; FLUSH PRIVILEGES;

    • 创建sh脚本:run_docker.sh

    #!bin/bash
    
        HOSTIP=`ip -4 addr show scope global dev eth0 | grep inet | awk '{print \$2}' | cut -d / -f 1`
    
    
          docker run -it -d --name web-app \
                      --add-host=local:${HOSTIP} \
                      -p 8080:8080 \
                      -e DATABASE_HOST=${HOSTIP} \
                      -e DATABASE_PORT=3306 \
                      -e DATABASE_NAME=demo \
                      -e DATABASE_USER=root \
                      -e DATABASE_PASSWORD=root \
                      sopheamak/springboot_docker_mysql
    
    • 使用docker-composer运行
    version: '2.1' 
    
     services: 
      tomcatwar:
         extra_hosts:
            - "local:10.1.2.232"
         image: sopheamak/springboot_docker_mysql 
         ports:
             - 8080:8080
         environment:
                - DATABASE_HOST=local
                - DATABASE_USER=root
                - DATABASE_PASSWORD=root
                - DATABASE_NAME=demo
                - DATABASE_PORT=3306
    
  • 1108

    这对我来说非常适合NGINX / PHP-FPM堆栈而无需触及应用程序只是希望能够连接到 localhost 的任何代码或网络

    mysqld.sock 从主机挂载到容器内部 .

    在运行mysql的主机上找到mysql.sock文件的位置:
    netstat -ln | awk '/mysql(.*)?\.sock/ { print $9 }'

    将该文件挂载到docker中的预期位置:
    docker run -v /hostpath/to/mysqld.sock:/containerpath/to/mysqld.sock

    mysqld.sock的可能位置:

    /tmp/mysqld.sock
    /var/run/mysqld/mysqld.sock 
    /var/lib/mysql/mysql.sock
    /Applications/MAMP/tmp/mysql/mysql.sock # if running via MAMP
    

相关问题