larstobi / terraform-provider-multipass

Terraform provider for the Canonical Multipass virtual machine manager
MIT License
60 stars 13 forks source link

Discussion: Networking configuration and static IP assignments #25

Open Schachte opened 8 months ago

Schachte commented 8 months ago

Currently, I've been working on static IPv4 assignments for my VMs, which is necessary for local IaC development when IP addresses are expected to be deterministic as I bring up/tear down machines.

Unfortunately, there isn't great interop with ARM & Multipass (ie. M1 mac), but I've gotten something jank kind of working per these docs. https://multipass.run/docs/configure-static-ips

Weirdly though, I cannot have this entire setup work with cloud-init alone. It seems like I have to manually apply a new netplan file and apply it as two separate steps. Only then do I see an IP assignment and MAC addresses matching what I expect.

multipass exec -n test12 -- sudo bash -c 'cat << EOF > /etc/netplan/10-custom.yaml                                                                      
network:
    version: 2
    ethernets:
        extra0:
            dhcp4: no
            match:
                macaddress: "52:54:00:4b:ab:dd"
            addresses: [192.168.64.78/24]
EOF'

In order for this to work, I create my VMs with some special arguments:

multipass launch --name my-vm-test --network name=en0,mode=manual,mac="52:54:00:4b:ab:dd" --cloud-init cloud-init.yaml

As you can see, I supply the en0 interface, which is listed as an available bridge via multipass networks along with a static MAC address that is later linked via the netplan yaml above.

It would be nice to be able to support passing in the network_name and mac_address and maybe ip_address_v4/ip_address_v6 as parameters in the Terraform provider to handle this during initialization so I can keep it all self-contained in Terraform. Does this seem reasonable? I'd be happy to work on this if so. Open to discussing this as I'm new to Multipass and find the networking to be both poorly documented and convoluted.

I'm still a bit confused why the cloud-init supplied doesn't take affect, but I'm all ears.

Schachte commented 8 months ago

Ok, I hacked this together to make sure my mental model was correct and it seems to be working. I wrote an article to document these learnings in case it's of any use to anyone. https://ryan-schachte.com/blog/ansible_multipass/

If it seems worthwhile, I can work on making this more production ready so we can release it to support the networking options provided by the Multipass team.

Tested locally, this seems to work well assuming I manually apply a netplan config file.

resource "multipass_instance" "dev_vm" {
  for_each = { for i, name in var.instance_names : name => {
    ip_address  = var.ip_addresses[i]
    mac_address = var.mac_addresses[i]
  } }

  name   = each.key
  cpus   = var.cpus
  memory = var.memory
  disk   = var.disk
  image  = var.image

  cloudinit_file    = local_file.cloudinit[each.key].filename
  network_interface = "en0"
  mac_address       = each.value.mac_address

  provisioner "local-exec" {
    command = <<-EOT
      multipass exec ${each.key} -- sudo bash -c 'cat << EOF > /etc/netplan/10-custom.yaml
      network:
        version: 2
        ethernets:
          extra0:
            dhcp4: no
            match:
              macaddress: "${each.value.mac_address}"
            addresses: ["${each.value.ip_address}"]
      EOF'
      multipass exec ${each.key} -- sudo netplan apply
    EOT
  }
}
natted commented 8 months ago

Thanks for the write-up @Schachte, I am just about to embark on a similar test using multipass. It's made things a bit clearer