|
| 1 | +## 导读 |
| 2 | +绝大部分`Kubernetes`资源对象都包含`status.conditions`字段,用来表示资源状态,比如`deployment`资源中的`status.conditions`如下所示: |
| 3 | +``` |
| 4 | + conditions: |
| 5 | + - lastTransitionTime: "2020-12-15T01:52:53Z" |
| 6 | + lastUpdateTime: "2020-12-15T01:52:53Z" |
| 7 | + message: Deployment has minimum availability. |
| 8 | + reason: MinimumReplicasAvailable |
| 9 | + status: "True" |
| 10 | + type: Available |
| 11 | + - lastTransitionTime: "2020-12-15T01:52:39Z" |
| 12 | + lastUpdateTime: "2020-12-15T01:52:53Z" |
| 13 | + message: ReplicaSet "nginx-deployment-85b45874d9" has successfully progressed. |
| 14 | + reason: NewReplicaSetAvailable |
| 15 | + status: "True" |
| 16 | + type: Progressing |
| 17 | +``` |
| 18 | +以上`conditions`的信息是很容易读懂的(后面还会详细解释),然而有个问题曾经令笔者非常困惑,那就是: |
| 19 | +- `conditions`和`status`到底有什么区别? |
| 20 | +- `conditions`的设计原则是什么?在设计API扩展时,该如何定义`conditions`? |
| 21 | + |
| 22 | +本节会先从`conditions`的设计原则讲起,再介绍一些设计`conditions`时的一些经验总结。 |
| 23 | + |
| 24 | +## conditions设计原则 |
| 25 | +`conditions`寄居于资源对象的`status`字段中,与`status`其他字段值一样都用来表示资源的状态。不过`conditions`的设计初衷是提供一种通用的数据结构表示资源的状态,它通常能够提供比`status`其他字段更详细的信息(比如状态切换时间、更新时间),`conditions`实际上是一种扩展机制,外部监控程序可以根据`conditions`无差别地监控各种资源的状态,而不必过分关注资源对象`status`中的其他信息。 |
| 26 | + |
| 27 | +通常`status.conditions`字段类型为切片,切片中的每个元素表示资源的某个状态,该状态由特定的控制器更新,比如`Deployment`控制器会更新`deployment`对象的`status.conditions`信息。`conditions`作为扩展机制,它还支持第三方控制器增加新的状态。通常`status.conditions`中的信息由控制器根据资源的`status`其他字段计算出来。 |
| 28 | + |
| 29 | +## condition字段内容 |
| 30 | +通常一个`condition`必须包含`type`(状态类型)和`status`(状态值)两个信息。在`Kubernetes` v1.19版之前,关于`condition`并没有统一的标准,导致众多API都自行定义了`condition`。比如: |
| 31 | +- Deployment使用的Condition为`type DeploymentCondition struct`; |
| 32 | +- Pod使用的Condition为`type PodCondition struct`; |
| 33 | + |
| 34 | +庆幸的是,在`Kubernetes`v1.19版本社区提供了一个标准的`condition`类型定义,由于兼容性考虑,`Kubernetes`既有的API不一定能采用这一标准类型,但对于后续新增的API将会使用这一标准类型,并且笔者建议用户设计的CRD扩展也应使用这一标准类型。 |
| 35 | + |
| 36 | +标准的`condition`类型定义如下所示: |
| 37 | +```golang |
| 38 | +type ConditionStatus string |
| 39 | + |
| 40 | +const ( |
| 41 | + ConditionTrue ConditionStatus = "True" |
| 42 | + ConditionFalse ConditionStatus = "False" |
| 43 | + ConditionUnknown ConditionStatus = "Unknown" |
| 44 | +) |
| 45 | + |
| 46 | +type Condition struct { |
| 47 | + // 类型(使用驼峰风格),如”Available“。 |
| 48 | + Type string |
| 49 | + // 状态(枚举值:”True“、”False“和”Unknown“)。 |
| 50 | + Status ConditionStatus |
| 51 | + // 观察到的generation。 |
| 52 | + // 如果ObservedGeneration 比metada.generation小,说明不是最新状态。 |
| 53 | + // +optional |
| 54 | + ObservedGeneration int64 |
| 55 | + // 上次变化时间 |
| 56 | + LastTransitionTime Time |
| 57 | + // 状态变化原因(使用驼峰风格),如”NewReplicaSetAvailable“ |
| 58 | + Reason string |
| 59 | + // 描述信息,如”Deployment has minimum availability“ |
| 60 | + Message string `json:"message" protobuf:"bytes,6,opt,name=message"` |
| 61 | +} |
| 62 | +``` |
| 63 | + |
| 64 | +标准的`condition`类型定义对`condition`的各个字段做了进一步约定。除了`ObservedGeneration`是可选的以外,其他字段都是必填的。 |
| 65 | + |
| 66 | +## condition设计约定 |
| 67 | +为了让`condition`起到最大的作用,需要遵守一定的设计约定: |
| 68 | + |
| 69 | +### 约定一:condition定义要有明确的信息 |
| 70 | +每个`condition`需要传递一个用户关心的明确信息,用户读取到该信息不需要再根据资源的其他状态来揣测和计算。 |
| 71 | + |
| 72 | +### 约定二:condition需要保持兼容 |
| 73 | +`condition`一旦被定义,它就成了API中的一部分,需要跟API一样提供稳定性保证。如果需要新的`condition`仍然可以增加(没有破坏兼容性),但既有的`condition`是否能够改变就需要根据API成熟度来决定,比如`stable`的API不可以改变,`alpha`的API可以改变。 |
| 74 | + |
| 75 | +## 约定三:condition需要控制器第一次处理资源时更新 |
| 76 | +控制器需要尽快地更新`condition`状态值(`condition.status`),即便该状态值为`Unknown`,这么做的好处是可以让其他组件了解到控制器正在`调谐`这个资源。 |
| 77 | + |
| 78 | +然而,并不是所有的控制器都能遵守这个约定,即控制器并不会报告特定的`condition`(此时该`condition`状态值可能为`Unknown`),可能该`condition`还无法确定,需要在下一次`调谐`时决定。此种情况下,外部组件无法读取到特定的`condition`,可以假设该`condition`为`Unknown`。 |
| 79 | + |
| 80 | +## 约定四:condition类型名需要确保可读性 |
| 81 | +`condition`类型名要使用人类可读的名称,而且尽量避免出现双重否定的语境。例如,类型名`Ready`比使用`Failed`要好,因为`condition`状态为`False`时,`Failed = False`将出现双重否定,不如`Ready = False`容易理解。 |
| 82 | + |
| 83 | +## 约定五:condition不要定义成状态机 |
| 84 | +`condition`需要描述当前资源的确定状态,而不是当前资源状态机中的状态。通俗地讲,`condition`类型需要使用形容词(如`Ready`)或过去动词(`Succeeded`),而不是使用当前运行时(如`Deploying`)。 |
0 commit comments