{"record":{"id":"4488cd1a40682dbb","repo":"kubernetes/kops","slug":"there-should-be-an-odd-number-of-control-plane-zon","errorCode":null,"errorMessage":"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","messagePattern":"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","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"upup/pkg/fi/cloudup/populate_cluster_spec.go","lineNumber":158,"sourceCode":"\t\t\t\t\t\treturn fmt.Errorf(\"EtcdMembers found with same name %q in etcd-cluster %q\", m.Name, etcd.Name)\n\t\t\t\t\t}\n\n\t\t\t\t\tinstanceGroupName := fi.ValueOf(m.InstanceGroup)\n\n\t\t\t\t\tif _, ok := etcdInstanceGroups[instanceGroupName]; ok {\n\t\t\t\t\t\tklog.Warningf(\"EtcdMembers are in the same InstanceGroup %q in etcd-cluster %q (fault-tolerance may be reduced)\", instanceGroupName, etcd.Name)\n\t\t\t\t\t}\n\n\t\t\t\t\t//if clusterSubnets[zone] == nil {\n\t\t\t\t\t//\treturn fmt.Errorf(\"EtcdMembers for %q is configured in zone %q, but that is not configured at the k8s-cluster level\", etcd.Name, m.Zone)\n\t\t\t\t\t//}\n\t\t\t\t\tetcdNames[m.Name] = m\n\t\t\t\t\tetcdInstanceGroups[instanceGroupName] = m\n\t\t\t\t}\n\n\t\t\t\tif (len(etcdNames) % 2) == 0 {\n\t\t\t\t\t// Not technically a requirement, but doesn't really make sense to allow\n\t\t\t\t\treturn 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\")\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tconfigBase, err := clientset.VFSContext().BuildVfsPath(cluster.Spec.ConfigStore.Base)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error parsing ConfigStore.Base %q: %v\", cluster.Spec.ConfigStore.Base, err)\n\t}\n\tif !vfs.IsClusterReadable(configBase) {\n\t\t// We could implement this approach, but it seems better to get all clouds using cluster-readable storage\n\t\treturn fmt.Errorf(\"ConfigStore.Base path is not cluster readable: %v\", cluster.Spec.ConfigStore.Base)\n\t}\n\n\tkeyStore, err := clientset.KeyStore(cluster)\n\tif err != nil {\n\t\treturn err\n\t}","sourceCodeStart":140,"sourceCodeEnd":176,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/upup/pkg/fi/cloudup/populate_cluster_spec.go#L140-L176","documentation":"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.","triggerScenarios":"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.","commonSituations":"`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.","solutions":["Use an odd number of etcd members (typically 3 or 5) by using an odd number of control-plane zones/instance groups.","For worker-heavy even zone counts, pass --control-plane-zones (e.g. 3 zones) separately from --zones.","If a member was removed leaving an even count, add or remove one more member to restore odd quorum, then `kops update cluster`."],"exampleFix":"# before\nkops create cluster --zones us-east-1a,us-east-1b --control-plane-count 2 ...\n# after\nkops create cluster --zones us-east-1a,us-east-1b,us-east-1c --control-plane-zones us-east-1a,us-east-1b,us-east-1c ...","handlingStrategy":"validation","validationCode":"if len(cluster.Spec.EtcdClusters[0].Members)%2 == 0 {\n\treturn fmt.Errorf(\"etcd needs an odd number of members for quorum\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use 3 or 5 control-plane nodes/zones for HA etcd","Pass --control-plane-zones separately from --zones when worker zones are even","After removing a control-plane instance group, re-check member count is still odd"],"tags":["etcd","quorum","high-availability"],"backgroundTag":"etcd-quorum-configuration","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}