Skip to content

Latest commit

 

History

History
74 lines (58 loc) · 6.93 KB

QA to Understand K8s.md

File metadata and controls

74 lines (58 loc) · 6.93 KB

在掌握 K8s 路上,应该理解下面这些 QA:

  • controller 中的 retry 和失败之后重新 requeue 的区别是什么?

RetryOnConflict 表示在进行 K8s 对象增删改 的时候,可能存在冲突(被其他组件改动导致 resourceVersion 变化) 从而需要重试,一般通过获取最新 obj 重试即可成功;

WorkQueue 中的对象,表示为了快速响应 ResourceEventHandler 调用,一般采用 queue 先存起来(即完成 response),再通过 sync logic 异步解耦去 process。若 process 失败可 requeue 重新放回队列(可以设置 backoff 策略),再次重试。

controller 模式高纬度的视角是:获取当前所有相关资源的状态,其中有一个资源作为描述预期的核心对象,控制器通过对比预期状态,调整对下属资源进行 CUD

基于这样的设计,在 reconcile 的流程中出现 Conflict, 证明需要 Reconcile 的对象已经发生了变化,个人(@sxllwx) 认为应该进行 requeue,等待下一次的 reconcile,重新获取这些对象然后进行 Reconcile。

  • 如何理解 Status 中的 Phase 是一种状态机?

Phase 是 Pod lifecycle 中一种抽象的 high-level 状态值,通过枚举值(Enumeration)实现,包括 Pending、Running、Failed 等,状态之间切换存在依赖限制,比如可以从 Pending => Running,但不能从 Running => Pending;

另外枚举值变更(如新增一种状态值)不便于旧版本兼容。因此社区建议使用 Conditions 代替 Phase typical-status-properties

  • Status 中的 PhaseConditions 区别是?

Phase 是一种站在更高维度、聚合的总结性状态,通过枚举值(Enumeration)实现,状态之间存在依赖(状态机模式)。

Conditions 则是一种通过对观察到的对象,进行实时计算所得的状态,提供了一种 open-world 开放视角,通过列表(Slice list) 实现,包括了 Status, Reason, Message 等字段,不依赖历史状态。

  • 为什么 Controller 不直接从 informer 中取出 Object 进行处理,而是从 WorkQueue 中取一个 StringKey

因为每个 Object 在 K8s 各组件内经过 Reconcile,obj 随时都在进行变化。Informer 中对象是以 key-accumulator 方式存储,即一个 obj 随时时间的变化存在很多版本,通过取 Key 间接取到最新的 obj,保证了取到的 obj 是实时最新的对象。

  • 如何理解 k8s 中对象的 Status 是一种观察到的情况?

K8s 中对资源的调谐采用 水平触发(level-based)模式,在控制器 control-loop 中每次都是根据当前观察到的对象进行状态计算,因此 Status 是一种实时观察到的状态。

  • 如何理解 Controller 是一种水平触发(level-based),边缘触发(edge-based)只是一种优化?

水平触发(level-based) 是一种只关心当前状态、不关心、不依赖历史状态的控制器机制,也是一种 crash-safe 的设计模式;缺点是会牺牲一定的性能,因为需要获取全量数据进行计算,而不是仅计算 delta 增量数据;

边缘触发(edge-based) 是一种依赖历史、当前状态的控制器机制,crash-safe 恢复需要依赖历史数据,优点是性能高,因为只需要在应用启动时进行一次全量数据计算,之后就可以仅计算 delta 增量数据。

在其他的组件稳定的情况下,两者等价; 在其他的组件不太稳定的情况下,两者对于错误的容忍度,水平触发的包容性更强;

  • 如何理解 k8s 所谓的有状态和无状态(statelessstateful)?

无状态(stateless) 服务表示应用重启、多副本之间没有依赖关系,容器运行不需要依赖历史数据,也就可以不需要数据的持久化;另外,对外提供的 service dns 也会动态变更(如 pod_xxx.ns.svc.cluster.local),一般通过 Deployment 实现;

有状态(stateful) 服务表示应用重启、多副本之前存在依赖关系,容器运行需要依赖历史数据,也依赖多副本之间的关系(如数据库主从),因此数据需要进行持久化存储,保证应用重启后数据保持一致性、完整性。另外,对外提供的 service dns 会保持不变(如 mysql_0/mysql_1.ns.svc.cluster.local),一般通过 StatefulSet 实现;

  • 如何理解论文中描述的 borg 将面向机器转向到了面向应用?

容器化(containerization) 将 data center、machine OS 进行了抽象封装,使得应用开发者不必关心底层物理机器资源,而是专注以应用(application) 为单位进行管理、部署和运维,提高资源利用率和 DevOps 效率;

  • CRD 为何还需要使用 Extension APIServer

CRD 是 K8s 提供的一种资源扩展方式,相关后端存储(etcd)、controller 都使用 K8s 内部组件实现,对象资源必会持久化到etcd

而 extension APIServer 是 K8s 提供的另一种更加开放的扩展方式,以 Aggregator API 方式通过 proxy 到自定义 APIService,实现资源、控制器、GC等 都开放给用户,可以配置单独的 etcd 存储。

  • K8s 代码组织方式是怎样的?staging/vendor 为何如此设计?

K8s 相关 SIGs 核心代码在 /kubernetes/staging/src/k8s.io 目录下,相关 PR 修改基本都是在 staging 目录下;

vendor 目录下是 K8s 自身依赖的一些第三方包,以及通过 symlink 软链接到 staging/src/k8s.io 的内部依赖包(/kubernetes/vendor/k8s.io),这样组织代码,既可以很方便的以 vendor 方式统一进行包管理,又可以稳定的作为第三方的包引用(staging 代码结构变更,vendor 对外可保持不变)。

  • informer 内在 ListWatch 的时候,是只 list 一次,之后靠 watch 来同步 etcd,还是会定时 list 呢?

ListAndWatch 会在启动时一次性获取全部(可设置分页)对象 items,及对应的 resourceVersion,之后根据 resourceVersion 版本号不断 watch etcd 中 items 的 Add/Update/Delete 变化。

  • etcd cluster 是否可以切换 httphttps

先通过 http 建立了 cluster,然后再用自签证书 https 来建立,这样就会报错:

tls: first record does not look like a TLS handshake

经过验证:无论是从 http => https,还是从 https => http 的切换都会报这个错,因为一旦建立 cluster 成功,则把连接的协议(http/https) 写入到 etcd 存储里了,不能再更改连接协议。

解决:如果真正遇到需要切换协议,可尝试下面方式

  • 允许删除数据:删除后重新建立 cluster
  • 不允许删数据:可以尝试采用 snapshot & restore 进行快照与恢复操作

如何编写 k8s-Controller

Controller 设计概要