From c6e5c4364d3e67a41526f736986fb86d7ec5cfe0 Mon Sep 17 00:00:00 2001 From: Ole Markus With Date: Sat, 27 Mar 2021 08:38:10 +0100 Subject: [PATCH] Allow setting dedicated apiserver node count from create cluster cmd --- cmd/kops/create_cluster.go | 4 +++ upup/pkg/fi/cloudup/awsup/aws_cloud.go | 2 +- upup/pkg/fi/cloudup/new_cluster.go | 49 ++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/cmd/kops/create_cluster.go b/cmd/kops/create_cluster.go index 7216d2503c..f42afb5975 100644 --- a/cmd/kops/create_cluster.go +++ b/cmd/kops/create_cluster.go @@ -319,6 +319,10 @@ func NewCmdCreateCluster(f *util.Factory, out io.Writer) *cobra.Command { cmd.Flags().StringVar(&options.SpotinstOrientation, "spotinst-orientation", options.SpotinstOrientation, "Set the prediction strategy (valid values: balanced, cost, equal-distribution and availability)") } + if featureflag.APIServerNodes.Enabled() { + cmd.Flags().Int32Var(&options.APIServerCount, "api-server-count", options.APIServerCount, "Set number of API server nodes. Defaults to 0.") + } + // Openstack flags cmd.Flags().StringVar(&options.OpenstackExternalNet, "os-ext-net", options.OpenstackExternalNet, "The name of the external network to use with the openstack router") cmd.Flags().StringVar(&options.OpenstackExternalSubnet, "os-ext-subnet", options.OpenstackExternalSubnet, "The name of the external floating subnet to use with the openstack router") diff --git a/upup/pkg/fi/cloudup/awsup/aws_cloud.go b/upup/pkg/fi/cloudup/awsup/aws_cloud.go index a39d2e5eb1..7f89110ddb 100644 --- a/upup/pkg/fi/cloudup/awsup/aws_cloud.go +++ b/upup/pkg/fi/cloudup/awsup/aws_cloud.go @@ -1635,7 +1635,7 @@ func (c *awsCloudImplementation) DefaultInstanceType(cluster *kops.Cluster, ig * var candidates []string switch ig.Spec.Role { - case kops.InstanceGroupRoleMaster, kops.InstanceGroupRoleNode: + case kops.InstanceGroupRoleMaster, kops.InstanceGroupRoleNode, kops.InstanceGroupRoleAPIServer: // t3.medium is the cheapest instance with 4GB of mem, unlimited by default, fast and has decent network // c5.large and c4.large are a good second option in case t3.medium is not available in the AZ candidates = []string{"t3.medium", "c5.large", "c4.large"} diff --git a/upup/pkg/fi/cloudup/new_cluster.go b/upup/pkg/fi/cloudup/new_cluster.go index d379c4913c..efe2854561 100644 --- a/upup/pkg/fi/cloudup/new_cluster.go +++ b/upup/pkg/fi/cloudup/new_cluster.go @@ -110,6 +110,8 @@ type NewClusterOptions struct { // MasterCount is the number of masters to create. Defaults to the length of MasterZones // if MasterZones is explicitly nonempty, otherwise defaults to 1. MasterCount int32 + // APIServerCount is the number of API servers to create. Defaults to 0. + APIServerCount int32 // EncryptEtcdStorage is whether to encrypt the etcd volumes. EncryptEtcdStorage *bool // EtcdStorageType is the underlying cloud storage class of the etcd volumes. @@ -268,6 +270,11 @@ func NewCluster(opt *NewClusterOptions, clientset simple.Clientset) (*NewCluster return nil, err } + apiservers, err := setupAPIServers(opt, &cluster, zoneToSubnetMap) + if err != nil { + return nil, err + } + err = setupNetworking(opt, &cluster) if err != nil { return nil, err @@ -284,6 +291,7 @@ func NewCluster(opt *NewClusterOptions, clientset simple.Clientset) (*NewCluster } instanceGroups := append([]*api.InstanceGroup(nil), masters...) + instanceGroups = append(instanceGroups, apiservers...) instanceGroups = append(instanceGroups, nodes...) instanceGroups = append(instanceGroups, bastions...) @@ -794,6 +802,47 @@ func setupNodes(opt *NewClusterOptions, cluster *api.Cluster, zoneToSubnetMap ma return nodes, nil } +func setupAPIServers(opt *NewClusterOptions, cluster *api.Cluster, zoneToSubnetMap map[string]*api.ClusterSubnetSpec) ([]*api.InstanceGroup, error) { + var nodes []*api.InstanceGroup + + numZones := len(opt.Zones) + nodeCount := opt.APIServerCount + + if nodeCount == 0 { + return nodes, nil + } + + countPerIG := nodeCount / int32(numZones) + remainder := int(nodeCount) % numZones + + for i, zone := range opt.Zones { + count := countPerIG + if i < remainder { + count++ + } + + g := &api.InstanceGroup{} + g.Spec.Role = api.InstanceGroupRoleAPIServer + g.Spec.MinSize = fi.Int32(count) + g.Spec.MaxSize = fi.Int32(count) + g.ObjectMeta.Name = "apiserver-" + zone + + subnet := zoneToSubnetMap[zone] + if subnet == nil { + klog.Fatalf("subnet not found in zoneToSubnetMap") + } + + g.Spec.Subnets = []string{subnet.Name} + if cp := api.CloudProviderID(cluster.Spec.CloudProvider); cp == api.CloudProviderGCE || cp == api.CloudProviderAzure { + g.Spec.Zones = []string{zone} + } + + nodes = append(nodes, g) + } + + return nodes, nil +} + func setupNetworking(opt *NewClusterOptions, cluster *api.Cluster) error { cluster.Spec.Networking = &api.NetworkingSpec{} switch opt.Networking {