本文主要是针对docker 和 kubernetes 的网络的一个简单学习,让大家对docker 和 kubernetes 有一个基本的认知,方便后面深入的学习。
前端为什么要学习Docker 和 kubernetes
Docker的背景:
- 2013年Docker项目发布,迅速风靡全球。
- 2015~2017年,容器编排项目k8s,docker swarm,mesos展开激烈竞争。
- 2017年10月docker企业版内置k8s项目。标志k8s最终胜出。
- k8s作为云计算的操作系统,直接影响了服务部署运维,因此普通开发者需要理解容器相关的各种概念,掌握k8s的使用技能。
环境隔离
docker 实现了资源隔离,一台机器运行多个容器互无影响。
更高效的资源利用
docker 容器的运行不需要额外的虚拟化管理程序的支持,它是内核级的虚拟化,可以实现更高的性能,同时对资源的额外需求很低。
更快速的交付部署
使用 docker,开发人员可以利用镜像快速构建一套标准的研发环境,开发完成后,测试和运维人员可以直接通过使用相同的环境来部署代码。
更易迁移扩展
docker 容器几乎可以在任意的平台上运行,包括虚拟机、公有云、私有云、个人电脑、服务器等,这种兼容性让用户可以在不同平台之间轻松的迁移应用。
更简单的更新管理
使用 Dockerfile,只需要很少的配置修改,就可以替代以往大量的更新工作。并且所有修改都是以增量的方式进行分发和更新,从而实现自动化和高效的容器管理。
介绍一下Linux network namespace
network namespace 是实现网络虚拟化的重要功能。
- linux 内核提供的功能
- 它能创建多个隔离的网络空间,这些网络空间有独自的网络栈信息(独立的网卡、路由表、ARP 表、iptables 等和网络相关的资源)。
这篇文章借助 ip
命令来完成各种操作。ip
命令来自于 iproute2
安装包,一般系统会默认安装
Network Namespace隔离网络
- 容器与容器之间,如果网络不隔离的话,比如我们起了一个nginx,占了80端口,就不能再启动监听80端口的服务,如果两个容器客户租用同一台宿主机,显然不隔离网络的容器不能满足需求。
- Network Namespace提供了网络资源的隔离功能,包括网络设备,IPv4和IPv6协议栈,IP路由表,防火墙,套接字等。一个物理网络设备最多存在于一个Network Namespace中,不同的Network Namespace的进程如果需要网络通信,可以创建veth pair(虚拟网络设备对:有两端,类似管道,如果数据从一端传入,另一端也能收到,反之亦然),veth pair一端放在Network Namespace A中,另一端放在Network Namespace B中,这样A和B就可以网络通信了。
- 下面实地操作下,使用ip netns命令,在宿主机上添加两个Network Namespace。
root@10-25-125-129:~# ip netns add net1
root@10-25-125-129:~# ip netns ls
net1
net0
- 创建veth pair,然后执行ip link可以看到veth0和veth1两个虚拟网卡。
root@10-25-125-129:~# ip link add type veth
root@10-25-125-129:~# ip link
1: lo: mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
...
4: veth0@veth1: mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 62:47:4c:5a:bd:da brd ff:ff:ff:ff:ff:ff
5: veth1@veth0: mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether de:ad:ee:97:79:39 brd ff:ff:ff:ff:ff:ff
- 接下来把两个虚拟网卡分别放到两个namespace里面,可以用命令’ip link set DEV netns NAME‘来实现。然后分别在两个namespace里面查看网络设备。可以看到网卡设备已绑定。
root@10-25-125-129:~# ip link set veth0 netns net0
root@10-25-125-129:~# ip link set veth1 netns net1
root@10-25-125-129:~# ip netns exec net0 ip addr
1: lo: mtu 65536 qdisc noop state DOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
4: veth0@if5: mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 62:47:4c:5a:bd:da brd ff:ff:ff:ff:ff:ff link-netnsid 1
root@10-25-125-129:~# ip netns exec net1 ip addr
1: lo: mtu 65536 qdisc noop state DOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
5: veth1@if4: mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether de:ad:ee:97:79:39 brd ff:ff:ff:ff:ff:ff link-netnsid 0
- 最后,我们先启用这对网卡,给这对网卡配置上IP地址。
root@10-25-125-129:~# ip netns exec net0 ip link set veth0 up
root@10-25-125-129:~# ip netns exec net0 ip addr add 10.0.1.1/24 dev veth0
root@10-25-125-129:~# ip netns exec net1 ip link set veth1 up
root@10-25-125-129:~# ip netns exec net1 ip addr add 10.0.1.2/24 dev veth1
- 配置完以后,我们在namespace net0里面ping net1的IP地址。ping通后,说明这两个namespace已经网络连通了。
root@10-25-125-129:~# ip netns exec net0 ping 10.0.1.2
PING 10.0.1.2 (10.0.1.2) 56(84) bytes of data.
64 bytes from 10.0.1.2: icmp_seq=1 ttl=64 time=0.045 ms
64 bytes from 10.0.1.2: icmp_seq=2 ttl=64 time=0.035 ms
64 bytes from 10.0.1.2: icmp_seq=3 ttl=64 time=0.032 ms
64 bytes from 10.0.1.2: icmp_seq=4 ttl=64 time=0.038 ms
^C
--- 10.0.1.2 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3062ms
rtt min/avg/max/mdev = 0.032/0.037/0.045/0.007 ms
- Docker的容器之间通信原理基本类似,我们实现了两个namespace通信,但是如果多个namespace通信如何实现呢?直觉告诉我们需要一个虚拟交换机,在docker里面就是docker0网桥。我们创建一个veth pair,一端绑在容器的network namespace,另一端绑在宿主机的docker0网桥上。网络拓扑图如下所示。(图片来自《容器与容器云(第二版)》)。这样每个容器都有自己的网络设备,对开发者来说,每个容器都有自己的IP地址。
Docker网络模式
Docker网络模式 | 配置 | 说明 |
---|---|---|
host模式 | –net=host | 容器和宿主机共享Network namespace |
container模式 | –net=container:NAME_or_ID | 容器和另外一个容器共享Network namespace。 kubernetes中的pod就是多个容器共享一个Network namespace。 |
none模式 | –net=none | 容器有独立的Network namespace,但并没有对其进行任何网络设置,如分配veth pair 和网桥连接,配置IP等。 |
bridge模式 | –net=bridge | (默认为该模式) |
k8s简介
架构体系
. API Server(kube-apiserver)
API Server是k8s的前端接口,各种客户端工具以及k8s其他组件可以通过它管理集群的各种资源。
2.Scheduler(kube-scheduler)
scheduer负责决定将pod放在哪个node上运行。另外scheduler在调度时会充分考虑集群的架构,当前各个节点的负载,以及应用对高可用、性能、数据亲和性的需求。
3.Controller Manager(kube-controller-manager)
负责管理集群的各种资源,保证资源处于预期的状态。
4.etcd
负责保存k8s集群的配置信息和各种资源的状态信息,当数据发生变化时,etcd会快速的通知k8s相关组件。
5.kubelet~~~~
是node的agent,当scheduler去确定在某个node上运行pod后,会将pod的具体配置信息发送给该节点的kubelet,kubelet会根据遮羞信息创建和运行容器,并向master报告运行状态。
6.kube-proxy
每个node都会运行kube-proxy服务,外界通过service访问pod,kube-proxy负责将降访问service的TCP/UDP数据流转发到后端的容器。如果有多个副本,kube-proxy会实现负载均衡。
K8S网络模型
根据以上的一些要求,需要解决的问题
- 容器之间的网络
- Pod与Pod之间的网络
- Pod与Service之间的网络?
1.容器和容器之间的网络
pod有多个容器,它们之间怎么通信?
pod中每个docker容器和pod在一个网络命名空间内,所以ip和端口等等网络配置,都和pod一样,主要通过一种机制就是,docker的一种网络模式,container,新创建的Docker容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等
2.pod与pod之间的网络
pod与pod之间的网络:首先pod自身拥有一个IP地址,不同pod之间直接使用IP地址进行通信即可
3.同一台node节点上pod和pod通信
疑问:那么不同pod之间,也就是不同网络命名空间之间如何进行通信(现在还是,同一台node节点上)
解决:
简单说veth对就是一个成对的端口,所有从这对端口一端进入的数据包,都将从另一端出来。
为了让多个Pod的网络命名空间链接起来,我们可以让veth对的一端链接到root网络命名空间(宿主机的),另一端链接到Pod的网络命名空间。
嗯,那么继续。
还需要用到一个Linux以太网桥,它是一个虚拟的二层网络设备,目的就是把多个以太网段连接起来,它维护一个转发表,通过查看每个设备mac地址决定转发,还是丢弃数据
- pod1-->pod2(同一台node上),pod1通过自身eth0网卡发送数据,eth0连接着veth0,网桥把veth0和veth1组成了一个以太网,然后数据到达veth0之后,网桥通过转发表,发送给veth1,veth1直接把数据传给pod2的eth0。
4.不同node节点上pod和pod通信
CIDR的介绍:
CIDR(Classless Inter-Domain Routing,无类域间路由选择)它消除了传统的A类、B类和C类地址以及划分子网的概念,因而可以更加有效地分配IPv4的地址空间。它可以将好几个IP网络结合在一起,使用一种无类别的域际路由选择算法,使它们合并成一条路由从而较少路由表中的路由条目减轻Internet路由器的负担。
192.168.19.0 / 255.255.255.0
.。。。。
192.168.50.0 / 255.255.255.0
这几个地址展开来是:
11000000 10101000 00010011 00000000
11000000 10101000 00110010 00000000
他们都有相同的前18位,所以汇总地址就是192.168.0.0/18
k8s集群中,每个node节点都会被分配一个CIDR块,(把网络前缀都相同的连续地址组成的地址组称为CIDR地址块)用来给node上的pod分配IP地址,另外还需要把pod的ip和所在nodeip进行关联