Docker Bridge 网络原理

Docker 的网络子系统是可插拔驱动式的,默认情况下存在或支持多种网络接口,如 bridge、host、overlay、macvlan 以及 none 类型的网络接口。

bridge 是 docker 默认的网络模式,如果在创建容器时不指定网络类型,则默认时 bridge 模式,会为每个容器分配一个 Network Namespace、IP 等,并将容器的网络连接到一个网桥(docker0)上。 这种模式的特点是:同一个网桥下的容器间可以相互通信,不同网桥间的容器网络会被隔离。

Linux 网桥

bridge 模式是基于 Linux 的网桥(一同通过软件实现的虚拟设备)实现的,网桥可以将 Linux 内部多个网络接口连接在一起,如下图:

通过网桥将网络接口连接起来可以实现:一个网络接口接收到网络数据包后,会复制到其他网络接口中,如下图所示: 如上图所示,当网络接口A收到数据包后,网桥会将数据包复制发送给连接到网桥的其他接口(如上图中的网卡B和网卡C)。

docker0

Docker 通过 Linux 的网桥实现了 bridge模式,原理如下图: Docker 在启动时,会创建一个名为docker0网桥,并把其IP地址设为172.17.0.1/16(私有IP地址)。

$ brctl show
bridge name	bridge id		STP enabled	interfaces
docker0		8000.0242526953be	no		
复制代码

注:brclt 一个用来管理网桥的命令行工具。详见

$ 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: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:0c:29:9c:30:76 brd ff:ff:ff:ff:ff:ff
    inet 192.168.59.128/24 brd 192.168.59.255 scope global noprefixroute dynamic ens33
       valid_lft 1512sec preferred_lft 1512sec
    inet6 fe80::b356:f4d:f6ea:25aa/64 scope link noprefixroute 
       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:52:69:53:be 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:52ff:fe69:53be/64 scope link 
       valid_lft forever preferred_lft forever
复制代码

然后 Docker 在创建容器时,会通过虚拟设备对veth-pair来将容器与docker0 连接起来。对于172.17.0.0/16网段的数据包, Docker 会定义一条iptables NAT的规则来讲这些数据包的 IP 地址转换成公网 IP 地址,然后通过真实的网络接口(如上图 ens160 接口)发送出去。 启动一个容器,默认使用 bridge 模式

$ docker run -d --name redis -p6379:6379 redis:4
5bd019a047ce
复制代码

查看网桥状态,已有 veth 连接

$ brctl show
bridge name	bridge id		STP enabled	interfaces
docker0		8000.0242526953be	no		vethed2a70d
复制代码

查看容器的netns

当容器启动时,Docker 内部会自动为这个容器创建一个netns用于网络隔离,但当我们使用ip netns list查看时却看不到数据,是因为 Docker 把netns创建到了其他地方,而ip netns list命令只能读目录/var/run/netns下面的数据。

我们可以通过以下命令来解决这个问题,方便我们学习docker网络。

# 得到容器对应的进程
pid=`docker inspect -f '{{.State.Pid}}' $container_id`
# 手动创建防止文件夹不存在
mkdir -p /var/run/netns/
# 建立软连接
ln -s /proc/$pid/ns/net /var/run/netns/$container_id
复制代码

将上面的命令修改为和当前环境一致并验证netns中的网卡。

$ pid=`docker inspect -f '{{.State.Pid}}' 5bd019a047ce`
$ mkdir -p /var/run/netns/
$ ln -s /proc/$pid/ns/net /var/run/netns/5bd019a047ce
$ ip netns list
5bd019a047ce (id: 0)

$ ip netns exec 5bd019a047ce 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
6: eth0@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
复制代码

可以看到我们成功输出了netns中的网卡信息,并且此信息和从docker容器中看到的一致。

猜你喜欢

转载自juejin.im/post/7017352634793197582