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

Nginx 如何从Docker容器内部连接到机器的本地主机?,nginx,docker,reverse-proxy,docker-networking,Nginx,Docker,Reverse Proxy,Docker Networking,所以我有一个运行在docker容器中的Nginx,我有一个运行在localhost上的mysql,我想从Nginx中连接到mysql。MySql在localhost上运行,并且没有向外界公开端口,因此它绑定在localhost上,而不是绑定在机器的ip地址上 是否有任何方法可以从docker容器中连接到此MySql或localhost上的任何其他程序 这个问题与“如何从docker容器中获取docker主机的IP地址”不同,因为docker主机的IP地址可能是网络中的公共IP或私有IP,而这些I

所以我有一个运行在docker容器中的Nginx,我有一个运行在localhost上的mysql,我想从Nginx中连接到mysql。MySql在localhost上运行,并且没有向外界公开端口,因此它绑定在localhost上,而不是绑定在机器的ip地址上

是否有任何方法可以从docker容器中连接到此MySql或localhost上的任何其他程序

这个问题与“如何从docker容器中获取docker主机的IP地址”不同,因为docker主机的IP地址可能是网络中的公共IP或私有IP,而这些IP可能在docker容器中可以访问,也可能无法访问(我指的是公共IP,如果托管在AWS或其他地方)。即使您拥有docker主机的IP地址,但这并不意味着您可以从容器内连接到docker主机,因为docker网络可能是覆盖、主机、网桥、MAVLAN、无等,这限制了该IP地址的可访问性。

编辑:

如果您使用的是or 18.03+,只需使用主机
host.docker.internal
(而不是连接字符串中的
127.0.0.1
)连接到mysql服务即可

如果您正在使用Docker for Linux 20.10.0+,也可以使用主机
host.Docker.internal
如果您使用
--add host host.Docker.internal:host gateway
选项启动Docker容器

否则,请阅读下面的内容


太长,读不下去了 在您的
docker run
命令中使用
--network=“host”
,然后docker容器中的
127.0.0.1
将指向您的docker主机

注意:此模式仅适用于Docker for Linux


关于docker容器网络模式的说明 Docker在运行容器时提供。根据您选择的模式,您将以不同的方式连接到docker主机上运行的MySQL数据库

docker运行--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
这里我的容器有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主机的IP地址
172.17.42.1
被设置为默认路由,可以从您的容器访问

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 --pid=container:http alpine ps aux
docker运行--network=“host” 或者,您可以使用运行docker容器。这样的容器将与docker主机共享网络堆栈,并且从容器的角度来看,
localhost
(或
127.0.0.1
)将引用docker主机

请注意,docker容器中打开的任何端口都将在docker主机上打开。而这不需要

我的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
然后在应用程序中,使用
DOCKER\u HOST\u IP
环境变量打开到MySQL的连接

注意:如果使用
bind address=0.0.0.0
您的MySQL服务器将侦听所有网络接口上的连接。这意味着你的MySQL服务器可以通过互联网访问;确保相应地设置防火墙规则

注意2:如果使用
bind address=172.17.42.1
您的MySQL服务器将不会侦听到
127.0.0.1
的连接。在docker主机上运行的要连接到MySQL的进程必须使用
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>

注意:一定要使用
mysql-h 127.0.0.1
而不是
mysql-h localhost
;否则MySQL客户端将尝试使用unix套接字进行连接。

我不同意Thomaslevel的答案


将mysql绑定到172.17.42.1将阻止其他程序使用主机上的数据库访问它。仅当所有数据库用户都已停靠时,此操作才起作用

将mysql绑定到0.0.0.0将向外界开放数据库,这不仅是一件非常糟糕的事情,而且与原始问题作者想要做的相反。他明确表示“MySql在localhost上运行,没有向外界公开端口,因此它绑定在localhost上”

回答伊万特的评论


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

这是不可能的。mysql/mariadb文档明确指出,不可能绑定到多个接口。只能绑定到0、1或所有接口


作为结论,我没有找到任何方法从docker容器访问主机上的(仅本地主机)数据库。这显然是一种非常常见的模式,但我不知道如何做到这一点。

对于我来说,这在NGINX/PHP-FPM堆栈上非常有效,而不涉及任何代码或网络,而应用程序只是希望能够连接到
localhost

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

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 --pid=container:http alpine ps aux
在运行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

我做了一个类似于上面文章的黑客操作,让本地IP映射到容器中的别名(DNS)。主要的问题是使用一个简单的脚本动态获取,该脚本在Linux和OSX主机IP地址中都能工作。我写了这个剧本
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
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
myapp:
  build: .
  ports:
    - "80:80"
  extra_hosts:
    - "dockerhost:$DOCKERHOST"
$ ifconfig | grep 'inet 192'| awk '{ print $2}'
#!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 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

$ docker run -it alpine ifconfig
$ docker run -it --net=host alpine ifconfig
$ docker run -it alpine ps aux
$ docker run -it --pid=host alpine ps aux
$ 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
docker run --rm alpine ip route | awk 'NR==1 {print $3}'
DOCKER_HOST=$(docker run --rm alpine ip route | awk 'NR==1 {print $3}') docker-compose up
version: '3' # specify docker-compose version

services:
  nginx:
    build: ./ # specify the directory of the Dockerfile
    ports:
      - "8080:80" # specify port mapping
    extra_hosts:
      - "dockerhost:<yourIP>"
ip -4 route list match 0/0 | cut -d' ' -f3
docker run --rm -e "HOST_IP=$(ip route get 1 | sed -n 's/^.*src \([0-9.]*\) .*$/\1/p')" ...
docker run --add-host host.docker.internal:host-gateway ...
bash-5.0# ping dockerhost
PING dockerhost (172.20.0.2): 56 data bytes
64 bytes from 172.20.0.2: seq=0 ttl=64 time=0.523 ms
bash-5.0# telnet dockerhost 22
SSH-2.0-OpenSSH_7.6p1 Ubuntu-4ubuntu0.3
bash-5.0# telnet dockerhost 1025
telnet: can't connect to remote host (172.20.0.2): Connection refused
sysctl -w net.ipv4.conf.<bridge_name>.route_localnet=1
iptables -t nat -A PREROUTING -p tcp -i <docker_bridge_name> --dport <service_port> -j DNAT --to-destination 127.0.0.1:<service_port>
iptables -t nat -A PREROUTING -p tcp -i br-5cd80298d6f4 --dport 1025 -j DNAT --to-destination 127.0.0.1:1025
bash-5.0# telnet dockerhost 1025
220 127.0.0.1 ESMTP Service Ready
$ sudo mysql<br>
mysql> create user 'username'@'172.17.0.2' identified by 'password';<br>
Query OK, 0 rows affected (0.00 sec)

mysql> grant all privileges on database_name.* to 'username'@'172.17.0.2' with grant option;<br>
Query OK, 0 rows affected (0.00 sec)

$ sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf
<br>bind-address        = 172.17.0.1

$ sudo systemctl restart mysql.service
version: '3.5'
services:
  yourservice-here:
    container_name: container_name
    ports:
      - "4000:4000"
    extra_hosts: # <---- here
      - localhost:192.168.1.202
      - or-vitualhost.local:192.168.1.202
backend:
  restart: always
  image: backend
  build:
    dockerfile: backend.Dockerfile
    context: .
  environment:
    # add django setting.py os.getenv("var") to bd config and ALLOWED_HOSTS CORS_ORIGIN_WHITELIST
    DJANGO_ALLOWED_PROTOCOL: http
    DJANGO_ALLOWED_HOSTS: docker.for.mac.localhost
    POSTGRES_PASSWORD: 123456
    POSTGRES_USER: user
    POSTGRES_DB: bd_name
    WAITDB: "1"
  volumes:
    - backend_static:/app/static
    - backend_media:/app/media
  depends_on:
    - db

frontend:
  restart: always
  build:
    dockerfile: frontend.Dockerfile
    context: .
  image: frontend
  environment:
    #  replace baseURL for axios
    API_URL: http://docker.for.mac.localhost/b/api
    API_URL_BROWSER: http://docker.for.mac.localhost/b/api
    NUXT_HOST: 0.0.0.0
  depends_on:
    - backend

caddy:
  image: abiosoft/caddy
  restart: always
  volumes:
    - $HOME/.caddy:/root/.caddy
    - ./Caddyfile.local:/etc/Caddyfile
    - backend_static:/static
    - backend_media:/media
  ports:
  - 80:80
  depends_on:
    - frontend
    - backend
    - db
http://docker.for.mac.localhost {

  proxy /b backend:5000 {
    header_upstream Host {host}
    header_upstream X-Real-IP {remote}
    header_upstream X-Forwarded-For {remote}
    header_upstream X-Forwarded-Port {server_port}
    header_upstream X-Forwarded-Proto {scheme}
  }

  proxy / frontend:3000 {
    header_upstream Host {host}
    header_upstream X-Real-IP {remote}
    header_upstream X-Forwarded-For {remote}
    header_upstream X-Forwarded-Port {server_port}
    header_upstream X-Forwarded-Proto {scheme}
  }

  root /

  log stdout
  errors stdout
  gzip
}

http://docker.for.mac.localhost/static {
  root /static
}

http://docker.for.mac.localhost/media {
  root /media
}
ALLOWED_HOSTS = [os.getenv("DJANGO_ALLOWED_HOSTS")]
    
CORS_ORIGIN_WHITELIST = [f'{os.getenv("DJANGO_ALLOWED_PROTOCOL")}://{os.getenv("DJANGO_ALLOWED_HOSTS")}']

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql_psycopg2",
        "NAME": os.getenv("POSTGRES_DB"),
        "USER": os.getenv("POSTGRES_USER"),
        "PASSWORD": os.getenv("POSTGRES_PASSWORD"),
        "HOST": "db",
        "PORT": "5432",
    }
}
axios: {
  baseURL: 'http://127.0.0.1:8000/b/api'
},
host.docker.internal
localhost
 ---
 version: "3"
 services:
   php-apache:
+    network_mode: host
     image: geerlingguy/php-apache:latest
     container_name: php-apache
...
 ---
 version: "2.2"

 services:
   php-apache:
+    network_mode: "host"
        or
+    network_mode: host
...
❯ docker network inspect bridge | grep Gateway
                    "Gateway": "172.17.0.1"
[Unit]
After=docker.service
❯ python -m http.server &> /dev/null &
[1] 149976

❯ docker run --rm python python -c  "from urllib.request import urlopen;print(b'Directory listing for' in urlopen('http://172.17.0.1:8000').read())" 
True