lyelyelye 2019-11-04
编者按:关于容器网络的解决方案业界已经有较多的讨论,笔者无意继续赘述。K8S及其网络模型体现了鲜明的解耦设计思想,采用SDN技术实现K8S容器网络,并与相应的生态组件形成SDN监管控一体化解决方案,可以更好地提高整个系统的运营水平,更有效地提升企业的核心竞争力。本文拟抛砖引玉,从K8S的网络实现入手,重点阐述SDN在容器网络中的应用价值。欢迎业界联系云杉同仁交流讨论。
综述:K8S的基本机制是以“对象-控制器”架构模式为基础设计与实现的。组件API server与etcd的主要功能是实现对各种资源对象REST风格的增删改查CRUD、盯Watch操作。组件controller-manager包括了各种内置控制器,其功能是确保整个系统处于预期的状态。这种数据存储与应用逻辑分离的模式是各功能组件实现无状态服务架构的关键。K8S集群针对基本操作单元Pod对象的处理主要是通过scheduler和kubelet实现的。实际上我们可以把scheduler看作是集群层面的Pod对象控制器,把kubelet理解为是节点层面Pod对象的创建与维护的控制器。
K8S场景下实现Pod互联等的容器网络特别是其数据转发面遵循并适用网络技术的一般性原则与实现。当前主流的容器网络基本解决方案主要包括以Calico为代表的基于经典的三层路由/Netfilter模型的实现,和以OpenShift的SDN实现为代表的二层SDN/OVS模型的实现。而容器网络的特殊性则主要体现在管理控制面实现、以及基于K8S扩展机制的SDN控制器云原生部署等方面。K8S及其网络模型体现了鲜明的解耦设计思想,不论是在K8S与网络组件之间,还是在容器网络与Pod接入容器网络之间等各方面。
狭义地讲SDN的核心技术主要包括数据转发面特别是流表流水线的设计和相应的基于SDN控制器平台的应用的设计与实现等。SDN控制器与各节点上的SDN代理相配合完成系统容器网络的项目租户、网络子网路由、安全策略与服务、SLA/QoS的管控,以及监控可视化等各项操作。而CNI-Plugin的主要功能则是在kubelet创建Pod时将之挂接到容器网络上。在K8S场景下,SDN的价值同样体现在与各种生态组件的集成,实现一体化监管控解决方案,可以更好地提升整个系统的运营水平。
K8S集群提供了以Pod为基本操作单元的容器化应用的部署、维护、扩展等功能。主要由主节点上的API server、etcd、controller-manager、scheduler等,以及工作节点上的kubelet、容器运行时等功能组件构成。命令行kubectl可以在主节点上也可以部署在远程运行,并通过API server调用与K8S交互。etcd集群可以采用与主节点分离的方式进行部署。另外,即使是主节点不参与业务Pod的分配与创建,也需要运行有一个kubelet实例。
kube-apiserver:API server通过Web Service端点处理函数的注册与相应的访问请求相关联,实现REST机制对资源对象的增删改查CRUD盯Watch等的操作功能,是系统对外访问的统一入口。而K8S集群内部各功能组件之间的互动也主要是通过API server 的Watch机制实现的。API server本身实现了无状态服务架构模式。Web Service端点以API Group来管理分组和版本适配,分组包括核心组、扩展组、其他的auto scaling组、metrics组等。而Proxy类API server接口的作用是代理REST请求,并转发给相应节点的kubelet进行实际的操作处理。此外API server还提供有各种web hook扩展机制。
etcd集群:etcd是Go语言编写的基于raft分布式协议的高可用key-value存储系统,作为API server的后台用于K8S存储各种资源对象包括外部扩展定制资源对象(CRD – Custom Resource Definition)等配置与状态信息,支持典型的消息发布与订阅机制,即Watch机制。etcd是K8S组件API server等实现无状态服务架构的基础。K8S集群内任何对资源对象的增删改查CRUD盯Watch操作等都通过API server的storage接口统一与etcd交互,各组件不会直接操作etcd。
控制器模式是K8S各组件功能实现的基本框架,其基础主要是基于etcd特性在API server内部实现的List-Watch机制和相应的client-go客户端平台库函数。
List-Watch机制:该机制是基于etcd本身的Watch特性的,属于API server对资源对象CRUD操作的体系化扩展。API server内部向etcd发起的Watch只能订阅某个对象的创建、删除事件,无法设置过滤条件的,不能针对对象的属性进行过滤;而API server实现对外部各种K8S功能组件发起的Watch请求的处理,并能够针对对象属性进行过滤。在集群运行过程中组件向API server发送REST请求进行Watch订阅,告诉API server对什么资源对象及发生什么样的变化事件感兴趣。而每一个Watch操作的生命周期对应相应的HTTP REST请求的生命周期。
库函数client-go包括了访问API server的客户端,以及支持控制器实现所需的相关平台机制List-Watch的客户端informer和事件队列workqueue等。这样控制器的设计与实现只需专注应用逻辑,只需提供对象的事件回调函数和相应的队列处理功能即可。
控制器架构模式:一个典型的控制器实现通过API server的List-Watch机制的客户端informer与API server保持交互,跟踪特定的资源对象的状态与变化。对象变化的事件由informer调用相应的callback完成一些基本的处理,然后把相关的变更及对象信息放到workqueue里面。控制器的应用逻辑实现在worker协程里面。控制器可启动多个worker协程来处理workqueue里的对象事件。根据对象的期望状态和当前系统的实际情况进行相应的处理,并通过clients向API server发送行动请求。实际上也是通过对其他相关下游资源对象的设置,以实现整个集群向期望的状态演进。此外,为缓解各组件模块对API server的访问压力、提高处理性能,控制器的内部实现也都采用了缓存机制的支持。
kube-controller-manager:K8S内置了一系列的控制器,基本上都是嵌入在controller-manager进程里的。其中,Replication Controller的功能是确保集群中有且仅有指定个数的Pod实例在运行,可以通过调整RC中的副本数量来实现系统扩容或缩容,通过改变RC中的Pod模板来实现系统的滚动升级;Endpoints Controller中的Endpoints表示某个Service对应的所有Pod副本的访问地址。此控制器的功能是通过监听Service和对应的Pod副本的变化,负责生成和维护所有Endpoints对象。而工作节点上的kube-proxy就是通过监听Service和Endpoints来配置相应的数据面实现相应的转发和负载均衡功能。我们可以理解Endpoints Controller是K8S除Pod以外最重要的资源对象服务Service的集群层面的控制器,而kube-proxy则是相应节点层面的控制器;Namespace Controller的功能是在后台实现优雅地删除某Namespace下的Service Account、RC、Pod等资源对象及此Namespace本身;Service Accounts Controller的功能是为每个Namespace维护一个默认的Service Account,并与Token Controller配合实现K8S集群内部Pod访问API server的认证功能。
K8S除本身内置的这些控制器外,也支持各种用户自定义扩展。控制器可以运行在K8S主节点上,也可以运行在工作节点上;可以运行在K8S集群内,甚至运行在集群外。
基本上K8S的资源对象与控制器都是相互对应、配合起来完成相应的功能的。但如果注意到的话,在controller-manager中并没有针对Pod对象操作的控制器。实际上K8S决定把Pod放到哪个节点上是由组件scheduler按照相应的策略决策的,而Pod的实际创建与维护是由kubelet完成的。所以我们可以理解为scheduler是Pod集群层面的控制器,kubelet则是Pod节点层面的控制器。
具体的来说,就是scheduler通过API server提供的Watch等接口,监听并获取到未调度的Pod和节点等的相关信息,通过过滤和优先级算分对节点筛选,选择出最合适的节点,将Pod与此节点绑定,并把结果通过API server存储到etcd中。在相应工作节点上的kubelet会去实际创建Pod的SandBox,调用CNI-Plugin进行相应的网络配置挂接SandBox网络名字空间到容器网络上,创建和运行Pod所属的init容器,最终创建和运行Pod的常规业务容器。
业界所谓的“There is no such thing called container networking”的说法更多的是从连接容器或Pod的网络的一般性而言的。或者说从二层交换、三层路由转发面的角度认为连接容器的网络与连接VM或BM等的网络没有本质的区别,即容器网络特别是其数据转发面仍遵循和适用网络技术的一般性原则与实现。在这个层面上“容器网络”有点类似“业务网络”的概念之与物理网络、虚拟网络。但另一方面,特别是从服务发现与负载均衡、管理模式与部署实施等的角度看,K8S的“容器网络”是有其特殊性的。这主要体现在Pod生命周期到服务Service映射的动态性、SDN控制器云原生模式部署、Pod挂接网络的Plugin机制、以及安全策略的实现等方面。
针对当前主流的典型的容器网络解决方案的分析,特别从数据转发面特征的角度,可以认为主要包括以Calico为代表的基于经典的三层路由、Netfilter模型的实现,和以OpenShift的SDN及Neutron等为典型代表的二层SDN/OVS模型的实现等。
三层路由/Netfilter模型:这类实现的优点是网络拓扑直观易懂,扁平化结构,可扩展性强;容器间三层网络隔离,不需要解决arp风暴问题;基于经典的、成熟的Netfilter/IPtables与Linux路由技术,包转发与细粒度策略控制效率高等。
二层SDN/OVS模型:这类模式的转发面是指Overlay二层和流表流水线设计与实现采用OVS,支持与各种Underlay模式的对接,可扩展实现DVR、广播抑制特性等。这是当前比较典型和成熟的转发面SDN模式方案;三层采用网络名字空间的Linux路由器;安全策略以及NAT功能采用Netfilter/IPtables实现。
基于这种参考架构实现的方案的优点是兼容了传统网络二层交换、三层路由、策略控制,可以通过VLAN实现多租户隔离;可以采用典型的SDN套路对容器网络的访问做到细粒度控制;Overlay与Underlay的解耦设计,具备良好的混合云、公有云的互联能力;逻辑结构清晰,可适应不同场景和规模的网络方案;有成熟的开发部署运维实践经验,稳定性经过生产环境验证。这种类型的典型有OpenShitf的SDN转发面方案,从某种角度来看它与Neutron模型的转发面实现有很多类似的地方。
SDN容器网络转发面设计与实现的关键因素,特别是对于虚拟网络方案而言,是选择以Linux路由、Netfilter为基础,叠加基于OVS的多租户支持等的SDN扩展。一般而言以OVS为主体的SDN容器网络解决方案,也是需要有三层互联的功能配合的。而对于简单扁平的三层容器网络方案是可以没有二层OVS特性支持的。
相应的管理控制面的设计与实现主要也分为基于BGP的分布式管理控制面和基于SDN控制器的集中式管理控制面等。
K8S实现了典型的平台与应用解耦的架构设计,提供了种针不同场景的扩展手段。除了kubelet的CNI-Plugin网络挂接扩展外,与SDN容器网络相关的扩展机制还有Operator模式和Aggregated Server模式。
Operator扩展模式:这种模式基于CRD机制和定制控制器实现。CRD是通过apiextensions-apiserver实现的。K8S集群可以动态定义并注册用户资源对象的schema,可以像操作其他内置资源对象一样通过kubectl创建和访问这些自定义对象。定制化控制器是用户可以在集群内部署的定制开发的控制器,与定制化资源CRD等结合起来解决特定的应用问题。这种模式比较适合作为SDN控制器云原生部署的实施方案。
Aggregated-server扩展模式:这种方式的设计思路是通过增加API的扩展性,使K8S的用户可以扩展自己的API服务器,而不需要更改核心代码。这种方式的优点还在于可以将开发工作分阶段进行,新的API先在单独的API服务器中开发,在稳定之后再把它集成在一起。这种模式比较适合K8S监控系统的实现与部署。apiextensions-apiserver和替代早期版本K8S监控Heapster的metrics-server都是采用这种模式实现的。
库函数client-go包括了访问API server的客户端,以及支持控制器实现所需的相关平台机制List-Watch的客户端informer和事件队列workqueue等。这样控制器的设计与实现只需专注应用逻辑,只需提供对象的事件回调函数和相应的队列处理功能即可。
SDN控制器部署:K8S的SDN控制器的部署首先是创建SDN控制器和节点代理及CNI-Plugin部署和运行所需的服务账号、角色和角色绑定。定义角色的访问对象涉及哪些资源以及相应许可的操作。定义访问的主体,即服务账号,在特定名字空间中对相应角色、对象的访问授权;然后以Deployment的方式在主节点上部署SDN控制器,可采用ConfigMap实现SDN控制器的相关配置管理;在每个集群工作节点上以DaemonSet的方式部署SDN代理,其中的CNI-Plugin安装脚本容器负责在部署时安装CNI-Plugin插件和相应的网络配置等;以及通过CRD机制扩展相关的资源对象管理,实现相应的定制控制器进行K8S与SDN间的封装、对接、适配。
kubelet:kubelet运行在集群的每个节点上,负责节点层面的Pod管理,周期执行Pod状态的探测和上报功能,并提供相应的http服务代理的各种Pod或容器操作等功能。
在系统运行过程中,kubelet的配置协程通过监听Watch apiserver获取分配给该节点的Pod。Pod工作协程池通过事件队列接收从配置协程发来的Pod创建请求,调用相应的Pod管理、容器管理、设备管理等Manager模块,最终通过容器运行时(以及dockershim)与Docker和CNI-Plugin互动完成Pod的创建。PLEG(Pod Lifecycle Event Generator)协程会持续地通过容器运行时监视本节点Pod的状态,与相应的期望值进行比较,当发现状态变化时生成相应事件给Pod工作协程实现相应的同步处理以保证Pod正常工作。
除监听apiserver外,kubelet还支持监听本地静态配置文件创建Pod的功能。K8S集群主节点上的apiserver、controller-manager、scheduler以及etcd就是以这种静态Pod的方式由kubelet启动运行的。
CNI-Plugin:Docker在创建容器的过程中支持四种网络模式。bridge模式会为每个容器分配网络名字空间、IP地址等,并连接到本地的bridge上;host模式会共用主机的网络名字空间;none模式会分配给容器独立的网络名字空间,但不会对容器进行相关的网络配置;container模式是将新创建的容器和已经存在的某个容器共享网络名字空间。
kubelet在创建Pod过程中,首先通过容器运行时调用Docker以none模式创建Pod的SandBox,即pause容器,也就是在这个时间点kubelet调用CNI-plugin来进行相应的接口、IP、路由等网络相关配置任务的,即挂接Pod到容器网络上;然后调用Docker以container模式创建并运行init容器,共享SandBox的网络名字空间;最终调用Docker也是以container模式创建常规业务容器,同样共享SandBox的网络名字空间。
所以CNI-Plugin是在kubelet创建Pod时被直接或通过dockershim调用来为Pod的SandBox做网络配置的。原则上CNI-Plugin不包含“容器网络”主体项目租户、网络子网等的创建和管理等功能。从这个意义上我们可以理解CNI-Plugin的作用主要是kubelet在创建Pod时将之挂接到“容器网络”上。
广义地讲SDN的基本功能是把可形式化的、繁琐的各种网络的监管控操作自动化以支持运维人员直观便捷地获取各种信息、执行各种操作、解决各种问题。狭义地讲SDN的核心技术主要包括OpenFlow模式等交换机的转发面流表流水线设计和相应的基于SDN控制器平台的应用开发等方面。
SDN转发面OpenFlow流表实际上可以理解为是对二层交换mac表、三层路由表、防火墙ACL策略表等的一种抽象。所以从原理上讲OpenFlow/OVS交换机可以特定化承载传统的二层交换、三层路由、以及各种ACL等功能。SDN这种转发面从特例到一般再到特例的演进过程中,其价值更体现在可以支持更多种千变万化的应用上,这就是SDN转发面流表流水线设计的核心。通常SDN网络解决方案的套路就是针对特定问题设计相应的流水线,并基于SDN控制器平台开发相应的应用配合起来形成整体的解决方案。OpenShift的SDN容器网络以及Neutron架构中的DVR、SFC就是比较典型的案例。而现阶段多数SDN解决方案的转发面通常是在经典的二层交换、三层路由基础之上扩展细粒度流表设计实现相应的Overlay/Underlay转发、策略、服务链功能等的。
SDN控制器主要包括平台和应用两个层面。平台通常采用基于分布式内存存储的某开源方案,控制器集群遵循CAP理论约束,支持基于任务和功能的负载分割,实现相应的二层交换、三层路由、拓扑管理、规则下发、状态统计等功能。而各种应用的开发是与相应的转发面、流表流水线设计相配合实现相应的网络互联、安全隔离、服务链、网络SLA/QoS、以及监控分析可视化与闭环控制等功能。
SDN控制器的南向接口对接各种网元设备,可以是物理的、也可以是虚拟的;北向接口提供各种网络主体的管理操作,可以通过各种封装适应不同的应用场景与不同的云管平台对接;西向接口可以是与K8S各种相关资源对象的动态互动适配;而东向接口可以作为CNI-Server提供给kubelet的CNI-Plugin完成将Pod挂接到容器网络的相关操作。
SDN这种架构适用于物理Fabric网络的管控和/或服务器Overlay虚拟网络的管控;适用于OpenStack、vSphere、K8S的网络管控;适用于DCN、WAN,也适用于混合云;适用于网络主干的管控、资源池的管控,也适用于边缘互联的管控。
一个典型的生产环境K8S和SDN容器网络部署除划分有各个网络平面外,服务器节点也可以拆分为不同的功能组。其中主节点组承担K8S集群的Master角色,主要运行apiserver、controller-manager、scheduler组件等;基础节点组属于K8S集群的工作节点,通过设置节点标签,只用于部署系统基础服务,包括注册仓库、三层路由、监控可视化、SDN控制器等;业务节点组也属于工作节点,用于运行业务系统的各种应用容器Pod。如果以OpenStack为参照的话,主节点组对应OpenStack的控制器节点;基础节点组部分功能对应OpenStack的网络节点;业务节点组则对应OpenStack的计算节点。
SDN解决方案包括在K8S场景下的应用可以支持业务应用弹性扩展、更新迭代;自动化可提高系统运行稳定、有效的运维排障;细粒度控制可以提高访问和数据的安全性。采用SDN技术实现K8S容器网络,SDN控制器基于K8S平台扩展机制的云原生部署,并与相应的生态组件形成SDN监管控一体化解决方案,可以更好地提高整个系统的运营水平,更有效地提升企业的核心竞争力。
###host字段指定授权使用该证书的etcd节点IP或子网列表,需要将etcd集群的3个节点都添加其中。cp etcd-v3.3.13-linux-amd64/etcd* /opt/k8s/bin/