运维docker08-docker网络

1、docker网络架构

  • Docker网络架构源自一种叫作容器网络模型(CNM)的方案,该方案是开源的并且支持插接式连接。
  • Libnetwork是Docker对CNM的一种实现,提供了Docker核心网络架构的全部功能。
    • 不同的驱动可以通过插拔的方式接入Libnetwork来提供定制化的网络拓扑。
    • Libnetwork提供了本地服务发现和基础的容器负载均衡解决方案。
  • 在顶层设计中,Docker网络架构由3个主要部分构成:CNM、Libnetwork和驱动。
    • CNM:Docker网络架构的设计规范文档。在CNM中,规定了Docker网络架构的基础组成要素。
    • Libnetwork:CNM的具体实现,并且被Docker采用。Libnetwork通过Go语言编写,并实现了CNM中列举的核心组件。
    • 驱动:通过实现特定网络拓扑的方式来拓展该模型的能力。

1、CNM

  • CNM定义了3个基本要素:沙盒(Sandbox)终端(Endpoint )和网络(Network )。
    • 沙盒:代表一个容器,准确的说是一个独立的网络栈(网络命名空间)。其中包括以太网接口、端口、路由表以及DNS配置。
    • 终端:虚拟网络接口,会分配IP地址,将沙盒(容器)连接到网络。就像普通网络接口一样,终端主要职责是负责创建连接。
    • 网络:802.1d网桥(类似大家熟知的交换机)的软件实现。因此,网络就是需要交互的终端的集合,并且终端之间相互独立。(可以连通多个终端的一个子网)
  • 3个组件是如何连接的

  • 一个终端只能接入某一个网络。因此,如果容器需要接入到多个网络,就需要多个终端。
  • Docker环境中最小的调度单位就是容器。
    • CNM组件是如何与容器进行关联的--沙盒被放置在容器内部,为容器提供网络连接。
    • 虽然容器A和容器B运行在同一个主机上,但其网络堆栈在操作系统层面是互相独立的,这一点由沙盒机制保证。
    • 容器A只有一个接口(终端)并连接到了网络A。容器B有两个接口(终端)并且分别接入了网络A和网络B,容器A与B之间是可以相互通信的,因为都接入了网络A。但是,如果没有三层路由器的支持,容器B的两个终端之间是不能进行通信的。

2、Libnetwork

  • CNM是设计规范文档,Libnetwork是标准的实现。Libnetwork是开源的,采用Go语言编写,它跨平台(Linux以及Windows),并且被Docker所使用。
    • 在Docker早期阶段,网络部分代码都存在于daemon当中。这简直就是噩梦--daemon变得臃肿,并且不符合UNIX工具模块化设计原则,即既能独立工作,又易于集成到其他项目。
    • Docker将网络部分从daemon中拆分,并重构为一个叫作Libnetwork的外部类库。现在,Docker核心网络架构代码都在Libnetwork当中。
  • Libnetwork实现了CNM中定义的3个组件(沙盒、终端、网络)。此外它还实现了本地服务发现( Service Discovery )、基于Ingress的容器负载均衡,以及网络控制层和管理层功能。

3、驱动

  • Libnetwork实现了控制层和管理层功能。
  • 驱动实现了数据层功能。比如,网络连通性和隔离性是由驱动来处理的,驱动层实际创建网络对象。
  • Docker封装了若干内置驱动,通常被称作原生驱动或者本地驱动
    • 在Linux上包括Bridge、overlay以及Macvlan。
    • 在Windows上包括NAT、Overlay、Transport以及12 Bridge。
  • 第三方Docker网络驱动,叫作远程驱动,例如Calico、Contiv、Kuryr和Weave。
  • 每个驱动都负责其上所有网络资源的创建和管理。例如,一个叫作"prod-fe-cuda"的覆盖网络,归overlay驱动所有,并管理。这意味着overlay驱动会在创建、管理和删除其上网络资源的时候被调用。
  • 为了满足复杂且不固定的环境需求,Libnetwork支持同时激活多个网络驱动。这意味着Docker环境可以支持一个庞大的异构网络
  • 控制层、管理层与数据层的关系

2、docker网络和驱动

1、用软件模拟路由设备

  • linux内核支持两种级别设备的模拟:二层设备和三层设备
  • OVS:开源的虚拟交换机

2、Linux系统支持的docker网络的驱动

]# docker info | grep 'Network'
  Network: bridge host ipvlan macvlan null overlay    #linux上docker支持6种网络驱动

3、docker主机有三种网络

  • bridge:桥接网络,这里是NAT桥。安装docker时,会自动创建一个软交换机叫docker0(查看ifconfig),也可以当网卡使用。不给ip地址就是交换机,给ip地址即可以当交换机,也可以当网卡。
  • host:容器共享宿主机的网络名称空间。
  • none:没有网络,不能进行网络通信,只有lo接口。
]# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
e37a43a98f2d   bridge    bridge    local
fa682e5b62fa   host      host      local
fa86c5d0c589   none      null      local

4、docker网络有四种网络模型

  • Closed container(封闭式容器,none):只有lo接口。
  • Bridged container(桥接容器,bridge):创建容器时默认使用bridge,拥有172.17的地址。
  • Joined container(联盟式容器):两个容器的名称空间一部分是隔离的(User、Mount、Pid),一部分是共享的(UTS、NET、IPC),两个容器通过lo就可以通信。
  • Open container(开放式容器,host):容器直接共享宿主机的名称空间,宿主机开放哪些他就开放哪些。

3、管理网络

  • 基本语法格式如下
docker network COMMAND
    ls          列出网络
    inspect     显示一个或多个网络的详细信息
    create      创建一个网络
    rm          删除一个或多个网络
    prune       删除所有未使用的网络
    connect     将容器连接到网络
    disconnect  断开容器与网络的连接

1、查看网络

  • 基本语法格式如下
docker network ls [OPTIONS]                              #列出网络
    -f, --filter filter       提供过滤值(e.g. 'driver=bridge')
    -q, --quiet               只显示网络id
    --format string           使用Go模板输出
    --no-trunc                不截断输出
docker network inspect [OPTIONS] NETWORK [NETWORK...]    #显示一个或多个网络的详细信息
    -f, --format string       使用Go模板输出
    -v, --verbose             用于诊断的详细输出

2、创建网络

  • 基本语法格式如下
docker network create [OPTIONS] NETWORK                  #创建一个网络
    -d, --driver string       为创建的网络设置驱动程序(default "bridge")
    -o, --opt map             设置驱动程序特定选项(default map[])
    --attachable              启用手动容器附件
    --aux-address map         网络驱动程序使用的辅助IPv4或IPv6地址(default map[])
    --config-from string      要从其中复制配置的网络
    --config-only             只创建配置网络
    --gateway strings         IPv4或IPv6主子网网关
    --ingress                 创建群路由网状网络
    --internal                限制外部访问网络
    --ip-range strings        从子范围分配容器ip
    --ipam-driver string      IP地址管理驱动(default "default")
    --ipam-opt map            设置IPAM驱动程序的特定选项(default map[])
    --ipv6                    启用IPv6网络
    --label list              设置网络中的元数据
    --scope string            控制网络的范围
    --subnet strings          CIDR格式的子网,表示一个网段

3、删除网络

  • 基本语法格式如下
docker network rm NETWORK [NETWORK...]                   #删除一个或多个网络
docker network prune [OPTIONS]                           #删除所有未使用的网络
    -f, --force               不提示确认
    --filter filter           提供过滤值(e.g. 'until=<timestamp>')

4、将容器连接网络

  • 基本语法格式如下
docker network connect [OPTIONS] NETWORK CONTAINER       #将容器连接到网络
    --alias strings           为容器添加网络作用域别名
    --driver-opt strings      用于网络的驱动程序选项
    --ip string               IPv4地址(e.g., 172.30.100.104)
    --ip6 string              IPv6地址(e.g., 2001:db8::33)
    --link list               添加到另一个容器的链接
    --link-local-ip strings   为容器添加链接本地地址
docker network disconnect [OPTIONS] NETWORK CONTAINER    #断开容器与网络的连接
    -f, --force               强制容器与网络断开连接

4、桥接网络(Bridge驱动)

  • 单机桥接网络:Linux使用bridge驱动,Windows使用NAT驱动。
  • Bridged container桥接容器(bridge):创建容器时默认使用bridge,拥有172.17的地址。

1、什么是单机桥接网络

  • 单机意味着该网络只能在单个Docker主机上运行,并且只能与所在Docker主机上的容器进行连接。
  • 桥接意味着这是802.1.d桥接的一种实现(二层交换机)
  • Linux Docker创建单机桥接网络采用内置的桥接驱动(bridge),而Windows Docker创建时使用内置的NAT驱动。实际上,这两种驱动工作起来毫无差异。
  • 每个Docker主机都有一个默认的单机桥接网络
    • 在Linux上网络名称为bridge,在Windows上叫作nat。
    • 默认情况下,新创建的容器都会连接到该网络。但可以通过命令行创建容器时使用参数--network指定其他网络。

2、linux桥接网络

  • 在Linux主机上,Docker网络由Bridge驱动创建,而Bridge底层是基于Linux内核中久经考验达15年之久的Linux Bridge技术。这意味着Bridge是高性能并且非常稳定的!
  • 在Linux Docker主机之上,默认的单机桥接网络"bridge"被映射到内核中为"docker0"的Linux网桥
  • "bridge"网络和"docker0"网桥之间的关系

]# docker network ls                           #查看docker网络
NETWORK ID     NAME      DRIVER    SCOPE
67a2e5033fa9   bridge    bridge    local
3fff3ca94078   host      host      local
a6e79f333a43   none      null      local

]# docker network inspect bridge               #查看docker的bridge网络
...
            "com.docker.network.bridge.name": "docker0",
...

]# ip address ls docker0                       #查看Linux的docker0网卡接口
5: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:0e:1f:cb:78 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:eff:fe1f:cb78/64 scope link
       valid_lft forever preferred_lft forever

3、连接到桥接网络

1、将容器连接到桥接网络

]# docker container run --name b1 -d  busybox:latest sleep 1d       #网络默认使用bridge,因此两者等价
]# docker container run --name b2 --network bridge -d  busybox:latest sleep 1d

]# docker network inspect bridge --format "{{json .Containers}}"    #查看bridge网络,可以看到有两个容器被连接到bridge网络
{
    "1f25e6e3d573e1a34afa4091f61eb0db663bcc55b61c4e4a8e462f89f26efe19":{
        "Name":"b1",
        "EndpointID":"4f383885497c1733c25d101e178d1cf947d7b20042c3176669f80e496ef11033",
        "MacAddress":"02:42:ac:11:00:02",
        "IPv4Address":"172.17.0.2/16",
        "IPv6Address":""
        },
    "ce55bb1b0cd00712b8eccc1e7dd22d6a89ece2d52db44a20bea5ada163d2ec3d":{
        "Name":"b2",
        "EndpointID":"c9a095748ee2e42787aacd96c16a890b114065d695c0f51693f98a819e1ec2a5",
        "MacAddress":"02:42:ac:11:00:03",
        "IPv4Address":"172.17.0.3/16",
        "IPv6Address":""
		}
}

]# brctl show    #查看linux网桥,可以看到有两个网卡接口连接到了docker0网桥上
bridge name     bridge id               STP enabled     interfaces
docker0         8000.02425f828ded       no              veth07d8bef
                                                        vethc5d00b7
virbr0          8000.52540002efa6       yes             virbr0-nic

2、使用注入的方式修改容器的配置

]# docker container run --name b1 -it --rm -h b1.hengha.com busybox:latest
/ # hostname
b1.hengha.com

]# docker container run --name b2 -it --rm --dns 8.8.8.8 --dns-search hengha.com busybox:latest
/ # cat /etc/resolv.conf
search hengha.com
nameserver 8.8.8.8

]# docker container run --name b3 -it --rm --add-host b2.hengha.com:1.1.1.1 busybox:latest
/ # cat /etc/hosts
127.0.0.1       localhost
1.1.1.1 b2.hengha.com
172.17.0.2      49ce1639e084

4、端口映射

  • 桥接网络中的容器只能与位于相同网络中的容器进行通信。但是,可以使用端口映射( Port Mapping )来绕开这个限制。
  • 端口映射允许将某个容器端口映射到Docker主机端口上。对于配置中指定的Docker主机端口,任何发送到该端口的流量,都会被转发到容器。
  • 端口映射有四种:(-p, --publish list:将容器的端口暴露到主机,可以多次使用)
    • -p <containerPort>:将指定的容器端口映射到宿主机所有IP地址的一个随机接口。
    • -p <hostPort>:<containerPort>:将容器端口映射到指定的宿主机端口。
    • -p <ip>::<containerPor>:将容器端口映射到宿主机指定ip的一个随机接口。(注意,有两个冒号)
    • -p <ip>:<hostPort>:<containerPort>:将容器端口映射到宿主机指定ip的指定端口。

示例1:

]# docker container run --name web1 --network bridge -p 80 -d  nginx:latest
]# docker container port web1
80/tcp -> 0.0.0.0:49153

]# curl 192.168.248.130:49153    #在192.168.248.131上访问

示例2:

]# docker container run --name web2 --network bridge -p 8080:80 -d  nginx:latest
]# docker container port web2
80/tcp -> 0.0.0.0:8080

]# curl 192.168.248.130:8080    #在192.168.248.131上访问

示例3:

]# docker container run --name web3 --network bridge -p 192.168.248.130::80 -d  nginx:latest
]# docker container port web3
80/tcp -> 192.168.248.130:49154

]# curl 192.168.248.130:49154    #在192.168.248.131上访问

示例4:

]# docker container run --name web4 --network bridge -p 192.168.248.130:8888:80 -d  nginx:latest
]# docker container port web4
80/tcp -> 192.168.248.130:8888

]# curl 192.168.248.130:8888    #在192.168.248.131上访问

5、其他docker网络模型

1、开放式网络(host驱动)

  • Open container(开放式容器,host):容器直接共享宿主机的名称空间,宿主机开放哪些他就开放哪些。

示例1:

]# docker container run --name b1 --network host -d busybox:latest sleep 1d

]# docker container exec -it b1 sh
/ # ifconfig    #和在宿主机上的结果一样

]# ifconfig

]# docker network inspect host --format "{{json .Containers}}"
{
    "d8c5c71ad440cbad4929b724a81d79dee7677ccd2b2a8bcc554ee73550b8e748":{
        "Name":"b1",
        "EndpointID":"60aedad85ef69d2bea340efbca7ea11925add954afdefdf94b733bf92d38e026",
        "MacAddress":"",
        "IPv4Address":"",
        "IPv6Address":""
    }
}

示例2:web应用

]# docker container run --name web1 --network host -d nginx:latest

]# curl -I 192.168.248.130:80    #容器共享宿主机的网络名称空间,因此可以直接使用宿主机的端口访问容器

2、封闭式网络(null驱动)

  • Closed container(封闭式容器,none):只有lo接口。
]# docker container run --name b2 --network none -d busybox:latest sleep 1d

]# docker container exec -it b2 sh
/ # ifconfig    #只有lo网卡接口

]# docker network inspect none --format "{{json .Containers}}"
{
    "018a44a493a96895bc0f30efcc612c8d42f6396451af78670a4cb951852a9651":{
        "Name":"b2",
        "EndpointID":"02cc5a4aec269d463565ea19247b50bacb2aae78917e8cfdb26d2a943394c5f0",
        "MacAddress":"",
        "IPv4Address":"",
        "IPv6Address":""
    }
}

3、联盟式网络

  • container(联盟式容器):两个容器的名称空间一部分是隔离的(User、Mount、Pid),一部分是共享的(UTS、NET、IPC),两个容器通过lo就可以通信。
]# docker container run --name b3 -d busybox:latest sleep 1d
]# docker container run --name b4 --network container:b3 -d busybox:latest sleep 1d

]# docker container exec -it b3 sh
/ # ifconfig
... 172.17.0.2 ...
/ # mkdir /data210919

]# docker container exec -it b4 sh
/ # ifconfig
... 172.17.0.2 ...
/ # ls /data210919
ls: /data210919: No such file or directory

6、创建docker桥接网路

  • 每个docker主机只允许一个“host”网络实例。
  • 每个docker主机只允许一个“null”网络实例。

1、创建桥接网络

]# docker network create -d bridge localnet    #创建单机桥接网络

]# docker network ls                           #查看docker网络
NETWORK ID     NAME       DRIVER    SCOPE
307c8ca2e98d   bridge     bridge    local
e034e6807ff3   host       host      local
c59b6329c91a   localnet   bridge    local
8cf6e5ae5493   none       null      local

]# brctl show                                  #查看Linux网桥。两个网桥目前都没有开启STP,并且也都没有任何设备接入
bridge name       bridge id               STP enabled     interfaces
br-c59b6329c91a   8000.02429c57d04c       no
docker0           8000.02427dceb76c       no

]# ip link ls                                  #查看linux网卡接口
...
5: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default
    link/ether 02:42:7d:ce:b7:6c brd ff:ff:ff:ff:ff:ff
6: br-c59b6329c91a: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default
    link/ether 02:42:9c:57:d0:4c brd ff:ff:ff:ff:ff:ff
  • 目前,主机上的网桥配置如下图所示。

2、将容器连接到桥接网络

  • 同一网络上的容器可以相互通信,不同网络上的容器不能通信。

1、连接到localnet网络

]# docker container run --name b1 --network localnet -d  busybox:latest sleep 1d    #将容器连接到网络
]# docker container run --name b2 --network localnet -d  busybox:latest sleep 1d

]# docker network inspect localnet --format "{{json .Containers}}"                  #查看localnet网络,可以看到有两个容器被连接到localnet网络
{
    "d3428894db22a840a6d552cd8d79211422cd67afdc7ba45a893e917438dd8ccc":{
        "Name":"b1",
        "EndpointID":"e4f640ee65997dcbf83d4113cae456dec5d05bff3d8caae43c294eef155ae49f",
        "MacAddress":"02:42:ac:12:00:02",
        "IPv4Address":"172.18.0.2/16",
        "IPv6Address":""
    },
    "ea2ec45b0013041ee69641f3015d02dc800aa584edd0d330d4abd34b8a27a1ef":{
        "Name":"b2",
        "EndpointID":"dcbf7ee873fa4f1d70c615b3ef836dbf08d771a896b55542395649fc1d461c12",
        "MacAddress":"02:42:ac:12:00:03",
        "IPv4Address":"172.18.0.3/16",
        "IPv6Address":""
    }
}

]# brctl show    #查看linux网桥,可以看到有两个网卡接口连接到了br-c59b6329c91a网桥上 
bridge name       bridge id               STP enabled     interfaces
br-c59b6329c91a   8000.02429c57d04c       no              veth5f99e65
                                                          vethd9a9213
docker0           8000.02427dceb76c       no

2、连接到bridge网络

]# docker container run --name b3 -d  busybox:latest sleep 1d

]# docker network inspect bridge --format "{{json .Containers}}"
{
    "b1923ac72bfb14b93784e1a478ccca4e8d31c52292dc61d5e54f29ae2a89158f":{
        "Name":"b3",
        "EndpointID":"8671f4cfa171c44b26a823e2035fc0cb1c3fa20d88ef883912e34cb12727b385",
        "MacAddress":"02:42:ac:11:00:02",
        "IPv4Address":"172.17.0.2/16",
        "IPv6Address":""
    }
}

]# brctl show
bridge name       bridge id               STP enabled     interfaces
br-c59b6329c91a   8000.02429c57d04c       no              veth5f99e65
                                                          vethd9a9213
docker0           8000.02427dceb76c       no              veth86b6a31

3、测试连通性

  • 各容器连接到网络示意图

  • 同一网络上的容器可以相互通信,不同网络上的容器不能通信
]# docker container exec -it b1 sh
/ # ping -c 2 172.18.0.3    #b1能ping通b2
PING 172.18.0.3 (172.18.0.3): 56 data bytes
64 bytes from 172.18.0.3: seq=0 ttl=64 time=0.192 ms
64 bytes from 172.18.0.3: seq=1 ttl=64 time=2.397 ms

/ # ping -c 2 172.17.0.2    #b1不能ping通b3
PING 172.17.0.2 (172.17.0.2): 56 data bytes

7、Macvlan驱动

  • Docker内置的Macvlan驱动(Windows上是Transparent),可以使容器与外部系统以及物理网络进行通信。
  • Macvlan驱动通过为容器提供MAC和IP地址,让容器在物理网络上成为“一等公民”,如图所示:

  • Macvlan的优点是性能优异,因为无须端口映射或者额外桥接,可以直接通过主机接口(或者子接口)访问容器接口。
  • Macvlan的缺点是需要将主机网卡(NIC)设置为混杂模式(Promiscuous Mode),这在大部分公有云平台上是不允许的。
  • Macvlan对于公司内部的数据中心网络来说很棒(假设公司网络组能接受NIC设置为混杂模式),但是Macvlan在公有云上并不可行。
###没有实验成功,可能是因为使用的是虚拟机的关系
]# docker network create -d macvlan --subnet=172.16.10.0/24 --gateway=172.16.10.1 -o parent=ens33.1 mac1
]# docker container run -itd --name c1 --ip=172.16.10.11 --network mac1 busybox

]# ifconfig ens33.1 promisc  设置混杂模式
]# ifconfig ens33.1 -promisc 取消混杂模式

8、overlay驱动

  • overlay驱动创建网络是覆盖网络。
  • 在单机上创建overlay网络,会报错,需要将主机加入到swarm集群中。(docker network create -d overlay overlay-net)
  • 覆盖网络可以实现分属于不同网络中的不同主机上的容器之间可靠的、安全的通信。它创建一个扁平的、安全的二层网络来连接多个主机,容器可以连接到覆盖网络并直接互相通信。

1、VXLAN

  • Docker使用VXLAN隧道技术创建了虚拟二层覆盖网络。
  • 在VXLAN中,允许用户基于已经存在的三层网络结构创建虚拟的二层网络。
  • VXLAN是一种封装技术。为了创建二层覆盖网络,VXLAN基于现有的三层IP网络创建了隧道。
  • VXLAN隧道两端都是VXLAN隧道终端(VXLAN Tunnel Endpoint,VTEP),VTEP完成了封装和解压的步骤,以及一些功能实现所必需的操作。

2、创建覆盖网络

  • 在每台主机上都新建了一个Sandbox (网络命名空间)。Sandbox就像一个容器,但其中运行的不是应用,而是当前主机上独立的网络栈。
  • 在Sandbox内部创建了一个名为Br0的虚拟交换机(又称做虚拟网桥)。同时Sandbox内部还创建了一个VTEP,其中一端接入到名为Bro的虚拟交换机当中,另一端接入主机网络栈(VTEP),在主机网络栈中的终端从主机所连接的基础网络(三层“网络层”一下的基础部分)中获取到IP地址,并以UDP Socket的方式绑定到4789端口。
  • 不同主机上的两个VTEP通过VXLAN隧道创建了一个覆盖网络,如图所示。

3、将容器连接到覆盖网络

  • 每个容器都会有自己的虚拟以太网(veth)适配器,并接入本地Bro虚拟交换机。
  • 如图所示,虽然是在主机所属网络互相独立的情况下,但这样能更容易看出两个分别位于不同主机上的容器之间是如何通过VXLAN上层网络进行通信的。

posted @ 2021-09-17 16:13  麦恒  阅读(137)  评论(0编辑  收藏  举报