转载请注明出处
总体逻辑架构在k8s+docker的基础上分为“三纵”、“四横”。
三纵”分别是控制器的公共服务列(非业务服务)、业务服务列以及外部依赖服务列(三方服务)。其中公共服务和业务服务都属于控制器自身的服务,也部署在同一个k8s集群中;而三方服务可以是控制器独占的服务也可以是与其它系统共享的服务,部署形式也不局限,只需要服务间访问可达。
三方服务这里只举一些典型的例子:
资源层的作用将应用层和南向层解耦,无论是应用层实例扩展还是南向层实例扩展都不需要对方感知。
对于南向层收到从设备报上来的数据如果要上报给应用层,则通过消息总线进行上报。
对于数据库选型这里不做特别限制,可以根据业务需要选择关系型/非关系型数据库,对用于做历史分析的数据可以考虑采用类似elasticsearch的数据库。
根据前面的假设,控制器系统的单表静态数据为百万级,所以这里暂不考虑数据存储层面的性能、高可用性等复杂问题。
从网络设计出发,也应当避免同一网络域出现强一致性的海量数据。
由于服务是运行在docker的容器中,所以整个系统对开发语言没有特别限制,要求所有的服务都打包成docker的镜像进行交付。
服务间的接口通过x-rpc(具体rpc框架选择只要满足语言无关性并且内部达成一致即可)定义。
开发依赖的包由统一的包管理文件进行管理(golang使用gomod进行管理,java使用pom管理,java的包通过团队maven仓进行管理)。
保持单容器单进程的设计,利用k8s的特性,可以帮助我们监控服务进程状态,并且当服务down掉后由k8s重新拉起容器。
而服务内部应当有“防呆”设计:
避免线程长期占有“锁”;
所有有状态的等待都有超时处理;
监控线程的意外退出(如java线程)。
控制器系统是基于k8s+docker的,所以可以认为系统部署的基础设施为k8s集群。
k8s集群天然具备可伸缩性,因此控制器系统的扩缩容在这不是一个复杂问题,不再赘述。
K8s自身没有提供容器网络的实现,对于跨POD间的通信,无外乎两种方案:一种是underlay直接互通,即通信的双方有彼此的路由信息并且该路由信息在underlay的路径上存在;另一种是overlay方案,通过vxlan隧道实现互通(方案的具体通信流程可以参考k8s的网络插件介绍)。
除了k8s POD间通信外,对于Agent还需要POD内容器与网络设备间进行通信,所以对于k8s集群的服务器需要支持双网卡,一个用来进行POD间通信,另一个与设备网络打通。
考虑到系统可用性,k8s的集群节点可以分布到多个园区,在部署服务时每个服务的多个实例可以分布到多个园区。
系统的访问通道分为外部通道和内部通道,内部通道是与设备建立连接的通道,属于内网;外部通道则通过统一网关访问k8s集群服务。
系统的架构原则继承云原声系统的设计理念:
面向分布式设计(Distribution):容器、微服务、API 驱动的开发;
面向配置设计(Configuration):一个镜像,多个环境配置;
面向韧性设计(Resistancy):故障容忍和自愈;
面向弹性设计(Elasticity):弹性扩展和对环境变化(负载)做出响应;
面向交付设计(Delivery):自动拉起,缩短交付时间;
面向性能设计(Performance):响应式,并发和资源高效利用;
面向自动化设计(Automation):自动化的 DevOps;
面向诊断性设计(Diagnosability):集群级别的日志、metric 和追踪;
面向安全性设计(Security):安全端点、API Gateway、端到端加密。
还有一些具体的补充:
服务无状态:即服务不对容器的磁盘数据有依赖,服务运行数据可以自恢复,通过持久化数据解决有状态的场景。这样做是确保系统的scale-out能力,同时解决服务进程失败后自恢复的问题。
系统内通信自闭环:系统内服务间通信不依赖外部服务(包括三方服务列),系统内的服务发现和寻址均依赖k8s的APIServer和DNS。这样做是提高系统的健壮性,在系统基础设施正常的情况下系统可用性得到保障。
服务间只有接口依赖,无状态依赖:不要让服务间感知对方是否“活着”。这样做是为了满足架构目标——避免系统复杂度随着系统内服务规模呈指数级增长,如果服务间有状态依赖,当服务的实例增加时,这种状态依赖也会成倍增长。
失败是必然的:服务的失败不可避免,不要多度设计抵制故障,在设计中尽可能考虑发生故障后阻止问题扩散并如何从故障中恢复。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。