Scaleway

You can use Scaleway instances as worker nodes in your CFKE cluster. The recommended approach combines the Cloudfleet Terraform provider with cloud-init automation. You deploy Scaleway instances with Terraform. The cloud-init configuration that Cloudfleet generates automatically then registers these instances with your CFKE cluster.

Prerequisites

  • Terraform CLI installed on your local machine.
  • Cloudfleet CLI configured with these setup instructions, or API token authentication configured per these guidelines.
  • An active Scaleway account with API credentials (access key, secret key, and project ID). Generate these credentials in the Scaleway console.
  • An existing CFKE cluster. This example uses a cluster with ID CLUSTER_ID. You can also create a new cluster with Terraform by following the Terraform setup documentation.

Deploying Scaleway instances to your CFKE cluster

Deploy and connect Scaleway instances to your CFKE cluster using the following Terraform configuration. Update the variable values to match your environment.

terraform
variable "cfke_cluster_id" {
  type    = string
  default = "CFKE Cluster ID"
}

variable "scaleway_access_key" {
  type        = string
  description = "Scaleway Access Key"
}
variable "scaleway_secret_key" {
  type        = string
  description = "Scaleway Secret Key"
}

variable "scaleway_project_id" {
  type        = string
  description = "Scaleway Project ID"
}

terraform {
  required_providers {
    cloudfleet = {
      source = "terraform.cloudfleet.ai/cloudfleet/cloudfleet"
    }
    scaleway = {
      source = "scaleway/scaleway"
    }
  }
}

provider "scaleway" {
  access_key = var.scaleway_access_key
  secret_key = var.scaleway_secret_key
  region     = "fr-par"
  project_id = var.scaleway_project_id
}

data "cloudfleet_cfke_cluster" "cluster" {
  id = var.cfke_cluster_id
}

resource "cloudfleet_cfke_node_join_information" "scaleway" {
  cluster_id = data.cloudfleet_cfke_cluster.cluster.id
  region     = "fr-par"
  zone       = "fr-par-1"

  node_labels = {
    "cfke.io/provider" = "scaleway"
  }

  # Scaleway requires uncompressed userdata
  base64_encode = false
  gzip          = false
}

resource "scaleway_instance_server" "worker" {
  count = 2
  name  = "cfke-worker-${count.index+1}"
  type  = "DEV1-M"
  image = "ubuntu_jammy"

  user_data = {
    cloud-init = cloudfleet_cfke_node_join_information.scaleway.rendered
  }

  enable_dynamic_ip = true
}

Run these commands to provision your Scaleway infrastructure and add the instances to your CFKE cluster:

bash
terraform init
terraform apply

After provisioning completes, check that your new nodes joined the cluster:

bash
kubectl get nodes

Adding a load balancer

To expose applications to the internet, deploy a Kubernetes Service of type NodePort. Then configure a Scaleway Load Balancer to route traffic to your nodes on that port. The following example shows a complete setup: an Nginx Deployment exposed on port 31001 and a matching Scaleway Load Balancer configuration.

terraform
locals {
  node_port = 31001
}

// An example Kubernetes deployment using a Load Balancer
data "cloudfleet_client_config" "me" {}

provider "kubernetes" {
  host                   = data.cloudfleet_cfke_cluster.cluster.endpoint
  cluster_ca_certificate = data.cloudfleet_cfke_cluster.cluster.certificate_authority
  token                  = data.cloudfleet_client_config.me.access_token
}

resource "kubernetes_namespace" "test" {
  depends_on = [
    data.cloudfleet_cfke_cluster.cluster
  ]
  metadata {
    name = "scaleway"
  }
}

resource "kubernetes_deployment" "app" {
  wait_for_rollout = false
  metadata {
    name      = "nginx"
    namespace = kubernetes_namespace.test.id
    labels = {
      app = "nginx"
    }
  }

  spec {
    replicas = 2
    selector {
      match_labels = {
        app = "nginx"
      }
    }

    template {
      metadata {
        labels = {
          app = "nginx"
        }
      }

      spec {
        affinity {
          node_affinity {
            required_during_scheduling_ignored_during_execution {
              node_selector_term {
                match_expressions {
                  key      = "cfke.io/provider"
                  operator = "In"
                  values = [
                    "scaleway"
                  ]
                }
              }
            }
          }
        }

        container {
          image = "nginx:latest"
          name  = "nginx"

          port {
            name           = "http"
            container_port = 80
          }

        }
      }
    }
  }
}


resource "kubernetes_service" "app" {
  depends_on = [
    data.cloudfleet_cfke_cluster.cluster
  ]
  metadata {
    name      = "nginx"
    namespace = kubernetes_namespace.test.id
    labels = {
      app = "nginx"
    }
  }
  spec {
    selector = {
      app = "nginx"
    }
    external_traffic_policy = "Local"
    type                    = "NodePort"
    port {
      name        = "http"
      port        = 80
      target_port = 80
      node_port   = local.node_port
    }
  }
}


resource "scaleway_lb_ip" "cfke" {
  zone = "fr-par-1"
}

resource "scaleway_lb" "cfke" {
  ip_ids = [scaleway_lb_ip.cfke.id]
  zone = scaleway_lb_ip.cfke.zone
  type = "LB-S"
}


resource "scaleway_lb_backend" "cfke" {
  lb_id            = scaleway_lb.cfke.id
  name             = "nginx"
  forward_protocol = "http"
  forward_port     = local.node_port
  server_ips       = [for ip in flatten(scaleway_instance_server.worker.*.public_ips) : ip.address]

  health_check_http {
    uri = "/"
  }
}

resource "scaleway_lb_frontend" "cfke" {
  lb_id        = scaleway_lb.cfke.id
  backend_id   = scaleway_lb_backend.cfke.id
  inbound_port = 80
}

output "load_balancer_ip" {
  value = scaleway_lb_ip.cfke.ip_address
}

For production deployments, consider an Ingress controller instead of this basic NodePort approach.

Firewall configuration

Configure Scaleway security groups to restrict network access to your cluster:

  • Port 31001 (TCP): Allow access to the NodePort Service only from the Scaleway load balancer IP address. Do not expose this port to all sources (0.0.0.0/0), because this creates unnecessary security exposure.

This configuration follows the principle of least privilege: only the load balancer can access the NodePort. All external traffic goes through the load balancer, which gives you one central point for access control and public exposure.

On this page