+++ hpc/README.md +++ hpc/ovh/cloudinit.tf +++ hpc/ovh/credentials.tf +++ hpc/ovh/main.tf +++ hpc/ovh/outputs.tf +++ hpc/ovh/variables.tf +++ hpc/ovh/versions.tf
182 lines
6.1 KiB
HCL
182 lines
6.1 KiB
HCL
// HPC cluster: one head node (the single entry point, public SSH + private
|
|
// cluster network) and N compute nodes (private network only, internet egress
|
|
// through an OVH gateway for package installation). The cloud provider
|
|
// credentials and the project (OVH_CLOUD_PROJECT_SERVICE) reach the provider
|
|
// from the runner environment, never from module inputs.
|
|
|
|
data "ovh_cloud_project_flavors" "head" {
|
|
service_name = var.project_id
|
|
name_filter = var.head_flavor
|
|
region = var.region
|
|
}
|
|
|
|
data "ovh_cloud_project_flavors" "compute" {
|
|
service_name = var.project_id
|
|
name_filter = var.compute_flavor
|
|
region = var.region
|
|
}
|
|
|
|
data "ovh_cloud_project_images" "all" {
|
|
service_name = var.project_id
|
|
region = var.region
|
|
}
|
|
|
|
locals {
|
|
selected_image = one([for m in data.ovh_cloud_project_images.all.images : m if m.name == var.image])
|
|
selected_head_flavor = one(data.ovh_cloud_project_flavors.head.flavors)
|
|
selected_compute_flavor = one(data.ovh_cloud_project_flavors.compute.flavors)
|
|
|
|
head_hostname = "${var.name_prefix}-head-${var.deployment_id}"
|
|
head_ip = cidrhost(var.cluster_cidr, 50)
|
|
|
|
compute_hostnames = [for i in range(var.compute_node_count) : "${var.name_prefix}-compute${i + 1}-${var.deployment_id}"]
|
|
compute_ips = [for i in range(var.compute_node_count) : cidrhost(var.cluster_cidr, 60 + i)]
|
|
}
|
|
|
|
// Private cluster network (vRack) with one subnet. Fixed addresses are
|
|
// assigned outside the DHCP pool; DHCP stays enabled so the instances
|
|
// configure their private interface from the OpenStack metadata.
|
|
resource "ovh_cloud_project_network_private" "cluster" {
|
|
service_name = var.project_id
|
|
name = "${var.name_prefix}-hpc-${var.deployment_id}"
|
|
regions = [var.region]
|
|
vlan_id = var.vlan_id
|
|
}
|
|
|
|
resource "ovh_cloud_project_network_private_subnet" "cluster" {
|
|
service_name = var.project_id
|
|
network_id = ovh_cloud_project_network_private.cluster.id
|
|
region = var.region
|
|
network = var.cluster_cidr
|
|
// The allocation pool must cover the fixed addresses: the OVH API refuses
|
|
// an instance ip outside it ("instance ip must be in subnet allocation
|
|
// pool"). The DHCP agent ports allocate from the start of the pool, so the
|
|
// fixed addresses (head at 50, compute nodes from 60) stay clear of them:
|
|
// an instance on a DHCP-port address is silently canceled by the cloud.
|
|
start = cidrhost(var.cluster_cidr, 10)
|
|
end = cidrhost(var.cluster_cidr, 200)
|
|
dhcp = true
|
|
}
|
|
|
|
locals {
|
|
openstack_network_id = one([
|
|
for r in ovh_cloud_project_network_private.cluster.regions_attributes : r.openstackid if r.region == var.region
|
|
])
|
|
}
|
|
|
|
// Internet egress for the whole cluster: the gateway attaches to the
|
|
// cluster subnet and DHCP distributes it as the default route. A NAT
|
|
// through a dual-homed head node is not an option: the cloud's port
|
|
// anti-spoofing silently drops the forwarded return traffic.
|
|
resource "ovh_cloud_project_gateway" "egress" {
|
|
service_name = var.project_id
|
|
name = "${var.name_prefix}-hpc-gw-${var.deployment_id}"
|
|
model = var.gateway_model
|
|
region = var.region
|
|
network_id = local.openstack_network_id
|
|
subnet_id = ovh_cloud_project_network_private_subnet.cluster.id
|
|
}
|
|
|
|
// One project SSH key per deployment, managed by the module so it is removed
|
|
// on destroy. The instance level ssh_key_create is avoided on purpose: keys it
|
|
// creates outlive the instance and collide on the next deployment.
|
|
resource "ovh_cloud_project_ssh_key" "admin" {
|
|
service_name = var.project_id
|
|
name = "${var.name_prefix}-${var.deployment_id}-key"
|
|
public_key = trimspace(tls_private_key.admin.public_key_openssh)
|
|
}
|
|
|
|
resource "ovh_cloud_project_instance" "head" {
|
|
service_name = var.project_id
|
|
region = var.region
|
|
billing_period = "hourly"
|
|
name = local.head_hostname
|
|
|
|
boot_from {
|
|
image_id = local.selected_image.id
|
|
}
|
|
|
|
flavor {
|
|
flavor_id = local.selected_head_flavor.id
|
|
}
|
|
|
|
ssh_key {
|
|
name = ovh_cloud_project_ssh_key.admin.name
|
|
}
|
|
|
|
// The head node cannot sit on the public network: the cloud refuses an
|
|
// instance attached to both the public network and a subnet served by a
|
|
// gateway. Public reachability comes from a floating ip routed through
|
|
// the gateway instead. The fixed address stays clear of the DHCP agent
|
|
// ports at the start of the pool: an instance on a DHCP-port address is
|
|
// silently canceled by the cloud, which also broke the floating ip
|
|
// creation until the address moved.
|
|
network {
|
|
private {
|
|
ip = local.head_ip
|
|
floating_ip_create {
|
|
description = "${var.name_prefix}-hpc-head-${var.deployment_id}"
|
|
}
|
|
network {
|
|
id = local.openstack_network_id
|
|
subnet_id = ovh_cloud_project_network_private_subnet.cluster.id
|
|
}
|
|
}
|
|
}
|
|
|
|
user_data = data.cloudinit_config.head.rendered
|
|
|
|
depends_on = [ovh_cloud_project_gateway.egress]
|
|
}
|
|
|
|
// Resolves the public (floating) address of the head node: the instance
|
|
// addresses only carry the fixed private ip, the floating ip is matched
|
|
// through the port it is associated with.
|
|
data "ovh_cloud_project_floatingips" "all" {
|
|
service_name = var.project_id
|
|
region_name = var.region
|
|
|
|
depends_on = [ovh_cloud_project_instance.head]
|
|
}
|
|
|
|
resource "ovh_cloud_project_instance" "compute" {
|
|
count = var.compute_node_count
|
|
service_name = var.project_id
|
|
region = var.region
|
|
billing_period = "hourly"
|
|
name = local.compute_hostnames[count.index]
|
|
|
|
boot_from {
|
|
image_id = local.selected_image.id
|
|
}
|
|
|
|
flavor {
|
|
flavor_id = local.selected_compute_flavor.id
|
|
}
|
|
|
|
ssh_key {
|
|
name = ovh_cloud_project_ssh_key.admin.name
|
|
}
|
|
|
|
network {
|
|
private {
|
|
ip = local.compute_ips[count.index]
|
|
network {
|
|
id = local.openstack_network_id
|
|
subnet_id = ovh_cloud_project_network_private_subnet.cluster.id
|
|
}
|
|
}
|
|
}
|
|
|
|
user_data = data.cloudinit_config.compute[count.index].rendered
|
|
|
|
depends_on = [ovh_cloud_project_gateway.egress]
|
|
}
|
|
|
|
locals {
|
|
head_public_ip = one([
|
|
for f in data.ovh_cloud_project_floatingips.all.cloud_project_floatingips : f.ip
|
|
if try(f.associated_entity.ip, "") == local.head_ip
|
|
])
|
|
}
|