首页
/ Kubernetes Addon Manager 深入解析:基于声明式标签的集群 Addon 生命周期管理机制

Kubernetes Addon Manager 深入解析:基于声明式标签的集群 Addon 生命周期管理机制

2026-09-06 18:35:32作者:虞亚竹Luna

导读

本文围绕 Kubernetes 主仓库中 addon-manager 组件(控制面自带的 Addon 生命周期管理器)展开:它扫描 /etc/kubernetes/addons/ 目录下的模板清单,通过 addonmanager.kubernetes.io/mode 标签把 Addon 分为 ReconcileEnsureExists 两类,分别采用「周期性收敛 + 清理」与「仅确保存在」两种治理语义,从而让 DNS、网络策略、监控等集群组件以声明式方式随控制面长期稳定运行。读完本文你将掌握这两类标签的行为差异、addon-manager 的循环调度与选主实现原理、真实清单的标签编写规范,以及镜像的多架构发布流程。


一、Addon-manager 是什么:运行在控制面节点上的"声明式 Addon 管家"

Addon-manager 是随 Kubernetes 控制面(master / control-plane 节点)一起部署的控制器类 Pod,核心职责是管理一组以模板文件形式存放在 $ADDON_PATH 目录(默认 /etc/kubernetes/addons/)中的集群组件清单,这些组件通常被称为"集群 Addon",例如 DNS、Calico 网络策略、集群级监控与指标采集器、日志收集器等。

与用户空间中自行 kubectl apply 的业务应用不同,这些 Addon 属于集群基础设施,需要与控制面同生命周期:节点初始化(如 GCE 通过 kube-addons.sh / salt / cloud-init 生成清单)时投放,addon-manager 负责保证它们在集群中"应该是什么样就是什么样"。

kube-addons.sh 中可以看到其运行时基线的定义:

KUBECTL=${KUBECTL_BIN:-/usr/local/bin/kubectl}
ADDON_CHECK_INTERVAL_SEC=${TEST_ADDON_CHECK_INTERVAL_SEC:-60}
ADDON_PATH=${ADDON_PATH:-/etc/kubernetes/addons}
SYSTEM_NAMESPACE=kube-system
ADDON_MANAGER_LABEL="addonmanager.kubernetes.io/mode"
CLUSTER_SERVICE_LABEL="kubernetes.io/cluster-service"

其中两个标签(ADDON_MANAGER_LABELCLUSTER_SERVICE_LABEL)正是决定 Addon 治理语义的钥匙,下面详细展开。


二、两类治理模式:ReconcileEnsureExists 的行为与选型

addon-manager 根据清单文件上 addonmanager.kubernetes.io/mode 标签的取值,将 Addon 划分成两个"类别(class)",分别套用完全不同的管理策略。下表整理了 README 描述与 kube-addons.sh 实现中两个核心函数 reconcile_resource_from_string / ensure_resource_from_string 的对应行为:

维度 Reconcile(收敛类) EnsureExists(仅确保存在类)
标签取值 addonmanager.kubernetes.io/mode: Reconcile addonmanager.kubernetes.io/mode: EnsureExists
底层命令 kubectl apply ... --prune=true(周期性) kubectl create ...(仅探测创建)
对象被删除 会按模板文件重新创建 对象不存在时重新创建(存在则跳过)
对象字段被手工改 周期性纠正回模板定义的状态 用户可自由编辑,addon-manager 不干预
模板文件被删 对应的 Addon 会被 prune 删除 Addon 不会被删除,继续保留
适用场景 完全由仓库/发布方托管的组件(DNS、Calico 等) 需要允许集群管理员自行配置的组件

2.1 Reconcile:全状态收敛 + 垃圾清理

对于标记为 Reconcile 的 Addon,addon-manager 周期性地对它执行带有 --prune=truekubectl apply。README 明确列出的行为包括:

  • 对象被删除后会被重新创建;
  • 对象会被周期性重置为模板文件所给定字段描述的状态(管理员绕过模板的直接改动能被"拉回");
  • 当清单文件从 $ADDON_PATH 被删除时,对应的线上对象会被一并删除(这正是 --prune 清理机制的效果)。

kube-addons.shreconcile_resource_from_string 中可以看到收敛执行是按标签选择器精准作用的:

kubectl_output=$(echo "${config_string}" | ${KUBECTL} ${KUBECTL_OPTS} apply -f - \
  --namespace="${namespace}" -l ${ADDON_MANAGER_LABEL}=Reconcile 2>&1)

而在整目录层面的 reconcile_addonskube-addons.sh)中则分别对"旧式标签 Addon"和"新式 Reconcile Addon"执行了两轮带 prune 的递归 apply:

${KUBECTL} ${KUBECTL_OPTS} apply -f ${ADDON_PATH} \
  -l ${CLUSTER_SERVICE_LABEL}=true,${ADDON_MANAGER_LABEL}!=EnsureExists \
  --prune=true ${prune_allowlist_flags} --recursive | grep -v configured

${KUBECTL} ${KUBECTL_OPTS} apply -f ${ADDON_PATH} \
  -l ${CLUSTER_SERVICE_LABEL}!=true,${ADDON_MANAGER_LABEL}=Reconcile \
  --prune=true ${prune_allowlist_flags} --recursive | grep -v configured

注意 --prune=true--recursive 的配合:addon-manager 是对整个 $ADDON_PATH 目录做递归收敛,prune 会把"目录中已不存在的清单对应的线上对象"删除,因此在 Reconcile 模式下删除模板文件就等于下线该 Addon

2.2 EnsureExists:只保证"存在",不保证"内容"

标记为 EnsureExists 的 Addon 走的是更宽松的语义,README 列出的行为包括:

  • 仅当同名对象不存在时,才用模板文件创建/重建;
  • 对象一旦存在,用户怎么编辑都行,addon-manager 不会覆盖;
  • 清单文件被删除时对象不会被删除——即"存在性"一旦达成即被放权。

对应实现 ensure_resource_from_stringkube-addons.sh)使用 kubectl create 并显式处理幂等冲突:

kubectl_output=$(echo "${config_string}" | ${KUBECTL} ${KUBECTL_OPTS} create -f - \
  --namespace="${namespace}" -l ${ADDON_MANAGER_LABEL}=EnsureExists 2>&1)
...
if echo "${kubectl_output}" | grep --silent "AlreadyExists"; then
  log INFO "== Skipping start ${config_name} in namespace ${namespace}, already exists at $(date -Is)"
  return 0

也就是说:创建遇到 AlreadyExists 被当作"正常情况"跳过,其余错误才会进入重试。整目录层面的 ensure_addonskube-addons.sh)同样把 AlreadyExists 输出过滤掉以免刷日志。

2.3 多资源清单混用两种模式

同一份清单文件中允许通过 --- 分隔同时包含两类资源,addon-manager 会按各自标签分开处理。这一行为在集成测试 kube-addons-test.shtest_create_multiresource 中得到验证:同一次 create_resource_from_string 调用中,EnsureExistslimits 不会被新清单覆盖,而 Reconcilelimits2 会被更新到新值。


三、标签编写规范与历史遗留说明(务必阅读)

1. 目录内每个资源必须声明二选一标签。 README 明确指出:$ADDON_PATH 下的资源若既不带 addonmanager.kubernetes.io/mode=EnsureExists 也不带 Reconcile,会被 addon-manager 直接忽略(omitted)

2. kubernetes.io/cluster-service=true 已弃用。 该标签只对 Addon Manager 有意义。目前(README 注明的过渡期内)带 kubernetes.io/cluster-service=true不带 EnsureExists 的 Addon,仍会被当作 Reconcile 类处理——这点在 kube-addons.shreconcile_addons 中以"为了向后兼容而保留的两轮 apply"形式实现(第一轮专门处理 CLUSTER_SERVICE_LABEL=trueADDON_MANAGER_LABEL!=EnsureExists 的旧式对象)。但未来版本中 addon-manager 可能不再识别它,新清单一律应使用 addonmanager.kubernetes.io/mode

3. 真实 Addon 清单的标签写法。 仓库中大量内置 Addon 都以 Reconcile 模式托管,例如 calico-node-daemonset.yaml

kind: DaemonSet
apiVersion: apps/v1
metadata:
  name: calico-node
  namespace: kube-system
  labels:
    addonmanager.kubernetes.io/mode: Reconcile
    k8s-app: calico-node

可见 Addon 通常落在 kube-system 命名空间,并直接以 addonmanager.kubernetes.io/mode 作为元标签供 addon-manager 选择器过滤。


四、运行时主循环:从启动到周期收敛的完整链路

Addon-manager 的入口容器命令是 /opt/kube-addons-main.sh(见 DockerfileCMD ["/opt/kube-addons-main.sh"]),实际部署形态见 kube-addon-manager.yaml:一个以 hostNetwork: truepriorityClassName: system-node-critical 运行在控制面宿主机上的特权级 Pod,把宿主的 /etc/kubernetes/ 以只读方式挂入容器(addon 模板文件来源),并把日志写到 /var/log/kube-addon-manager.log

kube-addons-main.sh 的主流程可分四步:

  1. 加载公共函数库:优先 source 同目录或 /opt/kube-addons.sh,找不到即报错退出(对应生产镜像安装在 /opt 的布局)。

  2. 启动前等待:阻塞等待 kube-system 命名空间下的 default ServiceAccount 就绪(kubectl get --namespace=kube-system serviceaccount default 失败则每 0.5 秒重试),确保后续创建对象具备可用的认证身份。

  3. 先投放 admission-control 对象:遍历 /etc/kubernetes/admission-controls/ 下的 .yaml/.json,通过 start_addon 以"100 次尝试、10 秒间隔"的强度在 default 命名空间创建(例如默认 LimitRange),保证配额类限制在任何 Addon 之前就位。

  4. 进入周期循环(见 kube-addons-main.sh):

while true; do
  start_sec=$(date +"%s")
  if is_leader; then
    ensure_addons
    reconcile_addons
  else
    log INFO "Not elected leader, going back to sleep."
  fi
  end_sec=$(date +"%s")
  ...
  if [[ ${len_sec} -lt ${ADDON_CHECK_INTERVAL_SEC} ]]; then
    sleep $((ADDON_CHECK_INTERVAL_SEC-len_sec))
  fi
done

循环每 ADDON_CHECK_INTERVAL_SEC(默认 60 秒)触发一轮;若当轮收敛耗时较长,则用"总周期减去已耗时"的方式补足睡眠,避免循环漂移叠加。每轮顺序固定为 ensure_addons(EnsureExists 类)再 reconcile_addons(Reconcile 类),这与 CHANGELOG 中 v6.4-beta.1 的设计一致——先保证"存在",再收敛"内容"。

start_addon / create_resource_from_stringkube-addons.sh)则为单对象创建提供了"带指数式有限重试"的能力:默认在给定尝试次数内循环执行"先 reconcile 后 ensure",失败后记录 WRN 日志并 sleep delay,直到成功或次数耗尽。


五、多主(HA)场景下的选主机制

在多控制面节点(multi-master)部署中,若每个节点都跑一个 addon-manager 同时做 prune,可能互相冲突。addon-manager 的解法是复用 kube-controller-manager 的 Lease 选举结果,而不是自己实现一套选举(见 kube-addons.shis_leader)。

逻辑要点如下:

  • 默认开启,可用环境变量 ADDON_MANAGER_LEADER_ELECTION=false 关闭(关闭后所有 addon-manager 都自认 leader,适用于单主等无需选主的场景)。
  • 开启时,addon-manager 查询 kube-system 下名为 kube-controller-manager 的 Lease(leases.v1.coordination.k8s.io)的 spec.holderIdentity
  • 若该值以当前节点 $HOSTNAME 为前缀,则认为自己是 leader,执行 ensure_addons + reconcile_addons;否则输出 "Not elected leader, going back to sleep" 进入下一轮。
  • 查不到 Lease 信息时记录 ERR 并返回失败(本轮不做收敛)。

即:addon-manager 跟随 kube-controller-manager 的活跃主节点运行,把"谁在收敛 Addon"收敛到与"谁在领导控制面"一致。CHANGELOG 记录了 v9.1.4 与 v9.0.2 曾分别修复过该处选举相关的 bug。


六、prune 白名单与可覆盖的运行时环境变量

由于 Reconcile 依赖 kubectl apply --prune=true,被 prune 的资源类型必须处于"白名单"内。addon-manager 在 kube-addons.sh 内置了一份默认白名单(与 kubectl 自身默认一致),并在 kube-addons.sh 中据此生成 --prune-allowlist 参数:

core/v1/ConfigMap
core/v1/Endpoints
core/v1/Namespace
core/v1/PersistentVolumeClaim
core/v1/PersistentVolume
core/v1/Pod
core/v1/ReplicationController
core/v1/Secret
core/v1/Service
batch/v1/Job
batch/v1/CronJob
apps/v1/DaemonSet
apps/v1/Deployment
apps/v1/ReplicaSet
apps/v1/StatefulSet
networking.k8s.io/v1/Ingress

与之配套的可调环境变量(在 kube-addon-manager.yaml 中通过 Pod env 注入)整理如下:

环境变量 作用 默认值
KUBECTL_PRUNE_WHITELIST_OVERRIDE 用空格分隔的资源列表整体替换默认 prune 白名单 空(使用内置默认白名单)
KUBECTL_EXTRA_PRUNE_WHITELIST 在默认白名单基础上追加允许 prune 的资源
KUBECTL_OPTS 追加到每次 kubectl 调用的全局参数,如 --kubeconfig=/etc/srv/kubernetes/addon-manager/kubeconfig
ADDON_MANAGER_LEADER_ELECTION 是否参与基于 controller-manager Lease 的选主 true
ADDON_CHECK_INTERVAL_SEC 收敛周期(秒),测试时可用 TEST_ADDON_CHECK_INTERVAL_SEC 覆盖 60
ADDON_PATH Addon 清单目录 /etc/kubernetes/addons

KUBECTL_EXTRA_PRUNE_WHITELIST 为例,脚本会在默认列表基础上拼接出合并后的 prune_allowlist,再统一生成 --prune-allowlist <resource> 参数串,从而在 kubectl apply 执行 prune 时覆盖全部需要清理的资源类型。


七、行为契约如何被验证:真集群集成测试

为了让两类标签的行为差异"可回归、可证明",仓库在 kube-addons-test.sh 中提供了面向真实集群的集成测试(运行前提:一个可用的集群且 kubectl 已配置,或通过 make test 使用随镜像发布的 kubectl 版本)。测试框架在每个用例前后自动 create namespace kube-addon-manager-test 与删除清理,并针对三类场景逐项断言:

  1. test_create_resource_reconcilekube-addons-test.sh):验证 Reconcile 模式下——模板由 100m 改为 50m 后集群对象被更新;而用户用 kubectl edit 手工把对象改成 600m 后,addon-manager 再次执行会把用户的改动覆盖回模板值 50m

  2. test_create_resource_ensureexistskube-addons-test.sh):验证 EnsureExists 模式下——模板改动(100m50m不会反映到已存在的对象上;用户手工 edit 成 600m 后再次执行仍保留用户配置;但对象被 kubectl delete 后,addon-manager 会按模板重建它。

  3. test_create_multiresource:验证同一清单里混用 EnsureExistslimitsReconcilelimits2 时,两类语义并行成立、互不干扰。

这套测试与 README 中列出的行为一一对应,是把"删除重建 / 纠正回滚 / 放权用户 / 删除不放权"等承诺固化成可执行断言的关键证据,也解释了 CHANGELOG v9.1.2 中"修复 start_addon 覆盖 EnsureExists 资源"这类历史缺陷的回归背景。


八、镜像产物与多架构发布流程

Addon-manager 镜像托管于 registry.k8s.io,按架构拆分,格式为:

registry.k8s.io/addon-manager/kube-addon-manager-$(ARCH):$(VERSION)

作为对照,仓库内置的 kube-addon-manager.yaml 部署清单引用的当前发布版本为 registry.k8s.io/addon-manager/kube-addon-manager:v9.1.8(该无架构后缀的 tag 用于兼容旧命名)。当前 MakefileVERSION=v9.1.8KUBECTL_VERSION?=v1.32.2,基础镜像为 registry.k8s.io/build-image/debian-base-$(ARCH):bookworm-v1.0.6(CHANGELOG 记录 v9.1.8 起随 kubectl v1.32.2 一起更新)。CHANGELOG 还记录了一个关键演进:v9.1.7 起使用 --prune-allowlist 取代了已弃用的 --prune-whitelist

README 给出的标准发布流程如下:

  1. 修改 addon-manager 源码(kube-addons.sh 等);
  2. Makefile 中递增 VERSION
  3. 如需要,同步递增 Makefile 中的 KUBECTL_VERSION
  4. 先构建 amd64 镜像并在真实集群上验证;
  5. 再推送全部架构的镜像。

对应命令(摘自 README,实际仓库中 make push ARCH=<arch> 会依次执行 build 与 push):

# Build for linux/amd64 (default)
$ make push ARCH=amd64
# ---> registry.k8s.io/addon-manager/kube-addon-manager-amd64:VERSION
# ---> registry.k8s.io/addon-manager/kube-addon-manager:VERSION   # amd64 额外打无架构后缀 tag(向后兼容)

$ make push ARCH=arm
$ make push ARCH=arm64
$ make push ARCH=ppc64le
$ make push ARCH=s390x

Makefile 中对应实现的关键细节:

  • build 会把目录内脚本拷入临时目录,并从 https://dl.k8s.io/release/$(KUBECTL_VERSION)/bin/linux/$(ARCH)/kubectl 下载对应架构 kubectl,再 docker build 打上 $(IMAGE)-$(ARCH):$(VERSION) 标签;
  • amd64 架构构建前需调用 third_party/multiarch 下的 qemu 注册脚本做跨架构仿真注册;
  • push 目标在推送单架构镜像后,仅对 amd64 额外用无架构后缀的 $(IMAGE):$(VERSION) 再打一次 tag 并推送,以满足历史命名兼容(Makefile 中以 TODO 标注未来拟弃用该 tag);
  • 只构建不推送则执行 makemake build
  • make test 会下载 kubectl 并直接运行前文提到的 kube-addons-test.sh

九、小结:如何为你的集群编写一个"正确治理"的 Addon

结合 README 契约与源码实现,可以总结出使用 addon-manager 时最核心的几条实践准则:

  1. 凡是随集群发布、由仓库方完全托管的组件(如 DNS、网络控制器),一律打 addonmanager.kubernetes.io/mode: Reconcile——让 addon-manager 周期性收敛状态,并在你删除清单文件时自动清理线上对象;
  2. 凡是需要集群管理员自行定制的组件,使用 EnsureExists——addon-manager 只负责"首次确保存在",此后内容完全放权给用户,删除清单也不会导致对象消失;
  3. 不要遗漏标签:位于 $ADDON_PATH 但没有任何一种 mode 标签的对象会被直接跳过;
  4. 不要再使用 kubernetes.io/cluster-service=true 标记新对象,它仅作为过渡期兼容逻辑存在,未来可能不再被识别;
  5. 为 prune 的新资源类型(例如你希望 Reconcile 模式能清理的 CRD 之外的资源)通过 KUBECTL_EXTRA_PRUNE_WHITELIST 加入白名单,或整体替换默认列表。

如此,addon-manager 就能像"控制面组件的 kube-controller-manager"一样,用声明式的方式守护整个集群 Addon 集,让你对集群基础设施的变更拥有可预期、可回滚、可审计的收敛语义。

登录后查看全文
热门项目推荐
相关项目推荐