Kubernetes的网络基础原理介绍

前言

对刚刚接触Kubernetes的新手来说,在开始使用之前,有必要先理解一些关于Kubernetes的基本概念。

很多地方都会把Kubernetes和容器技术放在一起去讨论,但需要明确的是,Kubernetes是一个容器编排和管理的工具,为用户提供自动部署和容器监控等解决方案。目前在大多数主流的容器管理平台都支持Kubernetes,国外的有:OpenShift、Docker EE、AWS EKS、Rancher,而国内的则以腾讯云的TKE和阿里云的ACS为主流。

在下面的内容,我们主要来讨论一下在Kubernetes集群中的网络原理。

术语介绍

以下是关于Kubernetes的一些关键术语和概念:

  • 主节点(Master Node):在集群中负载管理所有工作节点并控制pod的生命周期。

  • 工作节点(Worker Node):用于承载应用服务的运行,同时运行Kubernetes中的各个组件。

  • 服务(Service):充当Pod的代理,以实现多个Pod的负载均衡。

  • Pod:Kubernetes部署的原子单位,每个Pod都有自己的IP地址,并且可以包含一个或多个容器。

  • API Server:运行在主节点中的关键进程之一。提供HTTP RESTful API接口的主要服务,是Kubernetes里对所有资源进行增、删、改、查等操作的唯一入口,也是集群控制的入口进程。

  • kubelet:集群中的每个服务器都会安装运行,可以理解为kubernetes集群的客户端或者Agent,负责Pod对应容器的创建、启停等任务,同时与主节点密切协作,实现集群管理的基本功能。

Pod网络基础

在Kubernetes中,每个pod都有自己的IP地址,同一个pod中的容器共享pod的IP地址,以localhost方式侦听pod的端口。而Kubernetes的网络通过网络插件(如:Calico、Flannel、Weave等)实现,负责把来自集群外部或者是内部节点之间的网络请求转发到正确的pod中。

下面是一个示例:

pod-network

  1. 当pod完成部署后,kubernetes中的网络插件会为其分配一个IP地址,并根据既定的规则完成iptables和路由表的动态配置。
  2. 当Pod1需要发送数据包到Pod2时,可以使用Pod2的IP或者是Pod2所在的Service的ClusterIP作为目标地址。在图示的例子中,使用的是Pod2的IP地址作为目标地址,如果使用的是Service的ClusterIP,kube-proxy会通过负载均衡规则和DNAT得到一个后端Pod的IP地址(这个我们后面会讨论到)。
  3. 确定了目标地址后,节点会根据路由表决定如何转发这个数据包:
    • 如果目标Pod在同一个节点上,数据包会通过网桥直接转发到目标Pod。
    • 当需要跨节点转发,数据包会根据路由表的规则转发到所需的网络接口。
    • 在本例中,数据包会加上IPIP tunnel的header,通过IPIP tunnel转发到另一个节点的pod中。
  4. 当数据包到达目标节点,会先解除IPIP tunnel的IP头,并根据目标节点的路由表转发到目标Pod中。

Pod间网络通信总结

简单来说,每一个Pod都有一个独立的IP地址,其他Pod看到的和自身看到的都是同一个IP地址。任意两个Pod之间都是可以直接通信。

但如果想要再进行简单的总结,就是:

  • 同一个pod中的容器使用localhost来完成网络通信,所以同一个pod内的容器不能使用同一个port。

  • 同一个节点的pod通过网桥实现互通,不同的容器运行环境使用不同的网桥,如是docker则使用docker bridge。

  • 跨节点的pod则通过节点上的路由表进行转发,转发到目标pod所在的节点,再在目标节点的路由表进行进一步的转发。而不同的CNI(Container Network Interface)网络插件有不一样的处理方式。

Service的网络实现

知道了Pod间如何实现网络互通后,Service就很好理解了。

在实际的使用场景中,Pod的生命周期是短暂的,随着版本迭代或者是扩缩容,Pod的IP地址会不断发生变化,所以我们往往不会使用固定的IP地址去访问Pod中的应用。因此,我们会为一个或者一组Pod创建一个统一的访问入口,以及因应实际需求设置不同的负载均衡策略,这就是Service。

kube-proxy简介

在Kubernetes集群中的每个节点,都会有kube-proxy这个组件,它会监听service以及pod的变化,完成自身的iptables规则或者是其他网络功能的配置。

kube-proxy可以有多种启动模式,下面使用iptables proxy mode举例:

在这个模式下,kube-proxy会通过kubernetes的apiserver获悉集群中Service或者是Endpoint或者Pod的变化,并根据具体的变化内容动态增减本机的iptable规则,把网络请求转发到Service的clusterIP和端口中,最终去到Service所关联的后端应用。

services-iptables-overview

集群内访问Service

假设集群内的一个client pod向集群内的一个Service请求数据,其主要步骤如下:

  1. 如果是使用域名去请求Service,首先会通过集群的Coredns去解析出Service的clusterIP,client pod就会拿着这个Service IP作为目标地址发起请求。
  2. 在节点的宿主机网络中,会使用kube-proxy所配置的iptables或者IPVS做一层拦截处理,确定要转发到哪个后端pod中,接着就到了前面提到的pod之间的网络通讯步骤了。

外部流量请求Service

当我们要向集群外暴露Service,可以有NodePort或者LoadBalancer等多种类型:

  • NodePort:即是把Service绑定到所在Worker Node的一个端口上,当节点宿主机接到这个端口的请求后,就转发到Service IP。
  • LoadBalancer:就是在NodePort的基础上再做一层转换,可以理解为云平台上的一个负载均衡实例,后端服务器就是Kubernetes集群中的Worker Node。

以上的这两种Service的暴露方式,都是通过Kubernetes集群中的Cloud Controller Manager实现统一管理。

当外部的流量对集群内的Service发起请求,会到达节点或者是LoadBalance的IP地址,最后会转发到一个具体的Worker Node的网络端口上。Worker Node会经过kube-proxy找到Service IP,从而找到后端的Pod IP。

Service的网络通信总结

  • 每个Service也会有一个clusterIP,可以是由集群分配或者是自己定义,同时会监听一个或者多个port,并会配置好对应的backend port。
  • 在主流的解决方案中,Service通过selector指定后端pod。
  • 集群内的每个Node通过kube-proxy的规则去确认每个访问service的网络请求最终要去到哪一个pod。

总结

理解了Pod之间的网络通信以及Service的网络实现后,基本上就可以覆盖到大多数常用的网络场景了。当遇到了网络不通或者服务请求失败的时候,就可以根据具体的现象去分析背后的网络配置是否存在异常。

自从开始编写关于容器和K8s相关的文章后,笔者也收到了多个朋友关于Kubernetes学习建议的咨询,和之前的文章类似,我的建议就是:

  • 以实际场景为起点,先建立一个简单的demo。
  • 在demo的基础上,预设不同的需求,并想办法实现。
  • 在实践过程中,留意各个场景的底层实现原理,并适当进行深挖。

而这篇文章,也正是在实践上面的建议。我已经搭出了一个基于Kubernetes的demo了,虽然已经实现了基础的功能,但在搭建的过程中,往往会遇到一些知其然不知其所以然的地方,就会留在后面做进一步的理解和巩固。

扩展阅读

本站文章:使用minikube模拟基于Kubernetes平台的容器化改造

Services, Load Balancing, and Networking

Service

Cluster Networking