Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 104 additions & 0 deletions docs/networks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Networks

The default OpenTofu configurations in the appliance do not provision networks,
subnets or associated infrastructure such as routers. The requirements are that:
1. At least one network exists.
2. At least one network spans all nodes, referred to as the "access network".
3. Only one subnet per network is attached to nodes.
4. One network on each node provides outbound internet access (either directly,
or via a proxy).

Futhermore, it is recommended that the deploy host has an interface on the
access network. While it is possible to e.g. use a floating IP on a login node
as an SSH proxy to access the other nodes, this can create problems in recovering
the cluster if the login node is unavailable and can make Ansible problems harder
to debug.

This page describes supported configurations and how to implement them using
the OpenTofu variables. These will normally be set in
`environments/site/tofu/terraform.tfvars` for the site base environment. If they
need to be overriden for specific environments, this can be done via an OpenTofu
module as discussed [here](./production.md).

Note that if an OpenStack subnet has a gateway IP defined then nodes with ports
attached to that subnet will get a default route set via that gateway.

## Single network
This is the simplest possible configuration. A single network and subnet is
used for all nodes. The subnet provides outbound internet access via the default
route defined by the subnet gateway (often an OpenStack router to an external
network).

```terraform
cluster_networks = [
{
network = "netA"
subnet = "subnetA"
}
]
...
```

## Multiple homogenous networks
This is similar to the above, except each node has multiple networks. Therefore
`access_network` must be explicitly set. Note that only one subnet must have
a gateway defined, else default routes via both subnets will be present causing
routing problems. It also shows the second network as using direct-type vNICs
for RDMA.

```terraform
cluster_networks = [
{
network = "netA"
subnet = "subnetA"
access_network = true
},
{
network = "netB"
subnet = "subnetB"
},
]

vnic_types = {
netB = "direct"
}
...
```


## Additional networks on some nodes

This example shows how to override variables for specific node groups. In this
case a baremetal node group has a second network attached. As above, only a
single subnet can have a gateway IP.

```terraform
cluster_networks = [
{
network = "netA"
subnet = "subnetA"
}
]

compute = {
baremetal = {
nodes = ["baremetal-0", "baremetal-1"]
networks = [
{
network = "netA"
subnet = "subnetA"
access_network = true
},
{
network = "netB"
subnet = "subnetB"
}
]
vnic_types = {
netA = "baremetal"
netB = "baremetal"
...
}
}
...
```
8 changes: 6 additions & 2 deletions environments/.stackhpc/tofu/LEAFCLOUD.tfvars
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
cluster_net = "slurmapp-ci"
cluster_subnet = "slurmapp-ci"
cluster_networks = [
{
network = "slurmapp-ci"
subnet = "slurmapp-ci"
}
]
control_node_flavor = "ec1.medium" # small ran out of memory, medium gets down to ~100Mi mem free on deployment
other_node_flavor = "en1.xsmall"
state_volume_type = "unencrypted"
Expand Down
13 changes: 5 additions & 8 deletions environments/.stackhpc/tofu/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,10 @@ variable "cluster_image" {
type = map(string)
}

variable "cluster_net" {}
variable "cluster_networks" {}

variable "cluster_subnet" {}

variable "vnic_type" {
default = "normal"
variable "vnic_types" {
default = {}
}

variable "state_volume_type"{
Expand Down Expand Up @@ -63,9 +61,8 @@ module "cluster" {
source = "../../skeleton/{{cookiecutter.environment}}/tofu/"

cluster_name = var.cluster_name
cluster_net = var.cluster_net
cluster_subnet = var.cluster_subnet
vnic_type = var.vnic_type
cluster_networks = var.cluster_networks
vnic_types = var.vnic_types
key_pair = "slurm-app-ci"
cluster_image_id = data.openstack_images_image_v2.cluster.id
control_node_flavor = var.control_node_flavor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,25 @@ module "compute" {
nodes = each.value.nodes
flavor = each.value.flavor

# always taken from top-level value:
cluster_name = var.cluster_name
cluster_domain_suffix = var.cluster_domain_suffix
cluster_net_id = data.openstack_networking_network_v2.cluster_net.id
cluster_subnet_id = data.openstack_networking_subnet_v2.cluster_subnet.id

key_pair = var.key_pair
environment_root = var.environment_root
# can be set for group, defaults to top-level value:
networks = lookup(each.value, "networks", var.cluster_networks)
image_id = lookup(each.value, "image_id", var.cluster_image_id)
vnic_type = lookup(each.value, "vnic_type", var.vnic_type)
vnic_profile = lookup(each.value, "vnic_profile", var.vnic_profile)
vnic_types = lookup(each.value, "vnic_types", var.vnic_types)
vnic_profiles = lookup(each.value, "vnic_profiles", var.vnic_profiles)
volume_backed_instances = lookup(each.value, "volume_backed_instances", var.volume_backed_instances)
root_volume_size = lookup(each.value, "root_volume_size", var.root_volume_size)
extra_volumes = lookup(each.value, "extra_volumes", {})

compute_init_enable = lookup(each.value, "compute_init_enable", [])
ignore_image_changes = lookup(each.value, "ignore_image_changes", false)

key_pair = var.key_pair
environment_root = var.environment_root
# computed
k3s_token = local.k3s_token
control_address = [for n in openstack_compute_instance_v2.control["control"].network: n.fixed_ip_v4 if n.access_network][0]
control_address = openstack_compute_instance_v2.control.access_ip_v4
security_group_ids = [for o in data.openstack_networking_secgroup_v2.nonlogin: o.id]
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,27 @@ locals {

resource "openstack_networking_port_v2" "control" {

name = "${var.cluster_name}-control"
network_id = data.openstack_networking_network_v2.cluster_net.id
for_each = {for net in var.cluster_networks: net.network => net}

name = "${var.cluster_name}-control-${each.key}"
network_id = data.openstack_networking_network_v2.cluster_net[each.key].id
admin_state_up = "true"

fixed_ip {
subnet_id = data.openstack_networking_subnet_v2.cluster_subnet.id
subnet_id = data.openstack_networking_subnet_v2.cluster_subnet[each.key].id
}

security_group_ids = [for o in data.openstack_networking_secgroup_v2.nonlogin: o.id]

binding {
vnic_type = var.vnic_type
profile = var.vnic_profile
vnic_type = lookup(var.vnic_types, each.key, "normal")
profile = lookup(var.vnic_profiles, each.key, "{}")
}
}

resource "openstack_compute_instance_v2" "control" {

for_each = toset(["control"])

name = "${var.cluster_name}-${each.key}"
name = "${var.cluster_name}-control"
image_id = var.cluster_image_id
flavor_name = var.control_node_flavor
key_pair = var.key_pair
Expand All @@ -49,19 +49,23 @@ resource "openstack_compute_instance_v2" "control" {
}
}

network {
port = openstack_networking_port_v2.control.id
access_network = true
dynamic "network" {
for_each = {for net in var.cluster_networks: net.network => net}
content {
port = openstack_networking_port_v2.control[network.key].id
access_network = length(var.cluster_networks) == 1 ? true : lookup(network.value, "access_network", false)
}
}

metadata = {
environment_root = var.environment_root
k3s_token = local.k3s_token
# TODO: set k3s_subnet from access_network
}

user_data = <<-EOF
#cloud-config
fqdn: ${var.cluster_name}-${each.key}.${var.cluster_name}.${var.cluster_domain_suffix}
fqdn: ${var.cluster_name}-control.${var.cluster_name}.${var.cluster_domain_suffix}

bootcmd:
%{for volume in local.control_volumes}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ resource "local_file" "hosts" {
{
"cluster_name": var.cluster_name,
"cluster_domain_suffix": var.cluster_domain_suffix,
"control_instances": openstack_compute_instance_v2.control
"control": openstack_compute_instance_v2.control
"login_groups": module.login
"compute_groups": module.compute
"state_dir": var.state_dir
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,13 @@ all:

control:
hosts:
%{ for control in control_instances ~}
${ control.name }:
ansible_host: ${[for n in control.network: n.fixed_ip_v4 if n.access_network][0]}
instance_id: ${ control.id }
%{ endfor ~}
ansible_host: ${control.access_ip_v4}
instance_id: ${control.id}
networks: ${jsonencode({for n in control.network: n.name => {"fixed_ip_v4": n.fixed_ip_v4, "fixed_ip_v6": n.fixed_ip_v6}})}
vars:
appliances_state_dir: ${state_dir} # NB needs to be set on group not host otherwise it is ignored in packer build!


%{ for group_name in keys(login_groups) ~}
${cluster_name}_${group_name}:
hosts:
Expand All @@ -22,6 +20,7 @@ ${cluster_name}_${group_name}:
ansible_host: ${node.access_ip_v4}
instance_id: ${ node.id }
image_id: ${ node.image_id }
networks: ${jsonencode({for n in node.network: n.name => {"fixed_ip_v4": n.fixed_ip_v4, "fixed_ip_v6": n.fixed_ip_v6}})}
%{ endfor ~}
%{ endfor ~}

Expand All @@ -39,6 +38,7 @@ ${cluster_name}_${group_name}:
ansible_host: ${node.access_ip_v4}
instance_id: ${ node.id }
image_id: ${ node.image_id }
networks: ${jsonencode({for n in node.network: n.name => {"fixed_ip_v4": n.fixed_ip_v4, "fixed_ip_v6": n.fixed_ip_v6}})}
%{ endfor ~}
%{ endfor ~}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,12 @@ module "login" {

cluster_name = var.cluster_name
cluster_domain_suffix = var.cluster_domain_suffix
cluster_net_id = data.openstack_networking_network_v2.cluster_net.id
cluster_subnet_id = data.openstack_networking_subnet_v2.cluster_subnet.id


# can be set for group, defaults to top-level value:
networks = lookup(each.value, "networks", var.cluster_networks)
image_id = lookup(each.value, "image_id", var.cluster_image_id)
vnic_type = lookup(each.value, "vnic_type", var.vnic_type)
vnic_profile = lookup(each.value, "vnic_profile", var.vnic_profile)
vnic_types = lookup(each.value, "vnic_types", var.vnic_types)
vnic_profiles = lookup(each.value, "vnic_profiles", var.vnic_profiles)
volume_backed_instances = lookup(each.value, "volume_backed_instances", var.volume_backed_instances)
root_volume_size = lookup(each.value, "root_volume_size", var.root_volume_size)
extra_volumes = lookup(each.value, "extra_volumes", {})
Expand All @@ -26,6 +25,6 @@ module "login" {
key_pair = var.key_pair
environment_root = var.environment_root
k3s_token = local.k3s_token
control_address = [for n in openstack_compute_instance_v2.control["control"].network: n.fixed_ip_v4 if n.access_network][0]
control_address = openstack_compute_instance_v2.control.access_ip_v4
security_group_ids = [for o in data.openstack_networking_secgroup_v2.login: o.id]
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@

data "openstack_networking_network_v2" "cluster_net" {
name = var.cluster_net

for_each = {for net in var.cluster_networks: net.network => net}

name = each.value.network
}

data "openstack_networking_subnet_v2" "cluster_subnet" {

name = var.cluster_subnet
for_each = {for net in var.cluster_networks: net.network => net}

name = each.value.subnet
}

data "openstack_networking_secgroup_v2" "login" {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

data "openstack_networking_network_v2" "network" {

for_each = {for net in var.networks: net.network => net}

name = each.value.network
}

data "openstack_networking_subnet_v2" "subnet" {

for_each = {for net in var.networks: net.network => net}

name = each.value.subnet
}
Loading