ResourceDistribution
在对 Secret、ConfigMap 等 namespace-scoped 资源进行跨 namespace 分发及同步的场景中,原生 kubernetes 目前只支持用户 one-by-one 地进行手动分发与同步,十分地不方便。
典型的案例有:
- 当用户需要使用 SidecarSet 的 imagePullSecrets 能力时,要先重复地在相关 namespaces 中创建对应的 Secret,并且需要确保这些 Secret 配置的正确性和一致性。
- 当用户想要采用 ConfigMap 来配置一些通用的环境变量时,往往需要在多个 namespaces 做 ConfigMap 的下发,并且后续的修改往往也要求多 namespaces 之间保持同步。
因此,面对这些需要跨 namespaces 进行资源分发和多次同步的场景,我们期望一种更便捷的分发和同步工具来自动化地去做这件事,为此我们设计并实现了一个新的CRD —- ResourceDistribution。
ResourceDistribution 目前支持 Secret 和 ConfigMap 两类资源的分发和同步。
API 说明
ResourceDistribution是一类 cluster-scoped 的 CRD,其主要由 resource
和 targets
两个字段构成,其中 resource
字段用于描述用户所要分发的资源,targets
字段用于描述用户所要分发的目标命名空间。
apiVersion: apps.kruise.io/v1alpha1
kind: ResourceDistribution
metadata:
name: sample
spec:
resource:
... ...
targets:
... ...
Resource 字段说明
resource
字段必须是一个完整、正确的资源描述。
一个配置正确的 resource
例子如下所示:
apiVersion: apps.kruise.io/v1alpha1
kind: ResourceDistribution
metadata:
name: sample
spec:
resource:
apiVersion: v1
kind: ConfigMap
metadata:
name: game-demo
data:
game.properties: |
enemy.types=aliens,monsters
player.maximum-lives=5
player_initial_lives: "3"
ui_properties_file_name: user-interface.properties
user-interface.properties: |
color.good=purple
color.bad=yellow
allow.textmode=true
targets:
... ...
Tips: 用户可以先在本地某个命名空间中创建相应资源并进行测试,确认资源配置正确后再拷贝过来。
Target 字段说明
targets
字段目前支持四种规则来描述用户所要分发的目标命名空间,包括 allNamespaces
、includedNamespaces
、namespaceLabelSelector
以及 excludedNamespaces
:
allNamespaces
: bool值,如果为true
,则分发至所有命名空间;includedNamespaces
: 通过 Name 来匹配目标命名空间;namespaceLabelSelector
:通过 LabelSelector 来匹配目标命名空间;excludedNamespaces
: 通过 Name 来排除某些不想分发的命名空间;
目标命名空间的计算规则:
- 初始化目标命名空间 T = ∅;
- 如果用户设置了
allNamespaces=true
,T 则会匹配所有命名空间; - 将
includedNamespaces
中列出的命名空间加入 T; - 将与
namespaceLabelSelector
匹配的命名空间加入 T; - 将
excludedNamespaces
中列出的命名空间从 T 中剔除;
allNamespaces
、includedNamespaces
、namespaceLabelSelector
之间是 或(OR) 的关系,而excludedNamespaces
一旦被配置,则会显式地排除掉这些命名空间。另外,targets还将自动忽略kube-system 和 kube-public 两个命名空间。
一个配置正确的targets字段如下所示:
apiVersion: apps.kruise.io/v1alpha1
kind: ResourceDistribution
metadata:
name: sample
spec:
resource:
... ...
targets:
includedNamespaces:
list:
- name: ns-1
- name: ns-4
namespaceLabelSelector:
matchLabels:
group: test
excludedNamespaces:
list:
- name: ns-3
上例中,该 ResourceDistribution 的目标命名空间一定会包含ns-1和ns-4,并且Labels满足namespaceLabelSelector
的命名空间也会被包含进目标命名空间,但是,即使ns-3即使满足namespaceLabelSelector
也不会被包含,因为它已经在excludedNamespaces
中被显式地排除了。
完整用例
分发资源
当用户将 ResourceDistribution 的 resource 和 targets 两个字段正确配置,并创建这个 ResourceDistribution 资源后,相应的 Controller 会执行资源分发逻辑,这一资源会自动地在各个目标命名空间中创建。一个完整的用例如下所示:
apiVersion: apps.kruise.io/v1alpha1
kind: ResourceDistribution
metadata:
name: sample
spec:
resource:
apiVersion: v1
kind: ConfigMap
metadata:
name: game-demo
data:
game.properties: |
enemy.types=aliens,monsters
player.maximum-lives=5
player_initial_lives: "3"
ui_properties_file_name: user-interface.properties
user-interface.properties: |
color.good=purple
color.bad=yellow
allow.textmode=true
targets:
excludedNamespaces:
list:
- name: ns-3
includedNamespaces:
list:
- name: ns-1
- name: ns-4
namespaceLabelSelector:
matchLabels:
group: test
分发状态跟踪
当然,资源分发并不总是成功的,在分发的过程中可能会遇到各种各样的错误导致分发失败。为此,我们在ResourceDistribution.Status字段中记录了资源分发的一些状态,以便用户对其进行追踪。 首先,Status记录了目标命名空间总数(Desired)、成功分发的目标命名空间数量(Succeeded)、以及失败的目标命名空间数量(Failed):
status:
Desired: 3
Failed: 1
Succeeded: 2
为了进一步方便用户了解分发失败的原因及地点(命名空间),ResourceDistribution 还对分发错误类型进行了归纳整理,总共分为了六类,并记录在status.conditions之中:
- 四类 condition 记录了操作资源时出现失败的相关原因,即记录资源的 Get、Create、 Update 和 Delete 四类操作出现的错误信息以及对应的失败命名空间;
- 一类 condition 记录了命名空间不存在的错误;
- 一类 condition 记录了资源冲突的情况,即目标命名空间中已经存在Name、Kind、APIVersion都相同的资源,且该资源不是该ResourceDistribution分发,则会发生资源冲突,相应的命名空间会被记录下来。
Status:
Conditions:
Last Transition Time: 2021-09-06T08:42:28Z
Reason: Succeeded
Status: False
Type: GetResourceFailed
Last Transition Time: 2021-09-06T08:42:28Z
Reason: Succeeded
Status: False
Type: CreateResourceFailed
Last Transition Time: 2021-09-06T08:42:28Z
Reason: Succeeded
Status: False
Type: UpdateResourceFailed
Last Transition Time: 2021-09-06T08:42:28Z
Reason: Succeeded
Status: False
Type: DeleteResourceFailed
Last Transition Time: 2021-09-06T08:42:28Z
Reason: Succeeded
Status: False
Type: ConflictOccurred
Failed Namespace:
ns-1
ns-4
Last Transition Time: 2021-09-06T08:45:08Z
Reason: namespace not found
Status: True
Type: NamespaceNotExists
上述例子遇到目标命名空间 ns-1 和 ns-4 不存在的错误,相应的错误类型和命名空间被记录了下来。
更新并同步资源
ResourceDistribution 允许用户更新resource字段,即更新资源,并且会自动地对所有目标命名空间中的资源进行同步更新。 每一次更新资源时,ResourceDistribution 都会计算新版本资源的哈希值,并记录到资源的Annotations之中,当 ResourceDistribution 发现新版本的资源与目前资源的哈希值不同时,才会对资源进行更新。
apiVersion: v1
kind: ConfigMap
metadata:
name: game-demo
annotations:
kruise.io/resourcedistribution.resource.from: sample
kruise.io/resourcedistribution.resource.distributed.timestamp: 2021-09-06 08:44:52.7861421 +0000 UTC m=+12896.810364601
kruise.io/resourcedistribution.resource.hashcode: 0821a13321b2c76b5bd63341a0d97fb46bfdbb2f914e2ad6b613d10632fa4b63
... ...
特别地,我们非常不建议用户绕过 ResourceDistribution 直接对资源进行修改,除非用户知道自己在做什么:
- 直接修改资源后,资源的哈希值不会被自动计算,因此,下次 resource字段被修改后,ResourceDistribution 可能将用户对这些资源的直接修改覆盖掉;
- ResourceDistribution 通过 kruise.io/resourcedistribution.resource.from 来判断资源是否由该 ResourceDistribution 分发,如果该 Annotation 被修改或删除,则被修改的资源会被 ResourceDistribution 当成冲突资源,并且无法通过 ResourceDistribution 进行同步更新。
级联删除
ResourceDistribution 通过 OwnerReference 来管控所分发的资源。因此,需要特别注意,当 ResourceDistribution 被删除时,其所分发的所有资源也会被删除。