Nightmare (Filesystem) on Triton (Street)

Aus verschiedenen Gründen habe ich neulich wieder Dokumentation gelesen - konkret ging es um die Erstellung von Packages - quasi den T-Shirt-Sizes - in Triton. Denn zwar gibt es schon länger den neuen bhyve Hypervisor auf der Plattform, aber "normale User" können ihn noch nicht verwenden, weil es noch keine Packages gibt, die ihn benutzen würden. Beim Lesen der Dokumentation fiel dann auf, dass um die neue Funktion "flexible diskspace" für bhyve-VMs benutzen zu können, ein relativ aktuelles Platform Image (PI) auf dem Headnode und den Compute Nodes (CN) gestartet sein muß. Da also alle Nodes einmal gebootet werden mußten, bot es sich an, gleichzeitig noch eine weitere Triton-Funktion zu aktivieren: virtuelle Netzwerke - in Triton "Fabrics" genannt.

Ich hatte im August 2015 das Konzept schonmal erläutert. Zur Einrichtung habe ich diesmal mit Unterstützung einer netten SE-Kollegin ein tagged VLAN zusätzlich zu dem dort schon vorhandenen native VLAN auf dem externen Interface der Nodes konfigurieren lassen. Dies fungiert als "underlay network" - alle Nodes, auf denen virtuelle Netzwerke verfügbar sein sollen, müssen ein Interface darin haben. Das Vorgehen ist in der Dokumentation unter Setup Fabric Networks beschrieben. Nach dem nächsten reboot hat jeder so konfigurierte Server dann eine enstprechende VNIC in dem gewünschten VLAN:

[root@hh24-gts2-de28 (de-gt-2) ~]# ifconfig sdc_underlay0
sdc_underlay0: flags=201000843<UP,BROADCAST,RUNNING,MULTICAST,IPv4,CoS> mtu 9000 index 3
    inet 172.20.2.24 netmask ffffff00 broadcast 172.20.2.255
    ether 90:b8:d0:6d:c0:83
[root@hh24-gts2-de28 (de-gt-2) ~]# dladm show-vnic
LINK         OVER       SPEED MACADDRESS        MACADDRTYPE VID  ZONE
sdc_underlay0 igb2      1000  90:b8:d0:6d:c0:83 fixed       1715 --
net0         igb0       0     90:b8:d0:46:93:19 fixed       0    81dcfe12-788f-4c97-bfae-1959e7e77e9e
net0         sdc_overlay5556037 0 90:b8:d0:96:f1:32 fixed   2    ab4203a9-75f9-4ef8-bb6f-942ba24bdd47

Vermutlich weil ich schonmal eine Fabric-Konfiguration in diesem Datacenter hatte, stehen den Usern damit sofort virtuelle Netzwerke zur Verfügung:

root@e3b75fc5-3621-cdd8-f9dd-c9acbf4a37e9:~# triton network ls
SHORTID   NAME               SUBNET            GATEWAY        FABRIC  VLAN  PUBLIC
2fa4d4ed  My-Fabric-Network  192.168.128.0/22  192.168.128.1  true    2     false
aa3f924b  extpool            -                 -              -       -     true
bc7d2db4  sdc_nat            -                 -              -       -     true

Und jetzt endlich zu NFS: Shared Storage ist in Triton - und generell in der Cloud - eigentlich ein Anti-Pattern. Das haben inzwischen auch andere bermerkt. Aber wie es halt so ist, geht es eben doch nicht ohne. Und deshalb hat Joyent schon vor einiger Zeit den RFD Network Shared Storage for Triton angelegt und dann umgesetzt. In einem der letzten Releases wurde dann volapiv2 verfügbar gemacht und da ist es natürlich sinnvoll, das Ganze mal zu testen. Voraussetzung für die Konfiguration von NFS shared volumes ist das Vorhandensein von Fabric networks - und die hatten wir ja oben schon eingerichtet:

[root@headnode (de-gt-2) ~]# sdcadm experimental nfs-volumes docker
Getting versions for image dependencies: cnapi, vmapi, volapi, workflow, docker
Checking dependencies are up to date
Checking platform version dependencies
Enabling docker has no platform dependency
Checking if experimental_docker_nfs_shared_volumes=true in SDC app...
Setting experimental_docker_nfs_shared_volumes to true in SDC app...
experimental_docker_nfs_shared_volumes set to true on SDC app
Enabled docker NFS volumes support
[root@headnode (de-gt-2) ~]# sdcadm experimental nfs-volumes cloudapi
Getting versions for image dependencies: cnapi, vmapi, volapi, workflow, cloudapi
Checking dependencies are up to date
Checking platform version dependencies
Enabling cloudapi has no platform dependency
Checking if experimental_cloudapi_nfs_shared_volumes=true in SDC app...
Setting experimental_cloudapi_nfs_shared_volumes to true in SDC app...
experimental_cloudapi_nfs_shared_volumes set to true on SDC app
Enabled cloudapi NFS volumes support
[root@headnode (de-gt-2) ~]# sdcadm experimental nfs-volumes cloudapi-automount
Feature cloudapi-automount has no image dependencies
Checking platform version dependencies
Getting servers list
Checking if experimental_cloudapi_automount_nfs_shared_volumes=true in SDC app...
Setting experimental_cloudapi_automount_nfs_shared_volumes to true in SDC app...
experimental_cloudapi_automount_nfs_shared_volumes set to true on SDC app
Enabled cloudapi-automount NFS volumes support
[root@headnode (de-gt-2) ~]# sdcadm experimental nfs-volumes docker-automount
Feature docker-automount has no image dependencies
Checking platform version dependencies
Getting servers list
Checking if experimental_docker_automount_nfs_shared_volumes=true in SDC app...
Setting experimental_docker_automount_nfs_shared_volumes to true in SDC app...
experimental_docker_automount_nfs_shared_volumes set to true on SDC app
Enabled docker-automount NFS volumes support

Und so sieht das dann für den User aus:

root@e3b75fc5-3621-cdd8-f9dd-c9acbf4a37e9:~# triton network list
SHORTID   NAME               SUBNET            GATEWAY        FABRIC  VLAN  PUBLIC
2fa4d4ed  My-Fabric-Network  192.168.128.0/22  192.168.128.1  true    2     false
aa3f924b  extpool            -                 -              -       -     true
bc7d2db4  sdc_nat            -                 -              -       -     true
root@e3b75fc5-3621-cdd8-f9dd-c9acbf4a37e9:~# triton volume create -n testvolume -s 10G -N 2fa4d4ed
Creating volume testvolume (e99b5d90-2678-6d43-9b59-dff0d316b5eb)
root@e3b75fc5-3621-cdd8-f9dd-c9acbf4a37e9:~# triton volume ls                             
SHORTID   NAME        SIZE   TYPE       STATE     AGE
e99b5d90  testvolume  10240  tritonnfs  creating  0s
root@e3b75fc5-3621-cdd8-f9dd-c9acbf4a37e9:~# triton volume ls
SHORTID   NAME        SIZE   TYPE       STATE  AGE
e99b5d90  testvolume  10240  tritonnfs  ready  0s
root@e3b75fc5-3621-cdd8-f9dd-c9acbf4a37e9:~# triton volume ls -l
ID                                    NAME        SIZE   TYPE       RESOURCE                                                        STATE  CREATED
e99b5d90-2678-6d43-9b59-dff0d316b5eb  testvolume  10240  tritonnfs  192.168.128.5:/zones/ab4203a9-75f9-4ef8-bb6f-942ba24bdd47/data  ready  -

Auf der Serverseite wird eine SmartOS-Zone als NFS-Server gestartet:

[root@hh24-gts2-de28 (de-gt-2) ~]# vmadm list
UUID                                  TYPE  RAM      STATE             ALIAS
ab4203a9-75f9-4ef8-bb6f-942ba24bdd47  OS    256      running           nfs-shared-volume-e99b5d90-2678-6d43-9b59-dff0d316b5eb
81dcfe12-788f-4c97-bfae-1959e7e77e9e  OS    8192     running           moray1
[root@hh24-gts2-de28 (de-gt-2) ~]# zfs list -r zones/ab4203a9-75f9-4ef8-bb6f-942ba24bdd47
NAME                                              USED  AVAIL  REFER     MOUNTPOINT
zones/ab4203a9-75f9-4ef8-bb6f-942ba24bdd47       9,00G  1,00G  48,7M  /zones/ab4203a9-75f9-4ef8-bb6f-942ba24bdd47
zones/ab4203a9-75f9-4ef8-bb6f-942ba24bdd47/data  8,99G  1,00G  8,99G  /zones/ab4203a9-75f9-4ef8-bb6f-942ba24bdd47/data

Wenn man dann eine Instanz erzeugt, die ein Netzwerkinterface in diesem virtuellen Netz hat, kann sie sich das Volume mounten:

ubuntu@a1bb03ff-2ab9-4298-826b-927d768a1dea:~$ ifconfig net1
net1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 8500
        inet 192.168.128.9  netmask 255.255.252.0  broadcast 192.168.131.255
        inet6 fe80::92b8:d0ff:fe12:2735  prefixlen 64  scopeid 0x20<link>
        ether 90:b8:d0:12:27:35  txqueuelen 1000  (Ethernet)
        RX packets 9  bytes 732 (732.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 19  bytes 1510 (1.5 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
root@a1bb03ff-2ab9-4298-826b-927d768a1dea:~# mount -t nfs 192.168.128.5:/zones/ab4203a9-75f9-4ef8-bb6f-942ba24bdd47/data /nfs
root@a1bb03ff-2ab9-4298-826b-927d768a1dea:~#

Wichtig! Vorher das nfs-common Paket installieren.

Der aufmerksame Leser wird sich jetzt ggfs. fragen "und was hat es mit dem automount auf sich". Das habe ich mich auch gefragt und bin dabei auf einen Bugview-Artikel gestossen. Offenbar gibt es einen bisher nicht dokumentierten Parameter -v. Damit kann man einerseits automatisch NFS-Mounts erzeugen und diese gleich in der neuen Instanz mounten oder andererseits bereits existierende NFS-Mounts in einer neuen Instanz mounten:

root@e3b75fc5-3621-cdd8-f9dd-c9acbf4a37e9:~# triton volume ls -l
ID                                    NAME        SIZE   TYPE       RESOURCE                                                        STATE  CREATED
e99b5d90-2678-6d43-9b59-dff0d316b5eb  testvolume  10240  tritonnfs  192.168.128.5:/zones/ab4203a9-75f9-4ef8-bb6f-942ba24bdd47/data  ready  -
root@e3b75fc5-3621-cdd8-f9dd-c9acbf4a37e9:~# triton inst create --name mounttest -v testvolume:/nfsmount e75c9d82 dd683c97

Das erzeugt eine Instanz mit Namen mounttest, in der das NFS-Volume unter /nfsmount gemountet ist:

[root@mounttest ~]# mount |grep nfsmount
/nfsmount on 192.168.128.5:/zones/ab4203a9-75f9-4ef8-bb6f-942ba24bdd47/data remote/read/write/setuid/nodevices/xattr/zone=e18e4d55-9bfe-6a99-a406-8bb9a6f4de18/dev=9300003 on Fr. Apr. 17 21:34:07 2020
[root@mounttest ~]# ls /nfsmount/
catalog  composer.json  customer  downloadable  import  LICENSE.txt  theme_customization  wysiwyg

Wie ich festgestellt habe, bieten weder Amazons EFS noch Googles Filestore diese Option (dabei liegt sie eigentlich auf der Hand - die wenigsten werden ein NFS-Volume erzeugen wollen, ohne es hinterher an irgendwelchen Compute-Instanzen zu mounten). Da soll noch jemand sagen, OpenSource Projekte wären nicht zur Innovation fähig.

Natürlich ist dann ein kleiner Benchmark fällig:

GENERIC: (g=0): rw=randrw, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=libaio, iodepth=16
fio-3.1
Starting 1 process

GENERIC: (groupid=0, jobs=1): err= 0: pid=3677: Fri Apr  3 15:13:31 2020
  read: IOPS=48, BW=48.7MiB/s (51.1MB/s)(14.3GiB/300067msec)
    slat (usec): min=55, max=30320, avg=441.36, stdev=924.88
    clat (msec): min=9, max=202, avg=67.96, stdev=34.79
     lat (msec): min=9, max=202, avg=68.41, stdev=34.84
    clat percentiles (msec):
     |  1.00th=[   11],  5.00th=[   13], 10.00th=[   20], 20.00th=[   35],
     | 30.00th=[   48], 40.00th=[   59], 50.00th=[   68], 60.00th=[   78],
     | 70.00th=[   86], 80.00th=[   97], 90.00th=[  113], 95.00th=[  129],
     | 99.00th=[  157], 99.50th=[  167], 99.90th=[  190], 99.95th=[  192],
     | 99.99th=[  203]
   bw (  KiB/s): min= 2043, max=124678, per=100.00%, avg=68742.94, stdev=36666.15, samples=435
   iops        : min=    1, max=  121, avg=66.83, stdev=35.85, samples=435
  write: IOPS=20, BW=20.8MiB/s (21.8MB/s)(6243MiB/300067msec)
    slat (usec): min=64, max=49789, avg=724.46, stdev=1690.57
    clat (msec): min=41, max=3014, avg=606.36, stdev=604.04
     lat (msec): min=41, max=3015, avg=607.10, stdev=604.11
    clat percentiles (msec):
     |  1.00th=[   65],  5.00th=[   91], 10.00th=[  107], 20.00th=[  130],
     | 30.00th=[  146], 40.00th=[  167], 50.00th=[  194], 60.00th=[  659],
     | 70.00th=[ 1062], 80.00th=[ 1217], 90.00th=[ 1401], 95.00th=[ 1687],
     | 99.00th=[ 2500], 99.50th=[ 2668], 99.90th=[ 2869], 99.95th=[ 2937],
     | 99.99th=[ 3004]
   bw (  KiB/s): min= 2043, max=77824, per=100.00%, avg=35754.23, stdev=17567.43, samples=357
   iops        : min=    1, max=   76, avg=34.58, stdev=17.13, samples=357
  lat (msec)   : 10=0.14%, 20=7.08%, 50=15.28%, 100=37.45%, 250=27.57%
  lat (msec)   : 500=0.37%, 750=0.37%, 1000=1.79%, 2000=9.46%, >=2000=0.50%
  cpu          : usr=0.98%, sys=2.12%, ctx=118110, majf=0, minf=10
  IO depths    : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=99.9%, 32=0.0%, >=64=0.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.1%, 32=0.0%, 64=0.0%, >=64=0.0%
     issued rwt: total=14621,6243,0, short=0,0,0, dropped=0,0,0
     latency   : target=0, window=0, percentile=100.00%, depth=16

Run status group 0 (all jobs):
  READ: bw=48.7MiB/s (51.1MB/s), 48.7MiB/s-48.7MiB/s (51.1MB/s-51.1MB/s), io=14.3GiB (15.3GB), run=300067-300067msec
  WRITE: bw=20.8MiB/s (21.8MB/s), 20.8MiB/s-20.8MiB/s (21.8MB/s-21.8MB/s), io=6243MiB (6546MB), run=300067-300067msec

Fio Parameter:

DEFAULT_NAME=GENERIC
DEFAULT_RUNTIME=300
DEFAULT_DIRECTORY=/nfs
DEFAULT_READWRITE=randrw
DEFAULT_RWMIXREAD=70
#DEFAULT_BLOCKSIZE=8k
DEFAULT_BLOCKSIZE=1024k
DEFAULT_SIZE=9G
#DEFAULT_IODEPTH=1
DEFAULT_IODEPTH=16
DEFAULT_SLEEP=300
    fio --name=$FIO_NAME --time_based --runtime=$FIO_RUNTIME \
        --directory=$FIO_DIRECTORY --direct=1 --readwrite=$FIO_READWRITE \
        --rwmixread=$FIO_RWMIXREAD --blocksize=$FIO_BLOCKSIZE \
        --refill_buffers --size=$FIO_SIZE --ioengine=libaio \
        --iodepth=$FIO_IODEPTH --output=fio-output.txt

Die Parameter hatte ich aus diesem Isilon-Benchmark (Github) abgeguckt. Dabei geht der Traffic über die 1Gbit/s Netzwerkkarten in den Servern. 

Links


Slides

Setup Fabric Networks

Triton Networking Layout

Triton initial network configuration

Volumes in Triton