大家对使用 Kubernetes 管理应用已经比较熟悉,但是边缘场景下的应用部署和管理是否存在不同的需求呢?本文将和大家一起探讨边缘场景下常见的容器应用管理方案。
1 边缘简单服务场景
在笔者接触过的边缘需求中部分用户业务场景比较简单,如:拨测服务。这种场景的特点是用户希望在每个节点部署相同的服务,并且每个节点起一个 pod 即可,这种场景一般推荐用户直接使用 daemonset 部署。关于 daemonset 的特点和使用方式读者可以阅读 kubernetes 官方文档。
2 边缘单站点部署微服务场景
第二种场景是部署边缘 SAAS 服务,由于涉及客户商业机密,此处暂不举例。用户会在一个边缘机房内部署一整套微服务,包括账号服务、接入服务、业务服务、存储及消息队列,服务之间借助 kubernetes 的 dns 做服务注册和发现。这种情况下客户可以直接使用 deployment 和 service,其中最主要的困难不在于服务管理而是边缘自治能力。
关于 deployment 和 service 的使用方式读者可以阅读 kubernetes 官方文档,关于 TKE@edge 边缘自治能力我们将会在后续推出相关文章介绍。
3 边缘多站点部署微服务场景
场景特点:
•边缘计算场景中,往往会在同一个集群中管理多个边缘站点,每个边缘站点内有一个或多个计算节点。
•希望在每个站点中都运行一组有业务逻辑联系的服务,每个站点内的服务是一套完整的微服务,可以为用户提供服务
•由于受到网络限制,有业务联系的服务之间不希望或者不能跨站点访问
常规方案:
1. 将服务限制在一个节点内
该方案特点:
•服务以 daemonset 方式部署,以便每个边缘节点上均有所有服务的 pod。如上图所示,集群内有 A、B 两个服务,以 daemonset 部署后每个边缘节点上均有一个 Pod- A 和 Pod-B。
•服务通过 localhost 访问,以便将调用链锁定在同一个节点内。如上图所示,Pod- A 和 Pod- B 之间以 localhost 访问。
该方案缺点:
•每个服务在同一个节点内只能有一个 Pod,这是由于 daemonset 工作机制所限,对于需要在同一节点上运行多个 Pod 的服务来说这个限制极为不便。
•Pod 需要使用 hostnetWork 模式,以便 Pod 之间可以通过 localhost+port 访问。这意味着需要用户很好地管理服务对资源使用,避免出现资源竞争,如端口竞争。
2. 服务在不同站点叫不同的名字
该方案特点:
•相同服务在不同站点叫不同的名字,以便将服务间的访问锁定在同一个站点内。如上图所示,集群内有 A、B 两个服务,在 site- 1 中分别命名为 svr-A-1、Svc-B-1,在 site- 2 中分别命名为 svr-A-2、Svc-B-2。
该方案缺点:
•服务在不同站点名字不同,因而服务之间不能简单地通过服务名 A 和 B 来调用,而是在 site- 1 中用 Svc-A-1、Svc-B-1,在 site- 2 中用 Svc-A-2、Svc-B-2。对于借助 k8s dns 实现微服务的业务极为不友好。
场景痛点:
1.k8s 本身并不直接针对下述场景提供方案。
•首先是众多地域部署问题:通常,一个边缘集群会管理许多个边缘站点(每个边缘站点内有一个或多个计算资源),中心云场景往往是一些大地域的中心机房,边缘地域相对中心云场景地域更多,也许一个小城市就有一个边缘机房,地域数量可能会非常多;在原生 k8s 中,pod 的创建很难指定,除非使用节点亲和性针对每个地域进行部署,但是如果地域数量有十几个甚至几十个,以需要每个地域部署多个服务的 deployment 为例,需要各个 deployment 的名称和 selector 各不相同,几十个地域,意味着需要上百个对应的不同 name,selector,pod labels 以及亲和性的部署 yaml,单单是编写这些 yaml 工作量就非常巨大;
•services 服务需要与地域关联,比如音视频服务中的转码和合成服务,要在所属地域内完成接入的音视频服务,用户希望服务之间的相互调用能限制在本地域内,而不是跨地域访问。这同样需要用户同样准备上百个不同 selector 和 name 的本地域 deployment 专属的 service 的部署 yaml;
•一个更复杂的问题是,如果用户程序中服务之间的相互访问使用了 service 名,那么当前环境下,由于 service 的名称各个地域都不相同,对于用户而言,原来的应用甚至都无法工作,需要针对每个地域单独适配,复杂度太高。
2. 另外,使用方为了让容器化的业务在调度方案上与之前运行在 vm 或者物理机上的业务保持一致,他们很自然就想到为每个 pod 分配一个公网 IP,然而公网 IP 数量明显是不够用的。
综上所述,原生 k8s 虽然可以变相满足需求 1),但是实际方案非常复杂,对于需求 2)则没有好的解决案。
为解决上述痛点,TKE@edge 开创性地提出和实现了 serviceGroup 特性,两个 yaml 文件即可轻松实现即使上百地域的服务部署,且无需应用适配或改造。
SeviceGroup 功能介绍
ServiceGroup 可以便捷地在共属同一个集群的不同机房或区域中各自部署一组服务,并且使得各个服务间的请求在本机房或本地域内部即可完成,避免服务跨地域访问。
原生 k8s 无法控制 deployment 的 pod 创建的具体节点位置,需要通过统筹规划节点的亲和性来间接完成,当边缘站点数量以及需要部署的服务数量过多时,管理和部署方面的极为复杂,乃至仅存在理论上的可能性;与此同时,为了将服务间的相互调用限制在一定范围,业务方需要为各个 deployment 分别创建专属的 service,管理方面的工作量巨大且极容易出错并引起线上业务异常。
ServiceGroup 就是为这种场景设计的,客户只需要使用 ServiceGroup 提供的 DeploymentGrid 和 ServiceGrid 两种 tkeedge 自研的 kubernetes 资源,即可方便地将服务分别部署到这些节点组中,并进行服务流量管控,另外,还能保证各区域服务数量及容灾。
ServiceGroup 关键概念
1. 整体架构
NodeUnit
•NodeUnit 通常是位于同一边缘站点内的一个或多个计算资源实例,需要保证同一 NodeUnit 中的节点内网是通的
•ServiceGroup 组中的服务运行在一个 NodeUnit 之内
•tkeedge 允许用户设置服务在一个 NodeUnit 中运行的 pod 数量
•tkeedge 能够把服务之间的调用限制在本 NodeUnit 内
NodeGroup
•NodeGroup 包含一个或者多个 NodeUnit
•保证在集合中每个 NodeUnit 上均部署 ServiceGroup 中的服务
•集群中增加 NodeUnit 时自动将 ServiceGroup 中的服务部署到新增 NodeUnit
ServiceGroup
•ServiceGroup 包含一个或者多个业务服务
•适用场景:1)业务需要打包部署;2)或者,需要在每一个 NodeUnit 中均运行起来并且保证 pod 数量;3)或者,需要将服务之间的调用控制在同一个 NodeUnit 中,不能将流量转发到其他 NodeUnit。
•注意:ServiceGroup 是一种抽象资源,一个集群中可以创建多个 ServiceGroup
2. 涉及的资源类型
DepolymentGrid
DeploymentGrid 的格式与 Deployment 类似,<deployment-template> 字段就是原先 deployment 的 template 字段,比较特殊的是 gridUniqKey 字段,该字段指明了节点分组的 label 的 key 值;
apiVersion: tkeedge.io/v1
kind: DeploymentGrid
metadata:
name:
namespace:
spec:
gridUniqKey: <NodeLabel Key>
<deployment-template>
ServiceGrid
ServiceGrid 的格式与 Service 类似,<service-template> 字段就是原先 service 的 template 字段,比较特殊的是 gridUniqKey 字段,该字段指明了节点分组的 label 的 key 值;
apiVersion: tkeedge.io/v1
kind: ServiceGrid
metadata:
name:
namespace:
spec:
gridUniqKey: <NodeLabel Key>
<service-template>
3. 使用示例
以在边缘部署 nginx 为例,我们希望在多个节点组内分别部署 nginx 服务,需要做如下事情:
1)确定 ServiceGroup 唯一标识
这一步是逻辑规划,不需要做任何实际操作。我们将目前要创建的 serviceGroup 逻辑标记使用的 UniqKey 为:zone。
2)将边缘节点分组
这一步需要使用 TKE@edge 控制台或者 kubectl 对边缘节点打 label,tke@edge 控制台操作入口如下图:
3)界面在集群的节点列表页,点击”编辑标签“即可对节点打 label
•借鉴”整体架构“章节中的图,我们选定 Node12、Node14,打上 label,zone=NodeUnit1;Node21、Node23 打上 label,zone=NodeUnit2。
•注意:上一步中 label 的 key 与 ServiceGroup 的 UniqKey 一致,value 是 NodeUnit 的唯一 key,value 相同的节点表示属于同一个 NodeUnit。同一个 node 可以打多个 label 从而达到从多个维度划分 NodeUnit 的目的,如给 Node12 再打上 label,test=a1
•如果同一个集群中有多个 ServiceGroup 请为每一个 ServiceGroup 分配不同的 Uniqkey
4)部署 deploymentGrid
apiVersion: tkeedge.io/v1
kind: DeploymentGrid
metadata:
name: deploymentgrid-demo
namespace: default
spec:
gridUniqKey: zone
template:
selector:
matchLabels:
appGrid: nginx
replicas: 2
template:
metadata:
labels:
appGrid: nginx
spec:
containers:
– name: nginx
image: nginx:1.7.9
ports:
– containerPort: 80
apiVersion: tkeedge.io/v1
kind: ServiceGrid
metadata:
name: servicegrid-demo
namespace: default
spec:
gridUniqKey: zone
template:
selector:
appGrid: nginx
ports:
– protocol: TCP
port: 80
targetPort: 80
sessionAffinity: ClientIP
5)部署 serviceGrid
可以看到 gridUniqKey 字段设置为了 zone,所以我们在将节点分组时采用的 label 的 key 为 zone,如果有三组节点,分别为他们添加 zone: zone-0, zone: zone-1 ,zone: zone- 2 的 label 即可;这时,每组节点内都有了 nginx 的 deployment 和对应的 pod,在节点内访问统一的 service-name 也只会将请求发向本组的节点。
另外,对于部署了 DeploymentGrid 和 ServiceGrid 后才添加进集群的节点组,该功能会在新的节点组内自动创建指定的 deployment 和 service。