Nomaden im Nebel

Nachdem die Versuche, Nomad zusammen mit dem Consul Service Mesh dazu zu benutzen, gänzlich unabhängige Cluster miteinander zu integrieren, an (nicht) globalen Service Tokens gescheitert waren, kam ja dann Nebula zur Hilfe.

Damit ist ein Nomad-Cluster, der sich über mehrere unabhängige Umgebungen erstreckt, schnell aufgebaut.

Nebula startet man am besten per systemd:

[Unit]
Description=Nebula overlay networking tool
Wants=basic.target network-online.target nss-lookup.target time-sync.target
After=basic.target network.target network-online.target
Before=sshd.service

[Service]
Type=notify
NotifyAccess=main
SyslogIdentifier=nebula
ExecReload=/bin/kill -HUP $MAINPID
ExecStart=/usr/local/bin/nebula -config /etc/nebula/config.yaml
Restart=always

[Install]
WantedBy=multi-user.target

Für die Nomad-Server müssen nur wenige Ports im Overlay-Netzwerk erlaubt werden:

#     http
    - port: 4646
      proto: tcp
      host: any
#     rpc
    - port: 4647
      proto: tcp
      host: any
#     serf 
    - port: 4648
      proto: tcp
      host: any

    - port: 4648
      proto: udp
      host: any

Für die Nomad-Clients werden nur rpc und der gesamte Portrange, den Nomad für Allocations nutzt, freigegeben:

  inbound:
    - port: 20000-32000
      proto: any
      host: any

    - port: 4647
      proto: tcp
      host: any

In der Nomad Clientkonfiguration sollte man dann die verschiedenen Netzwerkinterfaces konfigurieren bzw. bekannt machen (so wie es auch in der Dokumentation beschrieben wird):

[...]
client {
  enabled          = true
# node_pool        = "static-clients"
  cni_path         = "/opt/cni/bin"
  cni_config_dir   = "/opt/cni/config"
  server_join {
    retry_join     = [ "10.21.20.12", "10.21.20.13", "10.21.20.14" ] 
    retry_max      = 5
    retry_interval = "15s"
  }
  host_network "overlay" {
    interface = "nebula1"
  }
  host_network "internal" {
    interface = "ens3"
  }
}

An der Serverkonfiguration muß nichts geändert werden. Nur die Clients müssen die anliegenden Netze kennen.

Diese Netzdefinitionen können dann in der group- und service-Definition in Nomad Job-Files verwendet werden:

job "memcached-prod1" { 
   datacenters = ["prod1"]

   group "group-memcached-prod1" {

      network {
         port "memcached" { 
           host_network = "overlay"
         }
      }

       task "task-memcached-prod1" {
         driver = "exec"
            config {
              command = "/usr/local/bin/memcached"
              args = [
                 "-l",
                 "${NOMAD_IP_memcached}",
                 "-p", 
                 "${NOMAD_PORT_memcached}"
              ]
            }

          service {
             tags = [ "${node.datacenter}" ]
             name = "memcached-prod1"
             port = "memcached"
             provider = "nomad"
             address_mode = "auto"

            check {
              type = "tcp"
              port = "memcached"
              interval = "10s"
              timeout = "2s"

              check_restart {
                limit = 3
                grace = "90s"
                ignore_warnings = "false"
              }
            }
          }
        }
    }
}

Der Job fährt z. B. einen memcached im vorher definierten "overlay" Netzwerk hoch und vergibt eine zufällige Portnummer aus dem Nomad Portrange:

root@triton-nomad1:~/nomad-jobs# nomad service info memcached-prod1
Job ID           Address            Tags     Node ID   Alloc ID
memcached-prod1  10.21.20.11:25417  [prod1]  cbe5e990  6329fce9

Damit wird es natürlich sehr einfach - wie in diesem Fall - mehrere memcached-Instanzen in verschiedenen Umgebungen über einen memcached-proxy mit Daten zu befüllen (bzw. zum Zwecke der Ausfallsicherheit aus diesen zu lesen):

job "memcached-proxy" { 
   datacenters = ["*"]

   group "group-memcachedp" {

      network {
         port "memcachedp" {} 
      }

       task "task-memcachedp" {
         driver = "exec"
            config {
              command = "/usr/local/bin/memcached"
              args = [
                "-l",
                 "${NOMAD_IP_memcachedp}",
                 "-p", 
                 "${NOMAD_PORT_memcachedp}",
                 "-o",
                 "proxy_config=routelib,proxy_arg=local/config.lua",
              ]
            }

         template {
            data = <<EOH
pools{
    set_all = {
        {  backends = { 
            {{- range nomadService "memcached-prod1" }}
              "{{ .Address }}:{{ .Port }}"{{- end}} 
            } 
        },
        {  backends = {
            {{- range nomadService "memcached-prod4" }}
              "{{ .Address }}:{{ .Port }}"{{- end}}
           }
        },
    } 
}
routes{
    cmap = {
        get = route_failover{
            children = "set_all",
            stats = true,
            miss = true,
            shuffle = true,
            failover_count = 2
        },
    },
    default = route_allsync{ 
            children = "set_all",
    },
}  
            EOH
        
            destination = "local/config.lua"
         } 

         service {
             name = "memcached-proxy"
             port = "memcachedp"
             provider ="nomad"

           check {
              type = "tcp"
              port = "memcachedp"
              interval = "10s"
              timeout = "2s"

             check_restart {
                limit = 3
                grace = "90s"
                ignore_warnings = "false"
              }
            }
          }
        }
    } 
}

Wenn man jetzt Daten beim memcached-proxy

root@triton-nomad1:~/nomad-jobs# nomad service info memcached-proxy
Job ID           Address              Tags  Node ID   Alloc ID
memcached-proxy  192.168.0.200:26290  []    cbe5e990  d41b143c

einwirft (s. a. memcached-Dokumentation):

root@nomad-client-0:~# telnet 192.168.0.200 26290
Trying 192.168.0.200...
Connected to 192.168.0.200.
Escape character is '^]'.
set quickstart/data 0 0 6
Hello!
STORED
get quickstart/data
VALUE quickstart/data 0 6
Hello!
END
quit

Dann kann man sie auch aus den beiden memcached-Instanzen wieder auslesen:

root@nomad-client-0:~# telnet 10.21.20.11 25417
Trying 10.21.20.11...
Connected to 10.21.20.11.
Escape character is '^]'.
get quickstart/data 
VALUE quickstart/data 0 6
Hello!
END
quit
Connection closed by foreign host.
root@nomad-client-0:~# telnet 10.21.20.10 28132
Trying 10.21.20.10...
Connected to 10.21.20.10.
Escape character is '^]'.
get quickstart/data
VALUE quickstart/data 0 6
Hello!
END
quit
Connection closed by foreign host.

Die Daten werden nicht repliziert! Man sollte also immer eine memcached-Instanz mehr haben als man Ausfälle hat!

Interessant auch: Wenn man keine host_network Definition im Job-File verwendet, wird offenbar "automagisch" das "normale" Netzwerkinteface der Client-Instanz verwendet.