Docker Swarm未暴露端口

8

我正在尝试在单个节点上运行docker swarm,并且在将docker应用程序端口暴露给主机时遇到了问题。

这与Docker swarm service port not exposed类似,但我正在单个节点上运行,并对我的问题和研究提供更多细节。

例如,给定以下myapp.yml文件:

version: "3"
services:
  app:
    image: myapp
    ports: 
      - "8123:8123"

通过以下命令启动堆栈:

docker stack deploy -c myapp.yml myapp

堆栈启动后,当我尝试访问端口时(通过curl),它会失败。例如,

curl -v http://localhost:8123/myapp

显示连接被拒绝(该端口上没有任何正在监听的内容)。

使用docker run运行代码正常

以下命令启动镜像并显示如何验证已公开端口。

docker run -p 8123:8123 --name myapp myapp

然后可以使用Curl。

curl -v http://localhost:8123/myapp

给出在Docker中运行的应用程序的输出。

当我运行docker ps时,端口部分的输出显示:0.0.0.0:8123:8123/tcp

docker network inspect bridge - 显示Docker容器分配给bridge网络。默认情况下,docker run命令使用Docker桥接网络。

命令docker port myapp显示:

8123/tcp -> 0.0.0.0:8123

这与docker psPORT字段输出匹配。

docker stack deploy不会暴露端口

在使用docker stack deploy -c myapp.yml myapp启动后,我运行docker ps命令,只在输出的PORTS字段中看到8123/tcp

命令docker port myapp没有输出(表示docker主机上没有可用端口)。

当我运行docker network ls时,我看到:

NETWORK ID       NAME            DRIVER     SCOPE
1234567890      bridge           bridge     local
9999999999      myapp_default   overlay    swarm

我注意到网络myapp_default与桥接(local)模式不同。我尝试使用现有的bridge网络,但是当我在堆栈/组合文件中引用bridge网络时,我发现创建了网络myapp_bridge
所以我阅读了Docker Networking https://blog.alexellis.io/docker-stacks-attachable-networks/的好文章。
到目前为止,我还没有让它工作,所以我写下这篇文章来寻求建议/帮助。
注意:此文章(日期为2017年2月)称它应该可以工作,但并未回答我的问题。
我认为我已经接近成功了。
资源和其他相关问题

https://docs.docker.com/v17.12/get-started/part5/ - 是Docker堆栈技术的主要文档。但在该页面上搜索网络并没有找到有用的信息。

https://blog.alexellis.io/docker-stacks-attachable-networks/ - 很好的文章。

Docker swarm service port not exposed - 类似于我正在问的问题。

https://runnable.com/docker/basic-docker-networking - Docker网络编写。当创建覆盖网络(使用docker stack deploy时默认)时,它会显示以下内容:

这些网络需要有效的键值存储服务,如Consul、Etcd或ZooKeeper。在创建网络之前,您必须安装和配置您的键值存储服务。

无法在Swarm Compose YAML文件中使用用户定义的桥接


这可能是一个冒险的尝试,但你是否尝试在你的堆栈文件中明确定义一个网络,并让Docker在启动堆栈时创建你的应用程序网络? - Yamuk
@YamaçKurtuluş,是的,我也尝试过了,但是没有成功。我认为问题在于创建的网络处于叠加模式。这篇文章 https://runnable.com/docker/basic-docker-networking 表示,当使用叠加网络时,您需要拥有另一个服务,例如Consul、Etcd或ZooKeeper。请参见创建叠加网络,而我没有设置其中任何一个服务(因为我正在单节点上运行)。 - PatS
这很奇怪,你不应该需要Consul或其他任何东西,Swarm应该可以直接使用。你尝试过使用docker service create命令吗? - Yamuk
你的 app.yml 文件中的关键字 'ports' 应该是列表格式。如果你在 yml 文件中将 'myapp' 替换为 'nginx' 并暴露端口 8123:80,这样做只是为了测试端口是否已经暴露? - lkq
2
我有点尴尬地说,问题出在有一个有故障的启动脚本(在Docker容器中)。当我修复它后,我可以使用curl,并且像YamacKurtulus所说的那样工作了。我还学到了docker ps输出的是Dockerfile EXPOSE中的内容,而不是实际被映射的内容。我认为docker inspect myapp并显示已映射的端口,但我不确定。 - PatS
显示剩余4条评论
6个回答

7
在Swarm中
ports:
  - "8123:8123"

将端口暴露在所有Swarm节点上,如果一些节点上的其他服务使用该端口,则可能会出现问题。

您只能在具有下一个docker-compose.yml配置的容器主机上公开端口。

services:
  app:
    ports:
      - target: 8123
        published: 8123
        mode: host

在多个场合中,我发现Docker中的某些东西会导致实例失去连接。这种情况很少发生(也许在5年以上的时间里只有2或3次)。需要强制重启(停止和启动)Docker服务本身才能解决这个问题。请参见https://dev59.com/DlgQ5IYBdhLWcg3wsGCZ,使用`sudo systemctl stop docker然后再使用sudo systemctl start docker`。 - PatS

2

您需要为您的服务定义一个网络。在Docker Swarm模式下,Docker容器变成了“服务”,要访问其端口,您需要调用该服务的名称。

我提供了一个docker-compose文件的示例,演示如何连接nginx和php-fpm。

原始答案:Original Answer

version: '3.2'
  services:
    nginx:
      image: nginx
      links:
      - php
      - mariadb:mysql
      ports:
      - "8880:80"
      networks:
      - frontend
      deploy:
        #mode: replicated
        replicas: 1
        placement:
          constraints: [node.role == manager]
    php:
       image: php
       deploy:
         #mode: replicated
         replicas: 1
         placement:
           constraints: [node.role == manager]
           #endpoint_mode: dnsrr
       links:
         - mariadb:mysql
    mariadb:
       image: mariadb:10.4.1
       #restart: always
       deploy:
         mode: replicated
         replicas: 1
         #placement:
         #constraints: [node.role == manage]
         environment:
           MYSQL_ROOT_PASSWORD: SECRET          
         volumes:
           - type: volume
              source: mydatabase
              target: /var/lib/mysql/data
              volume:
                nocopy: true # do not copy data from container when a volume is created?
    networks:
      frontend  
    volumes:
      mydatabase:

部署完(docker stack deploy -c docker-compose.yaml)后,要查看服务列表:

命令如下:

docker service ls 

要指导nginx如何访问您的php-fpm后端,请编辑您的nginx配置文件:

Original Answer翻译成"最初的回答"

location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass **NAME_OF_PHP_SERVICE_IN SWARM**:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

在Swarm集群中,您应该使用php Service的名称而不是容器的IP地址(IP:9000)。

将以下内容:

Original Answer

翻译成:

最初的回答


1

我有同样的问题。通过更改IP地址解决了该问题。 首先,获取当前Docker Swarm节点的IP地址。

docker info

管理地址行中找到您的IP地址。例如,我的IP地址是192.168.0.13。现在,您可以通过此地址192.168.0.13:8123打开您的应用程序。


1

我遇到了相同的问题 - 在我的ubuntu机器上可以工作 - 但在我的debian服务器上无法正常工作。 最终我将问题缩小到了debian使用的内核版本上。将其升级至5.11.22-5后,问题得以解决。

有些文章提到了缺少内核模块的情况(没有具体说明是哪些模块) - 可能与此相关。


1
我有一个端口,用于同时在SWARM和本地Linux OS上运行,这两个应用程序都可以正常运行,但只有一个可以接收传入的连接。
奇怪的是,当我期望其中一个应用程序使用该端口时出现错误时,没有发生EADDRINSE错误。
例如,将本地应用程序运行在8080端口,并在此主机上使用SWARM的8080端口。
传入连接到达8080端口,连接被发送到其中一个应用程序(我记不清是哪一个了)。

1

这种情况经常发生是由于IPv6的原因。在主机上,localhost被定义为::1而不是127.0.0.1/etc/hosts中。Swarm中的入口网络不支持IPv6。因此有两个可能的解决方案:

  1. Connect using IPv4: curl -v http://127.0.0.1:8123/myapp.

  2. Publish the port using host mode rather than ingress networking. This publishes the port only on the host where the containers are running. This can be useful for services that are deployed on every node (deploy: mode: global) to remove extra networking hops between nodes. To switch to host mode for the published port, follow Ryabchenko Alexander's advice by switching to the long syntax when publishing your port:

        ports:
          - target: 8123
            published: 8123
            mode: host
    

    More details are in the compose file v3 reference: https://docs.docker.com/compose/compose-file/compose-file-v3/#long-syntax-1. Note that publishing the port in host mode here is different from host networking that runs the container without a network sandbox, sadly "host" is used for two different things here, one being outside of swarm, and the other being outside of docker isolation.


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接