State Lock API no Kubernetes: Coordenação e Eleição de Líder

Em sistemas distribuídos, é comum precisar garantir que apenas uma instância de um serviço execute uma tarefa crítica — seja backup, sincronização de dados ou processamento de eventos. O Kubernetes oferece a State Lock API exatamente para isso: coordenação segura entre múltiplos pods.

Neste post, vamos entender como usar State Lock API, quando usá-la, compará-la com outras abordagens e implementar um exemplo prático.

O que é State Lock API?

A State Lock API do Kubernetes (via leaderelection) é um mecanismo que permite que múltiplas réplicas de um aplicativo coordenem entre si para eleger um “líder” — uma única instância responsável por executar tarefas críticas.

Ela funciona através de:

  • Recurso ConfigMap ou Lease: armazena informações sobre quem é o líder
  • Renovação periódica: o líder renova seu “lock” em intervalos regulares
  • Eleição automática: se o líder falhar, outro pod toma o lugar

Comparação com outras abordagens

1. State Lock API (Kubernetes-native)

✅ Integrado ao Kubernetes
✅ Não requer dependências externas
✅ Simples e confiável
❌ Limitado ao cluster Kubernetes

2. Banco de dados (PostgreSQL, MySQL)

✅ Funciona fora do Kubernetes
✅ Mais flexível
❌ Requer outro serviço
❌ Maior latência

3. Redis/Memcached

✅ Rápido
✅ Simples
❌ Não é persistente
❌ Requer cluster Redis

4. Etcd (direto)

✅ Muito consistente
✅ Usado pelo próprio Kubernetes
❌ Complexo de usar diretamente
❌ Overhead de gerenciamento

Recomendação: Use State Lock API para aplicações em Kubernetes. Para casos multi-cluster ou fora do K8s, considere banco de dados.

Como funciona internamente

A eleição de líder no Kubernetes usa um mecanismo baseado em leasing:

Pod A (candidato)
    ↓
Tenta adquirir lock em ConfigMap/Lease
    ↓
Se conseguir → Se torna líder e renova a cada 15s
    ↓
Se falhar → Aguarda e tenta novamente
    ↓
Se o líder cair → Outro pod adquire o lock após timeout

O Kubernetes fornece a biblioteca client-go com a função leaderelection que encapsula toda essa lógica.

Implementação prática

Aqui está um exemplo completo de um aplicativo que usa State Lock API:

package main

import (
	"context"
	"fmt"
	"os"
	"os/signal"
	"syscall"

	"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() {
	// Configuração do cliente Kubernetes
	config, err := rest.InClusterConfig()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Erro ao carregar config K8s: %v\n", err)
		os.Exit(1)
	}

	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Erro ao criar clientset: %v\n", err)
		os.Exit(1)
	}

	podName := os.Getenv("POD_NAME")
	namespace := os.Getenv("POD_NAMESPACE")
	lockName := "my-app-lock"

	// Define o lock (pode ser ConfigMap ou Lease)
	lock := &resourcelock.LeaseLock{
		LeaseMeta: metav1.ObjectMeta{
			Name:      lockName,
			Namespace: namespace,
		},
		Client: clientset.CoordinationV1(),
		LockConfig: resourcelock.ResourceLockConfig{
			Identity: podName,
		},
	}

	// Configuração da eleição
	leaderElectionConfig := leaderelection.LeaderElectionConfig{
		Lock:            lock,
		LeaseDuration:   15 * time.Second,
		RenewDeadline:   10 * time.Second,
		RetryPeriod:     2 * time.Second,
		ReleaseOnCancel: true,
		Callbacks: leaderelection.LeaderCallbacks{
			OnStartedLeading: func(ctx context.Context) {
				fmt.Println("🎯 Sou o líder! Iniciando tarefas críticas...")
				runLeaderTasks(ctx)
			},
			OnStoppedLeading: func() {
				fmt.Println("❌ Perdi a liderança")
			},
			OnNewLeader: func(identity string) {
				if identity != podName {
					fmt.Printf("✅ Novo líder: %s\n", identity)
				}
			},
		},
	}

	// Inicia eleição
	leaderelection.RunOrDie(context.Background(), leaderElectionConfig)
}

func runLeaderTasks(ctx context.Context) {
	ticker := time.NewTicker(10 * time.Second)
	defer ticker.Stop()

	for {
		select {
		case <-ctx.Done():
			fmt.Println("Parando tarefas do líder")
			return
		case <-ticker.C:
			fmt.Println("Executando tarefa crítica...")
			// Sua lógica aqui
		}
	}
}

Configuração no Kubernetes

Para que seu pod tenha permissão de criar/atualizar Leases, adicione RBAC:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-app-sa
  namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: my-app-leader-election
  namespace: default
rules:
- apiGroups: ["coordination.k8s.io"]
  resources: ["leases"]
  verbs: ["get", "create", "update"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: my-app-leader-election
  namespace: default
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: my-app-leader-election
subjects:
- kind: ServiceAccount
  name: my-app-sa
  namespace: default

E no Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      serviceAccountName: my-app-sa
      containers:
      - name: app
        image: my-app:latest
        env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace

Boas práticas

1. Use Lease, não ConfigMap
A partir do Kubernetes 1.14+, use Lease em vez de ConfigMap para eleição. É mais leve e eficiente.

2. Configure timeouts realistas

  • LeaseDuration: quanto tempo o lock dura (ex: 15s)
  • RenewDeadline: até quando tentar renovar (ex: 10s)
  • RetryPeriod: quanto aguardar antes de tentar novamente (ex: 2s)

3. Monitore a liderança
Registre quando um pod se torna ou deixa de ser líder. Use métricas:

leaderMetric.WithLabelValues(podName).Set(1) // sou líder
leaderMetric.WithLabelValues(podName).Set(0) // não sou líder

4. Evite tarefas bloqueantes
Se a tarefa do líder demorar muito, a renovação do lock pode falhar. Use contexts e timeouts.

Conclusão

State Lock API é a forma mais simples e nativa de implementar eleição de líder em Kubernetes. Se você precisa garantir que uma única instância execute uma tarefa crítica, esta é a solução recomendada.

A chave é entender os timeouts, usar Lease em vez de ConfigMap, e sempre implementar logging para visibilidade em produção.

Gostou do conteúdo?

  • Inscreva-se na newsletter para receber mais dicas práticas sobre Go e Kubernetes diretamente no seu e-mail!
  • 🚀 Conheça a Imersão Golang e leve seus conhecimentos em Go para o próximo nível!

Faça parte da comunidade!

Receba os melhores conteúdos sobre Go, Kubernetes, arquitetura de software, Cloud e esteja sempre atualizado com as tendências e práticas do mercado.

* indicates required

Deixe uma resposta