我正在使用Flask
和Docker
构建基于微服务的Web应用程序。目前,我有2个服务由docker-compose
在同一默认网络上运行。
docker-compose.yml 文件是-
version: '3'
services:
mysql:
image: mysql
environment:
MYSQL_USER: "mysqluser"
MYSQL_PASSWORD: "mysqlpassword"
MYSQL_ROOT_PASSWORD: "root"
MYSQL_DATABASE: "gadgetfreeiot"
container_name: mysql
ports:
- 3306:3306
restart: always
entrypoint: ['docker-entrypoint.sh', '--default-authentication-plugin=mysql_native_password']
product_api:
build: ./${SERVICE_ROOT:-src/services/product/api}
image: product_api:v1
container_name: product_api
volumes:
- ./${SERVICE_ROOT:-src/services/product/api}:${PROJECT_ROOT:-/usr/projects/gadgetfreeiot}/${SERVICE_ROOT:-src/services/product/api}
ports:
- 5000:5000
depends_on:
- mysql
environment:
username: "mysqluser"
password: "mysqlpassword"
host: "mysql"
port: "3306"
database: "gadgetfreeiot"
command: ["./wait-for-mysql.sh", "--", "python", "./run.py"]
docker ps
输出给我-
johir@ubuntu:gadgetfreeiot$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e10329e26e5c product_api:v1 "./wait-for-mysql.sh…" 18 minutes ago Up 18 minutes 0.0.0.0:5000->5000/tcp product_api
7fed5a136123 mysql "docker-entrypoint.s…" 18 minutes ago Up 18 minutes 0.0.0.0:3306->3306/tcp, 33060/tcp mysql
这两个服务都在gadgetfreeiot_default
网络下运行。 docker inspect gadgetfreeiot_default
表明两者都在同一个网络中-
johir@ubuntu:gadgetfreeiot$ docker inspect gadgetfreeiot_default
[
{
"Name": "gadgetfreeiot_default",
"Id": "67e09ae3a33c0ff4203eefe4fee6ba421d3f68564c6e32c7d1cd04e866ac6850",
"Created": "2018-10-18T14:56:05.86215576+03:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "192.168.0.0/20",
"Gateway": "192.168.0.1"
}
]
},
"Internal": false,
"Attachable": true,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"14ffb8eaa0c17de0122de142dbcf7aa5455b41b47eadb197e8be200c2375fbb3": {
"Name": "mysql",
"EndpointID": "38ed4140ed728271194ee82f12b3d937c53166f6159ab4e6fcf2d8087039ed06",
"MacAddress": "02:42:c0:a8:00:02",
"IPv4Address": "192.168.0.2/20",
"IPv6Address": ""
},
"e013059b510e42933d33f7c3fb7e141a19a6c78a0e34d031e5fce5e104aa8697": {
"Name": "product_api",
"EndpointID": "fdbe0ed92d0e53d6fc1040a50b1898e2bb87b34384f80b98e638a3a89a57c4e1",
"MacAddress": "02:42:c0:a8:00:03",
"IPv4Address": "192.168.0.3/20",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {
"com.docker.compose.network": "default",
"com.docker.compose.project": "gadgetfreeiot",
"com.docker.compose.version": "1.22.0"
}
}
]
现在,我正尝试从主机操作系统访问product_api
和mysql
服务。同时,我还尝试从一个容器访问另一个容器(从product_api
到mysql
,从mysql
到product_api
)。 mysql
可通过-
mysql -h172.19.0.2 -P3306 -umysqluser -p
product_api
也可以通过-
mysql -hmysql -P3306 -umysqluser -p
幸运的是,我可以从 product_api -
访问curl http://localhost:5000
节目
johir@ubuntu:gadgetfreeiot$ docker exec -it product_api bash
root@e013059b510e:/usr/projects/gadgetfreeiot/src/services/product/api# curl http://localhost:5000
{
"message": "Endpoint not found",
"status": "failed"
}
显示 curl http://172.19.0.3:5000
的位置-
curl: (7) Failed to connect to 172.19.0.3 port 5000: Connection refused
这意味着product_api
已启动并正在运行,并且只能由localhost
或127.0.0.1
从product_api容器内部进行访问,而不能通过IP或通过product_api容器外部的服务名称进行访问,主机操作系统,也不能来自mysql
容器。
最后,我在netstat -tln
的3个中检查了-活跃网络-
# product_api container
root@e10329e26e5c:/usr/projects/gadgetfreeiot/src/services/product/api# netstat -tln
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 127.0.0.11:40071 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:5000 0.0.0.0:* LISTEN
# mysql container
root@7fed5a136123:/# netstat -tln
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 127.0.0.11:35507 0.0.0.0:* LISTEN
tcp6 0 0 :::33060 :::* LISTEN
tcp6 0 0 :::3306 :::* LISTEN
# host OS
johir@ubuntu:gadgetfreeiot$ netstat -tln
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:631 0.0.0.0:* LISTEN
tcp6 0 0 ::1:631 :::* LISTEN
tcp6 0 0 :::5000 :::* LISTEN
tcp6 0 0 :::3306 :::* LISTEN
注意:
tcp6无法在product_api
容器中打开端口5000。另一方面,tcp6在mysql
容器上打开了端口3306。
问题:为什么我无法从主机操作系统甚至从mysql访问product_api
服务(因此,为什么tcp6不会为product_api服务公开端口5000)? >
答案 0 :(得分:1)
最简单的答案是,您无法访问curl http://localhost:5000
所显示的内容。谁(主机或容器)可以访问哪些IP并解析哪些主机名似乎有些令人困惑。
在您的docker-compose.yml
中,您将端口3306
和5000
映射到了各自的容器。因此,docker从主机上的任何接口(不带docker网络的接口)向您的容器添加了一个端口转发(如果您对引擎盖的工作方式感兴趣,请查看sudo iptables-save | grep 5000
的输出) ,它将看起来像这样:
-A DOCKER -d 192.168.0.2/20 ! -i br-e013059b510e -o br-e013059b510e -p tcp -m tcp --dport 5000 -j ACCEPT
因此,您可以从主机上通过以下方式获得服务
curl http://localhost:5000
通过另一台计算机(假设允许防火墙设置)通过
curl http://your.hostname:5000
从同一docker网络上的容器中
curl http://product_api:5000 # or
curl http://product_api.gadgetfreeiot_default:5000 # or
curl http://192.168.0.3:5000
要将两个容器都放在同一个docker网络上,请像这样调整docker-compose.yml
:
services:
mysql:
[...]
ports:
- 3306:3306
networks:
- gadgetfreeiot
[...]
product_api:
ports:
- 5000:5000
networks:
- gadgetfreeiot
[...]
networks:
gadgetfreeiot:
容器product_api
之类的主机名在主机上不可解析。但是它们在您的容器中。在容器内,您在127.0.0.11
处有一个额外的docker DNS服务器,它可以解析主机可以解析的内容,以及product_api.gadgetfreeiot_default
这样的docker主机名。试试
nslookup product_api.gadgetfreeiot_default
在主机上以及从容器内部
docker exec -it mysql bash
检查https://docs.docker.com/network/以获得更多信息。
关于您的注释:netstat
不会向您显示哪个端口是“开放”的,即防火墙允许的,但是哪个端口是由程序绑定的。程序是否绑定到IPv4,v6或两者上的接口上的端口,取决于程序。这与Docker网络无关。