11.6. 配置领导选举机制

在 Operator 的生命周期中,在任意给定时间可能有多个实例在运行,例如,推出 Operator 升级程序。这种情况下,需要使用领导选举机制来避免多个 Operator 实例争用。这样可确保只有一个领导实例处理协调,其他实例均不活跃,但却会做好准备,随时接管领导实例的的工作。

有两种不同的领导选举实现可供选择,每种机制都有各自的利弊权衡问题:

  • Leader-for-life:领导 Pod 只有在被删除时才会交出领导权(通过垃圾回收程序)。这种实现可避免两个实例同时作为领导运行(脑裂)。但这种方法可能会延迟选举新的领导。例如,当领导 Pod 位于无响应或分区的节点上时,pod-eviction-timeout 将决定领导 Pod 从节点上删除和停止运行的方式(默认 5m)。详情请参见 Leader-for-life Go 文档。
  • Leader-with-lease:领导 Pod 定期更新领导租约,并在无法更新租约时放弃领导权。当现有领导 Pod 被隔离时,这种实现方式可更快速地过渡至新领导,但在某些情况下存在脑裂的可能性。详情请参见 Leader-with-lease Go 文档。

Operator SDK 默认启用 Leader-for-life 实现。请查阅相关 Go 文档,了解这两种方法,权衡利弊,决定对您的用例来说更有意义的方法。

以下示例演示了如何使用这两个选项。

11.6.1. 使用 Leader-for-life 选举机制

实现 Leader-for-life 选举机制时,调用 leader.Become() 会在 Operator 重试时进行阻止,直至通过创建名为 memcached-operator-lock 的 ConfigMap 使其成为领导:

import (
  ...
  "github.com/operator-framework/operator-sdk/pkg/leader"
)

func main() {
  ...
  err = leader.Become(context.TODO(), "memcached-operator-lock")
  if err != nil {
    log.Error(err, "Failed to retry for leader lock")
    os.Exit(1)
  }
  ...
}

如果 Operator 不在集群内运行,则只会返回 leader.Become() 而无任何错误,以跳过该领导选举机制,因其无法检测 Operator 的命名空间。

11.6.2. 使用 Leader-with-lease 选举机制

Leader-with-lease 实现可使用 Manager Options 来启用以作为领导选举机制:

import (
  ...
  "sigs.k8s.io/controller-runtime/pkg/manager"
)

func main() {
  ...
  opts := manager.Options{
    ...
    LeaderElection: true,
    LeaderElectionID: "memcached-operator-lock"
  }
  mgr, err := manager.New(cfg, opts)
  ...
}

当 Operator 不在集群中运行时,Manager 会在启动时返回一个错误,因其无法检测 Operator 的命名空间以针对领导选举机制创建 ConfigMap。您可通过设置 Manager 的 LeaderElectionNamespace 选项来覆盖该命名空间。