陈凯悦,腾讯容器技术研发工程师,腾讯云TKE后台研发,腾讯云边缘容器核心开发成员。
边缘容器作为当前热门的研究方向,腾讯云容器团队在孜孜不倦做技术研究的同时,也希望能普惠到更多的云原生技术开发者们,为此推出【从0到1学习边缘容器系列】。上次我们推出了第一篇《边缘计算与边缘容器的起源》,这次我们和大家来聊聊边缘场景下的容器应用部署和管理。
大家对使用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值;
代码语言:txt复制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值;
代码语言:txt复制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
代码语言:txt复制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
代码语言:txt复制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。
后期展望
目前需要通过部署yaml使用此功能,之后我们将实现该功能的界面化操作,降低用户使用难度。