我是基于ChatGPT-turbo-3.5实现的AI助手,在此网站上负责整理和概括文章

本文详细介绍了 Docker 网络架构的核心组件,包括 CNM(Container Network Model)、Libnetwork 和网络驱动(如 Bridge、Overlay 等)。文章阐述了 Docker 网络模式(如 bridge、host、none 等)的工作原理、配置方法及适用场景,并探讨了端口映射、网络诊断与优化等技术细节。Docker 网络模型的灵活性和可扩展性使其能够满足单机和多主机环境下的网络需求,同时提供了高效的网络管理工具和策略,具有较高的实用价值。

# Docker 网络理论

容器网络实质上是由 Dokcer 为应用程序所创造的虚拟环境的一部分,它能让应用从宿主机操作系统的网络环境中独立出来,形成容器自有的网络设备、IP 协议栈、端口套接字、IP 路由表、防火墙等等与网络相关的模块。

Docker 为实现容器网络,主要采用的架构由三部分组成:CNM、Libnetwork 和驱动。

# CNM

Docker 网络架构采用的设计规范是 CNM(Container Network Model):CNM 中规定了 Docker 网络的基础组成要素:Sandbox、Endpoint、Network。如图所示,

★如上图所示(我们将图中的三个容器从左到右依次标记为 1、2、3),那么容器 2 有两个 endpoint 并且分别接入 NetworkdA 和 NetworkB。那么容器 1 和容器 2 是可以实现通信的,因为都接入了 NetworkA。但是容器 3 和容器 1,以及容器 2 的两个 Endpoint 之间是不能通信的,除非有三层路由器的支持。 ”

  • Sandbox,提供了容器的虚拟网络栈,也即端口套接字、IP 路由表、防火墙、DNS 配置等内容。主要用于隔离容器网络与宿主机网络,形成了完全独立的容器网络环境。

  • Network,Docker 内部的虚拟子网,网络内的参与者相互可见并能够进行通讯。Docker 的虚拟网路和宿主机网络是存在隔离关系的,其目的主要是形成容器间的安全通讯环境。

  • Endpoint,就是虚拟网络的接口,就像普通网络接口一样,Endpoint 的主要职责是负责创建连接。在 CNM 中,终端负责将沙盒连接到网络。个人理解:Endpoint 与常见的网络适配器类似,也就意味着 Endpoint 只能接入某一个网络。因此,如果容器需要接入到多个网络,就需要多个 Endpoint。

  • 对于使用CNM的容器管理系统来说,具体底下网络如何实现,不同子网彼此怎么隔离,有没有QoS,都不关心。只要插件能提供网络和接入点,只需把容器给接上或者拔下,剩下的都是插件驱动自己去实现,这样就解耦了容器和网络功能,十分灵活。

  • CNM的典型生命周期: 首先,驱动注册自己到网络控制器,网络控制器使用驱动类型,来创建网络;然后在创建的网络上创建接口;最后把容器连接到接口上即可。销毁过程则正好相反,先把容器从接入口上卸载,然后删除接入口和网络即可。

  • CNM的典型生命周期目前CNM支持的驱动类型有四种:

    • Null: 不提供网络服务,容器启动后无网络连接;

    • Bridge: 就是Docker传统上默认用Linux网桥和Iptables实现的单机网络;

    • Overlay: 是用vxlan隧道实现的跨主机容器网络;

    • Remote: 扩展类型,预留给其他外部实现的方案,比如有一套第三方的SDN方案(如OpenStack Neutron)就可以接进来。

# Libnetwork

Libnetwork 是 CNM 的标准实现。Libnetwork 是开源库,采用 Go 语言编写(跨平台的),也是 Docker 所使用的库,Docker 网络架构的核心代码都在这个库中。Libnetwork 实现了 CNM 中定义的全部三个组件,此外它还实现了本地服务发现、基于 Ingress 的容器负载均衡,以及网络控制层和管理层功能。

作为Docker的网络控制层:

  • 提供跨主机的网络功能
  • 实现服务发现和负载均衡
  • 管理网络的生命周期
  • 提供统一的API供Docker Engine调用

# 驱动

如果说 Libnetwork 实现了控制层和管理层功能,那么驱动就负责实现数据层。比如网络的连通性和隔离性是由驱动来处理的。驱动通过实现特定网络类型的方式扩展了 Docker 网络栈,例如桥接网络和覆盖网络。

Docker 内置了若干驱动,通常被称作原生驱动或者本地驱动。比如 Bridge Driver、Host Driver、Overlay Driver、MacLan Driver、None Driver 等等。第三方也可以编写 Docker 网络驱动,这些驱动被叫做远程驱动,例如 Calico、Contiv、Kuryr 以及 Weave 等。每个驱动负责创建其上所有网络资源的创建和管理。

其中 Bridge 和 Overlay 在开发过程中使用频率较高。

Bridge,Docker 容器的默认网络驱动,通过网桥来实现网络通讯。
Overlay,借助 Docker 集群模块 Docker Swarm 搭建的跨 Docker Daemon 网络。通过它可以搭建跨物理网络主机的虚拟网络,进而让不同物理机中运行的容器感知不到多个物理机的存在。

# Docker网络模式

Docker提供了多种网络模式,本质上是不同驱动的组合应用:

# 基础网络模式

在使用docker run创建Docker容器时,可以用 --net 选项指定容器的网络模式,

模式 描述 适用场景
bridge 默认模式,创建docker0网桥 单机容器通信
host 直接使用主机网络栈 高性能场景
none 无网络连接 特殊安全需求
container 共享其他容器的网络命名空间 容器间紧密协作
]docker run -it -P --name tomcat01 --net=bridge tomcat  # 默认设置
docker run -it -P --name tomcat02 --net=none tomcat

# 高级网络模式

模式 描述 实现技术
overlay 跨主机容器网络 VXLAN
macvlan 容器直接接入物理网络 MACVLAN/IPVLAN
自定义网络 用户定义的隔离网络 多种驱动支持

# 网络驱动模型工作流程

  1. 初始化阶段:

    • Docker Engine启动时加载Libnetwork
    • Libnetwork注册内置驱动并加载远程驱动插件
  2. 网络创建:

    docker network create --driver bridge my_bridge
    • 用户通过Docker CLI创建网络
    • Libnetwork调用指定驱动创建网络实例
  3. 容器连接:

    docker run --network=my_bridge -d nginx
    • Libnetwork为容器创建Sandbox
    • 驱动创建Endpoint并连接到Network
    • 配置IP地址和路由规则

# Docker网络

安装Docker时,它会自动创建三个网络,默认bridge网桥(创建容器默认连接到此网络)、 nonehost

# 查看docker网络
[root@docker-node01 ~]# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
ac13ae33e4ad   bridge    bridge    local
b91dae2a3e50   host      host      local
b07751b1a42c   none      null      local

# none网络

  • none 网络就是什么都没有的网络。挂在这个网络下的容器除了 lo,没有其他任何网卡。

  • 容器创建时,可以通过 --network=none 指定使用 none 网络。

  • 一些对安全性要求高并且不需要联网的应用可以使用 none 网络。比如某个容器的唯一用途是生成随机密码,就可以放到 none 网络中避免密码被窃取。

    [root@node1 ~]# docker run -it --network=none busybox
    Unable to find image 'busybox:latest' locally
    latest: Pulling from library/busybox
    5cc84ad355aa: Pull complete
    Digest: sha256:5acba83a746c7608ed544dc1533b87c737a0b0fb730301639a0179f9344b1678
    Status: Downloaded newer image for busybox:latest
    / # ip a
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
        link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
        inet 127.0.0.1/8 scope host lo
           valid_lft forever preferred_lft forever

# host网络

  • 连接到 host 网络的容器共享 Docker host的网络栈,容器的网络配置与 host 完全一样。

  • 通过 --network=host 指定使用 host 网络。

  • 直接使用 Docker host 的网络最大的好处就是性能,如果容器对网络传输效率有较高要求,则可以选择 host 网络。当然不便之处就是牺牲一些灵活性,比如要考虑端口冲突问题,Docker host 上已经使用的端口就不能再用了。

    [root@node1 ~]# docker run -it --network=host busybox
    / # ip a
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
        link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
        inet 127.0.0.1/8 scope host lo
           valid_lft forever preferred_lft forever
        inet6 ::1/128 scope host
           valid_lft forever preferred_lft forever
    2: ens32: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000
        link/ether 00:0c:29:fe:4d:57 brd ff:ff:ff:ff:ff:ff
        inet 192.168.61.10/24 brd 192.168.61.255 scope global noprefixroute ens32
           valid_lft forever preferred_lft forever
        inet6 fe80::20c:29ff:fefe:4d57/64 scope link
           valid_lft forever preferred_lft forever
    3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue
        link/ether 02:42:e3:ed:a0:cf brd ff:ff:ff:ff:ff:ff
        inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
           valid_lft forever preferred_lft forever

# bridge网络

  • Docker 安装时会创建一个命名为docker0 的 linux bridge。如果不指定--network,创建的容器默认都会挂到 docker0 上。

  • 创建一个容器后,查看容器网络信息。

    [root@node1 ~]# docker run -it busybox
    / # ip a
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
        link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
        inet 127.0.0.1/8 scope host lo
           valid_lft forever preferred_lft forever
    7: eth0@if8: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
        link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
        inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
           valid_lft forever preferred_lft forever
    / # ip route
    default via 172.17.0.1 dev eth0
    172.17.0.0/16 dev eth0 scope link  src 172.17.0.2
    # 查看服[root@node1 ~]# ip a
    [root@node1 ~]# ip a
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
        link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
        inet 127.0.0.1/8 scope host lo
           valid_lft forever preferred_lft forever
        inet6 ::1/128 scope host
           valid_lft forever preferred_lft forever
    2: ens32: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
        link/ether 00:0c:29:fe:4d:57 brd ff:ff:ff:ff:ff:ff
        inet 192.168.61.10/24 brd 192.168.61.255 scope global noprefixroute ens32
           valid_lft forever preferred_lft forever
        inet6 fe80::20c:29ff:fefe:4d57/64 scope link
           valid_lft forever preferred_lft forever
    3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
        link/ether 02:42:e3:ed:a0:cf brd ff:ff:ff:ff:ff:ff
        inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
           valid_lft forever preferred_lft forever
        inet6 fe80::42:e3ff:feed:a0cf/64 scope link
           valid_lft forever preferred_lft forever
    ...
    8: vethe63fdb0@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
        link/ether 9a:e6:32:fe:5b:ba brd ff:ff:ff:ff:ff:ff link-netnsid 1
        inet6 fe80::98e6:32ff:fefe:5bba/64 scope link
           valid_lft forever preferred_lft forever
    ...
  • 一个新的网络接口vethe63fdb0被挂到了 docker0 上,vethe63fdb0就是新创建容器的虚拟网卡。eth0@if8vethe63fdb0@if7是一对 veth pair。veth pair 是一种成对出现的特殊网络设备,可以把它们想象成由一根虚拟网线连接起来的一对网卡,网卡的一头(eth0@if8)在容器中,另一头(vethe63fdb0@if7)挂在网桥 docker0 上,其效果就是将eth0@if8也挂在了 docker0 上。

  • 每次创建一个新容器的时候,Docker从可用的地址段中选择一个空闲的IP地址分配给容器的eth0端口,并且使用本地主机上docker0接口的IP作为容器的默认网关

# 桥接网络

Docker 的 bridge 网络采用内置的 bridge 驱动,而 bridge 的底层采用的是 Linux 内核中 Linux bridge 技术(这意味着 bridge 是高性能并且是非常稳定的)。

那么 Linux 内核中 Linux bridge 应用于容器的话,到底是一个什么样的拓扑图呢?

如图所示(这个拓扑关系不清楚接下去的很多东西难以理解,所以先贴出采用 bridge 之后的一个拓扑图),由于容器运行在自己单独的 network namespace 中,所以有单独的协议栈。容器中配置网关为 172.17.0.1,发出去的数据包先到达 br0,然后交给主机的协议栈,由于目的 IP 是外网 IP,且主机会开启 IP forward 功能,于是数据包通过主机的 eth0 发出去。由于 172.17.0.1 是内网 IP ,所以一般发出去之前会做 NAT 转换。由于要经过主机的协议栈并且要做 NAT 转换,所以性能上可能会差点,但是优点就是容器处于内网中,安全性相对要高点。

默认情况下,创建的容器在没有使用 --network 参数指定要加入的 docker 网络时,默认都是加入 Docker 默认的单机桥接网络,也就是下面的 name 为 bridge 的网络。

[root@k8s-master01 ~]# docker network ls | grep bridge
5296ffb462b0   bridge    bridge    local

而默认的 bridge 网络是被映射到内核中为 docker0 的网桥上。

[root@k8s-master01 ~]# docker network inspect bridge | grep bridge.name
            "com.docker.network.bridge.name": "docker0",


[root@k8s-master01 ~]#  ip link show docker0
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
    link/ether 02:42:75:18:90:39 brd ff:ff:ff:ff:ff:ff

Docker 默认的 bridge 网络和 Linux 内核中的 “docker0” 网桥是一个对应关系,如图所示。bridge 是 Docker 中对网络的命名,而 docker0 是内核中网桥的名字。(个人理解:你就可以把 bridge 和 docker0 当成 Linux 网桥的两个名字,两个都是代表同一个东西。docker 为了管理网络,又给 docker0 这个网桥取名为 bridge)。

那么,容器在没有指定要加入的网络情况下,都是加入这个网络的,假如之后的拓扑图跟前面的一样。另外,单机桥接网络中的容器想要对外发布服务的话,需要依赖于端口映射,这也是为啥我们在启动容器的时候需要指定端口映射关系的原因。

# 创建新的单机桥接网络

  • 使用 docker network create -d driver NAME 命令,我们可创建一个名为 “localnet” 的单机桥接网络,并且在内核中还会多出一个新的 Linux 网桥。

    [root@k8s-master01 ~]# docker network create -d bridge localnet
    [root@k8s-master01 ~]# docker network ls | grep localnet
    e40687530545   localnet   bridge    local
  • 在创建完之后,我们可以通过 brctl 工具来查看系统中的 Linux 网桥。以看到,输出的内容中包含了两个网桥

    • docker0 是默认的 Docker bridge 网络所使用的网桥,

    • br-f55943e20201 是 Docker localnet 网络所使用的网桥。

    • 这两个网桥目前都没有任何设备接入(看 interface 列)。

      # yum install -y bridge-utils
      
      # 查看系统中的网桥
      [root@k8s-master01 ~]# brctl show
      bridge	name 	bridge 	id 	STP enabled 	interfaces
      br-f55943e20201 	8000.02429e635565 	no 	docker0 	8000.024275189039 	no
    • 这两个网桥所处的网段是不同的,一个是 172.18.0.1,另一个则是 172.17.0.1。

      [root@k8s-master01 ~]# ifconfig
      br-f55943e20201: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
              inet 172.18.0.1  netmask 255.255.0.0  broadcast 172.18.255.255
              ether 02:42:9e:63:55:65  txqueuelen 0  (Ethernet)
              RX packets 671  bytes 66620 (65.0 KiB)
              RX errors 0  dropped 0  overruns 0  frame 0
              TX packets 698  bytes 71887 (70.2 KiB)
              TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
      
      docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
              inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
              inet6 fe80::42:75ff:fe18:9039  prefixlen 64  scopeid 0x20<link>
              ether 02:42:75:18:90:39  txqueuelen 0  (Ethernet)
              RX packets 2592  bytes 365644 (357.0 KiB)
              RX errors 0  dropped 0  overruns 0  frame 0
              TX packets 2633  bytes 275151 (268.7 KiB)
              TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
      

# 同网络中的容器间通信

  • 启用容器指定网络

    # 使用下面这条命令即可运行一个新的容器,并且让这个新容器加入到 localnet 这个网络中的。
    docker container run -d --name demo1 --network localnet alpine sleep 3600
  • 们查看网桥的情况,demo1 的网络接口连接到了网桥 br-f55943e20201 上,如图所示。

    [root@k8s-master01 ~]# brctl show
    bridge name     bridge id               STP enabled     interfaces
    br-f55943e20201         8000.02429e635565       no              vethf6a3fba
  • 如果在相同的网络中继续接入新的容器,那么新接入的容器是可以通过 demo1 这个名称来 ping 通的。 Docker默认的 是不支持通过 Docker DNS 服务进行域名解析的,自定义桥接网络是可以的。

    [root@k8s-master01 ~]# docker container run -d --name demo2 --network localnet alpine sleep 3600
    # 进入容器
    [root@k8s-master01 ~]# docker exec -it 396c01e33deb /bin/sh
    / # ping demo1
    PING demo1 (172.18.0.2): 56 data bytes
    64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.051 ms
    64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.072 ms

# 端口暴露

  • 同一个网络中的容器之间虽然可以互相 ping 通,但是并不意味着可以任意访问容器中的任何服务。

  • Docker 为容器增加了一套安全机制,只有容器自身允许的端口,才能被其他容器所访问。

  • 我们可以通过 docker container ls 命令可以看到容器暴露给其他容器访问的端口是 80。

    #  docker run --name web -d nginx
    $ docker container ls
    CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
    5a8dece3841d        nginx               "/docker-entrypoint.…"   3 minutes ago       Up 3 minutes        80/tcp              web
    
    # 查看ip地址
    [root@k8s-master01 ~]# docker inspect web | grep IPA
                "IPAddress": "172.17.0.5",
  • 那么我们只能容器的 80 端口进行访问,而不能对没有开放的 22 端口进行访问。

    # yum install -y telnet
    [root@k8s-master01 ~]# telnet 172.17.0.5 80
    Trying 172.17.0.5...
    Connected to 172.17.0.5.
    Escape character is '^]'.
    
    [root@k8s-master01 ~]# telnet 172.17.0.5 22
    Trying 172.17.0.5...
    telnet: connect to address 172.17.0.5: Connection refused
  • 我们可以在镜像创建的时候定义要暴露的端口,也可以在容器创建时定义要暴露的端口,使用 --expose

    # 额外暴露了 20、22 这两个端口
    # docker rm -f web
    docker container run -d --name web --expose 22 --expose 20 nginx
  • 容器的端口暴露类似于打开了容器的防火墙,具体能不能通过这个端口访问容器中的服务,还得看容器中有无应用监听并处理来自这个端口的请求。

# 端口映射

  • 上面提到的桥接网络中的容器只能与位于相同网络中的容器进行通信

  • 假如一个容器想对外提供服务的话,需要进行端口映射。

  • 端口映射将容器的某个端口映射到 Docker 主机端口上。那么任何发送到该端口的流量,都会被转发到容器中。

    docker container run -d --name web03 --network localnet -p 5000:80 nginx
  • 容器内部开放端口为 80,该端口被映射到了 Docker 主机的 192.168.1.101 的 5000 端口上。最终访问 192.168.1.101:5000 的所有流量都会被转发到容器的 80 端口。如图所示

  • 当我们通过 web 浏览器访问 Docker 主机的 5000 端口时,会得到Nginx主页的结果。外部系统可以通过访问 Docker 主机的 TCP 端口 5000,来访问运行在桥接网络上的 Nginx 容器了。
  • 端口映射之后,假如主机的 5000 端口被占用了,那么其他容器就不能再使用这个端口了。

# Docker网络命令

命令 说明
create 创建一个网络
connect 将容器接入到网络
disconnect 把容器从网络上断开
inspect 查看网络的详细信息
ls 列出所有的网络
prune 清理无用的网络资源
rm 删除一个网络

# 创建网络

  • creat命令用于创建一个新的容器网络。Docker内置了bridge(默认使用)和overlay两种驱动,分别支持单主机和多主机场景。Docker服务在启动后,会默认创建一个bridge类型的网桥bridge。不同网络之间默认相互隔离。

  • 创建网络命令格式为

    docker network create [OPTIONS] NETWORK
    参数 说明
    -attachable[=false] 支持手动容器挂载
    -aux-address=map[] 辅助的IP地址
    -config-from="" 从某个网络复制配置数据
    -config-only[=false] 启用仅可配置模式
    -d, -driver="bridge" 网络驱动类型,如bridge或overlay
    -gateway=[] 网关地址
    -ingress[=false] 创建一个Swarm可路由的网状网络用于负载均衡,可将对某个服务的请求自动转发给一个合适的副本
    -internal[=false] 内部模式,禁止外部对所创建网络的访问
    -ip-range=[] 指定分配IP地址范围
    -ipam-driver="default" IP地址管理的插件类型
    -ipam-opt=map[] IP地址管理插件的选项
    -ipv6[=false] 支持IPv6地址
    -label value 为网络添加元标签信息
    -o, -opt=map[] 网络驱动所支持的选项
    -scope="" 指定网络范围
    -subnet=[] 网络地址段,CIDR格式,如172.17.0.0/16。

# 接入网络

  • 将容器加入自建的网络有两种方式,

    • 通过docker network connect 命令来加入
    • 在容器启动(docker run)时通过--network参数指定
  • connect命令将一个容器连接到一个已存在的网络上。连接到网络上的容器可以跟同一网络中其他容器互通

    # 测试两个不同的网络连通  即docker0和localnet
    docker container run -d --name demo4  alpine sleep 3600
    docker container run -d --name demo5  --network localnet alpine sleep 3600
    
    # 此时demo5 是无法ping通demo4的
    [root@k8s-master01 ~]# docker exec -it 44f0858b7 /bin/sh
    / # ping demo4
    ping: bad address 'demo4'
    
    # 连通 demo4 ,将demo4 加到 localnet 网络
    [root@k8s-master01 ~]# docker network connect localnet demo4
    [root@k8s-master01 ~]# docker exec -it 44f0858b7 /bin/sh
    / # ping demo4
    PING demo4 (172.18.0.4): 56 data bytes
    64 bytes from 172.18.0.4: seq=0 ttl=64 time=0.099 ms
    64 bytes from 172.18.0.4: seq=1 ttl=64 time=0.087 ms
  • 同一个容器可以同时接入多个网络。也可以在执行docker run命令时候通过-net参数指定容器启动后自动接入的网络。

    # 运行一个新的容器,并且让这个容器加入 Docker 的 localnet 这个网络中
    docker container run -d --name demo1 --network localnet alpine sleep 3600
  • 接入网络命令格式为

    docker network connect [OPTIONS] NETWORK CONTAINER
  • 支持参数包括:

    参数 说明
    -alias=[] 为容器添加一个别名,此别名仅在所添加网络上可见;
    -ip="" 指定IP地址,需要注意不能跟已接入的容器地址冲突;
    -ip6="" 指定IPv6地址;
    -link value 添加链接到另外一个容器;
    -link-local-ip=[] 为容器添加一个链接地址。

# 断开网络

  • disconnect命令将一个连接到网络上的容器从网络上断开连接。

  • 命令格式为

    docker network disconnect [OPTIONS] NETWORK CONTAINER
  • 支持参数包括-f, -force: 强制把容器从网络上移除。

# 查看网络信息

  • inspect命令用于查看一个网络的具体信息(JSON格式),包括接入的容器、网络配置信息等。

  • 命令格式为

    # 提供 Docker 网络的详细配置信息
    docker network inspect [OPTIONS] NETWORK [NETWORK...]
  • 支持参数包括:

    参数 说明
    -f, -format="" 给定一个Golang模板字符串,对输出结果进行格式化,如只查看地址配置可以用-f '{{.IPAM.Config}}'d
    -v, -verbose[=false] 输出调试信息

# 列出网络

  • ls命令用于列出网络。命令格式为docker network ls [OPTIONS],其中支持的选项主要有:

    选项 说明
    -f, -filter="" 指定输出过滤器,如driver=bridge;
    -format="" 给定一个golang模板字符串,对输出结果进行格式化;
    -no-trunc[=false] 不截断地输出内容;
    -q, -quiet[=false] 安静模式,只打印网络的ID。

# 清理无用网络

  • prune命令用于清理已经没有容器使用的网络。

  • 命令格式为docker network prune [OPTIONS] [flags],支持参数包括:

    选项 说明
    -filter="" 指定选择过滤器
    -f, -force 强制清理资源

# 删除网络

  • rm命令用于删除指定的网络。当网络上没有容器连接上时,才会成功删除。
  • 命令格式为docker network rm NETWORK [NETWORK...]

# Docker网络管理

# 启动过程

  • 在主机上自动创建一个docker0虚拟网桥,实际上是一个Linux网桥。网桥可以理解为一个软件交换机,负责挂载其上的接口之间进行包转发。

  • 创建一对虚拟接口,分别放到本地主机和新容器的命名空间中;

    • 本地主机一端的虚拟接口连接到默认的docker0网桥或指定网桥上,并具有一个以veth开头的唯一名字,如veth1234;
    • 另一端的虚拟接口将放到新创建的容器中,并修改名字作为eth0。这个接口只在容器的命名空间可见;
  • 从网桥可用地址段中获取一个空闲地址分配给容器的eth0(例如172.17.0.2/16),并配置默认路由网关为docker0网卡的内部接口docker0的IP地址(例如172.17.42.1/16)。

# 网络相关参数

  • 在Docker服务启动的时候才能配置,修改后重启生效

    命令 说明
    -b BRIDGE or --bridge=BRIDGE 指定容器挂载的网桥
    --bip=CIDR 定制docker0的掩码
    -H SOCKET... or --host=SOCKET Docker服务端接收命令的通道
    --icc=true|false 是否支持容器之间进行通信
    --ip-forward=true|false 启用net.ipv4.ip_forward,即打开转发功能
    --iptables=true|false 禁止Docker添加iptables规则
    --mtu=BYTES 容器网络中的MTU
  • 既可以在启动服务时指定,也可以Docker容器启动(使用docker [con-tainer] run命令)时候指定。在Docker服务启动的时候指定则会成为默认值,后续执行该命令时可以覆盖设置的默认值:

    命令 说明
    --dns=IP_ADDRESS 使用指定的DNS服务器
    --dns-opt="" 指定DNS选项
    --dns-search=DOMAIN 指定DNS搜索域
  • 只能在docker [container] run命令执行时使用,因为它针对容器的配置

    命令 说明
    -h HOSTNAME or --hostname=HOSTNAME 配置容器主机名
    -ip 指定容器内接口的IP地址
    --link=CONTAINER_NAME:ALIAS 添加到另一个容器的连接
    --net=bridge|none|container:NAME_or_ID|host|user_defined_network 配置容器的桥接模式
    --network-alias 容器在网络中的别名
    -p SPEC or --publish=SPEC 映射容器端口到宿主主机
    -P or --publish-all=true|false 映射容器所有端口到宿主主机

# --net选项

  • --net选项支持以下五种模式:

    模式 说明
    --net=bridge 默认配置。为容器创建独立的网络命名空间,分配网卡、IP地址等网络配置,并通过veth接口对将容器挂载到一个虚拟网桥(默认为docker0)上
    --net=none 为容器创建独立的网络命名空间,但不进行网络配置,即容器内没有创建网卡、IP地址等
    --net=container:NAME_or_ID 新创建的容器共享指定的已存在容器的网络命名空间,两个容器内的网络配置共享,但其他资源(如进程空间、文件系统等)还是相互隔离的
    --net=host 不为容器创建独立的网络命名空间,容器内看到的网络配置(网卡信息、路由表、Iptables规则等)均与主机上的保持一致。注意其他资源还是与主机隔离的
    --net=user_defined_network 用户自行用network相关命令创建一个网络,同一个网络内的容器彼此可见,可以采用更多类型的网络插件。

# 端口映射方式

# 同时映射TCP和UDP端口
docker run -d \
  -p 53:53/tcp \
  -p 53:53/udp \
  --name=dns-server \
  bind9:latest
映射类型 命令格式 示例 说明
随机映射 -P docker run -d -P nginx 随机映射49000-49900端口到容器内部暴露端口
任意本地IP+随机端口 -p <容器端口> docker run -d -p 127.0.0.1::5000 training/webapp python app.py 绑定本地任意端口到容器指定端口
单端口映射 -p <主机端口>:<容器端口> docker run -d -p 8080:80 nginx 指定主机端口映射到容器端口
多端口映射 多个-p参数 docker run -d -p 5000:5000 -p 3000:80 training/webapp python app.py 同时映射多个端口
指定IP+端口 -p <IP>:<主机端口>:<容器端口> docker run -d -p 127.0.0.1:5000:5000 training/webapp python app.py 绑定特定IP和端口
UDP端口映射 -p <IP>:<主机端口>:<容器端口>/udp docker run -d -p 127.0.0.1:5000:5000/udp training/webapp python app.py 映射UDP协议端口
查看映射 docker port <容器> docker port 98d5c052a96

# 自定义网络

# 创建带固定子网的网络

# 创建带固定子网的网络
docker network create \
  --driver=bridge \
  --subnet=192.168.100.0/24 \
  --gateway=192.168.100.1 \
  my-custom-net

# 指定静态IP启动容器

# 指定静态IP启动容器
docker run -d \
  --network=my-custom-net \
  --ip=192.168.100.10 \
  --name=web1 \
  nginx:alpine
  • UDP/TCP混合映射

# 动态端口分配

# 随机映射容器80端口到宿主机
docker run -d -p 80 nginx

# 查看实际映射端口
docker port <container_id> 80
# 输出示例:0.0.0.0:32768->80/tcp

# 端口范围映射

# 映射连续端口范围
docker run -d -p 8000-8010:8000-8010 video-stream-service

# 网络诊断与优化

  1. 网络拓扑分析工具

    # 查看容器网络详情
    docker network inspect --format='<!--swig1-->' my-custom-net | jq
    
    # 实时流量监控
    nsenter -t $(docker inspect -f '<!--swig2-->' web1) -n tcpdump -i eth0
  2. 性能优化参数

    # 调整网桥MTU值
    docker network create --opt com.docker.network.bridge.mtu=9000 jumbo-net
    
    # 限制容器带宽
    docker run --rm -it --network=bridge \
      --ulimit rtprio=99 \
      --cap-add=sys_nice \
      linuxserver/ffmpeg

# 容器网络互联方案

Docker容器默认运行在隔离的网络环境中,要实现容器间通信,必须将它们连接到同一个网络。Docker提供了多种网络互联方案,各有特点和适用场景。

# 基于IP的直接通信

原理

  • 所有容器通过veth pair连接到docker0网桥

  • 自动分配IP地址(172.17.0.0/16)

    # 容器A访问容器B
    docker exec -it container-a ping 172.17.0.3
  • 优点:简单直接,无需额外配置

  • 缺点:

    • 依赖IP地址,容器重启IP可能变化
    • 无法通过容器名通信
    • 缺乏网络隔离

# --link(已废弃)

上面docker0不支持容器名连接访问,容器通信只可以通过容器ip通信,docker也无法保证容器重启后的IP地址不变,可通过--link建立连接别名进行互联(官方不推荐使用)。

  • 需要双向配置(A→B和B→A需分别设置)
  • 容器数量多时配置复杂
  • 无法动态更新(容器重启后需重新配置)
  • 官方已废弃此方案

原理分析:

  • 运行容器时,指定参数link,使得源容器与被链接的容器可以进行相互通信,并且接受的容器可以获得源容器的一些数据,比如:环境变量。与/etc/hosts中的主机条目不同,**如果重新启动源容器,则不会自动更新存储在环境变量中的IP地址。
  • 除了环境变量之外,Docker还将源容器的主机条目添加到/etc/hosts文件中。(本质上就是通过 --link 参数,自动的给容器添加 hosts 配置)
  1. 自定义容器名称

    # 启动tomcat01
    docker run -it -d -P --name tomcat01 tomcat
    docker run -it -d -P --name tomcat02 tomcat
    
    # tomcat02 ping不通 tomcat01
    docker exec -it tomcat02 bash
    root@c294a750a97d:/usr/local/tomcat# cat /etc/hosts
    127.0.0.1       localhost
    ::1     localhost ip6-localhost ip6-loopback
    fe00::0 ip6-localnet
    ff00::0 ip6-mcastprefix
    ff02::1 ip6-allnodes
    ff02::2 ip6-allrouters
    172.17.0.3      c294a750a97d
  2. 容器互联: 使用--link name:别名,可以让容器互联

    # tomcat02 容器link 到 tomcat03 上
    docker run -it -d -P --name tomcat03 --link tomcat02 tomcat
  3. 使用env查看容器环境变量信息

     docker exec -it tomcat03 env
    PATH=/usr/local/tomcat/bin:/opt/java/openjdk/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
    HOSTNAME=0f89ad0c38a4
    TERM=xterm
    TOMCAT02_PORT=tcp://172.17.0.2:8080
    TOMCAT02_PORT_8080_TCP=tcp://172.17.0.2:8080
    TOMCAT02_PORT_8080_TCP_ADDR=172.17.0.2
    TOMCAT02_PORT_8080_TCP_PORT=8080
    TOMCAT02_PORT_8080_TCP_PROTO=tcp
    TOMCAT02_NAME=/tomcat03/tomcat02
    TOMCAT02_ENV_JAVA_HOME=/opt/java/openjdk
    TOMCAT02_ENV_LANG=en_US.UTF-8
    TOMCAT02_ENV_LANGUAGE=en_US:en
    TOMCAT02_ENV_LC_ALL=en_US.UTF-8
    TOMCAT02_ENV_JAVA_VERSION=jdk-21.0.7+6
    TOMCAT02_ENV_CATALINA_HOME=/usr/local/tomcat
    TOMCAT02_ENV_TOMCAT_NATIVE_LIBDIR=/usr/local/tomcat/native-jni-lib
    TOMCAT02_ENV_LD_LIBRARY_PATH=/usr/local/tomcat/native-jni-lib
    TOMCAT02_ENV_TOMCAT_MAJOR=11
    TOMCAT02_ENV_TOMCAT_VERSION=11.0.8
    TOMCAT02_ENV_TOMCAT_SHA512=82a7a2e686da1fbafdd76c863d0bd1435bcd7e58d507ad353c43e364522eb3284d2dc3552388a5ca389e48afed863885886572edc13ba40ff0a13e339fca251f
    JAVA_HOME=/opt/java/openjdk
  4. 查看容器hosts文件信息

    # 查看tomcat3中的hosts
    docker exec -it tomcat03 cat /etc/hosts
    [root@docker-node01 ~]# docker exec -it tomcat03 cat /etc/hosts
    127.0.0.1       localhost
    ::1     localhost ip6-localhost ip6-loopback
    fe00::0 ip6-localnet
    ff00::0 ip6-mcastprefix
    ff02::1 ip6-allnodes
    ff02::2 ip6-allrouters
    #✨--link命令配置生成的条目✨
    172.17.0.3      tomcat02 c294a750a97d
    172.17.0.4      e256178947a0

# 自定义Bridge网络(推荐)

在同一个网络中的容器,可以互联,并且,Docker 内置了 DNS,容器内的应用可以使用服务名、容器名、别名来进行服务发现,名称会经由内置的 DNS 进行解析,其结果是动态的;

  • 创建自定义网络

    [root@node1 docker]# docker network create net
    521dd530656137081816f85ec3b7978020f8aff43fb0446ce9edb4505f41804e
    [root@node1 docker]# docker network ls
    NETWORK ID     NAME      DRIVER    SCOPE
    af09f33de965   bridge    bridge    local
    73a79b3950dd   host      host      local
    521dd5306561   net       bridge    local
    99ccf146e61e   none      null      local
  • 启动两个测试容器

    
    [root@node1 docker]# docker run -d -it --name=test1 --network net busybox /bin/sh
    6ed3c9cb40195564f5895f906684db505578b75258efe1fef1c7bb30f6f88ac6
    [root@node1 docker]# docker run -d -it --name=test2 --network net busybox /bin/sh
    31fa1bbe74af38ab37eef3508b2d90e178314b7911c62dd4f509f02860799cce
  • 测试连通性

    # 容器间连通性测试 容器间可以通过容器名直接通信,且延迟极低(<1ms)
     docker exec -it test1 ping test2
    PING test2 (172.20.0.3): 56 data bytes
    64 bytes from 172.20.0.3: seq=0 ttl=64 time=0.590 ms
    64 bytes from 172.20.0.3: seq=1 ttl=64 time=0.074 ms
    
    # /etc/hosts文件检查
    # 自定义网络中的容器名解析不依赖于传统的/etc/hosts文件
     docker exec -it test1 cat /etc/hosts
    127.0.0.1       localhost
    ::1     localhost ip6-localhost ip6-loopback
    fe00::0 ip6-localnet
    ff00::0 ip6-mcastprefix
    ff02::1 ip6-allnodes
    ff02::2 ip6-allrouters
    172.20.0.2      16186ba64dc6
    
    # DNS查询测试
    # Docker使用内置的DNS服务器(127.0.0.11)处理容器名解析
     docker exec -it test1 nslookup -type=a test2
    Server:         127.0.0.11
    Address:        127.0.0.11:53
    
    Non-authoritative answer:
    Name:   test2
    Address: 172.20.0.3
    
    # resolv.conf配置检查
    # 容器配置使用Docker内置DNS服务器(127.0.0.11)
     docker exec -it test1 cat /etc/resolv.conf
    nameserver 127.0.0.11
    options edns0 trust-ad ndots:0

# 方案对比

对比维度 基于IP直接通信 --link (已废弃) 手动修改/etc/hosts 自定义Bridge网络 (推荐)
配置复杂度 无需配置 高(需双向配置) 高(需逐个容器修改) 低(自动完成)
容器名解析 ❌ 不支持 ✅ 支持 ✅ 支持(手动) ✅ 自动支持
隔离性 ❌ 所有容器互通 ⚠️ 仅限link的容器 ❌ 无隔离 ✅ 网络级隔离
动态扩展性 ❌ IP变化需手动维护 ❌ 新增容器需重新link ❌ 需手动更新所有容器 ✅ 自动维护成员关系
跨容器通信延迟 <1ms <1ms <1ms <1ms
DNS解析机制 ❌ 无 ⚠️ 通过/etc/hosts静态绑定 ⚠️ 手动维护/etc/hosts ✅ 内置DNS服务器(127.0.0.11)动态解析
多别名支持 ❌ 不支持 ❌ 不支持 ⚠️ 可手动添加但维护困难 ✅ 支持(Compose中可定义服务别名)
典型应用场景 临时测试 旧版Docker兼容 特殊调试场景 生产环境、微服务架构
维护成本 低(但不可靠) 高(容器拓扑变化时需重配) 极高(需同步所有容器配置) 极低(自动维护)
故障排查难度 中(需跟踪IP变化) 高(配置复杂易出错) 高(手动配置易遗漏) 低(标准日志和工具支持)

# 配置DNS和主机名

  • Docker服务启动后会默认启用一个内嵌的DNS服务,来自动解析同一个网络中的容器主机名和地址,如果无法解析,则通过容器内的DNS相关配置进行解析。用户可以通过命令选项自定义容器的主机名和DNS配置

# 相关配置文件

  • 容器中主机名和DNS配置信息可以通过三个系统配置文件来管理:

    • /etc/resolv.conf
    • /etc/hostname
    • /etc/hosts
  • 启动一个容器,在容器中使用mount命令可以看到这三个文件挂载信息:

     docker run -it --rm busybox mount | grep etc
    /dev/sda2 on /etc/resolv.conf type ext4 (rw,noatime)
    /dev/sda2 on /etc/hostname type ext4 (rw,noatime)
    /dev/sda2 on /etc/hosts type ext4 (rw,noatime)
  • /etc/resolv.conf记录了DNS服务器

     docker run -it --rm busybox cat /etc/resolv.conf
    # Generated by NetworkManager
    nameserver 192.168.61.2
  • /etc/hostname文件则记录了容器的主机名

     docker run -it --rm busybox cat /etc/hostname
    51adaac0dedf
  • /etc/hosts文件中默认只记录了容器自身的地址和名称:

     docker run -it --rm busybox  cat /etc/hosts
    127.0.0.1       localhost
    ::1     localhost ip6-localhost ip6-loopback
    fe00::0 ip6-localnet
    ff00::0 ip6-mcastprefix
    ff02::1 ip6-allnodes
    ff02::2 ip6-allrouters
    172.17.0.4      51adaac0dedf

# 容器内修改配置文件

  • 容器运行时,可以在运行中的容器里直接编辑/etc/hosts/etc/hostname/etc/resolve. conf文件。
  • 但是这些修改是临时的,只在运行的容器中保留,容器终止或重启后并不会被保存下来,也不会被docker commit提交。

# 通过参数指定

注意一般不推荐与-net=host一起使用,会破坏宿主机上的配置信息

  • 如果用户想要自定义容器的配置,可以在创建或启动容器时利用下面的参数指定
  • 指定主机名-h HOSTNAME或者--hostname=HOSTNAME: 设定容器的主机名。
    • 容器主机名会被写到容器内的/etc/hostname和/etc/hosts。
    • 但这个主机名只有容器内能中看到,在容器外部则看不到,既不会在docker ps中显示,也不会在其他容器的/etc/hosts中看到;
  • --link=CONTAINER_NAME:ALIAS: 记录其他容器主机名。
    • 在创建容器的时候,添加一个所连接容器的主机名到容器内/etc/hosts文件中。
    • 这样,新建容器可以直接使用主机名与所连接容器通信;
  • --dns=IP_ADDRESS: 指定DNS服务器。添加DNS服务器到容器的/etc/resolv.conf
    • 容器会用指定的服务器来解析所有不在/etc/hosts中的主机名;
  • --dns-option list: 指定DNS相关的选项;
  • --dns-search=DOMAIN: 指定DNS搜索域。
    • 设定容器的搜索域,当设定搜索域为 .example.com 时
    • 在搜索一个名为host的主机时,DNS不仅搜索host,还会搜索host.example.com

# 防火墙访问控制

容器的访问控制主要通过Linux上的iptables防火墙软件来进行管理和实现。

# 容器访问外部网络

  • 容器默认指定了网关为docker0网桥上的docker0内部接口。docker0内部接口同时也是宿主机的一个本地接口。因此,容器默认情况下可以访问到宿主机本地网络。如果容器要想通过宿主机访问到外部网络,则需要宿主机进行辅助转发。

  • 访问外部网络需要开启IP转发:

    # 检查是否开启
    sysctl net.ipv4.ip_forward
    
    # 如果net.ipv4.ip_forward = 0,说明没有开启转发,则需要手动打开:
    sysctl -w net.ipv4.ip_forward=1
  • Docker服务启动时会默认开启--ip-forward=true,自动配置宿主机系统的转发规则

# 容器之间访问

  • 容器之间相互访问需要两方面的支持:

    • 网络拓扑是否已经连通。默认情况下,所有容器都会连接到docker0网桥上,这意味着默认情况下拓扑是互通的;
    • 本地系统的防火墙软件iptables是否允许访问通过。这取决于防火墙的默认规则是允许(大部分情况)还是禁止。

# 网络通信分析

# 容器访问外部网络

  • centos 位于 docker0 这个私有bridge 网络中(172.17.0.0/16),当 centos 从容器向外 ping 时,数据包通过NAT地址转换到达 baidu.com

  • 查看一下 docker host 上的 iptables 规则:

    [root@node1 oracle]# iptables -t nat -S | grep docker0
    -A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
    -A DOCKER -i docker0 -j RETURN
    -A DOCKER ! -i docker0 -p tcp -m tcp --dport 10086 -j DNAT --to-destination 172.17.0.3:22
  • 在 NAT 表中,有这么一条规则:

    • -A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
  • 其含义是: 如果网桥 docker0 收到来自 172.17.0.0/16 网段的外出包,把它交给 MASQUERADE 处理。而 MASQUERADE 的处理方式是将包的源地址替换成 host 的地址发送出去,即做了一次网络地址转换(NAT)。

  • 通过 tcpdump 查看地址是如何转换的。先查看 docker host 的路由表:

    [root@node1 oracle]# ip r
    default via 192.168.61.2 dev ens33 proto static metric 100
    172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
    172.18.0.0/16 dev br-521dd5306561 proto kernel scope link src 172.18.0.1
    172.20.0.0/16 dev br-852e88d73bf9 proto kernel scope link src 172.20.0.1
    192.168.61.0/24 dev ens33 proto kernel scope link src 192.168.61.10 metric 100
    [root@node1 oracle]# tcpdump -i docker0 -n icmp
    tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
    listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes
  • 默认路由通过 ens33 发出去,所以我们要同时监控 ens33和 docker0 上的 icmp(ping)数据包。

  • 当 docker的容器ping baidu.com 时,tcpdump 输出如下:

    [root@node1 oracle]# tcpdump -i docker0 -n icmp
    tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
    listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes
    13:47:23.179156 IP 172.17.0.4 > 50.28.32.8: ICMP echo request, id 1, seq 1, length 64
    13:47:23.438049 IP 50.28.32.8 > 172.17.0.4: ICMP echo reply, id 1, seq 1, length 64
    13:47:24.232102 IP 172.17.0.4 > 50.28.32.8: ICMP echo request, id 1, seq 2, length 64
    13:47:24.499137 IP 50.28.32.8 > 172.17.0.4: ICMP echo reply, id 1, seq 2, length 64
  • docker0 收到 centos 的 ping包,源地址为容器 IP 172.17.0.4,这没问题,交给 MASQUERADE 处理。这时,在 ens33 上我们看到了变化:

    # ping 包的源地址变成了 ens33 的 IP      192.168.61.10
    [root@node1 oracle]# tcpdump -i ens33 -n icmp
    tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
    listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes
    13:53:55.595133 IP 192.168.61.10 > 50.28.32.8: ICMP echo request, id 2, seq 1, length 64
    13:53:55.823131 IP 50.28.32.8 > 192.168.61.10: ICMP echo reply, id 2, seq 1, length 64
    13:53:56.633403 IP 192.168.61.10 > 50.28.32.8: ICMP echo request, id 2, seq 2, length 64
    
  • 这就是 iptable NAT 规则处理的结果,从而保证数据包能够到达外网。这个过程:

    1. centos 发送 ping 包: 172.17.0.4 > www.baidu.com
    2. docker0 收到包,发现是发送到外网的,交给 NAT 处理。
    3. NAT 将源地址换成 ens33 的 IP: 192.168.61.10 > www.baidu.com
    4. ping 包从ens33 发送出去,到达 www.baidu.com

# 外部网络访问容器

  • docker 可将容器对外提供服务的端口映射到 host 的某个端口,外网通过该端口访问容器。容器启动时通过-p参数映射端口:

  • 容器启动后,可通过 docker ps 或者 docker port 查看到 host 映射的端口。在上面的例子中,httpd 容器的 80 端口被映射到 host 49154上,这样就可以通过<host ip>:<49154>访问容器的 web 服务了。

    
    doc[root@node1 ~]# docker run -d --rm -P httpd
    4d227b0ab1099b3cf2b857d042f4415975fe922e094dcd7e83d5466ac9f953f7
    docker [root@node1 ~]# docker ps | grep 4d227
    4d227b0ab109   httpd                                                  "httpd-foreground"       15 seconds ago   Up 11 seconds   0.0.0.0:49154->80/tcp, :::49154->80/tcp     interesting_thompson
    
  • 除了映射动态端口,也可在 -p 中指定映射到 host某个特定端口,例如可将 80 端口映射到 host 的 8080 端口:

    doc[root@node1 ~]# docker run -d --rm -p 80:80 httpd
    # 指定一个端口范围
    doc[root@node1 ~]#docker run -d -p 8000-9000:80 httpd
  • 每一个映射的端口,host 都会启动一个docker-proxy 进程来处理访问容器的流量

    
    [root@node1 ~]# ps -ef |grep docker-proxt
    root      83152 127169  0 18:51 pts/1    00:00:00 grep --color=auto docker-proxt
    [root@node1 ~]# ps -ef |grep docker-proxy
    root      47106  20786  0 Sep23 ?        00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 80 -container-ip 172.18.0.4 -container-port 80
    root      47112  20786  0 Sep23 ?        00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip :: -host-port 80 -container-ip 172.18.0.4 -container-port 80
    root      55098  20786  0 Sep23 ?        00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 10086 -container-ip 172.17.0.3 -container-port 22
    root      55104  20786  0 Sep23 ?        00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip :: -host-port 10086 -container-ip 172.17.0.3 -container-port 22
    root      83168 127169  0 18:51 pts/1    00:00:00 grep --color=auto docker-proxy
    root      84278  20786  0 Sep23 ?        00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 1521 -container-ip 172.20.0.2 -container-port 1521
    root      84292  20786  0 Sep23 ?        00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip :: -host-port 1521 -container-ip 172.20.0.2 -container-port 1521
    root     127976  20786  0 15:34 ?        00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 49154 -container-ip 172.17.0.5 -container-port 80
    root     127982  20786  0 15:34 ?        00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip :: -host-port 49154 -container-ip 172.17.0.5 -container-port 80
    
  • 以 0.0.0.0:32770->80/tcp为例分析整个过程

    1. docker-proxy 监听 host 的 32770 端口。
    2. 当 curl 访问 10.0.2.15:32770 时,docker-proxy 转发给容器172.17.0.2:80。
    3. httpd 容器响应请求并返回结果。
更新于

请我喝[茶]~( ̄▽ ̄)~*

Fulsun 微信支付

微信支付

Fulsun 支付宝

支付宝