容器技术 k8s 网络之宿主机网络

163 阅读6分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第14天。

在这里我们介绍宿主内的容器网络,当然我们还是以docker环境为例,介绍docker宿主环境中的容器网络。

在介绍docker宿主环境中的容器网络的时候我们先大致了解如下一些概念和知识:

  • Linux Network Namespace

  • Linux Bridge Device

  • Linux Veth Device

Linux Network Namespace:

一提到linux网络,本质上就是由一系列组件组成,从而共同协作完成网络功能,一般这些组件包括:

  • linux网络设备:例如network interface device,loop back device,bridge device,veth device,tun/tap device,vxlan device,ip tunnel device等等。这些设备可以完成网络数据包的收发,以及提供额外的修改数据包等功能。

  • linux路由表,arp表,fdb(forwarding database)等:路由表提供三层ip包的路由寻址功能,arp表提供ip对应的二层mac地址,fdb在在基于mac转发的功能里提供mac地址对应的网络接口。

  • linux协议栈:完成对网络协议包的封装与解析,例如二层ethernet包,三层ip包,四层tcp/udp包,还有类似icmp包等等。

  • linux iptable:iptable基于linux内核模块netfilter,完成对于linux的firewall管理,例如控制ingress与engress,nat地址转换,端口映射等等。

linux network namespace就像是一个可以相互隔离的组一样,把网络设备,路由表,协议栈,iptable等组件包装起来,然后通过namespace来相互隔离,互不干扰。所以更具体的说,网络设备,路由表,协议栈,iptable等是工作在某一个linux network namespace下的。而我们平时看到的linux主机的网卡,路由表,arp表,协议栈,iptable都是工作在主机的默认网络命名空间下,下图用来表述linux network namespace:

图片

当然linux不仅仅只有network namespace用来进行网络隔离,还有pid namespace用来隔离进程,user namespace用来隔离用户,mount namespace用来隔离挂载点,ipc namespace用来隔离信号量和共享内存等,uts namespace用来隔离主机名和域名。再结合上linux cgroup控制组,用来限制资源,例如cpu,memory,io等。这些基础知识构成了容器的底层实现,有兴趣的同学可以研究一下linux namespace,cgroup,ufs等基础知识,你会发现docker等容器技术都是基于这些实现的。

Linux Bridge网桥:

Bridge是一种linux网络设备,可以附加attach多个linux从设备。大家可以把linux bridge想象成二层交换机,可以进行二层数据包的广播,但是注意的是linux bridge设备可以有自己的ip地址。也就是说,多个linux网络设备attach到一个bridge上,那么这些网络设备的ip地址将会失效(只有二层功能),当一个设备收到数据包的时候,bridge会把数据包转发到其它所有attach到bridge上的从设备,从而实现广播的效果。下图用来表述linux bridge:

图片

Linux Veth设备:

Veth设备总是成对出现,它的特点是会有一对peer,两个端点,数据包从一个peer流入,总是可以从另一个peer流出。而且veth pair是可以跨linux network namespace的,这个特性很重要。玄外之音,因为network namespace本质就是相互隔离的,可以用veth来做到数据包的跨network namespace的访问。当然是一个peer在一个namespace里,另一个peer在另一个namespace里了。下图用来表述linux veth pair设备:图片

docker宿主环境中的容器网络:

有了以上知识基础,当讲到这里的时候,其实就已经比较清晰了。对于docker宿主环境中容器的网络一般是:

  • 每一个container都有一个network namespace,然后拥有container自己的网络设备,路由表,arp表,协议栈,iptable等,各个container的network namespace相互隔离。

  • 在宿主的default netwok nemespace中会有一个linux bridge设备,一般名称为docker0。

  • 每一个container对应一个veth pair设备,这个设备的一端在container的network namespace里,另一端attach到宿主networkwork namespace的docker0 linux bridge上。

  • 这样在宿主环境里,就好像有一个二层交换机(docker0 bridge),把宿主内的所有container连接起来。所以,在宿主内的container都是可以直接相互访问的,而且是直连的方式。

下图用来表述宿主环境中的容器网络:

图片

查看docker0 linux bridge:

在宿主机上运行如下命令,从这里面我们看到docker在host上的linux bridge device,另外这个bridge上attach了4个从设备。这些从设备一定是veth pair device的一端,而另一端一定是在docker container的network namespace里。

brctl show

图片

查看veth pair从设备:

在宿主机上运行如下命令,从这里面我们看到在host的default network namespace中确实有4个attach到上面docker0 linux bridge上的设备。

ip addr|grep docker0

图片

查看docker容器:

在宿主机上运行如下命令,我们看到确实是有4个k8s pod容器在运行,每个容器都有一个network namespace,所以也印证了上面命令中的docker0 bridge上attach的4个设备。这里有同学可能会有疑问,明明是8个容器,为什么说是4个呢?细心的同学应该观察出来了,另外4个都是同样的image。没错,这是因为在k8s中,每个pod都需要有infrastructure image的(安装kubelet的时候我们通过启动参数--pod-infra-container-image配置过这个基础image),而k8s中这个基础image的container和我们应用程序的image的container是共用同一个network namespace的。所以在k8s的pod之中,infrastructure image产生的container和同一个pod中所有application image产生的container在同一个network namespace里,当然这里就是4个network namespace了。

docker ps

图片

进入容器的network namespace里:

这里我们在宿主机器上选择一个container,用kube-debug进入容器内部

kubectl get pods

图片

图片

查看容器内的veth设备:

在容器中运行以下命令,我们可以看到容器内确实有一个veth设备,这个veth设备就是veth pair在容器的另一端,并且这一端的设备也是有ip地址的,ip地址就是container的ip地址。当然它的另一端是attach在宿主default namesapce中的docker0 bridge device里了。

ip -d link show

图片

查看容器内的路由表:

在容器中运行以下命令,我们可以看到容器network namesapce中的路由表信息。

  • 默认网关为10.1.27.1/32,其实就是host network namespace中的docker0 bridge的ip地址。

  • 对于10.1.27.0/24网络地址空间的访问都是直连访问,不需要下一跳ip地址。

route -n

图片

根据以上信息总结docker宿主中的网络:

  • 宿主中容器的网络地址空间一般为x.x.x.0/24,每一个container属于一个独立的network namespace。

  • 宿主的default network namespace中会有linux bridge,一般名字是docker0,其ip地址为x.x.x.1/32。

  • 每个container会有一个veth pair device对应,这个veth pair一端在container的network namespace中,有ip地址,并且这个ip地址就是容器的ip地址。veth pair的另一端attach到宿主network namespace中的linux bridge docker0上,以完成container network namespace和宿主network namespace之间的数据流动。

  • container的默认网关是x.x.x.1/32,也就是宿主network namespace中的linux bridge docker0的ip地址。

  • container到容器的网络地址空间(x.x.x.0/24)的访问方式为直连,不需要下一跳ip。