为什么我无法访问由docker-compose创建的服务?

时间:2018-10-19 09:05:00

标签: docker docker-compose

我正在使用FlaskDocker构建基于微服务的Web应用程序。目前,我有2个服务由docker-compose在同一默认网络上运行。

  • mysql
  • product_api

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_apimysql服务。同时,我还尝试从一个容器访问另一个容器(从product_apimysql,从mysqlproduct_api)。 mysql可通过-

从所有3个主机操作系统,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已启动并正在运行,并且只能由localhost127.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)? >

1 个答案:

答案 0 :(得分:1)

最简单的答案是,您无法访问curl http://localhost:5000所显示的内容。谁(主机或容器)可以访问哪些IP并解析哪些主机名似乎有些令人困惑。

在您的docker-compose.yml中,您将端口33065000映射到了各自的容器。因此,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:

DNS

容器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网络无关。