Nginx 如何从Docker容器内部连接到机器的本地主机?
所以我有一个运行在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+,只需使用主机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
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