kubernetes/kops · error

there should be an odd number of control-plane-zones, for et

Error message

there should be an odd number of control-plane-zones, for etcd's quorum.  Hint: Use --zones and --control-plane-zones to declare worker and control plane node zones separately

What it means

etcd requires an odd number of members to have a well-defined quorum; an even count gives no additional fault tolerance. run() throws this when the number of distinct etcd member names in any etcd cluster is even. The message hints at using --zones and --control-plane-zones to separate worker and control-plane zones.

Source

Thrown at upup/pkg/fi/cloudup/populate_cluster_spec.go:158

						return fmt.Errorf("EtcdMembers found with same name %q in etcd-cluster %q", m.Name, etcd.Name)
					}

					instanceGroupName := fi.ValueOf(m.InstanceGroup)

					if _, ok := etcdInstanceGroups[instanceGroupName]; ok {
						klog.Warningf("EtcdMembers are in the same InstanceGroup %q in etcd-cluster %q (fault-tolerance may be reduced)", instanceGroupName, etcd.Name)
					}

					//if clusterSubnets[zone] == nil {
					//	return fmt.Errorf("EtcdMembers for %q is configured in zone %q, but that is not configured at the k8s-cluster level", etcd.Name, m.Zone)
					//}
					etcdNames[m.Name] = m
					etcdInstanceGroups[instanceGroupName] = m
				}

				if (len(etcdNames) % 2) == 0 {
					// Not technically a requirement, but doesn't really make sense to allow
					return fmt.Errorf("there should be an odd number of control-plane-zones, for etcd's quorum.  Hint: Use --zones and --control-plane-zones to declare worker and control plane node zones separately")
				}
			}
		}
	}

	configBase, err := clientset.VFSContext().BuildVfsPath(cluster.Spec.ConfigStore.Base)
	if err != nil {
		return fmt.Errorf("error parsing ConfigStore.Base %q: %v", cluster.Spec.ConfigStore.Base, err)
	}
	if !vfs.IsClusterReadable(configBase) {
		// We could implement this approach, but it seems better to get all clouds using cluster-readable storage
		return fmt.Errorf("ConfigStore.Base path is not cluster readable: %v", cluster.Spec.ConfigStore.Base)
	}

	keyStore, err := clientset.KeyStore(cluster)
	if err != nil {
		return err
	}

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Use an odd number of etcd members (typically 3 or 5) by using an odd number of control-plane zones/instance groups.
  2. For worker-heavy even zone counts, pass --control-plane-zones (e.g. 3 zones) separately from --zones.
  3. If a member was removed leaving an even count, add or remove one more member to restore odd quorum, then `kops update cluster`.

Example fix

# before
kops create cluster --zones us-east-1a,us-east-1b --control-plane-count 2 ...
# after
kops create cluster --zones us-east-1a,us-east-1b,us-east-1c --control-plane-zones us-east-1a,us-east-1b,us-east-1c ...
Defensive patterns

Strategy: validation

Validate before calling

if len(cluster.Spec.EtcdClusters[0].Members)%2 == 0 {
	return fmt.Errorf("etcd needs an odd number of members for quorum")
}

Prevention

When it happens

Trigger: Creating/updating a cluster whose etcd cluster has an even number of members (e.g. 2 or 4), often because --zones was given an even number of zones and control-plane groups were spread evenly across them, or a member was removed leaving an even count.

Common situations: `kops create cluster --zones a,b,c,d` without separate --control-plane-zones, yielding 4 etcd members; removing one control-plane instance group/member from a 3-member cluster leaving 2; setting up a test cluster with only 2 control-plane nodes.

Related errors


AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05). Data as JSON: /api/errors/4488cd1a40682dbb. Report an issue: GitHub.