diff --git a/docs/getting-started.md b/docs/getting-started.md index f891c87..493dbb3 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -134,9 +134,13 @@ scw instance image create \ The [image-builder](https://github.com/kubernetes-sigs/image-builder) project allows you to build a CAPI-ready OS image using Packer and Ansible. -To begin, please clone the `scaleway` branch of this repository: . +To begin, please clone the `image-builder` repository: -Then, please follow this documentation to build your image: . +```bash +git clone https://github.com/kubernetes-sigs/image-builder.git +``` + +Then, please follow the [Building Images for Scaleway documentation](https://image-builder.sigs.k8s.io/capi/providers/scaleway). ## Create a basic workload cluster @@ -166,6 +170,8 @@ Then, please follow this documentation to build your image: [!WARNING] +> Pricing of Scaleway products may differ between availability zones. For better +> cost predictability, it is recommended to always set your desired `failureDomains`. + +You can find the current failure domains of a `ScalewayCluster` by running this command: + +```bash +$ kubectl get scalewaycluster my-cluster --template='{{.status.failureDomains}}' +map[fr-par-1:map[controlPlane:true] fr-par-2:map[controlPlane:true] fr-par-3:map[controlPlane:true]] +``` + +Here is an example of `ScalewayCluster` with the failure domains set to `fr-par-1` and `fr-par-2`: + +```yaml +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +kind: ScalewayCluster +metadata: + name: my-cluster + namespace: default +spec: + region: fr-par + failureDomains: + - fr-par-1 + - fr-par-2 + # some fields were omitted... +``` + +## Other features + +### DNS + +Set the `network.controlPlaneDNS` field to automatically configure DNS records that +will point to the Load Balancer address(es) of your workload cluster. + +In this example, the FQDN `my-cluster.subdomain.your-domain.com` will have `A` record(s) +configured to point to the Load Balancer IP address(es). + +```yaml +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +kind: ScalewayCluster +metadata: + name: my-cluster + namespace: default +spec: + network: + controlPlaneDNS: + domain: subdomain.your-domain.com + name: my-cluster + # some fields were omitted... +``` + +The `domain` must be an existing Scaleway DNS Zone. Please refer to the +[Scaleway Domains and DNS documentation](https://www.scaleway.com/en/docs/domains-and-dns/) +for more information. You may register an external domain by following +[this documentation](https://www.scaleway.com/en/docs/domains-and-dns/how-to/add-external-domain/). + +The `network.controlPlaneDNS` field is **immutable**, it cannot be updated after creation. + +### Load Balancer + +When creating a `ScalewayCluster`, a "main" Load Balancer is always created. +It is also possible to specify "extra" Load Balancers to achieve regional redundancy. + +#### Main Load Balancer + +The main Load Balancer is always created by default, it is not possible to disable it. + +Here is an example of main Load Balancer configuration: + +```yaml +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +kind: ScalewayCluster +metadata: + name: my-cluster + namespace: default +spec: + region: fr-par + network: + controlPlaneLoadBalancer: + type: LB-S + port: 443 + zone: fr-par-1 + ip: 42.42.42.42 # optional + # some fields were omitted... +``` + +- The `type` field can be updated to migrate the Load Balancer to another type. +- The `port` field specifies the port of the Load Balancer frontend that exposes the kube-apiserver(s). + This field is immutable. +- The `zone` field specifies where the Load Balancer will be created. Must be in the same region + as the `ScalewayCluster` region. This defaults to the first availability zone of the region. + This field is immutable. +- The `ip` field specifies an existing Load Balancer Flexible IP to use when creating + the Load Balancer. If not set, a new IP will be created. This field is immutable. + +#### Extra Load Balancers + +To specify extra Load Balancers, it is required to also set the `network.controlPlaneDNS` field. + +Here is an example that configures two extra Load Balancers in `nl-ams-2` and `nl-ams-3`. + +```yaml +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +kind: ScalewayCluster +metadata: + name: my-cluster +spec: + region: nl-ams + network: + controlPlaneLoadBalancer: + type: LB-S + zone: nl-ams-1 + controlPlaneExtraLoadBalancers: + - type: LB-S + zone: nl-ams-2 + - type: LB-S + zone: nl-ams-3 + controlPlaneDNS: + domain: subdomain.your-domain.com + name: my-cluster + # some fields were omitted... +``` + +> [!WARNING] +> You can freely add and remove extra Load Balancers, however, some requests to +> the workload cluster's API server may fail as the Load Balancers are reconfigured. + +#### Allowed ranges (ACLs) + +The workload cluster's API server is always exposed publicly though the Load Balancer(s). +To prevent unauthorized access to the API server, you may configure some allowed network ranges +in CIDR format. By default, when the `allowedRanges` is unset or set to an empty list (`[]`), +all network ranges are allowed. + +```yaml +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +kind: ScalewayCluster +metadata: + name: my-cluster + namespace: default +spec: + network: + controlPlaneLoadBalancer: + allowedRanges: + - 42.42.0.0/16 + - 1.1.1.1/32 + # some fields were omitted... +``` + +> [!NOTE] +> The public IPs of the nodes and Public Gateways will automatically be allowed. +> No additional configuration is required. + +### VPC + +#### Private Network + +To automatically attach the resources of the cluster to a Private Network, simply +set the `network.privateNetwork.enabled` field to true: + +```yaml +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +kind: ScalewayCluster +metadata: + name: my-cluster + namespace: default +spec: + network: + privateNetwork: + enabled: true + # id: 11111111-1111-1111-1111-111111111111 + # vpcID: 11111111-1111-1111-1111-111111111111 + # subnet: 192.168.0.0/22 + # some fields were omitted... +``` + +- The `id` field can be set to use an existing Private Network. If not set, the provider + will create a new Private Network and manage it. +- The `vpcID` field can be set to tell the provider to create a new Private Network inside a + specific VPC. If not set, Private Networks are created in the default VPC. +- The `subnet` field can be set to use a specific subnet. Make sure the subnet does not + overlap with the subnet of another Private Network in the VPC. + +#### Public Gateways + +To create `ScalewayMachines` without a Public IP, your Private Network must contain +at least one Public Gateway that advertises its default route. You can configure one +manually or let the provider configure that for you: + +```yaml +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +kind: ScalewayCluster +metadata: + name: my-cluster + namespace: default +spec: + region: fr-par + network: + privateNetwork: + enabled: true + publicGateways: + - type: VPC-GW-S + zone: fr-par-1 + - type: VPC-GW-S + zone: fr-par-2 + # ip: 42.42.42.42 + # Note: the Public Gateway product is currently not available in fr-par-3. + # some fields were omitted... +``` + +The `ip` field can be set on the spec of a Public Gateway to use an existing Public IP. +If not set, a new IP will be created. + +> [!CAUTION] +> The `publicGateways` field is fully mutable, but changes should be avoided as much as possible. +> +> 🚫📶 Updating existing Public Gateways can lead to a loss of network on the nodes, be +> very careful when updating this field. +> +> 🚮 Updating a Public Gateway will lead to its recreation, which will make its private IP change. +> +> ⏳ Because the default routes are advertised via DHCP, the DHCP leases of the nodes must +> be renewed for changes to be propagated (~24 hours). You can reboot the nodes or +> trigger a rollout restart of the `kubeadmcontrolplanes`/`machinedeployments` to force the propagation. diff --git a/docs/scalewaymachine.md b/docs/scalewaymachine.md new file mode 100644 index 0000000..86d6819 --- /dev/null +++ b/docs/scalewaymachine.md @@ -0,0 +1,171 @@ +# ScalewayMachine + +The `ScalewayMachine` resource provisions the necessary Scaleway infrastructure +to make a Kubernetes node of your workload cluster work. This includes +an [Instance server](https://www.scaleway.com/en/virtual-instances/). + +This document describes the various configuration options you can set to enable or disable +important features on a `ScalewayMachine`. + +The infrastucture resources for the `ScalewayMachine` will be created in a +Scaleway availability zone that is based on the associated `Machine`'s `failureDomain`. + +You will usually never create a `ScalewayMachine` directly, `ScalewayMachineTemplate` should be used instead: + +```yaml +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +kind: ScalewayMachineTemplate +metadata: + name: my-machine-template + namespace: default +spec: + template: + spec: # Put your ScalewayMachine spec here: + image: + name: cluster-api-rockylinux-9-v1.32.4 + commercialType: DEV1-S + rootVolume: + type: block +``` + +## Commercial Type + +The `commercialType` field can be any Scaleway Instance commercial type, as long as +it's available in the selected availability zone. This field is required and +cannot be updated later. For a list of available commercial types, you may refer +to the following pages: + +- [Scaleway Instances datasheet](https://www.scaleway.com/en/docs/instances/reference-content/instances-datasheet/) +- [Choosing the right GPU Instance type](https://www.scaleway.com/en/docs/gpu/reference-content/choosing-gpu-instance-type/) + +> [!WARNING] +> +> - The price of Scaleway Instances is based on commercial type AND availability zone. +> - Some commercial types may not be available in all availability zones. + +## Image + +An Instance image will be used to provision the Instance servers. This image must +exist and be compatible with the chosen Instance commercial type (e.g. same CPU architecture). +It must also be available in the availability zone of the server. + +The `image` field must contain one of the following: + +- An image ID: + + ```yaml + apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 + kind: ScalewayMachine + metadata: + name: my-machine + namespace: default + spec: + image: + id: 11111111-1111-1111-1111-111111111111 + # some fields were omitted... + ``` + +- An image name: + + ```yaml + apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 + kind: ScalewayMachine + metadata: + name: my-machine + namespace: default + spec: + image: + name: cluster-api-rockylinux-9-v1.32.4 + # some fields were omitted... + ``` + + Make sure this image exists in the zones when you plan to deploy your nodes. + You can list images by name with this command: + + ```bash + scw instance image list name=${IMAGE_NAME} zone=${SCW_ZONE} + ``` + +- An image [Marketplace label](https://www.scaleway.com/en/developers/api/marketplace/): + + ```yaml + apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 + kind: ScalewayMachine + metadata: + name: my-machine + namespace: default + spec: + image: + label: ubuntu_jammy + # some fields were omitted... + ``` + +To build your own image, you can use the [Kubernetes image-builder project](https://image-builder.sigs.k8s.io/capi/quickstart). + +## Root Volume + +During machine creation, a root volume will be provisioned based on the chosen image. + +```yaml +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +kind: ScalewayMachine +metadata: + name: my-machine + namespace: default +spec: + rootVolume: + size: 20 + type: block # can be block or local + iops: 15000 # can be 5000 or 15000 + # some fields were omitted... +``` + +The default size of the root volume is 20 GB, this corresponds to the `rootVolume.size` +field set to `20`. You should adjust this value depending on the expected disk space usage. + +The `type` field defaults to `block`, which uses the Scaleway Block Storage (SBS) product +to provision a root volume. This field can be set to `local` to use Instance SSD +Local Storage (`l_ssd`), note however that not all commercial types support local storage. + +> [!TIP] +> The type of the root volume MUST match the type of Instance image you specified in +> the `image` field of the `ScalewayMachine` spec. For example, if your image is +> based on an SBS Snapshot, you can't use a `local` root volume. + +The `iops` field is only applicable for `block` root volumes. By default, `block` +volumes will have 5000 IOPS. Currently, only `5000` and `15000` IOPS are supported. + +For GPU Instances that support [scratch storage](https://www.scaleway.com/en/docs/gpu/how-to/use-scratch-storage-h100-instances/), +an additional scratch volume is automatically created and attached to the Instance. + +## Public Network + +The `publicNetwork` field defines if an IPv4 and/or IPv6 should be created and attached +during the Instance server creation. + +```yaml +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +kind: ScalewayMachine +metadata: + name: my-machine + namespace: default +spec: + publicNetwork: + enableIPv4: true + enableIPv6: true + # some fields were omitted... +``` + +By default, no IPv6 is created unless `publicNetwork.enableIPv6` is set to `true`. + +The default behavior for IPv4 creation depends on the configuration of a +Private Network (`network.privateNetwork.enabled`) in the `ScalewayCluster`. + +- If `network.privateNetwork.enabled` is `false` or not set, a public IPv4 will **always** + be created, even if `publicNetwork.enableIPv4` is set to `false`. This is to prevent + the creation of a node that can't access the control-plane Load Balancer. +- If `network.privateNetwork.enabled` is `true`, no public IPv4 is created, unless + `publicNetwork.enableIPv4` is also set to `true`. If you do not enable public IPv4 + connectivity, make sure a Public Gateway advertises its default route in the + Private Network as nodes will not be able to access the control-plane Load Balancer + without public connectivity. diff --git a/docs/secret.md b/docs/secret.md new file mode 100644 index 0000000..ad8ded8 --- /dev/null +++ b/docs/secret.md @@ -0,0 +1,52 @@ +# Scaleway Secret + +When creating a `ScalewayCluster`, it is required to specify in the `scalewaySecretName` field +the name of an existing `Secret` in the same namespace as the `ScalewayCluster`. + +## Secret specification + +The Secret can have the following data keys: + +| Key | Required | Description | +| -------------- | -------- | --------------------------------------------------------- | +| SCW_ACCESS_KEY | yes | Your Scaleway Access Key | +| SCW_SECRET_KEY | yes | You Scaleway Secret Key | +| SCW_API_URL | no | Scaleway API URL. Defaults to | + +**All other keys inside the secret will be ignored.** + +## Permission sets + +Your Scaleway API Key must have the following permission sets: + +> [!WARNING] +> This list may change when new features are added to the provider. Make sure you +> read the changelogs before upgrading the provider. + +- `IPAMFullAccess` +- `LoadBalancersFullAccess` +- `VPCFullAccess` +- `PrivateNetworksFullAccess` +- `VPCGatewayFullAccess` +- `BlockStorageFullAccess` +- `DomainsDNSFullAccess` +- `InstancesFullAccess` + +If a permission set is missing, you may encounter reconcile errors in the logs of the provider. + +## Example + +Here is an example of valid secret, please update the values of `SCW_ACCESS_KEY` +and `SCW_SECRET_KEY` before applying it: + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: my-scaleway-secret + namespace: default +type: Opaque +stringData: + SCW_ACCESS_KEY: SCW11111111111111111 + SCW_SECRET_KEY: 11111111-1111-1111-1111-111111111111 +``` diff --git a/internal/service/scaleway/instance/instance.go b/internal/service/scaleway/instance/instance.go index 4b2fc83..4f4c8f4 100644 --- a/internal/service/scaleway/instance/instance.go +++ b/internal/service/scaleway/instance/instance.go @@ -210,7 +210,7 @@ func (s *Service) ensureServer(ctx context.Context) (*instance.Server, error) { case image.Name != nil: image, err := s.ScalewayClient.FindImage(ctx, zone, *image.Name) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to find image by name, make sure it exists in zone %s: %w", zone, err) } imageID = image.ID