Karmada声明式配置详细指引

1. 作用与应用场景

Karmada 的 ResourceInterpreterCustomization 允许用户通过 Lua 脚本自定义多集群资源的状态聚合、保留、健康判定等逻辑,解决如 CRD 状态同步、混沌实验等多集群一致性与差异性场景。

典型场景:

  • chaos-mesh 等 CRD 资源的 status 聚合
  • HPA、Job 等原生/自定义资源的副本数、健康等自定义同步
  • 解决成员集群本地控制器与 Karmada 控制面冲突

2. CRD 字段结构与关键字段

以 chaos-mesh 的 PodChaos CRD 为例,ResourceInterpreterCustomization 资源结构如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
apiVersion: config.karmada.io/v1alpha1
kind: ResourceInterpreterCustomization
metadata:
  name: podchaos-chaos-mesh
spec:
  target:
    apiVersion: chaos-mesh.org/v1alpha1
    kind: PodChaos
  customizations:
    statusReflection:
      luaScript: |
        ...
    statusAggregation:
      luaScript: |
        ...
    retention:
      luaScript: |
        ...

常用字段说明:

  • statusReflection:定义如何从成员集群资源提取 status 字段
  • statusAggregation:定义如何聚合所有成员集群的 status 到控制面
  • retention:定义保留哪些字段,解决本地控制器与控制面冲突

3. 常用自定义脚本用法

3.1 statusReflection 示例(以 PodChaos 为例)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
function ReflectStatus(observedObj)
  status = {}
  if observedObj == nil or observedObj.status == nil then
    return status
  end
  -- 只提取 conditions 和 experiment 字段,适配 PodChaos CRD
  status.conditions = observedObj.status.conditions
  status.experiment = observedObj.status.experiment
  return status
end

3.2 statusAggregation 优化聚合示例(以 PodChaos 为例,含注释)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
function AggregateStatus(desiredObj, statusItems)
  -- 聚合所有成员集群的 conditions 字段
  allConditions = {}
  -- 用于收集所有有效 experiment
  local experiments = {}
  -- 用于收集包含 Succeeded 事件的 experiment
  local succeededExperiments = {}

  -- 遍历所有成员集群的 status
  for i = 1, #statusItems do
    local status = statusItems[i].status
    if status ~= nil then
      -- 聚合 conditions
      if status.conditions ~= nil then
        for _, cond in ipairs(status.conditions) do
          table.insert(allConditions, cond)
        end
      end
      -- 收集 experiment 字段
      if status.experiment ~= nil then
        table.insert(experiments, status.experiment)
        -- 检查 experiment 是否包含 Succeeded 事件
        local exp = status.experiment
        if exp.containerRecords ~= nil then
          for _, record in ipairs(exp.containerRecords) do
            if record.events ~= nil then
              for _, event in ipairs(record.events) do
                if event.type == "Succeeded" then
                  -- 只要有 Succeeded 事件就收集该 experiment
                  table.insert(succeededExperiments, exp)
                  break
                end
              end
            end
          end
        end
      end
    end
  end

  desiredObj.status = desiredObj.status or {}
  desiredObj.status.conditions = allConditions

  -- 判断所有 experiment 是否完全一致
  local function isAllSame(exps)
    if #exps == 0 then return false end
    local first = exps[1]
    for i = 2, #exps do
      for k, v in pairs(first) do
        if exps[i][k] ~= v then
          return false
        end
      end
      for k, v in pairs(exps[i]) do
        if first[k] ~= v then
          return false
        end
      end
    end
    return true
  end

  -- 聚合优先级判断
  if #experiments == 0 then
    -- 所有成员集群都没有 experiment 字段,聚合默认值
    desiredObj.status.experiment = {
      desiredPhase = "",
      containerRecords = {},
      message = "no experiment field in any cluster"
    }
  elseif isAllSame(experiments) then
    -- 所有 experiment 完全一致,聚合该 experiment
    desiredObj.status.experiment = experiments[1]
  elseif #succeededExperiments > 0 then
    -- 有 experiment 不一致,但有 Succeeded,聚合第一个 Succeeded 的 experiment
    desiredObj.status.experiment = succeededExperiments[1]
  else
    -- 有 experiment 但都不一致且无 Succeeded,聚合默认对象并提示
    desiredObj.status.experiment = {
      desiredPhase = "",
      containerRecords = {},
      message = "experiment fields are inconsistent across clusters"
    }
  end

  return desiredObj
end

3.3 retention 示例(以 PodChaos 为例)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
function Retain(desiredObj, observedObj)
  -- 控制面已删除时不保留
  if desiredObj == nil then
    return nil
  end
  -- 控制面资源正在被删除时不保留
  if desiredObj.metadata ~= nil and desiredObj.metadata.deletionTimestamp ~= nil then
    return desiredObj
  end
  -- 其他情况下保留 status 字段
  if observedObj.status ~= nil then
    desiredObj.status = observedObj.status
  end
  return desiredObj
end

4. CRD 及 RBAC 注意事项

  • 目标 CRD 必须声明 subresources.status,否则无法写回 status 字段:
    1
    2
    3
    4
    5
    6
    
    spec:
      versions:
        - name: v1alpha1
          ...
          subresources:
            status: {}
  • Karmada 控制面 ServiceAccount 需有目标资源 /status 的 patch/update 权限:
    1
    2
    3
    
    - apiGroups: ["chaos-mesh.org"]
      resources: ["podchaos/status"]
      verbs: ["get", "update", "patch"]

5. 常见问题与排查建议

  • 控制面 status 字段无内容:优先排查 CRD 是否声明 status 子资源、RBAC 权限是否齐全。
  • 聚合失败报类型错误:聚合脚本返回的字段类型需与 CRD schema 完全一致(如 experiment 必须为 object,不能为 array 或缺失 required 字段)。
  • 部分成员集群无 workload:聚合时应只统计有有效 status 的成员集群,避免因部分集群无状态导致整体聚合失真。
  • Lua 脚本报错:可用 karmadactl interpret 本地调试脚本。

6. chaos-mesh 场景最佳实践

  • statusReflection 只提取 conditions/experiment 字段,避免冗余。
  • statusAggregation 优先聚合一致 experiment,不一致时优先聚合已 Succeeded 的 experiment。
  • retention 只保留 status 字段,避免本地控制器与控制面冲突。
  • CRD 必须声明 status 子资源,RBAC 权限需齐全。
  • 建议先用 karmadactl interpret 本地验证 Lua 脚本。

7. gopher-lua 支持特性与限制

Karmada 的 Lua 运行环境基于 gopher-lua,其特性如下:

  • 兼容 Lua 5.1 语法和标准库,支持 table、string、math、table.insert、ipairs、pairs 等常用操作。
  • 支持自定义函数注册,Karmada 会将资源对象、参数等以 table 形式注入 Lua 环境。
  • 不支持 C 扩展库(如 LuaSocket、lfs 等),仅支持纯 Lua 标准库。
  • 沙箱环境,无法访问文件、网络、系统等敏感资源。
  • 不支持 Lua 5.2/5.3 新特性,如 goto、bit32 等。
  • 推荐用法:聚合、遍历、条件判断、table 操作等常规 Lua 5.1 语法均可放心使用。

示例:

1
2
3
4
5
local arr = {}
table.insert(arr, 1)
for i, v in ipairs(arr) do
  print(i, v)
end

如需更复杂的聚合逻辑,可参考 gopher-lua 官方文档 和 Lua 5.1 语法手册。

8. 参考资料

0%