Exoscale
Cloudfleet supports adding Exoscale instances as nodes in your CFKE cluster. The simplest and most scalable approach is the cloud-init integration of the Cloudfleet Terraform provider. You create Exoscale instance pools with Terraform. The Cloudfleet Terraform provider generates a cloud-init configuration that registers the instances with your CFKE cluster automatically.
Prerequisites
- You have the Terraform CLI installed on your workstation.
- You configured the Cloudfleet CLI by following the instructions here. Alternatively, you can use token authentication by following the instructions here.
- You have an Exoscale account with API credentials. You can create API credentials from the Exoscale console.
- You have a running CFKE cluster. The example below assumes a cluster with the ID
CLUSTER_ID. You can also create the cluster with Terraform as shown in the Terraform introduction.
Adding Exoscale instances to your CFKE cluster
Use this Terraform configuration to create Exoscale instance pools and add them to your CFKE cluster. Replace the cfke_cluster_id, exoscale_api_key, and exoscale_api_secret variables with your values.
variable "cfke_cluster_id" {
type = string
default = "CFKE Cluster ID"
}
variable "exoscale_api_key" {
type = string
}
variable "exoscale_api_secret" {
type = string
}
variable "zone" {
type = string
default = "ch-gva-2"
}
terraform {
required_providers {
cloudfleet = {
source = "terraform.cloudfleet.ai/cloudfleet/cloudfleet"
}
kubernetes = {
source = "hashicorp/kubernetes"
version = "~> 2.0"
}
exoscale = {
source = "exoscale/exoscale"
}
}
}
provider "exoscale" {
key = var.exoscale_api_key
secret = var.exoscale_api_secret
}
data "cloudfleet_cfke_cluster" "cluster" {
id = var.cfke_cluster_id
}
// Generate the cloud-init configuration to join the Exoscale instances to the CFKE cluster
resource "cloudfleet_cfke_node_join_information" "exoscale" {
cluster_id = data.cloudfleet_cfke_cluster.cluster.id
region = var.zone
zone = var.zone
node_labels = {
"cfke.io/provider" = "exoscale"
}
}
// Exoscale resources
// Create a private network for the CFKE nodes
resource "exoscale_private_network" "cfke_network" {
zone = var.zone
name = "cfke-private-network"
description = "CFKE Private Network"
netmask = "255.255.255.0"
start_ip = "10.0.0.50"
end_ip = "10.0.0.250"
}
// Create a security group for the CFKE nodes
resource "exoscale_security_group" "cfke_security_group" {
name = "cfke-security-group"
description = "CFKE Security Group"
}
data "exoscale_template" "cfke_template" {
zone = var.zone
name = "Linux Ubuntu 24.04 LTS 64-bit"
}
// Create an instance pool for CFKE worker nodes
resource "exoscale_instance_pool" "cfke" {
zone = var.zone
name = "cfke-worker"
instance_prefix = "cfke-worker"
template_id = data.exoscale_template.cfke_template.id
instance_type = "standard.medium"
disk_size = 10
size = 3
ipv6 = true
security_group_ids = [exoscale_security_group.cfke_security_group.id]
network_ids = [exoscale_private_network.cfke_network.id]
user_data = cloudfleet_cfke_node_join_information.exoscale.rendered // Use the generated cloud-init configuration
}After you create the Terraform configuration, run these commands to provision the Exoscale instances and add them to your CFKE cluster:
terraform init
terraform applyAfter the deployment completes, verify that the nodes joined your cluster:
kubectl get nodesAdding a load balancer
To expose workloads to the internet, create a Kubernetes Service of type NodePort. Then configure an Exoscale Network Load Balancer to forward traffic to the nodes on that port. The example below shows an Nginx Deployment exposed on port 31001, with a matching Exoscale Network Load Balancer configuration.
locals {
node_port = 31001
}
// Security group rule to allow traffic to the NodePort
resource "exoscale_security_group_rule" "nodeport" {
security_group_id = exoscale_security_group.cfke_security_group.id
description = "NodePort range"
type = "INGRESS"
protocol = "TCP"
start_port = local.node_port
end_port = local.node_port
cidr = "0.0.0.0/0"
}
// Create Network Load Balancer
resource "exoscale_nlb" "nginx" {
zone = var.zone
name = "nginx"
}
resource "exoscale_nlb_service" "nginx" {
nlb_id = exoscale_nlb.nginx.id
zone = exoscale_nlb.nginx.zone
name = "nginx"
instance_pool_id = exoscale_instance_pool.cfke.id
protocol = "tcp"
port = 80
target_port = local.node_port
strategy = "round-robin"
healthcheck {
mode = "http"
port = local.node_port
uri = "/"
interval = 5
timeout = 3
retries = 1
}
}
// 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 = "exoscale"
}
}
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 = [
"exoscale"
]
}
}
}
}
}
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
}
}
}
output "load_balancer_ip" {
value = exoscale_nlb.nginx.ip_address
}For production deployments, consider an Ingress controller instead of this basic NodePort example.
Security configuration
To secure your cluster, configure the Exoscale security group with these essential rules:
- Port 31001 (TCP): Allow ingress traffic for the NodePort Service. The example above allows traffic from all sources (0.0.0.0/0). For production environments, consider restricting access to specific IP ranges or to the load balancer IP addresses only.
With this configuration, external traffic flows through the Network Load Balancer, which centralizes access control and public exposure. The private network gives secure communication between nodes in the same instance pool.
← Scaleway
Proxmox →