当前位置: 首页 > news >正文

Kubernetes架构原则和对象设计

云原生学习路线导航页(持续更新中)

  • 快捷链接
    • Kubernetes常见问题解答

本文从 Google Borg系统的架构设计开始,深入讲解Kubernetes架构及组件的基本原理

1.什么是云计算

1.1.传统行业应用

  • 假设有10台服务器,两个应用。
  • 小规模管理下
    • 每个应用分别部署在5台服务器上,就ok了
    • 但是出现故障时,需要人工下掉节点,重新配置应用等,涉及大量人工手动操作
  • 微服务架构盛行之后
    • 一个中大型公司,可能会有数千上万个服务器,或数千上万的应用实例
    • 此时人工进行应用的部署将变得不可想象。一定会出现大量的纰漏
  • 为了满足需求,出现了云计算的概念

1.2.云计算到底是什么

  • 云计算是对 网络资源、存储资源的 一种抽象。
  • 假设有5000台机器,那么可以做以下操作:
    • 1.把这5000台机器从网络上打通,形成一个集群;
    • 2.维护一个控制平面,把所有机器的计算资源、存储资源等都抽象出来,比如有 1w个CPU、1wGB内存。
    • 3.控制平面还维护了所有机器的状态,哪些机器能参与计算,哪些不能,哪些是up,哪些是down等
    • 这样,就形成一个庞大的资源池
  • 对于业务来说,不用管自己的业务跑到哪台机器,业务只需要告诉 控制平面,业务需要多少CPU、多少Memory,控制平面就会自动选择合适的节点把应用跑起来。
  • 这个控制平面,就是云计算平台。

1.3.云计算平台的分类

在这里插入图片描述

  • 云计算平台 主要分为两类:
    • 以OpenStack为典型的虚拟化平台
    • 以谷歌Borg系统为典型的基于进程的作业调度平台

1.3.1.以OpenStack为典型的虚拟化平台

  • 虚拟化平台其实发展很平滑,最早大家都在做大型机、小型机,计算机的计算能力遵循摩尔定律,18个月会将芯片的性能提高一倍。此时应用架构都是单体的
  • 后来发现摩尔定律逐渐失效了,发展到一定阶段就达到瓶颈了,因为计算能力到了一定上限就很难突破了。
  • 另一方面,应用架构开始从单体变向微服务架构,就出现了一个问题:
    • 一台物理机,资源可能很多,但是软件的需求很少。
    • 因此一台机器就会跑很多个应用,来提高服务器的使用效率
    • 可是这么做,应用的隔离性就变得很差
  • 为解决这个问题,就出现了虚拟化技术,将一台物理机切割为不同的虚拟机,在不同虚拟机中去部署应用
  • OpenStack是虚拟化云平台的代表,它是一个云平台管理的项目,它不是一个软件,而是由几个主要的组件组合起来,为公有云、私有云和混合云的建设与管理提供软件。
  • 目前依旧有很多公司在用OpenStack

1.3.2.以谷歌Borg系统为典型的基于进程的作业调度平台

  • 谷歌Borg系统完全没有走虚拟化的路,而是基于进程去做调度。
  • Borg也是利用容器相关技术实现,比如利用cgroups实现资源限制,早期Borg使用了Chroot技术做隔离,当然现在也可以利用namespace技术了

2.什么是Borg

在这里插入图片描述

2.1.Borg简介

  • Borg是谷歌最主要的集群管理系统,拥有数十万台机器,运行着数以万计的作业。
  • Borg通过将准入控制、高效的任务打包、过度承诺和机器共享与流程级性能隔离结合起来,实现了高利用率。
  • 它支持具有运行时特性的高可用性应用程序,这些运行时特性可以最小化故障恢复时间,并且调度策略可以降低相关故障的概率。
  • Borg通过提供声明性作业规范语言、名称服务集成、实时作业监控以及分析和模拟系统行为的工具,简化了用户的使用。
  • Kubernetes 直接继承自 Borg。 在 Google 的很多从事 Kubernetes 的开发人员以前都是 Borg 项目的开发人员。 他们在 Kubernetes 中结合了 Borg 的最佳创意,并试图解决用户多年来在 Borg 中发现的一些痛点。

2.2.Borg支持两类应用

  • Borg支持两类应用:
    • Production应用:LongRunningService,即长时间在线的应用,比如Gmail、GoogleDocs、WebSearch
    • NonProduction应用:离线作业

2.2.1.Production应用

  • Production应用,指长时间在线的应用,即我们日常接触的在线服务,如Gmail、GoogleDocs、WebSearch
  • 对在线服务来说,高可用性是命根子,必须保证任何时候都是可访问、可使用的
  • 因此在任何时候,都要满足Production应用的资源需求,保障可用性

2.2.2.NonProduction应用

  • NonProduction应用:即离线作业,不需要长时间在线
  • 比如:
    • 电信业务,白天会有很多的支付账单,电信需要把订单数据和收费数据做对账。所以每天半夜就会在Linux上使用Cron job的方式开启离线作业做统计。
    • 这种离线作业,可以按定时、按资源的需求、资源的利用率等情况来自行决定。有些离线作业,可以在有资源的时候跑一下,没有资源的时候先暂停。
  • NonProduction应用的目标:
    • 把整体的资源利用率提上去
    • 因为既然上了云,从公司经营角度来说,就一定要给公司降本,因此混部才是唯一出路

2.3.Borg系统的特性

在这里插入图片描述

  • 关于Google Borg,有一篇论文,论文中详细解释了Borg的特性。
    • Large-scale cluster management at Google with Borg
  • Borg为什么开源成为Kubernetes
    • Google为什么会发那篇paper,是想把这份技术公开出来,希望自己内部也能去做技术迭代,把Borg系统不能解决的一些问题解决掉
    • 因此Borg也是kubernetes的一个指导原则

2.3.1.Borg的特性

  • 资源利用高
    • 资源的额外开销少:类似容器技术,没有虚拟化,所有计算资源全部
    • 有混部:在线作业一般都会有资源需求的波峰波谷,机器在请求波谷时资源其实是浪费的,所以Borg通过混部实现在波谷时跑一些短时离线作业,进而提高资源利用率。(空的时候跑,跑完再把资源还回来,或者在线业务激增时直接抢占non-prod应用的资源)
  • 服务器共享,在进程级别做隔离
    • Borg早期用的 chroot jail做隔离,现在基本上也是通过namespace
      那还有就是
  • 应用高可用,故障恢复时间短
    • 后面会详细讲 borg和kubernetes 如何保证应用高可性
  • 调度策略灵活
    • 后面会详细讲 Borg和kubernetes有哪些调度策略
  • 应用接入和使用方便
    • 提供了完备的 job 描述语言(声明式API),服务发现,实时状态监控和诊断工具

2.3.2.Borg的优势

  • 对外隐藏了资源管理、调度和故障处理这些细节,也是云平台的一个优势
    • 声明式API特性,使得一切的业务需求都定义一个对象,这个对象发给控制平面,由控制平面去执行。
  • 实现应用的高可靠和高可用
  • 足够弹性,支持应用跑在成千上万的机器上

2.4.Borg基本概念

在这里插入图片描述

  • Workload
    • prod应用:在线任务
    • non-prod应用:离线任务
  • Cell,即集群
    • 在Borg中,一个集群就叫一个Cell
  • Job 和 Task
    • 一个Job是一个基本的调度单位,相当于kubernetes的Pod
    • 一个Task就是Job内部的一个作业,相当于kubernetes的Container
  • Naming
    • Borg系统的 服务注册、服务发现的机制。
    • 任何的微服务平台肯定要负责提供服务之间的调用关系,就需要服务注册、服务发现的机制
    • Borg系统 通过Borg Name Service 为 Borg 系统中的每个应用提供一个域名,那么 Borg 系统的所有应用都可以使用域名进行互相访问了

2.5.Borg系统架构

学习Borg的架构,会发现和kuebrnetes有很多的共通性。
假设有一天我希望做一套自己的云平台,那它的架构基本上也是八九不离十的

在这里插入图片描述
在这里插入图片描述

  • 如上图,即为Borg系统的架构图
    • Cell:就是这个集群,假设这个集群有5000台机器
    • BorgMaster
      • BorgMaster就是集群的管理节点,接收用户的指令,然后去做调度,把作业发到每个计算节点上面去
      • 假设从5000台机器中选5台作为master,则剩下的4995台就是它的计算节点
      • 在管理节点上面,需要安装额外的管理组件
        • persistent store:基于Paxos协议的数据库。Paxos是分布式键值对存储的一种协议,用于确保分布式一致性
        • UI组件:负责接收请求
        • Scheduler 调度器:负责将作业调度到具体的计算node上运行
      • 因此,BorgMaster整个运作流程即为:UI组件接收到请求,存储起来后,通过调度器下发到计算节点去运行。
    • Mini Node
      • 下面的每个蓝色立方体,就是一个Mini Node,它们参与计算。
      • 集群初始化的时候,这些节点上面都是空的,没有任何的作业,但他们每个都安装了一个叫 Borglet 的组件。
      • Borglet 是在每台机器上的一个代理,负责把BorgMaster下发到本节点的进程跑起来,并汇报应用状态
  • 调度策略
    • Worst Fit(最差适应):
      • Worst Fit 即 永远找最空闲 的节点来运行任务。
      • 即在所有可用节点中选择剩余资源最多的节点,以便能够容纳更多的任务。
      • 这种策略可以减少资源碎片化,但可能导致资源利用率不高。
    • Best Fit(最佳适应):
      • Best Fit 策略是选择最佳匹配的节点来运行任务。
      • 即在所有可用节点中选择剩余资源最接近任务需求的节点,以便能够最大程度地利用资源。
      • 这种策略可以提高资源利用率,但可能导致资源碎片化。
      • 简单来说,Best Fit就是把尽量多的应用往一个节点上叠,叠满了再去做下一个。这样的话,整个集群里面就永远是在理想状况下一半忙的一半闲的。公有云中,闲的那些可以通过收缩节点,踢出集群来降低费用,节省成本;私有云中,甚至可以把那些节点关机。
    • Hybrid(混合):
      • Hybrid 策略是一种综合利用 Worst Fit 和 Best Fit 的调度策略。
      • 它根据任务的资源需求和集群中的资源情况,动态选择使用 Worst Fit 或 Best Fit 策略来分配任务。
      • 这种策略可以在资源利用率和资源碎片化之间取得平衡。
  • 调度优化的玩法特别多
    • 比如 找局部最佳节点:
      • 假设一个集群里面有1万个节点,调度时最好的方式就是遍历这1万台机器,找一个最佳的。
      • 但有时候可能要求没有那么高,不要求那么完美。就可以其中选10台或100台,从这些机器中找一个最佳的,也可以提升调度效率
      • 不过这种能力,目前kubernetes不支持,很多公司会自己做,比如阿里自己做了
    • 再比如当有大量的作业要来调度,可以做一些批次调度,来提高效率

注:从这里可以看到,一个系统出现它并不是偶然的,一定是很多以前的经验,把这些思想带到新的系统上来,然后再用新的思维,新的技术手段去实现,这一般是一个系统的变革或者迭代。而为什么kubernetes现在发展前景这么好?包括它API的定义、model的定义等,后面会详细说。

2.6.Borg如何保证高可用?

2.6.1.运行在Borg系统上的应用如何保证高可用?

在这里插入图片描述

  • 保证鸡蛋不在同一个篮子里:多副本冗余部署、跨故障域部署、跨可用区部署、跨城市部署等等
  • 保证幂等性:幂等性就是容忍重复提交的场景。
    • 对于异步系统,我们经常会一个作业发出去,系统响应收到后就结束了,但是客户端有时候会重复提交。
    • 所谓的密等性,就是不管你的这个请求提交多少次,对提供服务的服务器端,处理都是一样的

2.6.2.Borg控制平面如何保证自身高可用?

在这里插入图片描述

2.7.Borg如何提高资源利用率

2.7.1.Borg提高资源利用率

在这里插入图片描述

  • 在kubernetes集群中,默认会给node设置pod上限110个,
    • 可以在kubelet启动命令中增加–max-pods参数,重启kubelet 服务,修改node的pod上限。
      • 可以参考:https://www.cnblogs.com/cheyunhua/p/18067849
    • 查看kubernetes集群node的pod上限
      kubectl describe node vm-226-235-tencentos | grep Capacity -A 10
      

在这里插入图片描述

  • 那么为什么要设置上限呢,不设置行不行,只要有资源,就让pod运行?

    • 跑在node上的应用,是需要定期巡检的,需要知道这些作业的状态,太多的pod会让巡检压力变大
    • 110个的默认值,大概是基于一些经验得出的数据,综合考量了资源利用率、管理成本。
  • 关于资源利用率,谷歌后来又发了一篇paper,叫 Autopilot

    • Autopilot: workload autoscaling at Google
    • Autopilot 是通过动态的调整作业的资源需求来完成更高的资源利用率,后面讲应用落地的时候会再讲,包括HPA和VPA等

2.7.2.Borg提高资源利用率的实现

在这里插入图片描述

  • Borg提高资源利用率的方法
    • 允许用户先设置资源limit,但是在实际运行中,Borg会动态监控应用实际的资源使用量,作出冗余分配,或者回收未用到的资源
  • 为什么在用户设置limit后还要动态监控?
    • 因为一个新开发的应用,它实际上会使用多少资源,研发和运维都是很难确认的
    • 一般会通过线上的访问量,高峰时段的QPS等,拿着QPS去乘以一个Buffer(比如50或60),拿着这个数据去对应用做压测,看它的CPU和memory会占多少
    • 然后以这个为数值作为参考去设置的资源limit
  • Borg在资源申请时的动态计算
    • 应用对资源申请的时候,Borg本身也会做一些动态计算
    • 比如你可以设的很高,但是接下来Borg会去监控你的资源利用率。在启动的300秒以后,Borg会一直做评估,如果你的真实使用率只有上面绿色框这么点,而你申请了黄色框这么多,那么意味着中间的这一部分都是可以回收的
    • 不过Borg会保守回收,他不会把绿色之外的都回收,而是会留一部分保留资源,让应用应对高峰期,剩下的其他资源就回收了。
  • 目前在腾讯云这边,这些功能都已经做好了。所以使用公有云和自建云,最大的差异就是各种保障性功能公有云都做好了,自建云的话都需要自己搞

2.8.Borg如何实现隔离性

在这里插入图片描述

  • 其实不止Borg系统,整个计算机领域中,都认为CPU是可压榨资源,Memory是不可压榨资源
    • CPU是分时复用的,假设说CPU不够,应该分给应用A 80ms的,只分给了40ms,其他被别人占用了。从应用程序的角度来说,只是感觉应用慢一点,也不会有太大的问题,可能是用户多了1ms、2ms的,感受也不是很深。这种就是可压榨资源,因此对可压榨资源不需要关照的特别厉害,偶尔超一下没关系的。
    • 但是对于Memory内存来说就不一样了,内存一旦超限,大部分情况下就会OOM,所以我们对内存要格外关照,这种资源就是不可压缩资源。

3.什么是Kubernetes(K8s)

3.1.认识Kubernetes

在这里插入图片描述

  • 上图是Kubernetes worker计算节点的架构
    • 在每一个计算节点上面,都会运行一个kubelet
    • kubelet会通过Docker本身的interface去起一个个的容器
    • 容器之间依然是通过namespace和cgroup做安全隔离和性能隔离
  • Kubernetes的功能大部分沿袭了Borg
    • 比如说基于容器的应用部署、维护,滚动升级,实现负载均衡,基于服务发现实现了跨集群、跨地域的调度,自动伸缩等
  • Kubernetes可以承载有状态服务和无状态服务
    • 关于云原生应用,其实更推荐把应用做成无状态的,这样才能更好的利用kubernetes平台的弹性伸缩、跨az部署升级等能力
    • 有状态应用一般是千人千面的,不同的中间件,它的内在机理可谓千差万别,没有办法做成一个通用的组件,所以针对一个有状态的中间件,一般会为其单独开发一个Operator,而且一般也需要这个应用的专家才能维护
    • 要使用有状态应用的话,建议先去社区搜一搜,可能就会有人已经做出来了,可以二次开发

3.2.Kubernetes代码结构

在这里插入图片描述

在这里插入图片描述

  • api包

    • api包里面有个open-api-spec
    • open-api-spec里面有个swagger.json
      • swagger一般就是做API声明的,API声明一般会提供一个json,作为接口文档
  • build:所有构建相关的脚本

  • cmd:

    • 所有应用程序的main方法入口,二进制的编译入口
    • kubernetes是一个项目,这个项目下可以编译出好多个binery二进制可执行文件,就是不同的组件。另外kubernetes支持交叉编译,通过Makefile可以编译出不同CPU架构的可执行文件,如amd、windows等
    • 其中的每一个目录都是一个应用模块。每个应用模块中都应该有一个options目录,用于作入参校验等。如kube-apiserver目录下:
      在这里插入图片描述
  • pkg:各种组件功能的核心逻辑
    在这里插入图片描述

    • api、apis:对象的定义
    • 每种组件都有一个独立的目录

3.3.命令式vs声明式 系统

在这里插入图片描述

  • 现在系统大部分会被分为两种模式:
    • 命令式
    • 声明式
  • 命令式(Imperative System)
    • 一般就是交互式的系统,它会关注如何做
    • 比如说我在一个操作系统里面,我敲一个ls,我来看系统会给我什么反馈,基于他的反馈决定我下一次怎么做,这就是命令式
    • 命令式系统需要人工全程参与,根据上一次的结果,决定下一条命令怎么敲
    • 可以类比电视遥控器换台,不喜欢的话再换
  • 声明式(declarative system)
    • 我只约定我需要做什么,不关心系统是怎么做的,即面向终态的
    • 可以类比空调遥控器,空调的温度设定为25摄氏度,我期望最终变成25度,但是过程我不管
  • 二者对比
    • 命令式系统一定是响应非常快,或者比较简单的系统,要求立刻给出反馈
    • 声明式系统更适合复杂系统,比如说一个复杂任务,可能20分钟才能做完,过程中不阻塞,等做完后给出个通知提示就好。或者任何时候我要去查询的时候,你只需要把实时的状态返回即可
  • kubernetes就是一种声明式系统
    • 用户通过定义资源的spec,表达自己的期望状态。Kubernetes通过资源的status,表示实际状态。如何把状态调整到spec,由Kubernetes系统完成,不需要用户做指导和介入。
    • kubernetes能够变成业界标准的原因主要有两个。
      • 其一:kubernetes只是定义了一堆厂商中立的、通用的API,并且提供了一套框架来支撑这些API,维护最核心的功能。
        • kubernetes使用这些通用的API和框架,企图解决业界的一些通用问题,比如高可用怎么做、滚动升级怎么做、故障转移怎么做、扩缩容怎么做等。kubernetes给出的解决方案,和任何的厂商实现、任何的技术实现都没有太大关系,因此从这个角度来看,这种项目一定是一个长寿项目,生命周期一定非常长。
        • 对于不同的厂商来说,你用什么样的架构(arm、x86…)、用什么样的网络,用什么样的存储,这是你厂商要去解决的,你自己去选plugin。其实每个开源的plugin后面可能都围绕着一个厂商
        • kubernetes把这些东西剥离开,自己只解决最核心的通用问题,这是它最厉害的地方。
    • 其二:kubernetes系统足够灵活,任何人任何方案都可以和其对接,你自己提供一个CA网络插件,你有可能就成了一个创业公司,给整个生态都带来了机会,所以大家也愿意跟你一块发展。

3.4.kubernetes声明式系统包含哪些资源

在这里插入图片描述

  • Node
    • Kubernetes的本质就是把一堆节点组成一个集群,如何把几百上千个节点 组成一个集群,涉及集群管理的功能。
    • Kubernetes抽象了一个Node的概念
    • Node的名称即为主机名,node资源中记录了当前节点的状态:CIDR是什么、状态怎么样、一共有多少资源、可分配资源数量…
  • Namespace
    • 为了把多个用户或者多个项目的对象隔离开,Kubernetes抽象了一个Namespace的概念
    • 比如一个操作系统,不同的文件目录,可以设置不同的权限,进行文件隔离。Namespace就相当于一个虚拟目录,Kubernetes可以为不同Namespace设置不同的访问权限,进行访问控制。
    • 当然有些对象,比如Node是为整个集群提供服务的,不属于任何的namespace,因此Kubernetes的对象被分为:Namesapce对象、NonNamespace对象
  • Pod
    • 前面提到的都是管控层面的资源,真正用来描述应用实例的核心对象是Pod。Pod对象的出现打通了基础架构和应用接入的两个维度
    • 以前存在的问题:假设 现在使用了OpenStack集群,OpenStack只管理到操作系统层,应用人员只管到应用部署,上下两层是割裂的。难以控制应用要部署到哪个集群上,那如果要对openStack集群进行升级,就很难知道哪些节点可以一起重启,重启的时候会影响哪些应用,因此使得我基础架构层面的升级变得非常困难。
    • Kubernetes使用Pod打通了基础架构和应用接入的两个维度,应用层面只需要将自己的代码构建成容器镜像,然后资源、存储等基础架构的设置 使用 Pod的统一API进行联动,就可以控制 应用 跑在哪些节点上面。也可以随时查看某个节点上正在运行哪些应用,就可以知道重启节点会影响哪些应用了。
  • Service
    • Pod完成对应用的部署,但是还没有发布出去。想要发布一个服务,一定需要负载均衡、服务发现、服务治理等能力,Service应运而生。

3.5.Kuberntes系统架构

  • 下图即为Kubernetes的架构, 乍一看去,跟BOG系统没有什么区别,因此可以看出Borg真的是Kubernetes的前身

  • 下图是一个Kuberntes集群,对应Borg的一个Cell
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • 控制平面组件(Master节点)

    • API Server
      • 整个Kubernetes系统的API网关,其实本质上就是一个Web服务,逻辑并不复杂,但是apiserver的地位非常重要,所有的Kubernetes组件都不会直接通信,而是通过API Server进行间接通信
      • API Server会对 所有的请求 进行 准入控制、鉴权等,然后对etcd数据库进行crud
    • etcd
      • 基于Raft协议的 Watchable(后面会讲)分布式键值对存储,所有资源的信息都存储在这里。
    • Scheduler
      • Scheduler 是控制平面的组件, 负责监视新创建的、未指定运行节点(node)的 Pods, 并选择节点来让 Pod 在上面运行。
      • 具体操作:当Scheduler发现etcd中有尚未调度的pod,就会从健康可调度node中选择一台,修改pod的node字段。之后对应node上的kubelet就会调用容器运行时接口,在自己node上创建pod了
    • Controller Manager
      • 一个好的云平台,一定有大量自动化逻辑,Controllers其实就是帮我们的资源实现大量自动化逻辑的位置。
      • controller-manager负责运行控制器进程。从逻辑上讲, 每个控制器都是一个单独的进程,但是为了降低复杂性,它们都被编译到同一个可执行文件,并在同一个进程中运行。
      • controller-manager包含了非常多的Controller,每个Controller都会监听自己关心的那部分资源,并负责将资源调谐到 spec中期望的状态
  • 计算节点组件(Worker节点)

    • Kubelet
      • 每个节点上都会跑一个Kubelet,负责上报 节点状态,pod状态、pod资源使用情况等信息到ApiServer,由ApiServer写入etcd。
      • 同时,Kubelet也会和apiserver交互,当发现 有pod被 Schedule 调度到自己所在node时,就会调用一系列标准接口,拉起pod的进程,并为之挂载网络和存储。
      • 厂商无关的标准接口:容器运行时(CRI)、容器网络标准(CNI)、容器存储标准(CSI)
    • Kube-Proxy(可选)
      • kube-proxy 实际上就是用来实现Service的一部分,是集群中每个节点(node)上所运行的网络代理。
      • kube-proxy 维护节点上的一些网络规则, 这些网络规则会允许从集群内部或外部的网络会话与 Pod 进行网络通信。
      • 如果操作系统提供了可用的数据包过滤层,则 kube-proxy 会通过它来实现网络规则。 否则,kube-proxy 仅做流量转发。
  • 上述组件并不是kubernetes所包含的所有组件,更详细的请参考:Kubernetes官网架构介绍

4.Kubernetes组件原理简介

4.1.etcd

4.1.1.etcd简介

在这里插入图片描述

  • ETCD本身是一个有状态的应用,基于Raft协议的分布式存储
  • 单节点存储的缺点
    • 如果数据库是单节点存储,那效率一定是很高的,但是会带来数据丢失的风险,万一磁盘坏了,数据就没了。
    • 因此就需要考虑数据备份,数据备份后安全性又需要保证,可以看出单节点存储本身会有很多问题。
  • 分布式存储
    • 那么有没有一种方式,可以让数据安全性提高一个等级呢?
    • 分布式存储,即使用多台服务器存储同一份数据,一个节点坏了,还有其他拷贝呢,这样的话数据的安全性就得以保证了。
    • 但是分布式存储需要解决两个问题:多副本的数据一致性、某个节点宕机后如何继续工作。Raft协议解决了这两个问题:
      • 分布式存储需要保证数据的一致性,写数据的时候需要 在多个存储中同时写入,使大家的数据状态都是一致的。
      • 节点宕机后整个应用还可以继续工作
    • etcd 集群使用 Raft 协议保障多节点集群状态下的数据一致性。etcd 是使用 Go 语言对 Raft 协议一种实现方式。
    • Etcd 中文文档
  • ETCD的应用场景
    • 服务发现,有点类似于concul的服务发现机制
    • 共享配置:相当于把它当成一个键值分布式数据库
    • 监听机制:
      • 一般的数据库,给它一个查询,它给你返回一个结果就结束了。但是在分布式系统里面经常需要用到消息队列实现监听,希望只在某件事情发生时才进行通知。
      • 对kubernetes的控制器来说,如果一直轮询请求apiserver来获取 资源的变化,实时性不好,而且对etcd本身的并发能力要求特别高
      • ETCD可以充当消息队列的角色,只在 数据发生变化时,通知 对应的监听者。
      • 实现方式:当一个客户端去访问ETCD数据库的时候,可以不是通过get的方式,而是watch的方式建立一个长连接,当数据发生变化的时候再来通知。这种方式减少了数据库的压力,而且听起来这就是一个消息中间件,巧妙地解决了组件和组件之间的协调关系。

4.1.2.etcd基本使用

在这里插入图片描述

  • kubernetes中。演示下etcd访问数据方式
    # 声明个别名
    [root@VM-226-235-tencentos ~]# alias ks='kubectl -n kube-system'# 获取etcd pod
    [root@VM-226-235-tencentos ~]# ks get pods | grep etcd
    NAME                                           READY   STATUS    RESTARTS   AGE
    etcd-vm-226-235-tencentos                      1/1     Running   0          223d# 进入pod内部[pod中没有bash命令,所以使用sh进入]
    [root@VM-226-235-tencentos ~]# ks exec -it etcd-vm-226-235-tencentos bash
    kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
    OCI runtime exec failed: exec failed: unable to start container process: exec: "bash": executable file not found in $PATH: unknown
    command terminated with exit code 126[root@VM-226-235-tencentos ~]# ks exec -it etcd-vm-226-235-tencentos sh
    kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
    sh-5.0## 查看etcd的主进程,发现没有ps命令
    sh-5.0# ps -ef
    sh: ps: command not found# 直接使用etcdctl也可以的,先指定要使用的 etcd API 版本为 v3
    sh-5.0# export ETCDCTL_API=3# 通过与 etcd 服务器建立安全连接,获取以斜杠(/registry/pods)开头的所有键的列表,而不返回与这些键关联的值# --endpoints 指定 etcd 服务器的地址和端口。本例中 etcd 服务器位于 localhost 的 2379 端口上# --cert、--key:指定用于进行 TLS 客户端身份验证的证书文件、私钥文件路径# --cacert:指定用于进行 TLS 服务器身份验证的 CA 证书文件路径# get --keys-only:具体要执行的操作,且只返回键(key),不返回与键关联的值(value)# --prefix:指定要获取的键的前缀
    sh-5.0# etcdctl --endpoints https://localhost:2379 --cert /etc/kubernetes/pki/etcd/server.crt --key /etc/kubernetes/pki/etcd/server.key --cacert /etc/kubernetes/pki/etcd/ca.crt get --keys-only --prefix /registry/pods
    /registry/pods/default/my-pod/registry/pods/default/mysql-pod/registry/pods/default/nginx-deployment-585449566-f9zt2/registry/pods/default/nginx-deployment-585449566-gtw92/registry/pods/default/nginx-deployment-585449566-kzdgv# 对etcd中的一个键值对,添加watch【以ns==default下的mysql-pod为例】# 该命令执行完,会建立一个长连接,控制台会进入实时展示的状态# 需要另外开一个窗口,编辑mysql-pod的yaml,查看watch机制的通知。
    sh-5.0# etcdctl --endpoints https://localhost:2379 --cert /etc/kubernetes/pki/etcd/server.crt --key /etc/kubernetes/pki/etcd/server.key --cacert /etc/kubernetes/pki/etcd/ca.crt watch --prefix /registry/pods/default/mysql-pod
    
  • 下面演示watch的效果
    在这里插入图片描述
  • apiserver如何利用etcd的watch机制,实现控制器的watch?
    • 首先apiserver会对etcd中所有的数据,进行watch,并把watch到的数据添加一份缓存
    • 同时,apiserver也对外提供watch机制,所有的kubernetes组件都可以 建立长连接 watch自己感兴趣的资源,当资源发生变化的时候,通过长连接将数据变更事件发往各个组件
      • kubernetes组件首先会采用List机制,获取资源数据,然后以 watch机制 获取增量变化
    • 当一个组件watch到资源有变化的时候,要做什么处理,就是自己的代码逻辑了

4.2.APIServer

4.2.1.APIServer简介

在这里插入图片描述

  • apiserver的watch缓存
    • 从前面我们知道,apiserver是唯一一个和etcd通信的组件
    • 并且apiserver维护了一个watch缓存,List+Watch etcd中的所有资源,使得所有对资源的读操作 都直接走apiserver的watch缓存,写操作才会到达etcd
    • 这么做,是保护了etcd不被击穿,不会承受太大压力
  • apiserver的版本
    • v3版本已经比较成熟,v2当时是存在很多问题的
  • apiserver的三大功能
    • 认证
    • 授权
    • 准入控制

4.2.2.APIServer架构

在这里插入图片描述

  • APIHandler:http服务一般都包括的 url–>function路由转发
  • AuthN:authentication认证
  • Rate Limit:做限流处理,防止自己被过多流量打挂
  • Auditing:记录审计日志,可以用于破坏命令的责任追查
  • AuthZ:鉴权,一般会使用RBAC
  • Aggregator:apiserver的聚合器
    • 如果你想要自己做后续的 数据变形+验证+etcd交互,可以编写自己的apiserver,作为Aggregated APIServer,链接到此处。这种方式让APIServer具备了极大的扩展性。
    • Aggregated Server有时也会称为 Extension Server
    • 一般kubernetes的内置资源,都会按照上面的流程走完。
  • MutatingWebhook:数据变形处理器,包括 apiserver默认的、外置的。
    • 很多CRD,都会有自己的数据变形需求,可以开发一个MutatingWebhookService,作为Webhook供Apiserver在此处调用
    • 需要注意,Apiserver调用MutatingWebhookService,肯定是需要TLS连接的,所以MutatingWebhookService一般要配置证书的
  • SchemaValidating:校验变形后的数据,是否符合Schema规范
  • ValidatingWebhook:数据校验器,包括 apiserver默认的、外置的。
    • 很多CRD,都会有自己的数据校验需求,可以开发一个ValidatingWebhookService,作为Webhook供Apiserver在此处调用
    • 需要注意,Apiserver调用ValidatingWebhookService,肯定是需要TLS连接的,所以ValidatingWebhookService一般要配置证书的
  • etcd:上述步骤都走完了之后,才会将数据写入etcd,保护还是很严密的

4.2.3.Aggregated APIServer的使用案例

  • 比如 要给pod做 HPA 自动扩缩容,肯定是需要一些cpu、memory等指标数据的,这些指标要在APIServer中取
  • 在原生APIServer中是不包含这些指标的,因此可以自己写一个 Metrics APIServer,以 Aggregated APIServer的方式嵌入到APIServer中去,HPA的一些Controller就可以在APIServer中获取这些metrics指标数据了
  • 目前kubernetes社区已经有了开源的 metrics APIServer:即 metrics-server
    • https://github.com/kubernetes-sigs/metrics-server
    • 官方文档
  • 安装之后,可以使用kubectl top命令,查看指标
    • 比如 kubectl top node 就可以展示node的cpu、memory等使用情况

4.3.ControllerManager

4.3.1.ControllerManager介绍

  • 每个Controller逻辑是大同小异的,都是一个生产者消费者模型
    • Controller里的watcher就是一个生产者:使用watch机制监控APIServer中某些资源的变化,观察到的变化事件都会放入一个队列中
    • Controller里的处理逻辑作为消费者:不断从队列中取出数据去做调谐。如果调谐失败,需要有将事件重新放回队列,等待一段时间后重试,以此达到 最终一致性。
  • 最终一致性:不能保证一次就能达到spec期望状态,但是会有重试机制,保证经过多轮处理后,最终能够达到spec期望状态。
  • ControllerManager其实是一个控制器合集,内置资源的Controller都在这里,比如:
    • Deployment Controller
    • ReplicaSet Controller
    • Service Controller

4.3.2.控制器的工作流程

在这里插入图片描述

  • Controllerde Informer机制一般都包含两部分
    • Lister:负责从APIServer List全量数据
    • Informer:负责从APIServer Watch数据的变化
  • Controller 处理的基本流程
    • 首先Lister会从 APIServer List 全量数据
    • 之后Informer 会持续Watch资源的变更事件,并将不同的事件使用对应的 EventHandler(可以自行注册) 处理后,将资源key(namespace+name)加入队列
    • 启动一个或多个协程goroutine,作为消费者,从队列中取出资源key并处理

4.3.3.Informer的内部机制

在这里插入图片描述

  • Informer的组件及其职责
    • Reflector:反射器,负责将APIServer发过来的对象数据(一般是json或protobuf),反序列化成一个go struct
    • Delta FIFO Queue:循环FIFO队列,负责存储 待处理对象,Delta FIFO Queue是一个循环队列,满的时候会覆盖旧数据
    • Informer:通知器,负责从Delta FIFO Queue中不断弹出对象,做两件事:
      • 通过Indexer索引器,以key-value形式存入Thread Safe Store本地存储。
        • key:namespaceName
        • value:对象本身
      • 通过Handler,调用当前Event类型 对应的那一个Handler,处理后将对象的key添加到WorkQueue
    • Indexer:本地缓存索引器,负责处理和本地缓存相关的查询、写入操作
    • Thread Safe Store:线程安全的本地存储,负责在Informer内部维护一份对象缓存,这样控制器的查询操作,就不用到APIServer查询,直接在本地查询即可
      • 提高了查询效率,而且缓解了 APIServer 的压力
    • Handler:事件处理器,负责处理资源的不同Event,每种Event都应该有一个对应的Handler,所以应该有很多类型的Resource Handler。比如 Create Handler、Update Handler、Delete Handler…
    • WorkQueue:工作队列,负责存储所有待处理对象的key(namespaceName)
    • Process NextWorkItem:负责不断从WorkQueue取出key,交给某个Worker处理
    • Worker:资源处理工作器,负责真正的对象调谐工作。一般会启动多个Worker,每个Worker都是一个goroutine,并行处理WorkQueue中的事件
  • Informer是一个概念,SharedInformer是一个具体实现
  • Controller的整个处理流程如下(以Deployment Controller为例):
    • Controller 启动时,会通过Lister 先List一下所有Deployment资源,存入Thread Safe Store本地缓存
    • 然后 SharedInformer 通过长连接建立 Deployment资源的 watch 连接
    • 当发生Deployment资源变化时,APIServer会把事件及对应的Deployment资源对象,发送给Deployment控制器的SharedInformer
    • 首先会使用 Reflector将其反序列化为go struct,然后存入Delta FIFO Queue
    • Informer 组件会 从Delta FIFO Queue中取出对象,通过IndexerThread Safe Store本地缓存,并通过Handler将对象的key放入WorkQueue
    • Process NextWorkItem会从WorkQueue中取出key,找到一个worker对这个deployment资源进行处理

4.3.4.控制器之间协同工作原理

以创建一个Deployment资源为例

在这里插入图片描述

  • 用户使用kubectl或http请求方式,发起一个创建Deployment资源的请求
  • 请求到达APIServer,会经过 认证、鉴权、资源变形、schema校验、其他校验 等一系列处理后,APIServer 将deployment数据写入自己的watch缓存 和 etcd
    • Kubernetes 内部的 DeploymentController 早就已经和APIServer建立了Watch长连接
    • 当APIServer将数据写入缓存之后,就会把Deployment资源的变化,通知所有关心Deployment资源的watcher,其中就包括DeploymentController
  • DeploymentController 根据自己的处理逻辑,编排出对应的 ReplicaSet 资源,向APIServer发送了创建ReplicaSet请求
  • 请求到达APIServer,会经过 认证、鉴权、资源变形、schema校验、其他校验 等一系列处理后,APIServer 将ReplicaSet数据写入自己的watch缓存 和 etcd
    • Kubernetes 内部的 ReplicaSetController 早就已经和APIServer建立了Watch长连接
    • 当APIServer将数据写入缓存之后,就会把ReplicaSet资源的变化,通知所有关心ReplicaSet资源的watcher,其中就包括ReplicaSetController
  • ReplicaSetController 根据自己的处理逻辑,编排出对应的 Pod 资源,向APIServer发送了创建Pod请求
  • 请求到达APIServer,会经过 认证、鉴权、资源变形、schema校验、其他校验 等一系列处理后,APIServer 将Pod数据写入自己的watch缓存 和 etcd
    • Kubernetes 内部的 Scheduler调度器 和 每个Worker Node上的Kubelet 都早就已经和APIServer建立了Watch长连接
    • 当APIServer将数据写入缓存之后,就会把Pod资源的变化,通知所有关心Pod资源的watcher,其中就包括 Scheduler调度器.
    • 同时,Scheduler调度器还 watch 了所有的node对象
  • Scheduler调度器 发现该pod属于新创建,还没有调度,就会经过计算选择一个node,将 node名称 写到 pod的 spec.nodeName 中,即绑定pod与node,并向 APIServer 发起 pod update 请求。
  • APIServer一样会更新 watch缓存+etcd,并通知所有关心Pod资源的watcher,其中就包括 Worker Node 上的 kubelet
  • 某一个node上的kubelet 发现pod的 spec.nodeName 和自己的名称一致,就知道这个pod被调度到自己身上
    • 这个node上的kubelet,就会以此调用 CRI、CNI、CSI,启动容器,挂载网络和数据卷

4.4.Scheduler

  • 简单了解下,详细内容会有专门的文章讲解
    在这里插入图片描述

4.5.Kubelet

在这里插入图片描述

  • Kubelet是每一个Worker Node上都会安装的组件,极为重要,因为该node上所有管理pod都是它拉起来的。比如
    • PodWorker:负责管理当前节点上Pod的启停
    • OOMWatcher:负责管理当前节点的内存
    • GPUManager:负责管理当前节点的GPU
    • ProbeManager:负责对当前节点的应用进行健康检查
    • ImageGC:负责回收当前节点无用的镜像
    • ContainerGC:负责回收当前节点无用的容器
  • 对下层,Kubelet通过调用CRI、CNI、CSI 创建容器,挂载网络和数据卷
  • 对上层,Kubelet会把node、pod状态等信息,汇报给APIServer

4.6.Kube-Proxy(可选)

在这里插入图片描述

  • Kube-Proxy 是用来搭建负载均衡的。
    • 创建一个Service,关联上相应的Pod,就可以实现负载均衡。
    • 实际上这个负载均衡的网络规则配置等,都是Kube-Proxy做的。

4.7.附加组件 add-ons

  • 下面是一些附加组件,根据需求安装即可
  • 官方文档:https://kubernetes.io/zh-cn/docs/concepts/architecture/
    在这里插入图片描述

常见问题解答

  • 请见:Kubernetes常见问题解答

相关文章:

Kubernetes架构原则和对象设计

云原生学习路线导航页(持续更新中) 快捷链接 Kubernetes常见问题解答 本文从 Google Borg系统的架构设计开始,深入讲解Kubernetes架构及组件的基本原理 1.什么是云计算 1.1.传统行业应用 假设有10台服务器,两个应用。小规模管…...

响应式编程一、Reactor核心

目录 一、前置知识1、Lambda表达式2、函数式接口 Function3、StreamAPI4、Reactive-Stream1)几个实际的问题2)Reactive-Stream是什么?3)核心接口4)处理器 Processor5)总结 二、Reactor核心1、Reactor1&…...

uniapp+vue3+ts请求接口封装

1.安装luch-request yarn add luch-requestnpm install luch-request2.新建文件src/utils/request.ts 需要自己修改config.baseURL和token(获取存储的token) // import HttpRequest from luch-request; import type { HttpRequestConfig, HttpRespons…...

【计算机网络】实验4:生成树协议STP的功能以及虚拟局域网VLAN

实验 4:生成树协议STP的功能以及虚拟局域网VLAN 一、 实验目的 加深对生成树协议STP的功能的理解。 了解虚拟局域网VLAN。 二、 实验环境 • Cisco Packet Tracer 模拟器 三、 实验内容 1、验证交换机生成树协议STP的功能 (1) 第一步:构建网络拓…...

基于Matlab BP神经网络的电力负荷预测模型研究与实现

随着电力系统的复杂性和规模的不断增长,准确的电力负荷预测对于电网的稳定性和运行效率至关重要。传统的负荷预测方法依赖于历史数据和简单的统计模型,但这些方法在处理非线性和动态变化的负荷数据时,表现出较大的局限性。近年来,…...

java 21 多线程

1.相关概念 进程: 进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存空间。当我们使用迅雷,又启动了一个进程,操作系统将为迅雷配新的内存空间。 进程是程序的一次执行过程&#…...

Rust学习笔记_07——枚举和范围

Rust学习笔记_04——引用 Rust学习笔记_05——控制流(1) Rust学习笔记_06——控制流(2) 文章目录 1. 枚举1.1基础1.2 给枚举变体起个“别名”1.3 枚举与匹配(match) 2. 范围2.1 介绍2.2 半开区间范围2.3 包含范围的语法糖2.4 步长范围(Range …...

40分钟学 Go 语言高并发:服务性能调优实战

服务性能调优实战 一、性能优化实战概述 优化阶段主要内容关键指标重要程度瓶颈定位收集性能指标,确定瓶颈位置CPU、内存、延迟、吞吐量⭐⭐⭐⭐⭐代码优化优化算法、并发、内存使用代码执行时间、内存分配⭐⭐⭐⭐⭐系统调优调整系统参数、资源配置系统资源利用率…...

Windows通过指令查看已安装的驱动

Windows通过指令查看已安装的驱动 在 Windows 操作系统中,有几种命令可以用来查看已安装的驱动程序。以下是常见的几种方法: 1. 使用 pnputil 查看已安装驱动程序 pnputil 是一个 Windows 内置工具,可以列出所有已安装的驱动程序包。 命令…...

Windows 11 如何配置node.js

一,官网下载 官网首页 下载最新LTS版本,比较稳定,如果想探索更新的版本去探索新的nodejs功能。 1. 下载完成后,双击运行程序,点击next 2. 勾选接受协议,点击next 3. 选择自己的安装路径(默认是…...

AWTK fscript 中的 串口 扩展函数

fscript 是 AWTK 内置的脚本引擎,开发者可以在 UI XML 文件中直接嵌入 fscript 脚本,提高开发效率。本文介绍一下 fscript 中的 ** 串口 扩展函数 ** 1.iostream_serial_create 创建串口输入输出流对象。 原型 iostream_serial_create(device) > ob…...

yolov11剪枝

思路:yolov11中的C3k2与yolov8的c2f的不同,所以与之前yolov8剪枝有稍许不同; 后续:会将剪枝流程写全,以及增加蒸馏、注意力、改loss; 注意: 1.在代码105行修改pruning.get_threshold(yolo.mo…...

智慧地图聚合(LockMap)标注系统开发说明文档

智慧地图聚合(LockMap)标注系统开发说明文档 1. 系统概述 智慧地图聚合(LockMap)标注系统是一个专为处理大规模地理信息数据而设计的综合解决方案。通过后端高效的数据管理和前端直观的地图展示,该系统能够实现对海量地理位置点的有效可视化。本项目旨在提供一个用…...

「Mac畅玩鸿蒙与硬件36」UI互动应用篇13 - 数字滚动抽奖器

本篇将带你实现一个简单的数字滚动抽奖器。用户点击按钮后,屏幕上的数字会以滚动动画的形式随机变动,最终显示一个抽奖数字。这个项目展示了如何结合定时器、状态管理和动画实现一个有趣的互动应用。 关键词 UI互动应用数字滚动动画效果状态管理用户交…...

cuda12.1版本的pytorch环境安装记录,并添加到jupyter和pycharm中

文章目录 前置准备使用anaconda prompt创建虚拟环境创建虚拟环境激活pytorch虚拟环境把pytorch下载到本地使用pip把安装包安装到pytorch环境中进入python环境检验是否安装成功将环境添加到jupyter在pycharm中使用该环境: 前置准备 安装anaconda,我的版本…...

Linux: network: nic: mellanox MRU初现

文章目录 在PPP协议了有提到过总结-吐槽MRU初现兼容问题详细的MRU的计算幸运下面这个commit缩小了幸运机会So在PPP协议了有提到过 MRU在RFC4638里有提到。但是在Linux内核里是的Ethernet是没有相关的概念。 总结-吐槽 说Mellanox的网卡驱动在2018年做了一个对进入packet的大…...

深入理解红黑树的底层逻辑

一、红黑树的定义 红黑树是一种自平衡的二叉查找树,每个节点都带有额外的颜色信息,可以是红色或黑色。红黑树的目的是通过引入颜色信息来确保树的平衡,从而提高查找、插入和删除等操作的效率。 二、红黑树的性质 每个节点都有颜色&#xf…...

【数据结构】手搓链表

一、定义 typedef struct node_s {int _data;struct node_s *_next; } node_t;typedef struct list_s {node_t *_head;node_t *_tail; } list_t;节点结构体(node_s): int _data;存储节点中的数据struct node_s *_next;:指向 node…...

ThinkPHP场景动态验证

一、缘由 今天在用thinkphp8写东西的时候发现,写验证器规则和场景优点费时间,就算用tinkphp的命令行生成也是生成一个空壳。内容还是要自己填写感觉麻烦。 就突发奇想能不能自动生成验证器,也不能是说自动生成验证器,生成验证其的…...

在M3上面搭建一套lnmp环境

下载docker-desktop 官网下载docker-desktop 切换镜像源 {"builder": {"gc": {"defaultKeepStorage": "20GB","enabled": true}},"experimental": false,"registry-mirrors": ["https://docke…...

19c补丁后oracle属主变化,导致不能识别磁盘组

补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...

RocketMQ延迟消息机制

两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后&#xf…...

从零实现富文本编辑器#5-编辑器选区模型的状态结构表达

先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...

对WWDC 2025 Keynote 内容的预测

借助我们以往对苹果公司发展路径的深入研究经验,以及大语言模型的分析能力,我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际,我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测,聊作存档。等到明…...

相机从app启动流程

一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...

全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比

目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...

基于TurtleBot3在Gazebo地图实现机器人远程控制

1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

免费PDF转图片工具

免费PDF转图片工具 一款简单易用的PDF转图片工具,可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件,也不需要在线上传文件,保护您的隐私。 工具截图 主要特点 🚀 快速转换:本地转换,无需等待上…...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能

1. 开发环境准备 ​​安装DevEco Studio 3.1​​: 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK ​​项目配置​​: // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...