Linux服务器docker部署的容器,端口映射后公网无法访问

问题描述

Docker部署的容器,端口映射后公网无法访问。

处理方案

重要

在应用此方案前,请先为您的磁盘创建快照。如果操作失败,您可以使用快照恢复数据。

  1. 检查是否已启用内核的路由转发功能。

    说明

    启用该功能后,系统才能将接收到的网络请求转发到docker0网桥,并最终传递给容器。

    1. 检查当前状态,运行以下命令。

      cat /proc/sys/net/ipv4/ip_forward

      如果返回值为1,则表示已启用。如果为0,请继续执行下一步。

    2. 临时启用该功能(重启后失效),请运行。

      echo 1 > /proc/sys/net/ipv4/ip_forward
  2. Docker为其bridge网络在宿主机上创建一个虚拟网桥接口,例如docker0。它还为此网桥配置一个子网。如果此子网与主机的物理网络子网重叠,则会发生路由冲突。

    1. 查看容器网络。

      docker network inspect bridge

      查看主机网络。

      ip route

      输出示例:

      default via 192.168.1.1 dev ethO
      192.168.1.0/24 dev eth proto kernel scope link src 192.168.1.100

      Docker网络子网不应与主机网络子网重叠。如果两个网络都使用相同的子网(例如192.168.1.0/24),就会发生路由冲突。

    2. 若要解决此冲突,请创建一个自定义bridge网络。为该网络指定一个不与主机网络重叠的子网。

      docker network create \
      -driver bridge \
      --subnet 10.10.0.0/16 \
      -gateway 10.10.0.1 \ custom_bridge
    3. 创建容器时使用自定义bridge网络。

      docker run --rm -it --net custom_bridge nginx:latest 
  3. 验证Docker端口映射,使用-p host_port:container_port参数将主机的端口映射到容器的端口。

    • 正确示例:映射到Nginx默认端口,nginx:latest镜像中的Nginx服务默认监听80端口。

      docker run -d --name nginx -p 80:80 nginx:latest

      此命令将主机的80端口映射到容器的80端口。因为Nginx默认在此端口上监听,所以访问主机的80端口可以成功连接到Nginx服务。

    • 错误示例:映射到容器内未监听的端口,下面的命令将导致连接超时或失败。

      docker run -d --name nginx1 -p 81:81 nginx:latest

      此命令将主机的81端口映射到容器的81端口。尽管主机会监听81端口,但容器内的Nginx服务仍在80端口上监听,并未监听81端口。因此,发送到主机81端口的流量在容器内找不到对应的服务,导致连接失败。

  4. Docker通过iptables规则实现端口映射。当iptables规则被意外清除或修改时,端口映射会失效。

    1. 检查端口转发规则,Dockernat表的DOCKER链中创建DNAT(目标地址转换)规则以转发流量。

      # 查看 nat 表中的 DOCKER 链
      sudo iptables -t nat -L DOCKER

      示例输出:

      DNAT   tcp  --  anywhere             anywhere             tcp dpt:http to:172.17.0.2:80

      此规则将所有访问主机80端口(HTTP)的TCP流量转发到容器172.17.0.280端口。

    2. 查找容器的IP地址,要确认规则中的IP地址是否正确,请运行以下命令查找容器的IP。

      docker inspect -f '{{.Name}} - {{.NetworkSettings.IPAddress}}' <container_name_or_id>
    3. 如果端口转发规则丢失(例如,在防火墙重置后),连接将会超时。

      重启Docker容器是恢复iptables规则最安全、最可靠的方法。Docker会自动重新生成所有必要的规则。

      重要

      请勿手动添加iptables规则来修复Docker端口映射。Docker创建的规则链很复杂,手动修改容易出错且可能导致安全风险。重启容器或Docker服务是正确的做法。

      # 将 "nginx" 替换为您的容器名
      docker restart nginx
  5. 是否是使用了自定义网络模型,docker默认有3种网络模式,分别是:bridge、host、none,可以使用参数:--network进行指定默认运行容器,不指定network,会已bridge模式进行创建使用。

    • Bridge模式(默认)

      • 说明:Docker创建一个私有的内部网络(bridge),容器连接到该网络上。容器与主机网络隔离。

      • 访问:要从外部访问容器,必须使用 -p 或 --publish 参数进行端口映射。

      • 场景:这是最常用的模式,提供了良好的网络隔离性。

    • Host模式

      • 说明:容器共享主机的网络命名空间。容器不会获得自己的IP地址,而是直接使用主机的网络接口。

      • 访问:容器内应用程序监听的端口将直接暴露在主机的IP地址上,无需端口映射。

      • 场景:追求网络性能,但牺牲了网络隔离性。

    • None模式

      • 说明:容器拥有自己的网络命名空间,但不进行任何网络配置。容器内只有一个 lo (loopback)网络接口,无法与外部网络或其它容器通信。

      • 场景:用于完全隔离的、不需要网络连接的任务(例如,批处理作业)。

    如何验证容器的网络模式,使用 docker inspect 命令可以精确地查看容器配置的网络模式。

    # 将 "container_name" 替换为你的容器名或 ID
    docker inspect -f '{{.HostConfig.NetworkMode}}' <container_name>

    输出示例:

    docker inspect -f '{{.HostConfig.NetworkMode}}' nginx_bridge
    # 输出: default (等同于 bridge)
    
    docker inspect -f '{{.HostConfig.NetworkMode}}' my_host_container
    # 输出: host