gitbook/Go 语言项目开发实战/docs/415606.md
2022-09-03 22:05:03 +08:00

29 KiB
Raw Blame History

45基于Kubernetes的云原生架构设计

你好,我是孔令飞。

前面两讲我们一起看了云技术的演进之路。软件架构已经进入了云原生时代云原生架构是当下最流行的软件部署架构。那么这一讲我就和你聊聊什么是云原生以及如何设计一种基于Kubernetes的云原生部署架构。

云原生简介

云原生包含的概念很多,对于一个应用开发者来说,主要关注点是如何开发应用,以及如何部署应用。所以,这里我在介绍云原生架构的时候,会主要介绍应用层的云原生架构设计和系统资源层的云原生架构设计。

在设计云原生架构时,应用生命周期管理层的云原生技术,我们主要侧重在使用层面,所以这里我就不详细介绍应用生命周期管理层的云原生架构了。后面的云原生架构鸟瞰图中会提到它,你可以看看。

另外在介绍云原生时也总是绕不开云原生计算基金会。接下来我们就先来简单了解下CNCF基金会。

CNCF云原生计算基金会简介

CNCFCloud Native Computing Foundation云原生计算基金会2015年由谷歌牵头成立目前已有一百多个企业与机构作为成员包括亚马逊、微软、思科、红帽等巨头。CNCF致力于培育和维护一个厂商中立的开源社区生态用以推广云原生技术。

CNCF目前托管了非常多的开源项目其中有很多我们耳熟能详的项目例如 Kubernetes、Prometheus、Envoy、Istio、etcd等。更多的项目你可以参考CNCF公布的Cloud Native Landscape,它给出了云原生生态的参考体系,如下图所示:

什么是云原生?

CNCF官方在2018年发布了云原生v1.0,并给出了定义:

“云原生技术有利于各组织在公有云、私有云和混合云等新型动态环境中构建和运行可弹性扩展的应用。云原生的代表技术包括容器、服务网格、微服务、不可变基础设施和声明式API。 这些技术能够构建容错性好、易于管理和便于观察的松耦合系统。结合可靠的自动化手段,云原生技术使工程师能够轻松地对系统作出频繁和可预测的重大变更。”

简单点说云原生Cloud Native是一种构建和运行应用程序的方法是一套技术体系和方法论。云原生中包含了3个概念分别是技术体系、方法论和云原生应用。整个云原生技术栈是围绕着Kubernetes来构建的具体包括了以下核心技术栈

这里来介绍下这些核心技术栈的基本内容。

  • 容器Kubernetes的底层计算引擎提供容器化的计算资源。
  • 微服务:一种软件架构思想,用来构建云原生应用。
  • 服务网格建立在Kubernetes之上作为服务间通信的底座提供强大的服务治理功能。
  • 声明式 API :一种新的软件开发模式,通过描述期望的应用状态,来使系统更加健壮。
  • 不可变基础设施:一种新的软件部署模式,应用实例一旦被创建,便只能重建不能更新,是现代运维的基础。

43讲44讲我介绍了容器、服务网格和微服务这里再补充介绍下不可变基础设施和声明式API。

不可变基础设施Immutable Infrastructure的构想是由Chad Fowler 于 2013 年提出的。具体来说就是一个应用程序的实例一旦被创建就会进入只读的状态后面如果想变更这个应用程序的实例只能重新创建一个新的实例。通过这种模式可以确保应用程序实例的一致性这使得落地DevOps更加容易并可以有效减少运维人员管理配置的负担。

声明式API是指我们通过工具描述期望的应用状态,并由工具保障应用一直处在我们期望的状态。

Kubernetes的API设计就是一种典型的声明式API。例如我们在创建Deployment时在Kubernetes YAML文件中声明应用的副本数为2,即设置replicas: 2Deployment Controller就会确保应用的副本数一直为2。也就是说,如果当前副本数大于2Deployment Controller会删除多余的副本如果当前副本数小于2,会创建新的副本。

声明式设计是一种设计理念,同时也是一种工作模式,它使得你的系统更加健壮。分布式系统环境可能会出现各种不确定的故障,面对这些组件故障,如果使用声明式 API ,你只需要查看对应组件的 API 服务器状态,再确定需要执行的操作即可。

什么是云原生应用?

上面,我介绍了什么是云原生,接下来再介绍下什么是云原生应用。

整体来看,云原生应用是指生而为云的应用,应用程序从设计之初就考虑到了云的环境,可以在云上以最佳姿势运行,充分利用和发挥云平台提供的各种能力。具体来看,云原生应用具有以下三大特点:

  • 从应用生命周期管理维度来看使用DevOps和CI/CD的方式进行开发和交付。
  • 从应用维度来看,以微服务原则进行划分设计。
  • 从系统资源维度来看采用Docker + Kubernetes的方式来部署。

看完上面的介绍,你应该已经对云原生和云原生应用有了一定的理解,接下来我就介绍一种云原生架构实现。因为云原生内容很多,所以这里的介绍只是起到抛砖引玉的作用,让你对云原生架构有初步的理解。至于在具体业务中如何设计云原生架构,你还需要根据业务、团队和技术栈等因素综合考虑。

云原生架构包含很多内容,如何学习?

云原生架构中包含了很多概念、技术那么我们到底如何学习呢在前面的两讲中我分别从系统资源层、应用层、应用生命周期管理层介绍了云技术。这3个层次基本上构成了整个云计算的技术栈。

今天,我仍然会从这三个层次入手,来对整个云原生架构设计进行相对完整的介绍。每个层次涉及到的技术很多,这一讲我只介绍每一层的核心技术,通过这些核心技术来看每一层的构建方法。

另外,因为应用生命周期管理层涉及到的技术栈非常多,所以今天不会详细讲解每种生命周期管理技术的实现原理,但会介绍它们提供的能力。

除了功能层面的架构设计之外,我们还要考虑部署层面的架构设计。对于云原生架构的部署,通常我们需要关注以下两点:

  • 容灾能力容灾能力是指应用程序遇到故障时的恢复能力。在互联网时代对应用的容灾能力有比较高的要求。理想情况是系统在出现故障时能够无缝切换到另外一个可用的实例上继续提供服务并做到用户无感知。但在实际开发中无缝切换在技术上比较难以实现所以也可以退而求其次允许系统在一定时间内不可用。通常这个时间需要控制在秒级例如5s。容灾能力可以通过负载均衡、健康检查来实现。
  • 扩缩容能力扩缩容能力指的是系统能够根据需要扩缩容可以手动扩缩容也可以自动扩缩容。互联网时代对扩缩容能力的要求也比较高需要实现自动扩缩容。我们可以基于一些自定义指标例如CPU使用率、内存使用率等来自动扩缩容。扩容也意味着能够承载更多的请求提高系统的吞吐量缩容意味着能够节省成本。扩缩容能力的实现需要借助于负载均衡和监控告警能力。

容灾能力和扩缩容能力都属于高可用能力。也就是说,在部署层面,需要我们的架构具备高可用能力。

接下来,我就重点介绍下系统资源层和应用层的云原生架构设计,并简单介绍下应用生命周期管理层的核心功能构建。在介绍完架构设计之后,我还会介绍下这些层面的高可用架构设计。

系统资源层的云原生架构设计

先来看系统资源层面的云原生架构设计。对于一个系统来说系统资源的架构是需要优先考虑的。在云原生架构中当前的业界标准是通过Docker提供系统资源例如CPU、内存等通过Kubernetes来编排Docker容器。Docker和Kubernetes的架构我在43讲中介绍过,这里我主要介绍下系统资源层面的高可用架构设计。

基于Docker+Kubernetes的方案高可用架构是通过Kubernetes高可用架构来实现的。要实现整个Kubernetes集群的高可用我们需要分别实现以下两类高可用

  • Kubernetes集群的高可用。
  • Kubernetes集群中所部署应用的高可用。

我们来分别看下这两个高可用方案。

Kubernetes集群高可用方案设计

通过43讲的学习我们知道Kubernetes由kube-apiserver、kube-controller-manager、kube-scheduler、cloud-controller-manager、etcd、kubelet、kube-proxy、container runtime 8大核心组件组成。

其中kube-apiserver、kube-controller-manager、kube-scheduler、cloud-controller-manager、etcd通常部署在master节点kubelet、kube-proxy、container runtime 部署在Node节点上。实现Kubernetes集群的高可用需要分别实现这8大核心组件的高可用。

Kubernetes集群的高可用架构图如下

上面图片展示的方案中所有管理节点都部署了kube-apiserver、kube-controller-manager、kube-scheduler、etcd等组件。kube-apiserver均与本地的etcd进行通信etcd在三个节点间同步数据而kube-controller-manager、kube-scheduler和cloud-controller-manager也只与本地的kube-apiserver进行通信或者通过负载均衡访问。

一个Kubernetes集群中有多个Node节点当一个Node节点故障时Kubernetes的调度组件kube-controller-manager会将Pod调度到其他节点上并将故障节点的Pod在其他可用节点上重建。也就是说只要集群中有两个以上的节点当其中一个Node故障时整个集群仍然能够正常提供服务。换句话说集群的kubelet、kube-proxy、container runtime组件可以是单点的,不用实现这些组件的高可用。

接下来,我们来看下Master节点各组件是如何实现高可用的。先来说下kube-apiserver组件的高可用方案设计

因为kube-apiserver是一个无状态的服务所以可以通过部署多个kube-apiserver实例其上挂载负载均衡的方式来实现。其他所有需要访问kube-apiserver的组件都通过负载均衡来访问以此实现kube-apiserver的高可用。

kube-controller-manager、cloud-controller-manager和kube-scheduler因为是有状态的服务所以它们的高可用能力不能通过负载均衡来实现。kube-controller-manager/kube-scheduler/cloud-controller-manager通过leader-elect=true参数开启分布式锁机制来进行leader election。

你可以创建多个kube-controller-manager/kube-scheduler/cloud-controller-manager实例同一时刻只有一个实例能够获取到锁成为leader提供服务。如果当前leader故障其他实例感知到leader故障之后会自动抢锁成为leader继续提供服务。通过这种方式我们实现了kube-controller-manager/kube-scheduler/cloud-controller-manager组件的高可用。

当kube-apiserver、kube-controller-manager、kube-scheduler、cloud-controller-manager故障时我们期望这些组件能够自动恢复这时候可以将这些组件以Static Pod的方式进行部署这样当Pod故障时上述实例就能够自动被拉起。

etcd的高可用方案有下面这3种思路

  • 使用独立的etcd集群独立的etcd集群自带高可用能力。
  • 在每个Master节点上使用Static Pod来部署etcd多个节点上的etcd实例数据相互同步。每个kube-apiserver只与本Master节点的etcd通信。
  • 使用CoreOS提出的self-hosted方案将etcd集群部署在kubernetes集群中通过kubernetes集群自身的容灾能力来实现etcd的高可用。

这三种思路,需要你根据实际需要进行选择,在实际生产环境中,第二种思路用得最多。

到这里我们就实现了整个Kubernetes集群的高可用。接下来我们来看下Kubernetes集群中应用的高可用是如何实现的。

Kubernetes应用的高可用

Kubernetes自带了应用高可用能力。在Kubernetes中应用以Pod的形式运行。你可以通过Deployment/StatefulSet来创建应用并在Deployment/StatefulSet中指定多副本和Pod的健康检查方式。当Pod健康检查失败时Deployment/StatefulSet的控制器ReplicaSet会自动销毁故障Pod并创建一个新的Pod替换故障的Pod。

你可能会问当Pod故障时怎么才能避免请求被调度到已故障的Pod上造成请求失败这里我也详细介绍下。

在Kubernetes中我们可以通过Kubernetes Service或者负载均衡来访问这些Pod。当通过负载均衡来访问Pod时负载均衡后端的RSReal Server实例其实就是Pod。我们创建了多个Pod负载均衡可以自动根据Pod的健康状况来进行负载。

接下来我们主要看下这个问题当通过Kubernetes Service访问Pod时如何实现高可用

高可用原理如下图所示:

在Kubernetes中我们可以给每个Pod打上标签Label标签是一个key-value对例如label: app=Nginx。当我们访问Service时Service会根据它配置的Label Selector匹配具有相同Label的Pod并将这些Pod的endpoint地址作为其后端RS。

举个例子你可以看看上面的图片Service的Label Selector是Labelsapp=Nginx,这样就会选择我们创建的具有label: app=Nginx的3个Pod实例。这时候Service会根据其负载均衡策略选取一个Pod将请求流量转发过去。当其中一个Pod故障时Kubernetes会自动将故障Pod的endpoint从Service后端对应的RS列表中剔除。

由Deployment创建的ReplicaSet这时候也会发现有一个Pod故障健康的Pod实例数变为2,这时候跟其期望的值3不匹配就会自动创建一个新的健康Pod替换掉故障的Pod。因为新Pod满足Service的Label Selector所以新Pod的endpoint会被Kubernetes自动添加到Service对应的endpoint列表中。

通过上面这些操作Service后端的RS中故障的Pod IP被新的、健康的Pod IP所替换通过Service访问的后端Pod就都是健康的。这样就通过Service实现了应用的高可用。

从上面的原理分析中我们也可以发现Service本质上是一个负载均衡器。

Kubernetes还提供了滚动更新RollingUpdate机制来确保在发布时服务正常可用。这个机制的大致原理是在更新时先创建一个Pod再销毁一个Pod依次循环直到所有的Pod都更新完成。在更新时我们还可以控制每次更新的Pod数以及最小可用的Pod数。

接下来,我们再来看下应用层的云原生架构设计和高可用设计。

应用层的云原生架构设计

在云原生架构中,我们采用微服务架构来构建应用。所以,这里我主要围绕着微服务架构的构建方式来介绍。先和你谈谈我对微服务的理解。

从本质上来说微服务是一个轻量级的Web服务只不过在微服务场景下我们通常考虑的不是单个微服务而是更多地考虑由多个微服务组成的应用。也就是说一个应用由多个微服务组成多个微服务就带来了一些单个Web服务不会面临的问题例如部署复杂、排障困难、服务依赖复杂、通信链路长等等。

在微服务场景下除了编写单个微服务轻量级的Web服务之外我们更多是要专注于解决应用微服务化所带来的挑战。所以在我看来微服务架构设计包括两个重要内容

  • 单个微服务的构建方式;
  • 解决应用微服务化带来的挑战。

微服务实现

我们可以通过两种方式来构建微服务:

如果要解决应用微服务化带来的挑战,我们需要采用多种技术和手段,每种技术和手段会解决一个或一部分挑战。

综上在我看来微服务本质上是一个轻量级的Web服务但又包含一系列的技术和手段用来解决应用微服务化带来的挑战。微服务的技术栈如下图所示

不同的技术栈可以由不同的方式来实现,并解决不同的问题:

  • 监控告警、日志、CI/CD、分布式调度可以由Kubernetes平台提供的能力来实现。
  • 服务网关、权限验证、负载均衡、限流/熔断/降级可以由网关来实现例如Tyk网关。
  • 进程间通信、REST/RPC序列化可以借助Web框架来实现例如Gin、Go Chassis、gRPC、Sprint Cloud。
  • 分布式追踪可以由Jaeger来实现。
  • 统一配置管理可以由Apollo配置中心来实现。
  • 消息队列可以由NSQ、Kafka、RabbitMQ来实现。

上面的服务注册/服务发现有3种实现方式

  • 通过Kubernetes Service来进行服务注册/服务发现Kubernetes自带服务注册/服务发现功能。使用此方式,我们不需要额外的开发。
  • 通过服务中心来实现服务注册/服务发现功能。采用这种方式需要我们开发并部署服务中心服务中心通常可以使用etcd/consul/mgmet来实现使用etcd的较多。
  • 通过网关,来进行服务注册/服务发现。这种情况下可以将服务信息直接上报给网关服务也可以将服务信息上报到一个服务中心例如etcd中再由网关从服务中心中获取。

这里要注意原生的Kubernetes集群是不支持监控告警、日志、CI/CD等功能的。我们在使用Kubernetes集群时通常会使用一个基于Kubernetes开发而来的Kubernetes平台例如腾讯云容器服务TKE。

在Kubernetes平台中通常会基于一些优秀的开源项目进行二次开发来实现平台的监控告警、日志、CI/CD等功能。

  • 监控告警基于Prometheus来实现。
  • 日志基于EFK日志解决方案来实现。
  • CI/CD可以自己研发也可以基于优秀的开源项目来实现例如 drone。

微服务架构设计

上面我介绍了如何实现微服务,这里我再来具体讲讲,上面提到的各个组件/功能是如何有机组合在一起,共同构建一个微服务应用的。下面是微服务的架构图:

在上图中我们将微服务应用层部署在Kubernetes集群中在Kubernetes集群之上可以构建微服务需要的其他功能例如监控告警、CI/CD、日志、调用链等。这些功能共同完成应用的生命周期管理。

我们在微服务的最上面挂载负载均衡。客户端例如移动端应用、Web应用、API调用等都通过负载均衡来访问微服务。

微服务在启动时会将自己的endpoint信息通常是ip:port格式上报到服务中心。微服务也会定时上报自己的心跳到服务中心。在服务中心中我们可以监控微服务的状态剔除不健康的微服务获取微服务之间的访问数据等等。如果要通过网关调用微服务或者需要使用网关做负载均衡那我们还需要网关从服务中心中获取微服务的endpoint信息。

微服务高可用架构设计

我们再来看下如何设计微服务应用的高可用能力。

我们可以把所有微服务组件以Deployment/StatefulSet的形式部署在Kubernetes集群中副本数至少设置为两个更新方式为滚动更新设置服务的监控检查并通过Kubernetes Service或者负载均衡的方式访问服务。这样我们就可以不用做任何改造直接使用Kubernetes自有的容灾能力实现微服务架构的高可用。

云原生架构鸟瞰图

上面,我介绍了系统资源层和应用层的云原生架构设计,但还不能构成整个云原生架构设计。这里,我通过一张云原生架构鸟瞰图,来整体介绍下云原生架构的设计方案。

上图的云原生架构分为4层除了前面提到的系统资源层、应用层、应用生命周期管理层之外又加了统一接入层。接下来我来介绍下这些层在云原生架构中的作用。

在最下面的系统资源层,我们除了提供传统的计算资源(物理机、虚拟机)之外,还可以提供容器化的计算资源和高可用的存储服务。其中,容器化的计算资源是基于传统的物理机/虚拟机来构建的。

在云原生架构中我们更应该使用容器化的计算资源通过Docker容器技术来隔离并对外提供计算资源通过Kubernetes来编排容器。Docker + Kubernetes的组合使用可以构建出一个非常优秀的系统资源层。这个系统资源层自带了资源管理、容器调度、自动伸缩、网络通信、服务发现、健康检查等企业应用需要的核心能力。

在云原生时代这些系统资源除了具有容器化、轻量化的特点之外还越来越倾向于朝着Serverless化的方向去构建系统资源免申请、免运维按需计费具备极致的弹性伸缩能力并能够缩容到0。Serverless化的系统资源可以使开发者只聚焦在应用层的应用功能开发上而不用再把时间浪费在系统层的运维工作上。

在系统资源层之上,就可以构建我们的应用层了。云原生架构中,应用的构建方式,基本上都是采用的微服务架构。开发一个微服务应用,我们可以使用微服务框架,也可以不使用。二者的区别是,微服务框架替我们完成了服务治理相关功能,让我们不需要再开发这些功能。

在我看来,这一点有利有弊。好处当然是节省了开发工作量。至于坏处,主要有两方面:一方面,在实现方式和实现思路上,微服务框架所集成的服务治理功能并不一定是最适合我们的方案。另一方面,使用微服务框架还意味着我们的应用会跟微服务框架耦合,不能自由选择服务治理技术和方式。所以,在实际开发中,你应该根据需要,自行选择微服务的构建方式。

一般来说,一个微服务框架中,至少集成了这些服务治理功能:配置中心、调用链追踪、日志系统、监控、服务注册/服务发现。

再往上,我们就实现了统一接入层。统一接入层中包含了负载均衡和网关两个组件其中负载均衡作为服务的唯一入口供API、Web浏览器、手机终端等客户端访问。通过负载均衡可以使我们的应用在故障时能够自动切换实例在负载过高时能够水平扩容。负载均衡下面还对接了网关网关提供了一些通用能力例如安全策略、路由策略、流量策略、统计分析、API管理等能力。

最后我们还可以构建一系列的应用生命周期管理技术例如服务编排、配置管理、日志、存储、审计、监控告警、消息队列、分布式链路追踪。这些技术中一些可以基于Kubernetes集成在我们的Kubernetes平台中另一些则可以单独构建供所有产品接入。

公有云版云原生架构

上面我们提到,云原生架构涉及到很多的技术栈。如果公司有能力,可以选择自己开发;如果觉得人力不够、成本太高,也可以使用公有云厂商已经开发好的云原生基础设施。使用云厂商的云原生基础设施,好处很明显:这些基础设施专业、稳定、免开发、免运维。

为了补全云原生架构设计版图,这里我也介绍一个公用云版的云原生架构设计。那么,公有云厂商会提供哪些云原生基础设施呢?这里我介绍下腾讯云提供的云原生解决方案。解决方案全景如下图所示:

可以看到,腾讯云提供了全栈的云原生能力。

腾讯云基于底层的云原生能力提供了一系列的云原生解决方案。这些解决方案是已经设计好的云原生架构构建方案可以帮助企业快速落地云原生架构例如混合云解决方案、AI解决方案、IoT解决方案等。

那么,腾讯云底层提供了哪些云原生能力呢?我们一起来看下。

在应用层通过TSF微服务平台我们可以实现微服务的构建以及微服务的服务治理能力。另外还提供了更多的应用构建架构例如

  • Serverless Framework可以构建Serverless应用。
  • CloudBase云原生一体化应用开发平台可以快速构建小程序、Web、移动应用。

在系统资源层腾讯云提供了多种计算资源提供形态。例如通过TKE可以创建原生的Kubernetes集群通过EKS可以创建Serverless化的Kubernetes集群通过TKE-Edge可以创建能够纳管边缘节点的Kubernetes集群。此外还提供了开源容器服务平台TKEStackTKEStack是一个非常优秀的容器云平台在代码质量、稳定性、平台功能等方面都在开源的容器云平台中处于龙头地位也欢迎你Star。

在应用生命周期管理这一层提供了云原生的etcd、Prometheus服务。此外还提供了CLS日志系统供你保存并查询应用日志提供了云监控供你监控自己的应用程序提供了容器镜像服务TCR用来保存Docker镜像提供了CODING DevOps平台用来支持应用的CI/CD提供了调用链跟踪服务TDW用来展示微服务的调用链。

在统一接入层腾讯云提供了功能强大的API网关。此外还提供了多种Serverless化的中间件服务例如消息队列TDMQ、云原生数据库TDSQL等。

所有这些云原生基础设施,都有共同的特点,就是免部署、免运维。换句话说,在腾讯云,你可以只专注于使用编程语言编写你的业务逻辑,其他的一切都交给腾讯云来搞定。

总结

云原生架构设计包含了系统资源层、应用层、统一接入层和应用生命周期管理层4层。

在系统资源层可以采用Docker + Kubernetes的方式来提供计算资源。我们所有的应用和应用生命周期管理相关的服务都可以部署在Kubernetes集群中利用Kubernetes集群的能力实现服务发现/服务注册、弹性伸缩、资源调度等核心能力。

在应用层可以采用微服务架构来构建我们的应用。具体构建时我们可以根据需要采用类似Gin这种轻量级的Web框架来构建应用然后再实现旁路的服务治理功能也可以采用集成了很多服务治理功能的微服务框架例如 go-chassis、go-micro等。

因为我们采用了微服务架构为了能够将微服务的一些功能例如认证授权、限流等功能最大化的复用我们又提供了统一接入层。可以通过API网关、负载均衡、服务网格等技术来构建统一接入层。

在应用生命周期管理这一层,我们可以实现一些云原生的管理平台,例如 DevOps、监控告警、日志、配置中心等并使我们的应用以云原生化的方式接入这些平台使用这些平台提供的能力。

最后,我还介绍了腾讯云的云原生基础设施。通过腾讯云提供的云原生能力,你可以专注于使用编程语言编写你的业务逻辑,其他的各种云原生能力,都可以交给云厂商来帮你实现。

课后练习

  1. 思考下,服务注册/服务发现的3种实现方式中哪种方法适用于你的项目为什么
  2. 思考下,在设计云原生架构时,还需要考虑哪些重要的点?欢迎你在留言区分享。

欢迎你在留言区与我交流讨论,我们下一讲见。