Kubernetes网络模型设计的基础原则:每个Pod拥有一个独立的IP地址,而且所有Pod都在一个可以直接连通、扁平的网络空间中。这种网络模型被称为IP-per-Pod模型。
IP-per-Pod模型使得:1. 所有Pod及其内部container(不管在不在同一个node上)都可以在不用NAT的方式直接相互访问;2.Pod内部的所有container共享一个网络堆栈,包括IP地址、网络设备、配置等,即同一个Pod内的容器可以通过localhost来连接对方的端口。
Kubernates网络的设计主要致力于解决以下问题:
1. 容器到容器之间的直接通信
2. 抽象的pod到pod之间的通信
3. pod到service之间的通信
4. 集群外部与内部组件之间的通信
容器到容器之间的直接通信
如上图中的阴影部分所示,在Node上运行着一个Pod实例。在我们的例子中,容器就是图中的容器1和容器2。容器1和容器2共享一个网络的命名空间,共享一个命名空间的结果就是它们好像在一台机器上运行,它们打开的端口不会有冲突,可以直接使用Linux的本地IPC进行通信(例如消息队列或者管道)。其实,这和传统的一组普通程序运行的环境是完全一样的,传统程序不需要针对网络做特别的修改就可以移植了,它们之间的互相访问只需要使用localhost就可以。例如,如果容器2运行的是MySQL,那么容器1使用localhost:3306就能直接访问这个运行在容器2上的MySQL了。
抽象的pod到pod之间的通信
1. 同一个Node内Pod之间的通信
可以看出,Pod1和Pod2都是通过Veth连接到同一个docker0网桥上的,它们的IP地址IP1、IP2都是从docker0的网段上动态获取的,它们和网桥本身的IP3是同一个网段的。
另外,在Pod1、Pod2的Linux协议栈上,默认路由都是docker0的地址,也就是说所有非本地地址的网络数据,都会被默认发送到docker0网桥上,由docker0网桥直接中转。
综上所述,由于它们都关联在同一个docker0网桥上,地址段相同,所以它们之间是能直接通信的。
2. 不同Node上Pod之间的通信
不同Node之间的通信只能通过宿主机的物理网卡进行,因此要想实现不同Node上Pod容器之间的通信,就必须想办法通过主机的这个IP地址进行寻址和通信。
另一方面,这些动态分配且藏在docker0之后的所谓“私有”IP地址也是可以找到的。Kubernetes会记录所有正在运行的Pod的IP分配信息,并将这些信息保存在etcd中(作为Service的Endpoint)。这些私有IP信息对于Pod到Pod的通信也是十分重要的,因为我们的网络模型要求Pod到Pod使用私有IP进行通信。所以首先要知道这些IP是什么。
Kubernetes的网络对Pod的地址是平面的和直达的,所以这些Pod的IP规划也很重要,不能有冲突。只要没有冲突,我们就可以想办法在整个Kubernetes的集群中找到它。
综上所述,要想支持不同Node上Pod之间的通信,就要满足两个条件:
(1)在整个Kubernetes集群中对Pod的IP分配进行规划,不能有冲突;
(2)找到一种办法,将Pod的IP和所在Node的IP关联起来,通过这个关联让Pod可以互相访问。
在上图中:IP1对应的是Pod1,IP2对应的是Pod2。Pod1在访问Pod2时,首先要将数据从源Node的eth0发送出去,找到并到达Node2的eth0。即先是从IP3到IP4的递送,之后才是从IP4到IP2的递送。
pod到service之间的通信
Service可以为一组具有相同功能的容器应用提供一个统一的入口地址,并且将请求负载分发到后段的各个容器应用上。也就是说Service不同于deployment和RC的是,它可以解决node发生故障时,ip地址发生变化,和负载均衡的问题。
在 Kubernetes 中创建一个新的 Service 对象需要两大模块同时协作,其中一个模块是控制器,它需要在每次客户端创建新的 Service 对象时,生成其他用于暴露一组 Pod 的 Kubernetes 对象,也就是 Endpoint 对象;另一个模块是 kube-proxy,它运行在 Kubernetes 集群中的每一个节点上,会根据 Service 和 Endpoint 的变动改变节点上 iptables 或者 ipvs 中保存的规则。(目前默认是iptables)
• endpoint
endpoint是k8s集群中的一个资源对象,存储在etcd中,用来记录一个service对应的所有pod的访问地址。
service配置selector,endpoint controller才会自动创建对应的endpoint对象;否则,不会生成endpoint对象.
• kube-proxy
在 Kubernetes 集群中的每一个节点都运行着一个 kube-proxy 进程,这个进程会负责监听 Kubernetes 主节点中 Service 的增加和删除事件并修改运行代理的配置,为节点内的客户端提供流量的转发和负载均衡等功能,但是当前 kube-proxy 的代理模式目前来看有三种:userspace、iptable、ipvs.
kube-proxy负责service的实现,即实现了k8s内部从pod到service和外部从node port到service的访问。
Kubernetes Proxy实现了以下功能:
1. 转发访问Service的Service-IP的请求到Endpoints(即Pod-IP)。
2. 监控Service和Endpoints的变化,实时刷新转发规则。
3. 负载均衡能力。
从集群外部访问pod或service
除了 Cluster 内部可以访问 Service,很多情况我们也希望应用的 Service 能够暴露给 Cluster 外部。Kubernetes 提供了多种类型的 Service,默认是 ClusterIP。
• ClusterIP
Service 通过 Cluster 内部的 IP 对外提供服务,只有 Cluster 内的节点和 Pod 可访问,这是默认的 Service 类型,前面实验中的 Service 都是 ClusterIP。
• NodePort
Service 通过 Cluster 节点的静态端口对外提供服务。Cluster 外部可以通过
集群外的客户端系统无法通过pod的IP地址或者Service的虚拟IP地址和虚拟端口号访问它们(ClusterIP和NodePort),想要访问,在此之前需要将Pod或Service的端口号映射到宿主机。
1. 将容器应用的端口号映射到物理机
1. 通过设置容器级别的hostPort, 将容器应用的端口号映射到物理机上。
2. 通过设置Pod级别的hostNetwork=true,该Pod中所有容器的端口号都将被直接映射到物理机上。
2. 将Service的端口号映射到物理机
1. 通过设置nodePort映射到物理机,同时设置Service的类型为NodePort
2. 通过设置LoadBalancer映射到云服务商提供的LoadBalancer地址
• LoadBalancer
这个是一个标准的方式来对外发布服务。
When would you use this?
If you want to directly expose a service, this is the default method. All traffic on the port you specify will be forwarded to the service. There is no filtering, no routing, etc. This means you can send almost any kind of traffic to it, like HTTP, TCP, UDP, Websockets, gRPC, or whatever.
The big downside is that each service you expose with a LoadBalancer will get its own IP address, and you have to pay for a LoadBalancer per exposed service, which can get expensive!
• Ingress
这个并不是跟上面并列的service类型,而是位于多个service之前的智能路由服务。由于它功能的强大,也可作为对外发布服务的一种方式。
When would you use this?
Ingress is probably the most powerful way to expose your services, but can also be the most complicated. There are many types of Ingress controllers, from the Google Cloud Load Balancer, Nginx, Contour, Istio, and more. There are also plugins for Ingress controllers, like the cert-manager, that can automatically provision SSL certificates for your services.
Ingress is the most useful if you want to expose multiple services under the same IP address, and these services all use the same L7 protocol (typically HTTP). You only pay for one load balancer if you are using the native GCP integration, and because Ingress is “smart” you can get a lot of features out of the box (like SSL, Auth, Routing, etc)
开源的网络组件
Kubernates的网络模型假定了所有pod都在一个可以直接连通的扁平网络空间中,虽然GCE中,这个网络已经存在,但是在私有云里搭建Kubernates集群,就不能假定这种网络已经存在。我们需要自己实现这个网络假设,将不同节点上的Docker容器之间的互相访问先打通,然后运行Kubernates.
Reference:
1.https://www.jianshu.com/p/6a5b58d08a0f
2.https://draveness.me/kubernetes-service/ (这个大神之作)
3.https://kubernetes.io/zh/docs/concepts/services-networking/service/
4.https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0