Karmada karmadactl uncordon 命令详解:将集群重新标记为可调度
2026/9/17 20:10:54 网站建设 项目流程

Karmada karmadactl uncordon 命令详解:将集群重新标记为可调度

【免费下载链接】karmadaOpen, Multi-Cloud, Multi-Cluster Kubernetes Orchestration项目地址: https://gitcode.com/GitHub_Trending/ka/karmada

karmadactl uncordon是 Karmada 多集群编排平台中用于"解除集群封锁"的命令,它通过移除集群上的cluster.karmada.io/unschedulable污点(taint),将处于维护/封锁状态的成员集群重新标记为可调度(schedulable),从而让 Karmada 调度器可以再次向该集群分发工作负载。本文以 karmadactl uncordon 官方命令参考 为骨架,结合 Karmada 仓库源码(cordon 命令实现、污点常量定义、调度器 TaintToleration 插件)深入讲解其用法、参数、底层原理与完整运维流程。读完本文,你将掌握:如何用一条命令安全地恢复一个被 cordon 的集群、--dry-run的预演用法、以及 uncordon 背后的"污点-容忍"调度机制。

命令概述:uncordon 与 cordon 的对称关系

在 Karmada 中,cordonuncordon是一对互补的集群管理命令,用于在不删除成员集群的前提下,临时控制该集群是否接收新的调度任务:

命令作用默认调度行为
karmadactl cordon CLUSTER将集群标记为不可调度(unschedulable),防止新资源被调度到该集群调度器不再向其分配新工作负载
karmadactl uncordon CLUSTER将集群重新标记为可调度(schedulable),恢复其接收调度任务的能力调度器恢复向其分配工作负载

从源码注释可以确认二者的语义定位(cordon.go):

cordonLong = templates.LongDesc(` Mark cluster as unschedulable.`) uncordonLong = templates.LongDesc(` Mark cluster as schedulable.`)

以及两个内部状态常量:

const ( // DesiredCordon a flag indicate karmadactl.RunCordonOrUncordon cordon a cluster, // cordon prevent new resource scheduler to cordoned cluster. DesiredCordon = iota // DesiredUnCordon a flag indicate karmadactl.RunCordonOrUncordon uncordon a cluster. DesiredUnCordon )

也就是说,uncordoncordon在实现上共享同一套核心逻辑RunCordonOrUncordon,仅通过desired参数区分"加污点"与"去污点"两种动作。在 karmadactl 主命令注册处 中,二者都被归入 "Cluster Management Commands"(集群管理命令组),与taint命令并列:

{ Message: "Cluster Management Commands:", Commands: []*cobra.Command{ cordon.NewCmdCordon(f, parentCommand), cordon.NewCmdUncordon(f, parentCommand), taint.NewCmdTaint(f, parentCommand), }, },

命令语法(Synopsis)

uncordon命令的用法非常简洁,只接收一个集群名作为位置参数:

karmadactl uncordon CLUSTER

官方文档给出的标准示例是(karmadactl_uncordon.md):

# Mark cluster "foo" as schedulable. karmadactl uncordon foo

即:执行karmadactl uncordon foo后,名为foo的成员集群将被标记为可调度。

参数约束从 cordon.go 的 Complete 方法 中可以明确看到:

func (o *CommandCordonOption) Complete(args []string) error { // Get cluster name from the command args. if len(args) == 0 { return errors.New("cluster name is required") } if len(args) > 1 { return errors.New("more than one cluster name is not supported") } o.ClusterName = args[0] return nil }

因此:

  • 必须提供集群名,缺省会直接报错cluster name is required
  • 一次只能操作一个集群,传入多个名称会报错more than one cluster name is not supported
  • 集群名不支持通配符或批量操作,需要逐个 uncordon。

另外,命令定义中通过ValidArgsFunction: utilcomp.SpecifiedResourceTypeAndNameCompletionFunc(f, []string{"cluster"})(cordon.go)为CLUSTER参数注册了基于集群资源的 shell 自动补全,交互式终端下输入 Tab 可以提示当前已注册的集群名。

选项(Options)详解

uncordon自身的选项(不含继承项)如下表,与 karmadactl_uncordon.md 完全一致:

选项类型说明
--dry-runbool以 dry-run 模式运行命令,不发起任何服务器请求,仅模拟执行
-h, --helpbool显示uncordon命令的帮助信息
--karmada-context stringstring要使用的 kubeconfig 中的 context 名称
--kubeconfig stringstringCLI 请求所使用的 kubeconfig 文件路径

这些 flag 的来源可从源码确认:NewCmdUncordon中通过options.AddKubeConfigFlags(flags)注册--kubeconfig--karmada-context,通过flags.BoolVar(&opts.DryRun, "dry-run", false, ...)注册--dry-run(cordon.go)。其中 kubeconfig 相关 flag 的具体实现在 pkg/karmadactl/options/global.go:

// AddKubeConfigFlags adds flags to the specified FlagSet. func AddKubeConfigFlags(flags *pflag.FlagSet) { flags.StringVar(DefaultConfigFlags.KubeConfig, "kubeconfig", *DefaultConfigFlags.KubeConfig, "Path to the kubeconfig file to use for CLI requests.") flags.StringVar(DefaultConfigFlags.Context, "karmada-context", *DefaultConfigFlags.Context, "The name of the kubeconfig context to use") }

使用要点:

  • --kubeconfig:默认取~/.kube/config(即默认 kubeconfig 路径)。uncordon操作的是 Karmada 控制面(karmada-apiserver)上的 Cluster 对象,因此这里的 kubeconfig 必须指向包含 Karmada 控制面访问凭据的配置文件,而不是成员集群的 kubeconfig;
  • --karmada-context:当 kubeconfig 中包含多个 context(例如同时有 Karmada 控制面与多个成员集群的 context)时,用它指定使用名为该值的 context 来发起请求;
  • --dry-run:预演模式,不真正修改集群状态,适合在维护窗口前核对命令参数是否正确。关于 dry-run 的语义,命令选项结构体注释 写得很清楚:"DryRun tells if run the command in dry-run mode, without making any server requests."(dry-run 模式下完全不发起服务器请求)。

继承自父命令的选项(Options inherited from parent commands)

与 Karmada 其他 CLI 命令一致,uncordon还会继承一组来自 klog 的日志控制选项,它们与命令本身的业务逻辑无关,仅在排查 CLI 行为时需要关注:

选项说明
--add-dir-header为日志消息头附加文件目录(true 时生效)
--alsologtostderr同时将日志输出到 stderr 和文件(当-logtostderr=true时无效果)
--alsologtostderrthreshold severity当日志级别达到或超过该阈值时输出到 stderr(--alsologtostderr=true时生效)
--legacy-stderr-threshold-behavior为 true 时,logtostderr=true情况下忽略stderrthreshold(默认 true)
--log-backtrace-at traceLocation当日志命中file:N时输出堆栈跟踪(默认:0
--log-dir string非空时,日志文件写入该目录(-logtostderr=true时无效果)
--log-file string非空时,使用该文件作为日志文件(-logtostderr=true时无效果)
--log-file-max-size uint日志文件最大大小(MB),0 表示不限制(默认 1800)
--logtostderr将日志输出到 stderr 而非文件(默认 true)
--one-output为 true 时只向日志自身的原生级别写入,不再向更低级别写入(-logtostderr=true时无效果)
--skip-headers为 true 时,日志消息不添加头部前缀
--skip-log-headers为 true 时,打开日志文件时不写头部(-logtostderr=true时无效果)
--stderrthreshold severity日志级别达到或超过该阈值时输出到 stderr(默认 2)
-v, --v Level日志详细程度级别
--vmodule moduleSpecpattern=N逗号分隔列表形式,按文件过滤日志级别

这些继承选项与karmadactl主命令共享,完整清单见 karmadactl_uncordon.md。

实战用法:完整运维流程示例

典型场景:集群维护与恢复

Karmada 中 uncordon 最常见的应用场景是集群维护窗口管理:

# 1. 查看当前已加入 Karmada 的成员集群 karmadactl get clusters # 2. 进入维护前,封锁目标集群,停止接收新的调度任务 karmadactl cordon member-cluster-1 # 3. 确认封锁生效(可观察到集群状态或结合 scheduler 日志确认) karmadactl get cluster member-cluster-1 -o yaml # 此时 member-cluster-1 的 spec.taints 中应包含: # - effect: NoSchedule # key: cluster.karmada.io/unschedulable # 4. 维护完成后,解除封锁,恢复调度能力 karmadactl uncordon member-cluster-1 # 5. 结果确认(控制台输出 "member-cluster-1 cluster uncordoned")

预演(Dry-run)用法

在对生产集群执行 uncordon 前,可以先预演一遍:

karmadactl uncordon member-cluster-1 --dry-run

dry-run 模式下命令不会向 karmada-apiserver 发起任何写请求,输出结果与真实执行一致(member-cluster-1 cluster uncordoned),可用于核对集群名、context 等参数是否配置正确。

指定 kubeconfig 与 context

当你的 kubeconfig 同时包含多个 Karmada 控制面(如多套环境)时:

# 使用指定 kubeconfig 文件 karmadactl uncordon foo --kubeconfig /path/to/karmada.config # 使用 kubeconfig 中的指定 context karmadactl uncordon foo --karmada-context karmada-prod

底层原理:污点(Taint)机制

uncordon并不是直接修改集群的某个"调度开关"字段,而是通过 Kubernetes 标准的污点(Taint)机制实现的。命令内部操作的关键对象是集群spec.taints中的一条不可调度污点

不可调度污点的定义

污点的键名定义在 pkg/apis/cluster/v1alpha1/well_known_constants.go:

const ( // TaintClusterUnscheduler will be added when cluster becomes unschedulable // and removed when cluster becomes schedulable. TaintClusterUnscheduler = "cluster.karmada.io/unschedulable" // TaintClusterNotReady will be added when cluster is not ready // and removed when cluster becomes ready. TaintClusterNotReady = "cluster.karmada.io/not-ready" // TaintClusterUnreachable will be added when cluster becomes unreachable // (corresponding to ClusterConditionReady status ConditionUnknown) // and removed when cluster becomes reachable (ClusterConditionReady status ConditionTrue). TaintClusterUnreachable = "cluster.karmada.io/unreachable" )

其中TaintClusterUnschedulercluster.karmada.io/unschedulable)是 cordon/uncordon 命令专门操作的污点,其 Effect 为NoSchedule,由命令内部构造:

unschedulerTaint := corev1.Taint{ Key: clusterv1alpha1.TaintClusterUnscheduler, Effect: corev1.TaintEffectNoSchedule, }

对应地,TaintClusterNotReadycluster.karmada.io/not-ready)与TaintClusterUnreachablecluster.karmada.io/unreachable)是由集群控制器根据集群健康状态自动添加的污点,与手动运维的 uncordon 命令无关。

uncordon 的执行链路

uncordon的完整执行链路在 cordon.go 的 RunCordonOrUncordon 中:

func RunCordonOrUncordon(desired int, f util.Factory, opts CommandCordonOption) error { cordonOrUncordon := "cordon" if desired == DesiredUnCordon { cordonOrUncordon = "un" + cordonOrUncordon } karmadaClient, err := f.KarmadaClientSet() if err != nil { return err } cluster, err := karmadaClient.ClusterV1alpha1().Clusters().Get(context.TODO(), opts.ClusterName, metav1.GetOptions{}) if err != nil { return err } cordonHelper := newCordonHelper(cluster) if !cordonHelper.updateIfRequired(desired) { fmt.Printf("%s cluster %s\n", cluster.Name, alreadyStr(desired)) return nil } if !opts.DryRun { err := cordonHelper.patchOrReplace(karmadaClient) if err != nil { return err } } fmt.Printf("%s cluster %sed\n", cluster.Name, cordonOrUncordon) return nil }

流程可以拆解为四步:

  1. 获取集群对象:通过KarmadaClientSet().ClusterV1alpha1().Clusters().Get()从 karmada-apiserver 读取目标 Cluster 的完整对象;
  2. 判断是否真的需要变更:调用updateIfRequired检查目标状态。若集群"本来就没有/本来就有"不可调度污点,则直接输出already uncordoned/already cordoned并返回,不产生任何写操作
  3. 非 dry-run 时写回:通过patchOrReplace将变更后的污点列表写回 karmada-apiserver;
  4. 输出结果:成功执行后输出xxx cluster uncordoned

幂等性判断逻辑

updateIfRequired(cordon.go)保证了命令的幂等性

func (c *cordonHelper) updateIfRequired(desired int) bool { c.desired = desired if desired == DesiredCordon && !c.hasUnschedulerTaint() { return true } if desired == DesiredUnCordon && c.hasUnschedulerTaint() { return true } return false } func (c *cordonHelper) hasUnschedulerTaint() bool { unschedulerTaint := corev1.Taint{ Key: clusterv1alpha1.TaintClusterUnscheduler, Effect: corev1.TaintEffectNoSchedule, } for _, taint := range c.cluster.Spec.Taints { if taint.MatchTaint(&unschedulerTaint) { return true } } return false }
  • 执行 uncordon 时,若集群存在cluster.karmada.io/unschedulable(NoSchedule)污点 → 执行移除;
  • 若集群不存在该污点 → 输出<cluster> cluster already uncordoned,直接结束。

这意味着重复执行 uncordon 是安全的,不会报错也不会产生多余的 API 请求。

写回方式:优先补丁,回退更新

patchOrReplace(cordon.go)采用"优先 patch、失败则 update"的策略:

patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, c.cluster) if err == nil { _, err = client.Patch(context.TODO(), c.cluster.Name, types.MergePatchType, patchBytes, metav1.PatchOptions{}) } else { _, err = client.Update(context.TODO(), c.cluster, metav1.UpdateOptions{}) }
  • 先将修改前后的 Cluster 对象分别序列化为 JSON;
  • 尝试生成strategic merge patch,生成成功则走Patch(MergePatchType)请求,避免覆盖其他字段的并发修改;
  • 若 patch 构造失败(例如对象无法编码为 JSON),则退化为整对象Update

对于 uncordon(DesiredUnCordon),移除污点的实现是典型的"swap-and-truncate"技巧,从 taints 切片中定位匹配项后与最后一个元素交换,再截断切片:

if c.desired == DesiredUnCordon { for i, n := 0, len(c.cluster.Spec.Taints); i < n; i++ { if c.cluster.Spec.Taints[i].MatchTaint(&unschedulerTaint) { c.cluster.Spec.Taints[i] = c.cluster.Spec.Taints[n-1] c.cluster.Spec.Taints = c.cluster.Spec.Taints[:n-1] break } } }

注意这里只移除第一个匹配的不可调度污点;同时,由于只匹配Key+EffectMatchTaint不校验Value),即使污点带任意 Value 也会被匹配移除。

调度器如何响应:TaintToleration 插件

了解 uncordon 如何生效,还需要理解调度器侧对污点的处理。Karmada 调度器的TaintToleration过滤插件(pkg/scheduler/framework/plugins/tainttoleration/taint_toleration.go)会在调度每个 ResourceBinding 时检查目标集群的污点是否被传播策略的clusterTolerations容忍:

filterPredicate := func(t *corev1.Taint) bool { return t.Effect == corev1.TaintEffectNoSchedule || t.Effect == corev1.TaintEffectNoExecute } taint, isUntolerated := v1helper.FindMatchingUntoleratedTaint(klog.Background(), cluster.Spec.Taints, bindingSpec.Placement.ClusterTolerations, filterPredicate, false) if !isUntolerated { return framework.NewResult(framework.Success) } return framework.NewResult(framework.Unschedulable, fmt.Sprintf("cluster(s) had untolerated taint {%s}", taint.ToString()))

核心结论:

  • 当集群带有cluster.karmada.io/unschedulable(Effect=NoSchedule)污点,且对应传播策略的 Placement 中没有配置能容忍该污点的clusterTolerations时,该集群在调度过滤阶段会被判定为Unschedulable,调度器不会向它分发新的工作负载;
  • 执行karmadactl uncordon移除该污点后,集群重新通过 TaintToleration 过滤,恢复接收新调度任务的能力。

因此uncordon的完整生效链路是:命令移除集群污点 → 调度器过滤插件重新评估 → 集群恢复可调度

相关字段:Placement 中的 clusterTolerations

与污点机制配套的ClusterTolerations字段定义在传播策略 API 中(pkg/apis/policy/v1alpha1/propagation_types.go):

// ClusterTolerations represents the tolerations. // +optional ClusterTolerations []corev1.Toleration `json:"clusterTolerations,omitempty"`

它是一个corev1.Toleration切片,语义与 Kubernetes Pod 的 tolerations 一致。运维上这意味着:即使集群被 cordon(带有 NoSchedule 污点),只要某个 PropagationPolicy/ClusterPropagationPolicy 在 Placement 中显式配置了匹配该污点的容忍(例如key: cluster.karmada.io/unschedulableeffect: NoSchedule),调度器仍可能将特定工作负载调度到该集群——这是"强制调度"的合法通道,与 uncordon 的"全局恢复调度"形成互补。

测试验证:行为由单元测试保障

uncordon的行为有完整的单元测试覆盖,见 pkg/karmadactl/cordon/cordon_test.go。TestRunCordonOrUncordon使用 fake clientset 覆盖了四种组合场景:

测试用例前置状态操作期望输出
CordonUncordonedCluster_ClusterCordoned未封锁cordon<name> cluster cordoned
CordonCordonedCluster_ClusterAlreadyCordoned已封锁cordon<name> cluster already cordoned
UncordonCordonedCluster_ClusterUncordoned已封锁uncordon<name> cluster uncordoned
UncordonUncordonedCluster_ClusterAlreadyUncordoned未封锁uncordon<name> cluster already uncordoned

测试中的状态校验函数(checkClusterCordonedCondition/checkClusterUncordonedCondition)直接断言集群spec.taints中是否存在cluster.karmada.io/unschedulable+NoSchedule组合:

var checkClusterUncordonedCondition = func(cluster *clusterv1alpha1.Cluster) error { for _, taint := range cluster.Spec.Taints { if taint.Key == clusterv1alpha1.TaintClusterUnscheduler && taint.Effect == corev1.TaintEffectNoSchedule { return fmt.Errorf("expected no noschedule taint for cluster %s, but got %v", cluster.GetName(), taint) } } return nil }

这从测试层面再次印证:uncordon 的本质就是"确保集群 spec.taints 中不存在不可调度污点",且对已处于可调度状态的集群重复执行会输出already uncordoned而不是报错。

关联命令与进一步阅读

  • karmadactl cordon:与 uncordon 配套的封锁命令,二者共享同一实现(cordon.go);
  • karmadactl taint:手动为集群添加/移除任意污点,比 cordon 更灵活,适用于自定义调度约束;
  • karmadactl 命令总览:查看全部 karmadactl 子命令;
  • 调度器侧污点容忍过滤插件实现:pkg/scheduler/framework/plugins/tainttoleration/taint_toleration.go;
  • 污点常量定义:pkg/apis/cluster/v1alpha1/well_known_constants.go。

小结

karmadactl uncordon是 Karmada 集群日常运维中"恢复调度"的标准动作,核心要点可归纳为:

  1. 用法极简karmadactl uncordon CLUSTER,一次只能操作一个集群;
  2. 本质是污点操作:命令移除集群上的cluster.karmada.io/unschedulable(NoSchedule)污点,而非修改其他调度字段;
  3. 幂等安全:对已可调度的集群重复执行输出already uncordoned,不会产生副作用;
  4. 预演友好--dry-run模式下不发起任何服务器请求,可在维护窗口前安全验证;
  5. 与调度器联动:污点移除后,调度器的 TaintToleration 过滤插件才会允许新工作负载调度到该集群,配合 Placement 的clusterTolerations可实现"强制调度"等高级策略。

掌握cordon/uncordon/taint这三个命令的组合使用,即可在不删除集群的前提下,对多集群调度行为进行精细的维护控制。

【免费下载链接】karmadaOpen, Multi-Cloud, Multi-Cluster Kubernetes Orchestration项目地址: https://gitcode.com/GitHub_Trending/ka/karmada

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询