阿里云《云原生》公开课笔记 第五章 应用编排与管理
课程文字:https://edu.aliyun.com/lesson_1651_18353?spm=5176.10731542.0.0.5a8220beAacF7d#_18353
Kubernetes的资源元
元数据的组成
- 用来识别资源的具有标识型的标签:Labels
- key value
- selector(筛选/组合资源):多个相等条件,逻辑与的关系; 集合型,in notin
- 用来描述资源的非标识型的注解:Annotations
- 扩展资源的spec/status
- 可以包含特殊字符
- 可以结构化也可以非结构化
- 用来描述多个资源之间相互关系的 OwnerReference
- 集合类的资源:replicaset、statefulset
- replicaset 控制器在操作中会创建 Pod,被创建 Pod 的 Ownereference 就指向了创建 Pod 的 replicaset【可以用来定位和级联删除】
控制器模式
- 三个逻辑组件:控制器,被控制的系统,以及能够观测系统的传感器
- 过程:外界通过修改资源 spec 来控制资源,控制器比较资源 spec 和 status,从而计算一个 diff,diff 最后会用来决定执行对系统进行什么样的控制操作,控制操作会使得系统产生新的输出,并被传感器以资源 status 形式上报,控制器的各个组件将都会是独立自主地运行,不断使系统向 spec 表示终态趋近。
控制模式中的传感器
- 构成:Reflector、Informer、Indexer 三个组件
Reflector
- Reflector 通过 List 和 Watch K8s server 来获取资源的数据。
- List 用来在 Controller 重启以及 Watch 中断的情况下,进行系统资源的全量更新;
- Watch 则在多次 List 之间进行增量的资源更新;
- Reflector 在获取新的资源数据后,会在 Delta 队列中塞入一个包括资源对象信息本身以及资源对象事件类型的 Delta 记录,Delta 队列中可以保证同一个对象在队列中仅有一条记录,从而避免 Reflector 重新 List 和 Watch 的时候产生重复的记录。
Informer
Informer 组件不断地从 Delta 队列中弹出 delta 记录,然后把资源对象交给 indexer,让 indexer 把资源记录在一个缓存中,缓存在默认设置下是用资源的命名空间来做索引的,并且可以被 Controller Manager 或多个 Controller 所共享。之后,再把这个事件交给事件的回调函数
控制模式中的控制器
- 控制循环中的控制器组件主要由事件处理函数以及 worker 组成,事件处理函数之间会相互关注资源的新增、更新、删除的事件,并根据控制器的逻辑去决定是否需要处理。
- 对需要处理的事件,会把事件关联资源的命名空间以及名字塞入一个工作队列中,并且由后续的 worker 池中的一个 Worker 来处理,工作队列会对存储的对象进行去重,从而避免多个 Woker 处理同一个资源的情况。
- Worker 在处理资源对象时,一般需要用资源的名字来重新获得最新的资源数据,用来创建或者更新资源对象,或者调用其他的外部服务,Worker 如果处理失败的时候,一般情况下会把资源的名字重新加入到工作队列中,从而方便之后进行重试。
控制模式的例子
- replicaset:replicas从2修改成3
- Reflector 会 watch 到 ReplicaSet 和 Pod 两种资源的变化,为什么我们还会 watch pod 资源的变化稍后会讲到。发现 ReplicaSet 发生变化后,在 delta 队列中塞入了对象是 rsA,而且类型是更新的记录。
- Informer 一方面把新的 ReplicaSet 更新到缓存中,并与 Namespace nsA 作为索引。另外一方面,调用 Update 的回调函数,ReplicaSet 控制器发现 ReplicaSet 发生变化后会把字符串的 nsA/rsA 字符串塞入到工作队列中,工作队列后的一个 Worker 从工作队列中取到了 nsA/rsA 这个字符串的 key,并且从缓存中取到了最新的 ReplicaSet 数据。
- Worker 通过比较 ReplicaSet 中 spec 和 status 里的数值,发现需要对这个 ReplicaSet 进行扩容,因此 ReplicaSet 的 Worker 创建了一个 Pod,这个 pod 中的 Ownereference 取向了 ReplicaSet rsA。
- 然后 Reflector Watch 到的 Pod 新增事件,在 delta 队列中额外加入了 Add 类型的 deta 记录,一方面把新的 Pod 记录通过 Indexer 存储到了缓存中,另一方面调用了 ReplicaSet 控制器的 Add 回调函数,Add 回调函数通过检查 pod ownerReferences 找到了对应的 ReplicaSet,并把包括 ReplicaSet 命名空间和字符串塞入到了工作队列中。
- ReplicaSet 的 Woker 在得到新的工作项之后,从缓存中取到了新的 ReplicaSet 记录,并得到了其所有创建的 Pod,因为 ReplicaSet 的状态不是最新的,也就是所有创建 Pod 的数量不是最新的。因此在此时 ReplicaSet 更新 status 使得 spec 和 status 达成一致。
控制器模式采用命令式api
- 两种API类型
- 命令式 API:家长和孩子交流方式,因为孩子欠缺目标意识,无法理解家长期望,家长往往通过一些命令,教孩子一些明确的动作,比如说:吃饭、睡觉类似的命令。容器编排体系中,命令式 API 就是通过向系统发出明确的操作来执行的。
- 存在问题1:如果命令没有正确执行,调用方只能反复充实恢复错误;某解决思路:交互系统后台会做一个巡检系统,修正数据不一致的场景;但巡检系统一般是人工触发的。
- 存在问题2:多并发访问,命令式系统会在操作前加锁,从而保证系统最后生效,但会降低操作效率。但是声明式系统天然记录了系统现在和最终的状态
- 声明式 API:老板对自己员工的交流方式。老板一般不会给自己的员工下很明确的决定,实际上可能老板对于要操作的事情本身,还不如员工清楚。因此,老板通过给员工设置可量化的业务目标的方式,来发挥员工自身的主观能动性。比如说,老板会要求某个产品的市场占有率达到 80%,而不会指出要达到这个市场占有率,要做的具体操作细节。
- 对资源状态的巡检属于正常操作
- 支持多并发的访问,合并多次对资源的修改
- 命令式 API:家长和孩子交流方式,因为孩子欠缺目标意识,无法理解家长期望,家长往往通过一些命令,教孩子一些明确的动作,比如说:吃饭、睡觉类似的命令。容器编排体系中,命令式 API 就是通过向系统发出明确的操作来执行的。
- k8s的控制器模式
- 声明式api,根据资源对象修改
- 异步控制系统,逼近于设置的最终状态
- 有状态的应用,可以自定义资源和控制器方式【后续的operator场景】
自测题目
-
下列哪个键值对无法作为 k8s对象的 label ?(单选题)D
- A. app.kubernetes.io/version=3.4.1
- B. failure-domain.beta.kubernetes.io/region=cn-shanghai
- C. app-name=trade
- D. scaling-config=“min-replicas:50”
-
下列哪个键值对不适合做为 annotations ?(单选题)B
- A. statefulset 的历史配置 yaml
- B. service 对应的应用名,用来方便筛选
- C. 用来表示 ingress 路由的正则表达式值
- D. 用来扩展 pod 状态,表示对应 pod 在第三方数据库的记录情况
-
下列哪个场景不是 selector 的使用场景?(单选题)C
- A. 设计一个查询的界面,根据 label 筛选资源
- B. 配置应用的调度规则, 选择必需调度到包含某些 label 的节点
- C. 存储数据库应用的配置信息
- D. 判断可能归属于 replicaset 的 pod
-
controller 中 reflector 不会对 apiserver 进行 LIST 操作的场景?(单选题)D
- A. controller 重启的时候
- B. 和 apiserver watch 操作异常的情况
- C. 配置定期的执行 LIST
- D. controller 中需要筛选符合标签的 pod 时候
-
Controller 中的 object store 默认以什么作为索引?(单选题) C
- A. 对象的 label
- B. 对象的 annotation
- C. 对象的 namespace
- D. 对象的名字
- https://blog.****.net/fy_long/article/details/89678829#%E5%AF%B9%E8%B1%A1%E7%BC%93%E5%AD%98-store
- Store 是key-value类型的对象缓存;NewStore函数返回struct cache类型对象
- Indexer是在Store的基础上,增加了索引,可以快速获取一批对象;NewIndexer返回实现该接口的struct cache类型对象。对象的索引是一个字符串列表,该列表是由IndexFunc函数生成;client-go package 提供了名为 NamespaceIndex 的 IndexFunc 类型函数 MetaNamespaceIndexFunc,它提取对象的 Namespace 作为索引。
-
Controller 中的 workerqueue 中可以存放什么内容?(单选题)B
- A. Namespace 名+ pod 名
- B. Namespace 名+pod 名+事件的类型
- C. Pod 的列表
- D. Pod 对象的指针
-
下列关于 controller 中 workqueue 描述不正确的(单选题)B
- A. 因为 workqueue 具备去重功能,可以往 workqueue 中反复加入资源
- B. 为了加速 controller 的处理,可以往 workqueue 中加入资源的指针
- C. 一个控制器的 workqueue 一般只存储一种类型资源的名字
- D. 对于处理 node 的控制器,可以只在 workqueue 中加入节点的名字而不包括命名空间
-
在 controller 的 event handler 中,不适合执行的操作是(单选题)D
- A. 根据资源的 ownerreference 找到资源的创建者
- B. 判断资源信息,对于不关心的对象, 直接返回
- C. 在 workqueue 中加入资源
- D. 执行控制器的实际处理工作 (workqueue实现的)
-
controller 中 worker 最不适合做什么操作(单选题)D
- A. 创建其他资源对象
- B. 重新往 workqueue 中塞入对象
- C. 更新资源对象的 status
- D. 调用其他耗时的 web 服务并等待返回(占用资源低效)
- E. 什么都不做
-
以下不是声明式的 API 设计(多选题)ABC
- A. 创建一个容器的 API 是 POST /containers/create,请求参数是容器的各种规格, 返回系统生成的容器 id
- B. 删除一个容器的 API 是 DELETE /containers/, 返回一个异步删除的工单号,可以根据工单号查询删除进度
- C. 给应用扩容的 API 是 PUT /containers/create?increaseReplicas=1, 参数指定扩容的增量容器数量
- D. 更新一个容器镜像的 API 是 PATCH /containers/?image=nginx, 返回的是容器新的目标状态
k8s命令
- kubectl get pods --show-labels
- kubectl label pods nginx1 env=test —overwrite (覆盖标签)
- kubectl label pods nginx1 env=test(添加标签)
- kubectl label pods nginx env- (删除标签)
- kubectl get pods —show-labels -l env=test (selector通过-l来指定)
- kubectl get pods —show-labels -l env=test,env=dev (与关系)
- kubectl get pods —show-labels -l ’env in (dev,test)’ (集合)
- kubectl annotate pods nginx1 my-annotate=‘my annotate,ok’ (增加注解)
- kubectl get pods nging1 -o yaml | less (通过less分页显示)
附录
apiserver的list-watch源码学习
- 相关链接:https://www.kubernetes.org.cn/174.html
- watchCache是数据从etcd过来的第一站
- 从watchCache中拿到从某个resourceVersion以来的所有数据——initEvents,然后用这个数据创建了一个watcher返回出去为某个客户端提供服务。
- apiserver向etcd发起的watch是没有条件的,只能知道某个数据发生了变化或创建、删除,但不能过滤具体的值。
- list是watch失败,数据太过陈旧后的弥补手段。list本身是一个简单的列表操作,和其它apiserver的增删改操作一样。
master核心组件之间的关系
- kubectl直接和master节点的apiserver通信
- master节点的apiserver和etcd通信
- master节点的controller-manager和sheduler,通过和apiserver通信获取信息(list-watch)
回调函数
- 回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。