下面给你一个 完整可运行的 Go 示例 ,演示如何使用 Kubernetes 的 Lease
实现 leader 选举机制 。
这个示例使用官方 client-go
提供的 leaderelection
库,能在多个副本间自动选主、续约、切换。
🧩 示例结构
lease-demo/
├── main.go
├── go.mod
└── go.sum
📦 1. go.mod
go
module lease-demo
go 1.20
require (
k8s.io/client-go v0.30.0
k8s.io/apimachinery v0.30.0
)
🧠 2. main.go
go
package main
import (
"context"
"flag"
"fmt"
"os"
"time"
v1 "k8s.io/api/coordination/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/leaderelection"
"k8s.io/client-go/tools/leaderelection/resourcelock"
)
func main() {
var leaseName, leaseNamespace string
flag.StringVar(&leaseName, "lease-name", "demo-lease", "Name of the lease resource")
flag.StringVar(&leaseNamespace, "lease-namespace", "default", "Namespace of the lease resource")
flag.Parse()
// 1️⃣ 连接集群(使用 InCluster 或本地 kubeconfig)
cfg, err := rest.InClusterConfig()
if err != nil {
fmt.Println("Not in cluster, try kubeconfig")
cfg, err = rest.InClusterConfig()
if err != nil {
panic(err)
}
}
clientset, err := kubernetes.NewForConfig(cfg)
if err != nil {
panic(err)
}
// 2️⃣ 设置租约对象(LeaseLock)
id, _ := os.Hostname()
lock := &resourcelock.LeaseLock{
LeaseMeta: metav1.ObjectMeta{
Name: leaseName,
Namespace: leaseNamespace,
},
Client: clientset.CoordinationV1(),
LockConfig: resourcelock.ResourceLockConfig{
Identity: id,
},
}
// 3️⃣ 创建上下文
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// 4️⃣ Leader 选举配置
leaderelectionCfg := leaderelection.LeaderElectionConfig{
Lock: lock,
LeaseDuration: 15 * time.Second, // 租约有效期
RenewDeadline: 10 * time.Second, // 续约截止时间
RetryPeriod: 2 * time.Second, // 重试间隔
ReleaseOnCancel: true,
Name: "lease-demo-election",
Callbacks: leaderelection.LeaderCallbacks{
OnStartedLeading: func(ctx context.Context) {
fmt.Printf("[%s] ✅ 成为 leader,开始执行任务...\n", id)
for {
select {
case <-ctx.Done():
return
default:
fmt.Printf("[%s] 正在执行任务中...\n", id)
time.Sleep(5 * time.Second)
}
}
},
OnStoppedLeading: func() {
fmt.Printf("[%s] ❌ 失去 leader 身份\n", id)
},
OnNewLeader: func(identity string) {
if identity == id {
fmt.Printf("[%s] 🏆 我是当前 leader\n", id)
} else {
fmt.Printf("[%s] 当前 leader 是 %s\n", id, identity)
}
},
},
}
// 5️⃣ 启动 leader 选举
leaderelection.RunOrDie(ctx, leaderelectionCfg)
}
🚀 3. 构建镜像 & 部署
Dockerfile
dockerfile
FROM golang:1.21 as builder
WORKDIR /app
COPY . .
RUN go mod tidy && go build -o lease-demo .
FROM alpine:3.18
COPY --from=builder /app/lease-demo /lease-demo
ENTRYPOINT ["/lease-demo"]
Kubernetes Deployment 示例
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: lease-demo
spec:
replicas: 3
selector:
matchLabels:
app: lease-demo
template:
metadata:
labels:
app: lease-demo
spec:
serviceAccountName: lease-demo-sa
containers:
- name: lease-demo
image: yourrepo/lease-demo:latest
args: ["--lease-name=demo-lease", "--lease-namespace=default"]
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: lease-demo-sa
namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: lease-demo-role
namespace: default
rules:
- apiGroups: ["coordination.k8s.io"]
resources: ["leases"]
verbs: ["get", "watch", "list", "update", "create"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: lease-demo-rb
namespace: default
subjects:
- kind: ServiceAccount
name: lease-demo-sa
roleRef:
kind: Role
name: lease-demo-role
apiGroup: rbac.authorization.k8s.io
🧾 4. 运行效果
部署后:
bash
kubectl logs -l app=lease-demo -f
输出类似:
[node1] 🏆 我是当前 leader
[node1] ✅ 成为 leader,开始执行任务...
[node2] 当前 leader 是 node1
[node3] 当前 leader 是 node1
...
(node1 被删除后)
[node2] 🏆 我是当前 leader
[node2] ✅ 成为 leader,开始执行任务...
✅ 5. 小结
功能 | 描述 |
---|---|
LeaseLock | 使用 coordination.k8s.io/v1 的 Lease 实现选举 |
LeaseDuration | leader 失效超时时间 |
RenewDeadline | leader 续租截止时间 |
RetryPeriod | 非 leader 的重试时间 |
OnStartedLeading | 成为 leader 后执行的逻辑 |
OnStoppedLeading | 失去 leader 时触发 |
OnNewLeader | 每次 leader 切换都会回调 |
是否希望我再帮你生成一个对应的 可直接运行的 YAML + Go 代码打包 zip 文件 ?(可以直接 kubectl apply -f
运行)