defined - nebula managed

Weil es bei nebula ja etwas nervt, die Hosts zu verwalten und händisch dem Overlaynetzwerk hinzuzufügen, war ich froh, als ich auf defined networking gestossen bin, die hinter nebula stehen und das Ganze auch "managed" anbieten.

Der Clou dabei ist, dass sie eine API bereithalten, mit der man Hosts automatisch dem Netz hinzufügen kann. Sie haben einen Guide dafür, der Automating Host Creation with the API heißt.

Netterweise gibt es bei defined einen "free tier", in dem man üben kann, wie das funktioniert. Mit einem API-Key bewaffnet, reicht es, sich mit einem Curl-Aufruf den Enrollment-Code zu generieren

curl -L 'https://api.defined.net/v1/host-and-enrollment-code' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer dnkey-grzlbrmpf' \
-d '{
  "name": "My new host",
  "networkID": "network-grzlbrmpf",
  "roleID": "role-grzlbrmpf"
}'

NetworkID und role-ID werden benötigt (wobei "role" hier nichts mit RBAC zu tun hat sondern die Rolle des Hosts beschreibt – insbesondere im Bezug auf die Firewallregeln, die dieser "Host-Rolle" zugewiesen werden sollen).

Mit dem Enrollment-Code kann man dann die folgenden Schritte (automatisiert) auf dem frischen Client durchführen:

Danach läuft auf dem Host der dnclient als systemd-Service. Die Konfiguration "entsteht" während des Enrollments in /etc/defined. Das Netzwerkinterface zum Overlaynetzwerk heisst dann defined1 und könnte dann z. B. in einer Nomad-Konfiguration entsprechend berücksichtigt werden:

[...] 
  host_network "overlay" {
    interface = "defined1"
  }
  host_network "internal" {
    interface = "ens3"
  }
  host_network "local" {
    interface = "lo"
  } 
}

advertise {
  # Defaults to the first private IP address.
  http = "{{ GetInterfaceIP \"defined1\" }}" # must be reachable by Nomad CLI clients
  rpc  = "{{ GetInterfaceIP \"defined1\" }}" # must be reachable by Nomad client nodes
  serf = "{{ GetInterfaceIP \"defined1\" }}" # must be reachable by Nomad server nodes
}
[...]

Das jetzt noch richtig in die Startsequenz eingebaut und dem Autoscaling auch im Overlaynetzwerk steht nicht mehr viel im Wege.

Offenbar hat sich da schonmal jemand Gedanken gemacht:

GitHub - quickvm/defined-systemd-units: Opinionated defined.net dnclient systemd units to automate host enrollment and unenrollment
Opinionated defined.net dnclient systemd units to automate host enrollment and unenrollment - quickvm/defined-systemd-units

Leider hat das Skript "dnctl" einen kleinen Bug beim "unenroll". Den habe ich in meiner Version behoben. In

GitHub - neuroserve/openstack-nomad-client-overlay: Nomad Client on OpenStack using a defined.net overlay network
Nomad Client on OpenStack using a defined.net overlay network - neuroserve/openstack-nomad-client-overlay

wird nur das obige Repository gezogen und mit sudo ./install installiert. Danach wird noch eine /etc/defined/dnctl Datei erstellt. Für mit Autoscaling erstellte Clients lässt man den Namen einfach weg. Genauso wie in der nomad.hcl. Damit bekommt der Client einen "zufälligen" Namen. Der Dienst dnctl wird nur enabled aber nicht gestartet – genauso wie bei Nomad.

Mit dem obigen Terraform wird dann einfach eine Instanz in der jeweiligen Umgebung erzeugt, abgeschaltet und ein Snapshot davon erzeugt. Diesen Snapshot verwendet dann der Autoscaler, um bei Scaling-Events neue Instanzen zu erzeugen.

Damit Nomad nach dem Start dann auch die richtige IP-Adresse "advertisen" kann, sollte man die Reihenfolge beim Start der Dienste festlegen:

[Unit]
Description=Nomad
Documentation=https://www.nomadproject.io/docs/
Wants=network-online.target dnctl.service
After=network-online.target dnctl.service
[...]

Wenn alles läuft wie es soll, kann es dann z. B. so aussehen:

root@triton-nomad1:~# nomad server members 
Name                   Address       Port  Status  Leader  Raft Version  Build  Datacenter  Region
triton-nomad1.de-west  100.102.0.10  4648  alive   false   3             1.9.7  de-gt-2     de-west
nomad-prod1-0.de-west  100.102.0.11  4648  alive   false   3             1.9.7  prod1       de-west
nomad-prod4-0.de-west  100.102.0.12  4648  alive   true    3             1.9.7  prod4       de-west

Eine hoch ausfallsichere Control Plane, die über drei komplett unabhängige Datacenter verteilt ist. Diese Datacenter können von ganz unterschiedlichen Anbietern betrieben werden. Da als Nomad Client und Nomad Server jede Plattform verwendet werden kann, auf der Nomad läuft, könnten das z. B. VMs oder auch bare metal Server sein - oder eine Mischung aus beidem.

ID        Node Pool  DC       Name                   Class    OS      Drain  Eligibility  Status  Running Allocs
692be17d  nom-pool   prod1    nom-10ceffd6-bbe1      dynamic  debian  false  eligible     ready   0
62074377  nom-pool   prod4    nom-0e83c376-560c      dynamic  debian  false  eligible     ready   0
e37a5586  default    prod4    nomad-client-prod4-0   <none>   debian  false  eligible     ready   2
25edb72f  default    prod1    nomad-client-prod1-0   <none>   debian  false  eligible     ready   3
ed492175  default    de-gt-2  nomad-client-triton-0  <none>   debian  false  eligible     ready   0
4828a13c  default    de-gt-2  nomad-client-triton-1  <none>   debian  false  eligible     ready   1

Dazu kommen über die Datacenter verteilte Clients, wobei die, die von den Autoscalern bereitgestellt werden, in ihrem eigenen Node-Pool landen. Es könnten noch weitere Clients in beliebigen anderen Datacentern (wieder VMs, bare metal Server, etc.) oder auch andere, föderierte Nomad-Cluster an diesen Cluster angehängt werden, da hier die Anforderungen an die Netzwerklatenz nicht so hoch sind wie innerhalb eines Clusters. Auch die Datacenter müssen nicht auf derselben Technologie aufsetzen. Hier sind nur zwei der Umgebungen auf OpenStack aufgebaut während eine auf Triton Datacenter basiert.