You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Cloud Kubernetes选主:为不同功能配置独立主节点

Spring Cloud Kubernetes 多任务独立选主实现方案

需求背景

已基于Spring Cloud Kubernetes实现单Pod执行定时任务的选主逻辑,现需扩展为Job1、Job2两个定时任务各自独立选主——不同Pod可分别作为两个任务的主节点,各自负责执行对应任务。

核心原理

Spring Cloud Kubernetes的Leader选举通过**独立的角色(Role)**区分不同选举组,每个角色对应一个Kubernetes ConfigMap作为锁资源。不同角色的选举完全隔离,只需为两个任务分配不同的选举角色,即可实现独立选主。

实现步骤

1. 保持Maven依赖不变

当前依赖已满足多角色选主需求,无需修改:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.3.3</version>
    <relativePath/>
</parent>

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-kubernetes-fabric8-leader</artifactId>
</dependency>

2. 配置多角色选举规则

修改application.yaml,为两个任务分别配置独立的选举角色:

spring:
  cloud:
    kubernetes:
      leader:
        enabled: true
        autoStartup: true
        # 配置多个独立选举角色
        roles:
          - name: job1-leader
            namespace: ${spring.cloud.kubernetes.client.namespace:default}
          - name: job2-leader
            namespace: ${spring.cloud.kubernetes.client.namespace:default}

说明:每个角色的name需唯一,namespace可选,默认使用Pod所在的K8s命名空间。

3. 调整定时任务代码

每个任务仅响应自身角色的选举事件,修改ScheduledJob1和ScheduledJob2的事件监听逻辑:

ScheduledJob1(对应job1-leader角色)

import lombok.log4j.Log4j2;
import org.springframework.context.event.EventListener;
import org.springframework.integration.leader.event.OnGrantedEvent;
import org.springframework.integration.leader.event.OnRevokedEvent;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Log4j2
@Component
public class ScheduledJob1 {

    private boolean isJob1Leader = false;
    private static final String JOB1_ROLE = "job1-leader";

    @EventListener(OnGrantedEvent.class)
    public void onLeaderGranted(OnGrantedEvent event) {
        if (JOB1_ROLE.equals(event.getLeaderContext().getRole())) {
            log.info("Job1的主节点权限已授予当前实例");
            isJob1Leader = true;
        }
    }

    @EventListener(OnRevokedEvent.class)
    public void onLeaderRevoked(OnRevokedEvent event) {
        if (JOB1_ROLE.equals(event.getLeaderContext().getRole())) {
            log.info("Job1的主节点权限已从当前实例收回");
            isJob1Leader = false;
        }
    }

    @Scheduled(fixedRate = 2000)
    public void foo1() {
        if (isJob1Leader) {
            log.info("我是Job1的主节点,正在执行foo1任务");
        } else {
            log.info("当前不是Job1主节点,跳过foo1任务");
        }
    }
}

ScheduledJob2(对应job2-leader角色)

import lombok.log4j.Log4j2;
import org.springframework.context.event.EventListener;
import org.springframework.integration.leader.event.OnGrantedEvent;
import org.springframework.integration.leader.event.OnRevokedEvent;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Log4j2
@Component
public class ScheduledJob2 {

    private boolean isJob2Leader = false;
    private static final String JOB2_ROLE = "job2-leader";

    @EventListener(OnGrantedEvent.class)
    public void onLeaderGranted(OnGrantedEvent event) {
        if (JOB2_ROLE.equals(event.getLeaderContext().getRole())) {
            log.info("Job2的主节点权限已授予当前实例");
            isJob2Leader = true;
        }
    }

    @EventListener(OnRevokedEvent.class)
    public void onLeaderRevoked(OnRevokedEvent event) {
        if (JOB2_ROLE.equals(event.getLeaderContext().getRole())) {
            log.info("Job2的主节点权限已从当前实例收回");
            isJob2Leader = false;
        }
    }

    @Scheduled(fixedRate = 2000)
    public void foo2() {
        if (isJob2Leader) {
            log.info("我是Job2的主节点,正在执行foo2任务");
        } else {
            log.info("当前不是Job2主节点,跳过foo2任务");
        }
    }
}

4. 可选:Java代码自定义LeaderCandidate(更灵活)

若需更精细控制选举逻辑,可通过Java配置类手动创建两个LeaderCandidate实例(与yaml配置二选一即可):

import org.springframework.cloud.kubernetes.fabric8.leader.Fabric8LeaderCandidate;
import org.springframework.cloud.kubernetes.fabric8.leader.Fabric8LeaderProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class LeaderConfig {

    private static final String JOB1_ROLE = "job1-leader";
    private static final String JOB2_ROLE = "job2-leader";

    @Bean
    public Fabric8LeaderCandidate job1LeaderCandidate(Fabric8LeaderProperties properties) {
        return new Fabric8LeaderCandidate(properties, JOB1_ROLE);
    }

    @Bean
    public Fabric8LeaderCandidate job2LeaderCandidate(Fabric8LeaderProperties properties) {
        return new Fabric8LeaderCandidate(properties, JOB2_ROLE);
    }
}

验证要点

  1. 部署至少2个Pod到K8s集群
  2. 查看Pod日志,确认:
    • 其中一个Pod成为Job1的主节点,执行foo1任务
    • 另一个Pod成为Job2的主节点,执行foo2任务
    • 当某个Pod下线时,对应任务的主节点会自动切换到其他可用Pod

K8s权限说明

确保Pod拥有ConfigMap的读写权限,可通过RBAC配置:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: leader-role
rules:
  - apiGroups: [""]
    resources: ["configmaps"]
    verbs: ["get", "watch", "list", "create", "update", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  namespace: default
  name: leader-role-binding
subjects:
  - kind: ServiceAccount
    name: default
roleRef:
  kind: Role
  name: leader-role
  apiGroup: rbac.authorization.k8s.io

内容的提问来源于stack exchange,提问作者Georgii Lvov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 12:17:07