KubeBlocks
BlogsEnterprise
⌘K
​
Blogs

Overview
Quickstart
Architecture

Operations

Lifecycle Management
Vertical Scaling
Horizontal Scaling
Volume Expansion
Switchover
Minor Version Upgrade
Manage Services

Backup & Restore

Backup
Restore

Observability

Observability for etcd Clusters
  1. Prerequisites
  2. Default Services
  3. Expose the etcd Service Externally
    1. Option 1: Cluster-level LoadBalancer (Client Port Only)
    2. Option 2: Per-Pod LoadBalancer (Peer Communication)
  4. Verify the External Service
  5. Connect via the External Endpoint
  6. Cleanup

Manage etcd Services in KubeBlocks

This guide explains how to expose an etcd cluster's client endpoint externally using a cloud provider LoadBalancer, and how to disable it when no longer needed.

Prerequisites

    Before proceeding, verify your environment meets these requirements:

    • A functional Kubernetes cluster (v1.21+ recommended)
    • kubectl v1.21+ installed and configured with cluster access
    • Helm installed (installation guide)
    • KubeBlocks installed (installation guide)
    • etcd Add-on installed and an etcd cluster running (see Quickstart)

    Default Services

    KubeBlocks creates a single headless service for each etcd cluster:

    kubectl get service -n demo | grep etcd-cluster
    Example Output
    NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE etcd-cluster-etcd-headless ClusterIP None <none> 2379/TCP,2380/TCP 5m
    PortNameDescription
    2379clientClient connections
    2380peerRaft peer communication

    The headless service provides DNS-based discovery: each member is reachable at <pod-name>.etcd-cluster-etcd-headless.<namespace>.svc.cluster.local:2379.

    For in-cluster access, this headless service is sufficient. External exposure is only needed when clients outside the Kubernetes cluster need to connect.

    Expose the etcd Service Externally

    Option 1: Cluster-level LoadBalancer (Client Port Only)

    This creates a single LoadBalancer that routes to all etcd members for client connections:

    apiVersion: apps.kubeblocks.io/v1 kind: Cluster metadata: name: etcd-cluster-lb namespace: demo spec: terminationPolicy: Delete # Cluster-level service for client access services: - name: client serviceName: client spec: type: LoadBalancer ports: - port: 2379 targetPort: 2379 componentSelector: etcd componentSpecs: - name: etcd componentDef: etcd serviceVersion: 3.6.1 replicas: 3 resources: limits: cpu: "0.5" memory: "0.5Gi" requests: cpu: "0.5" memory: "0.5Gi" volumeClaimTemplates: - name: data spec: storageClassName: "" accessModes: - ReadWriteOnce resources: requests: storage: 20Gi

    Apply it:

    kubectl apply -f https://raw.githubusercontent.com/apecloud/kubeblocks-addons/refs/heads/main/examples/etcd/cluster-with-lb.yaml

    Option 2: Per-Pod LoadBalancer (Peer Communication)

    For cross-region or multi-cluster deployments where etcd peers need external connectivity, use per-pod LoadBalancer services:

    componentSpecs: - name: etcd services: - name: peer serviceType: LoadBalancer podService: true # creates one LoadBalancer per pod

    This is included in cluster-with-lb.yaml. Each pod gets its own external IP for peer communication.

    NOTE

    Cloud Provider Annotations

    Add cloud-specific annotations to the services entry:

    • AWS
      annotations: service.beta.kubernetes.io/aws-load-balancer-type: nlb service.beta.kubernetes.io/aws-load-balancer-internal: "false"
    • Azure
      annotations: service.beta.kubernetes.io/azure-load-balancer-internal: "false"
    • GCP
      annotations: cloud.google.com/l4-rbs: "enabled"
    • Alibaba Cloud
      annotations: service.beta.kubernetes.io/alibaba-cloud-loadbalancer-address-type: "internet"

    Verify the External Service

    kubectl get service -n demo -l app.kubernetes.io/instance=etcd-cluster-lb
    Example Output
    NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE etcd-cluster-lb-etcd-client LoadBalancer 172.20.95.145 a3f9688f...elb.ap-southeast-1.amazonaws.com 2379:30411/TCP 68s etcd-cluster-lb-etcd-headless ClusterIP None <none> 2379/TCP,2380/TCP 68s

    The LoadBalancer DNS name may take 2–5 minutes to become resolvable after provisioning.

    Connect via the External Endpoint

    Once the DNS resolves, connect using etcdctl:

    etcdctl endpoint health --endpoints=<EXTERNAL-IP>:2379

    Expected response: <EXTERNAL-IP>:2379 is healthy

    Or test with netcat:

    echo "" | nc <EXTERNAL-IP> 2379

    Cleanup

    kubectl delete cluster etcd-cluster-lb -n demo kubectl delete ns demo

    © 2026 KUBEBLOCKS INC