01
背景
当同一集群中有多个 VPC 时,就会存在不同的 Pod 使用相同的 IP。虽然 IP 间具有不同的 VXLAN 属性,但 K8S 组件层面是无法识别和区分出这种属性。这显然破坏了 K8S 的网络平面设计,所以需要解决一些额外的问题。
其一需要解决的是,K8S Service 的连通性问题。
众所周知,Kube-proxy 会在节点上增加 IPVS/IPTables 规则,以实现 K8S Service 功能。但由于 VPC 场景中 PodIP 的重复,则会导致 Kube-proxy 加的规则无效。
02
方案
经过深入讨论,我们决定使用 司内既有的 LVS服务 实现 K8S Service 功能,并对 Kube-proxy 进行替换。
使用司内 LVS 方案的原因,有以下几点:
一是司内 LVS 已经经过改造,对 VPC 场景早已适配支持,拿过来就可以用,无需容器侧再自行实现一番 VPC 的逻辑,降低成本和复杂度。
二是既有的容器 LB 功能,就是基于 LVS 实现的,此设计的可靠性和性能已经历过线上的考验。并且其很多设计和实现可以复用,无需重复造轮子。
三是相较于修改 Kube-proxy 源码而言,这种不破坏 k8s 原生组件的方式,更便于集群的维护和升级。
此方案需 LB 组件增加对 Service 资源的适配支持,将之 IP+Port 映射到 LVS 侧,以及 Service 所关联的 Pod 管理。另外,CNI 组件,也需要在节点侧拦截所有访问 Service 的流量,路由给 VPC 网络处理。
由此可解 多 VPC 场景下的 Service 连通性问题。
03
实现
角色及职责
云平台:UI 层、屏蔽细节
K8S:容器编排、提供状态驱动的系统
CNI:Pod网络路由管理
LBController:调协集群状态、对接及实现
虚拟化(VPC/LVS 等):底层实现
流量路径示意图
流程图
创建 Service
集群 Service CIDR 规划
众所周知,K8S 集群中 Service 的 IP 地址,由控制组件进行分配。二控制组件具体从哪个网段中分配及管理,可由管理员在搭建集群时进行配置。
因此,需要提前规划好 K8S 集群所使用的 Service CIDR。
对于当前场景,有以下要求:
Service CIDR 多集群间唯一,不能重复;
Service CIDR 由虚拟化 VPC 侧提供,并 配置好打通规则 以及 关闭DHCP(防止地址被DHCP占用) 等;
Service 的隔离性
在 K8S 中的 Service,可按照 用户类型 和 系统类型 对 Service 进行区分,不同类型的 Service 对隔离性的要求是不同的。
例如,用户类型 Service,要求实现 VPC 维度的隔离,仅能允许放行同一 VPC 中 Client 发起的请求访问。不通 VPC 之间的网络资源,一定要由强隔离性。
但系统类型的 Service 则不然,系统类型 Service 要求所有 VPC 的 Client 皆可访问。以 K8S 集群中的 KubeDNS/CoreDNS 服务为例,它通过 Service IP 为集群中 Pod 提供 DNS 服务,这就一定所有 Pod 都能访问到,才能维持 集群/业务 功能正常。
因此我们规定,携带业务属性的 Service 为业务 Service,反之则为系统 Service。以此对隔离性要求进行区分实现。
Service 对象设计
需要给 Service 附加上业务相关 VPC 的属性,用以标识出此 Service 所属。另外为了支持 LBC 进行 GC,所以需要增加 finalizer 字段,保证无资源泄露。
apiVersion: v1kind: Servicemetadata: annotations: # Region,例如 beijing、shanghai k8s.hulk.qihoo.net/region: "xxxx" # 平台维度的 项目 ID k8s.hulk.qihoo.net/relate-id: "xxxx" # VPC 维度的网络信息 - NetworkID k8s.hulk.qihoo.net/network-id: "xxxx" # VPC 维度的网络信息 - SubnetID k8s.hulk.qihoo.net/subnet-id: "xxxx" finalizers: # LBC 的 finalizer - containercloud.qihoo.net/finalizer labels: app: nginx-cpf name: nginx-cpf namespace: defaultspec: internalTrafficPolicy: Cluster ipFamilies: - IPv4 ipFamilyPolicy: SingleStack ports: - name: http port: 80 protocol: TCP targetPort: 80 selector: app: nginx-cpf sessionAffinity: None type: ClusterIP
Service 功能兼容
TargetPort
Service 中的 targetPort 除了可以写具体端口号外,还允许用户填入 端口名。
LBC 组件需要根据用户输入的端口名,去 Pod 模板中进行查找,然后转换为具体的端口号进行处理。
apiVersion: v1kind: Servicespec: ports: - name: https port: 443 protocol: TCP targetPort: https
apiVersion: v1kind: Podspec: containers: - name: metrics-server ports: - containerPort: 4443 hostPort: 4443 name: https protocol: TCP
如上,示例 Service 中的 targetPort 为 https,对应到 Pod 中的 端口号为 4443。
Service Kubernetes
apiVersion: v1kind: Servicemetadata: name: kubernetes namespace: defaultspec: clusterIP: 100.101.0.1 clusterIPs: - 100.101.0.1 internalTrafficPolicy: Cluster ipFamilies: - IPv4 ipFamilyPolicy: SingleStack ports: - name: https port: 443 protocol: TCP targetPort: 443 sessionAffinity: None type: ClusterIP
可以看到,这个 Service 是 ClusterIP 类型,但不存在 selector 字段,需要根据 Endpoint 做特殊处理。
依赖方
容器云平台
虚拟化 VPC、LVS
04
总结
我们通过以上的设计及实现,成功落地了 多 VPC 场景下的 K8S 集群 Service 功能,为用户提供有效支撑。但是,在功能和性能上还存在进步空间,例如 Service 的 session affinity 功能未支持等。后续仍需继续保持演进。
云服务器和专有网路VPC服务,也可以咨询我们~
更多技术干货,
请关注“360智汇云开发者”👇
360智汇云官网:https://zyun.360.cn(复制在浏览器中打开)
更多好用又便宜的云产品,欢迎试用体验~
添加工作人员企业微信👇,get更快审核通道+试用包哦~