diff --git a/controller/linodemachine_controller.go b/controller/linodemachine_controller.go index e89bb74f4..7cbd359e3 100644 --- a/controller/linodemachine_controller.go +++ b/controller/linodemachine_controller.go @@ -317,7 +317,7 @@ func (r *LinodeMachineReconciler) reconcileCreate( linodeInstance = &linodeInstances[0] case 0: // get the bootstrap data for the Linode instance and set it for create config - createConfig, err := r.newCreateConfig(ctx, machineScope, tags, logger) + createOpts, err := r.newCreateConfig(ctx, machineScope, tags, logger) if err != nil { logger.Error(err, "Failed to create Linode machine create config") @@ -325,22 +325,32 @@ func (r *LinodeMachineReconciler) reconcileCreate( } if machineScope.LinodeCluster.Spec.VPCRef != nil { - iface, err := r.getVPCInterfaceConfig(ctx, machineScope, createConfig.Interfaces, logger) + iface, err := r.getVPCInterfaceConfig(ctx, machineScope, createOpts.Interfaces, logger) if err != nil { logger.Error(err, "Failed to get VPC interface config") return nil, err } - createConfig.Interfaces = append(createConfig.Interfaces, *iface) + createOpts.Interfaces = append(createOpts.Interfaces, *iface) } - linodeInstance, err = machineScope.LinodeClient.CreateInstance(ctx, *createConfig) + linodeInstance, err = machineScope.LinodeClient.CreateInstance(ctx, *createOpts) if err != nil { logger.Error(err, "Failed to create Linode machine instance") return nil, err } + + // create etcd disk only if this is a control plane node + if kutil.IsControlPlaneMachine(machineScope.Machine) { + if err = r.configureDisks(ctx, logger, machineScope, linodeInstance.ID); err != nil { + logger.Error(err, "Failed to configure instance disks") + + return nil, err + } + } + default: err = errors.New("multiple instances") @@ -378,6 +388,67 @@ func (r *LinodeMachineReconciler) reconcileCreate( return linodeInstance, nil } +func (r *LinodeMachineReconciler) configureDisks( + ctx context.Context, + logger logr.Logger, + machineScope *scope.MachineScope, + linodeInstanceID int, +) error { + // get the default instance config + configs, err := machineScope.LinodeClient.ListInstanceConfigs(ctx, linodeInstanceID, &linodego.ListOptions{}) + if err != nil || len(configs) == 0 { + logger.Error(err, "Failed to list instance configs") + + return err + } + instanceConfig := configs[0] + + // carve out half the root disk space to be used for the etcd disk + rootDiskID := instanceConfig.Devices.SDA.DiskID + rootDisk, err := machineScope.LinodeClient.GetInstanceDisk(ctx, linodeInstanceID, rootDiskID) + if err != nil { + logger.Error(err, "Failed to get root disk for instance") + + return err + } + //nolint:gomnd // cutting disk in half + diskSize := rootDisk.Size / 2 + if err = machineScope.LinodeClient.ResizeInstanceDisk(ctx, linodeInstanceID, rootDiskID, diskSize); err != nil { + logger.Error(err, "Failed to resize root disk") + + return err + } + + // create the etcd disk + etcdDisk, err := machineScope.LinodeClient.CreateInstanceDisk( + ctx, + linodeInstanceID, + linodego.InstanceDiskCreateOptions{ + Label: "etcd-data", + Size: diskSize, + Filesystem: string(linodego.FilesystemExt4), + }, + ) + if err != nil { + logger.Error(err, "Failed to create etcd disk") + + return err + } + + // attach etcd disk to sdc + instanceConfig.Devices.SDC = &linodego.InstanceConfigDevice{DiskID: etcdDisk.ID} + + // finally, boot the machine + err = machineScope.LinodeClient.BootInstance(ctx, linodeInstanceID, instanceConfig.ID) + if err != nil { + logger.Error(err, "Failed to boot instance") + + return err + } + + return nil +} + func (r *LinodeMachineReconciler) reconcileUpdate( ctx context.Context, logger logr.Logger, @@ -451,14 +522,13 @@ func (r *LinodeMachineReconciler) reconcileDelete( return nil } - err := services.DeleteNodeFromNB(ctx, logger, machineScope) - if err != nil { + if err := services.DeleteNodeFromNB(ctx, logger, machineScope); err != nil { logger.Error(err, "Failed to remove node from Node Balancer backend") return err } - err = machineScope.LinodeClient.DeleteInstance(ctx, *machineScope.LinodeMachine.Spec.InstanceID) - if err != nil { + + if err := machineScope.LinodeClient.DeleteInstance(ctx, *machineScope.LinodeMachine.Spec.InstanceID); err != nil { if util.IgnoreLinodeAPIError(err, http.StatusNotFound) != nil { logger.Error(err, "Failed to delete Linode machine instance") diff --git a/controller/linodemachine_controller_helpers.go b/controller/linodemachine_controller_helpers.go index 0bd8d8579..17aa7eb2d 100644 --- a/controller/linodemachine_controller_helpers.go +++ b/controller/linodemachine_controller_helpers.go @@ -57,6 +57,10 @@ func (*LinodeMachineReconciler) newCreateConfig(ctx context.Context, machineScop return nil, err } + + // if the machine is a control plane node, further configuration is needed before it can be booted + createConfig.Booted = util.Pointer(!kutil.IsControlPlaneMachine(machineScope.Machine)) + createConfig.PrivateIP = true bootstrapData, err := machineScope.GetBootstrapData(ctx) @@ -87,7 +91,6 @@ func (*LinodeMachineReconciler) newCreateConfig(ctx context.Context, machineScop if createConfig.Image == "" { createConfig.Image = reconciler.DefaultMachineControllerLinodeImage } - if createConfig.RootPass == "" { createConfig.RootPass = uuid.NewString() } diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index ce5859d2c..338918383 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -5,6 +5,7 @@ - [Getting Started](./topics/getting-started.md) - [Troubleshooting](./topics/troubleshooting.md) - [Addons](./topics/addons.md) + - [Etcd](./topics/etcd.md) - [Development](./developers/development.md) - [Releasing](./developers/releasing.md) - [Reference](./reference/reference.md) diff --git a/docs/src/topics/etcd.md b/docs/src/topics/etcd.md new file mode 100644 index 000000000..8cd6bdd40 --- /dev/null +++ b/docs/src/topics/etcd.md @@ -0,0 +1,9 @@ +# Etcd + +This guide covers etcd configuration for the control plane of provisioned CAPL clusters. + +## Default configuration + +By default, etcd is configured to be on a separate device from the root filesystem on +control plane nodes. The etcd disk is automatically sized to take up half of the +storage on the Linode instance. diff --git a/templates/flavors/clusterclass-kubeadm/kubeadm-controlplane-template.yaml b/templates/flavors/clusterclass-kubeadm/kubeadm-controlplane-template.yaml index 6f3f72dee..3706ca386 100644 --- a/templates/flavors/clusterclass-kubeadm/kubeadm-controlplane-template.yaml +++ b/templates/flavors/clusterclass-kubeadm/kubeadm-controlplane-template.yaml @@ -31,12 +31,25 @@ spec: preKubeadmCommands: - /kubeadm-pre-init.sh '{{ ds.meta_data.label }}' "${KUBERNETES_VERSION}" clusterConfiguration: + etcd: + local: + dataDir: /var/lib/etcd_data/etcd + extraArgs: + quota-backend-bytes: "8589934592" apiServer: extraArgs: cloud-provider: external controllerManager: extraArgs: cloud-provider: external + diskSetup: + filesystems: + - label: etcd_data + filesystem: ext4 + device: /dev/sdc + mounts: + - - LABEL=etcd_data + - /var/lib/etcd_data initConfiguration: nodeRegistration: kubeletExtraArgs: diff --git a/templates/flavors/default/kubeadm-control-plane.yaml b/templates/flavors/default/kubeadm-control-plane.yaml index d057a780a..18da0769a 100644 --- a/templates/flavors/default/kubeadm-control-plane.yaml +++ b/templates/flavors/default/kubeadm-control-plane.yaml @@ -36,12 +36,25 @@ spec: preKubeadmCommands: - /kubeadm-pre-init.sh '{{ ds.meta_data.label }}' "${KUBERNETES_VERSION}" clusterConfiguration: + etcd: + local: + dataDir: /var/lib/etcd_data/etcd + extraArgs: + quota-backend-bytes: "8589934592" apiServer: extraArgs: cloud-provider: external controllerManager: extraArgs: cloud-provider: external + diskSetup: + filesystems: + - label: etcd_data + filesystem: ext4 + device: /dev/sdc + mounts: + - - LABEL=etcd_data + - /var/lib/etcd_data initConfiguration: nodeRegistration: kubeletExtraArgs: