技术之道

长风破浪会有时,直挂云帆济沧海

  • 首页
  • 分类
  • 归档
  • 标签

  • 搜索
服务治理 k8s tabnine cursor github copilot ai chatgpt chatgpt ai sop 技术选型 bigdata 工具 多进程多线程 docker 计算机网络 mysql 事务 基础架构 kafka nio 分布式 服务搭建 监控 jvm 管理/成长 jenkins devops 云原生 nginx 架构 故障处理 hive spark mapreduce apm redis memcached java 性能 linux

Docker的网络实现

发表于 2022-12-29 | 分类于 云原生 | 0 | 阅读次数 281

Docker的网络实现

Docker网络模型

安装Docker以后,会默认创建三种网络,可以通过docker network ls查看:

$ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
33f07da2cd08        bridge              bridge              local
4fd0353013a1        host                host                local
f1dc5101bd58        none                null                local

这三种开箱即用的网络模型说明如下:

网络模式 配置 说明
bridge模式 –net=bridge (默认为该模式)此模式会为每一个容器分配、设置IP等,并将容器连接到一个docker0虚拟网桥,通过docker0网桥以及Iptables nat表配置与宿主机通信。
host模式 –net=host 容器将不会虚拟出自己的网卡,配置自己的 IP 等,而是使用宿主机的 IP 和端口。
none模式 –net=none 空置模式下,Docker 会给新容器创建独立的网络名称空间,但是不会创建任何虚拟的网络设备,此时容器能看到的只有一个回环设备(Loopback Device)而已。提供这种方式是为了方便用户去做自定义的网络配置,如自己增加网络设备、自己管理 IP 地址,等等

Bridge模式

在bridge模式下,Docker Daemon第一次启动时会创建一个虚拟的网桥(docker0),然后会给这个网桥分配一个子网。针对由Docker创建的每一个容器,都会创建一个虚拟的以太网设备(veth pair设备对),其中一端关联到网桥上,以类似vethxxx命名,另一端使用Linux网络命名空间技术,映射到容器内的eth0设备,然后从网桥的地址段给eth0接口分配一个IP地址。

image-20221229111622854

如上图,172.17.0.1/16是网桥的IP地址,Docker Daemon会在几个备选地址里给它选择一个地址,通常是以172开头的一个地址,这个地址和主机的IP地址是不重叠的。172.17.0.2/16、172.17.0.3/16分别是容器1和容器2的IP地址,10.20.0.129/16是宿主机的IP地址。

现在,我们以一个基于buybox镜像构建的容器作为一个具体案例,深入了解bridge模式:

  1. 运行一个容器名为box1,通过ip addr查看eth0网卡IP为:172.17.0.2/16

    $ docker run -it --name box1 busybox
    / # ip addr
    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
    181: eth0@if182: <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
    
  2. 在宿主机上查看名为vethxxx网络,可以看到网络接口为:veth3b625ab@if183即为容器box1网卡eth0的veth pair设备对的对端

    $ ip addr
    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: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
        link/ether 52:54:00:1b:75:f8 brd ff:ff:ff:ff:ff:ff
        inet 10.20.0.129/24 brd 10.20.0.255 scope global eth0
           valid_lft forever preferred_lft forever
        inet6 fe80::5054:ff:fe1b:75f8/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:ee:42:61:4e 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:eeff:fe42:614e/64 scope link 
           valid_lft forever preferred_lft forever
    184: veth3b625ab@if183: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
        link/ether be:f8:bf:51:ea:9a brd ff:ff:ff:ff:ff:ff link-netnsid 0
        inet6 fe80::bcf8:bfff:fe51:ea9a/64 scope link 
           valid_lft forever preferred_lft forever
    
  3. 还可以通过docker inspect <容器名称|ID>进行查看,在NetworkSettings节点中可以看到详细信息

    $ docker inspect box1
    "NetworkSettings": {
      "Bridge": "",
      "SandboxID": "665585a911b6edbe58d54582eb51973272cc8e7cf19023fd0ba76744dfafc837",
      "HairpinMode": false,
      "LinkLocalIPv6Address": "",
      "LinkLocalIPv6PrefixLen": 0,
      "Ports": { },
      "SandboxKey": "/var/run/docker/netns/665585a911b6",
      "SecondaryIPAddresses": null,
      "SecondaryIPv6Addresses": null,
      "EndpointID": "1082b2a470ac0cf059098e3923d8940fb9e7b1bfed7457ae2e6f47d0bb573bd8",
      "Gateway": "172.17.0.1",
      "GlobalIPv6Address": "",
      "GlobalIPv6PrefixLen": 0,
      "IPAddress": "172.17.0.2",
      "IPPrefixLen": 16,
      "IPv6Gateway": "",
      "MacAddress": "02:42:ac:11:00:02",
      "Networks": {
        "bridge": {
          "IPAMConfig": null,
          "Links": null,
          "Aliases": null,
          "NetworkID": "33f07da2cd08d7dfb0409293dd067db4daba6d665115b340dc869b3a21029116",
          "EndpointID": "1082b2a470ac0cf059098e3923d8940fb9e7b1bfed7457ae2e6f47d0bb573bd8",
          "Gateway": "172.17.0.1",
          "IPAddress": "172.17.0.2",
          "IPPrefixLen": 16,
          "IPv6Gateway": "",
          "GlobalIPv6Address": "",
          "GlobalIPv6PrefixLen": 0,
          "MacAddress": "02:42:ac:11:00:02",
          "DriverOpts": null
        }
      }
    }
    

    可以看到容器的默认网关是docker0的IP地址:172.17.0.1,也可以通过docker network inspect bridge命令查看所有bridge网络模式下的容器

bridge模式下的容器网络和宿主机属于不同的IP网段。这样做,结果就是在同一台机器内的容器之间可以相互通信,不同主机上的容器不能相互通信。为了让不同主机上的容器能跨节点相互通信,就必须在主机的地址上分配一个端口,然后通过这个端口路由或者代理到容器上,这种模式也就是Host模式。

Host模式

如果容器启动时采用host模式,那么这个容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个网络命名空间。也就是说容器将不会虚拟出自己的网卡,配置自己的IP地址等,而是使用宿主机的IP和端口。但容器其他方面,比如文件系统、进程列表等还是和宿主机是隔离的。

image-20221229141023445

使用host模式的容器可以直接使用宿主机的IP地址与外界通信,容器内部的服务端口也可以使用宿主机的端口,不需要进行NAT。所以,host最大的优势就是网络性能比较好。但缺点也很明显,在不同容器之间协调好端口分配是一件困难的事,特别是服务水平扩展时,动态的端口会带来高度的复杂度。

让我们一个基于nginx镜像构建的容器作为一个具体案例,深入了解host模式:

  1. 启动一个nginx镜像,容器名称为: ngx。查看host模型下的容器,可以看到此容器

    $ docker run -it --network host --name ngx nginx 
    $ docker network inspect host
    [
        {
            "Name": "host",
            "Id": "4fd0353013a1827f8b7e562a554a550b7fbbd34515ce58e8377cbb02eabfb79f",
            "Created": "2022-03-29T11:31:19.037479611+08:00",
            "Scope": "local",
            "Driver": "host",
            "EnableIPv6": false,
            "IPAM": {
                "Driver": "default",
                "Options": null,
                "Config": []
            },
            "Internal": false,
            "Attachable": false,
            "Ingress": false,
            "ConfigFrom": {
                "Network": ""
            },
            "ConfigOnly": false,
            "Containers": {
                "630358a2627b5fdbbf57d630246922f2ee26d1975fe073f7a9bf1b7cff5e6196": {
                    "Name": "ngx",
                    "EndpointID": "f254380df3f5002c5251aacd13e058f9013599405c0e3b1881e5b17f059a038d",
                    "MacAddress": "",
                    "IPv4Address": "",
                    "IPv6Address": ""
                }
            },
            "Options": {},
            "Labels": {}
        }
    ]
    
  2. 查看宿主机下80端口

    $ netstat -ntlp
    Active Internet connections (only servers)
    Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
    tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      8209/nginx: master  
    tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      1277/sshd           
    tcp6       0      0 :::80                   :::*                    LISTEN      8209/nginx: master  
    
  3. 尝试用curl命令验证nginx

    $ curl 127.0.0.1
    <!DOCTYPE html>
    <html>
    <head>
    <title>Welcome to nginx!</title>
    <style>
    html { color-scheme: light dark; }
    body { width: 35em; margin: 0 auto;
    font-family: Tahoma, Verdana, Arial, sans-serif; }
    </style>
    </head>
    <body>
    <h1>Welcome to nginx!</h1>
    <p>If you see this page, the nginx web server is successfully installed and
    working. Further configuration is required.</p>
    
    <p>For online documentation and support please refer to
    <a href="http://nginx.org/">nginx.org</a>.<br/>
    Commercial support is available at
    <a href="http://nginx.com/">nginx.com</a>.</p>
    
    <p><em>Thank you for using nginx.</em></p>
    </body>
    </html>
    
  4. 查看nginx日志验证宿主机上的访问是否指向容器

    $ docker logs ngx
    /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
    /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
    10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
    10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
    /docker-entrypoint.sh: Configuration complete; ready for start up
    2022/12/29 06:11:12 [notice] 1#1: using the "epoll" event method
    2022/12/29 06:11:12 [notice] 1#1: nginx/1.23.3
    2022/12/29 06:11:12 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6) 
    2022/12/29 06:11:12 [notice] 1#1: OS: Linux 3.10.0-1160.11.1.el7.x86_64
    2022/12/29 06:11:12 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
    2022/12/29 06:11:12 [notice] 1#1: start worker processes
    2022/12/29 06:11:12 [notice] 1#1: start worker process 29
    2022/12/29 06:11:12 [notice] 1#1: start worker process 30
    2022/12/29 06:11:12 [notice] 1#1: start worker process 31
    2022/12/29 06:11:12 [notice] 1#1: start worker process 32
    127.0.0.1 - - [29/Dec/2022:06:11:50 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.29.0" "-"
    

none模型

none 网络模式即不为 Docker Container 创建任何的网络环境,容器内部就只能使用 loopback 网络设备,不会再有其他的网络资源。可以说 none 模式为 Docke Container 做了极少的网络设定,但是俗话说得好“少即是多”,在没有网络配置的情况下,作为 Docker 开发者,才能在这基础做其他无限多可能的网络定制开发。这也恰巧体现了 Docker 设计理念的开放。

在创建容器时通过参数 --net none 或者 --network none 指定;

比如我基于 none 网络模式创建了一个基于 busybox 镜像构建的容器 bbox-none,查看 ip addr:

$ docker run -it --net none --name bbox-none busybox
/ # ip addr
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

其他网络模型

除了三种开箱即用的网络外,Docker 还支持以下由用户自行创建的网络:

  • 容器模式,创建容器后使用--network=container:容器名称指定。容器模式下,新创建的容器将会加入指定的容器的网络名称空间,共享一切的网络资源,但其他资源,如文件、PID 等默认仍然是隔离的。两个容器间可以直接使用回环地址(localhost)通信,端口号等网络资源不能有冲突。
  • MACVLAN 模式:使用docker network create -d macvlan创建,此网络允许为容器指定一个副本网卡,容器通过副本网卡的 MAC 地址来使用宿主机上的物理设备,在追求通信性能的场合,这种网络是最好的选择。Docker 的 MACVLAN 只支持 Bridge 通信模式,因此在功能表现上与桥接模式相类似。
  • Overlay 模式:使用docker network create -d overlay创建,Docker 说的 Overlay 网络实际上就是特指 VXLAN,这种网络模式主要用于 Docker Swarm 服务之间进行通信。然而由于 Docker Swarm 败于 Kubernetes,并未成为主流,所以这种网络模式实际很少使用。

容器模式

Container 网络模式是 Docker 中一种较为特别的网络的模式。在创建容器时通过参数 --net container:已运行的容器名称|ID 或者 --network container:已运行的容器名称|ID 指定;

这个模式指定新创建的容器和已经存在的一个容器共享一个 Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过 lo 网卡设备通信。

image-20221229151108354

如果已经掌握了Kubernetes的POD网络模型的话,一个Pod里有多个容器(docker),多个容器共享一个网络模型。多个容器之间通过lo设备通信,是不是很类似?

还是拿上面buybox镜像构建的容器举例:

$ docker run -it --name box busybox
/ # ip addr
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
189: eth0@if190: <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
       
       
$ docker run -it --net container:box --name box1 busybox
/ # ip addr
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
189: eth0@if190: <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 addr
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: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 52:54:00:1b:75:f8 brd ff:ff:ff:ff:ff:ff
    inet 10.20.0.129/24 brd 10.20.0.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::5054:ff:fe1b:75f8/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:ee:42:61:4e 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:eeff:fe42:614e/64 scope link 
       valid_lft forever preferred_lft forever
190: veth8e3e0c5@if189: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
    link/ether fe:d2:b3:d8:57:09 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::fcd2:b3ff:fed8:5709/64 scope link 
       valid_lft forever preferred_lft forever       

可以看到两个容器共享了第一个容器IP、端口。

小结

官网是这么定义Docker网络模型的:

  • 当您需要多个容器在同一台 Docker 主机上进行通信时,用户定义的桥接网络是最佳选择。
  • 当网络堆栈不应该与 Docker 主机隔离,但您希望容器的其他方面被隔离时,主机网络是最好的。
  • 当您需要在不同 Docker 主机上运行的容器进行通信时,或者当多个应用程序使用 swarm 服务协同工作时,覆盖网络是最佳选择。
  • 当您从 VM 设置迁移或需要您的容器看起来像网络上的物理主机时,Macvlan 网络是最好的,每个主机都有一个唯一的 MAC 地址。
  • 第三方网络插件允许您将 Docker 与专用网络堆栈集成。

此外,还可以通过 docker network create 命令创建自定义网络模型。

# 云原生 # docker
Linux网络命名空间
深入理解Netfilter和iptables架构
  • 文章目录
  • 站点概览
lw‘Blogs

lw‘Blogs

自信人生二百年,会当水击三千里

80 日志
8 分类
40 标签
RSS
Github E-mail
Creative Commons
© 2025 京ICP备2022025426号-1