动手实现一个docker网络

423 阅读5分钟

在昨天的 docker基础实现原理 文章中有讲到docker的网络隔离就是利用linux网络命名空间来实现的,本文动手来自己创建一个docker网络,实际就是docker帮我们自动完成的一些操作

docker网络

docker0

docker在安装后就会默认创建一个docker0网桥,IP默认是172.17.0.1,可通过ip link命令查看

// 查看网络设备
ip link | grep docker0

// 查看IP
ip addr | grep docker0

docker0的默认IP是可以修改的,编辑/etc/default/docker,添加以下内容

DOCKER_OPTS="--bip 192.168.0.1/24"

192.168.0.1/24是你自己需要的IP,再重启docker使配置生效,service docker restart

路由规则

docker在宿主机默认添加了一条路由规则route add -net 172.17.0.0/16 dev docker0,表示将目标网络为172.17.0.0/16的数据包发到docker0这个设备,可通过route -n命令查看

route -n

Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.3.1     0.0.0.0         UG    0      0        0 ens33
172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 docker0

创建一个docker容器时,docker会自动帮我们创建一个网络命名空间和一对veth网卡,将其中一个网卡移到容器网络命名空间下并修改网卡名为eth0,从docker0上分配一个空闲的IP给eth0,留在宿主机上的网卡与docker0网桥连接,这样可以通过docker0实现容器与宿主机通信和多个容器之间通信

五种网络模式

docker提供了以下5种网络模式供我们创建容器时选择使用

  1. bridge 默认的网络模式,通过网络命名空间隔离
  2. host 主机模式,创建容器时不会为其创建网络命名空间,直接在宿主机网络命名空间下
  3. none 为容器创建网络命名空间,但是不做任何网络配置,完全由用户自己去配置该容器的网络
  4. container 使用其他容器的网络命名空间
  5. 用户自定义

端口绑定

bridge模式下,需要将宿主机端口绑定到容器内指定的端口(以MySQL为例-p 3306:3306),docker是帮我们在iptables的nat表中添加了一条记录

iptables -t nat -L -n

DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:3306 to:172.17.0.2:3306

这条记录的意思是将任意源到任意目的地的数据包,只要端口是3306,就修改目的地为172.17.0.2:3306,172.17.0.2是MySQL容器内eth0的IP,由于上面有一条路由规则route add -net 172.17.0.0/16 dev docker0,所以这个数据包会交给docker0,docker0再发到MySQL容器内

自己动手

规划:

创建一个br0网桥来代替docker0网桥,创建两个网络命名空间来代替两个docker容器,两个网络分别为 192.168.10.0/24192.168.20.0/24

先创建br0网桥

ip link add br0 type bridge

为br0分配两个IP,这两个IP之后会分别作为两个网络命名空间的网关

ip addr add dev br0 192.168.10.1/24
ip addr add dev br0 192.168.20.1/24

添加两条路由规则

route add -net 192.168.10.0/24 dev br0
route add -net 192.168.20.0/24 dev br0

接下来模拟创建两个容器,在这里就是创建两个网络命名空间

ip netns add ns1
ip netns add ns2

创建两对veth网卡

ip link add veth1 type veth peer name veth12
ip link add veth2 type veth peer name veth22

将veth12和veth22分别放到对应的网络命名空间

ip link set veth12 netns ns1
ip link set veth22 netns ns2

修改两个命名空间中的veth的名称

ip netns exec ns1 ip link set veth12 name eth0
ip netns exec ns2 ip link set veth22 name eth0

启用这两个eth0

ip netns exec ns1 ip link set eth0 up
ip netns exec ns2 ip link set eth0 up

设置eth0的IP

ip netns exec ns1 ip addr add dev eth0 192.168.10.10/24
ip netns exec ns2 ip addr add dev eth0 192.168.20.20/24

现在将留在宿主机上(主网络命名空间)的veth1和veth2连接上br0网桥

ip link set veth1 master br0
ip link set veth2 master br0

此时通过bridge link命令可以查看到veth1和veth2已经连接到br0

现在来启用veth1、veth2和br0

ip link set veth1 up
ip link set veth2 up
ip link set br0 up

到这里所有设备都已经启用,且正常连接了,不过目前在ns1下面ping ns2下面的IP是不通的,ping命令会提示网络不可达,因为跨了网段,需要指明路由,在ns1和ns2分别添加路由规则

ip netns exec ns1 route add -net 192.168.20.0/24 gw 192.168.10.1
ip netns exec ns2 route add -net 192.168.10.0/24 gw 192.168.20.1

最后再添加两条iptables规则

iptables -t nat -I PREROUTING -s 192.168.10.0/24 -d 192.168.20.0/24 -j DNAT --to 192.168.10.1
iptables -t nat -I PREROUTING -s 192.168.20.0/24 -d 192.168.10.0/24 -j DNAT --to 192.168.20.1

到这里ns1和n2两边可以互相ping通

相关命令

ip link

对网络接口的管理,有以下常用命令

// 查看当前所有网络设备
ip link

// 添加一个桥
ip link add bridge1 type bridge

// 添加一个虚拟网卡
ip link add test1 type veth

//  添加一对虚拟网卡
ip link add test1 type veth peer name test2

// 将虚拟网卡移到指定命名空间下
ip link set test1 netns xxx

// 启用设备
ip link set test1 up

// 禁用设备
ip link set test1 down

// 修改网卡名
ip link set dev xxx name yyy

ip addr

网络接口的IP管理

// 在eth0这个网卡上添加IP
ip addr add dev eth0 192.168.10.10/24

// 删除IP
ip addr delete dev eth0 192.168.10.10/24

// 查看IP
ip addr

route

管理路由

// 查看路由
route -n

Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.10.1    0.0.0.0         UG    0      0        0 eth0
  • Destination是目标地址,0.0.0.0指所有目标IP
  • Gateway 是网关,如果是0.0.0.0表示目标地址和当前地址在同一个网段内
  • Genmask 是子网掩码
  • Flags是标志,其中U表示该条路由可用,G表示需要通过网关才能到达目标地址,!表示该条路由不会被使用
  • Iface 网络接口

tcpdump

参考本人前几天发的文章