Nahrazení LXCFS virtualizací v kernelu / Replacing LXCFS with in-kernel virtualization

Read in English below.

LXCFS je souborový systém založený na FUSE (Filesystem in Userspace), který jsme od počátku vpsAdminOS používali k virtualizaci vybraných souborů v /proc a /sys ve VPS jakožto kontejnerech. Linux sice podporuje izolaci pomocí jmenných prostorů a limity přes cgroups, nicméně i tak by bez LXCFS ve VPS šlo vidět v top-u všechny CPU nodu, free by ukazoval využití paměti celého nodu, load average by byl vyšší než čekáte, atp. Postupem času jsme roli LXCFS zmenšovali a od kernelu 6.6.10 pro vpsAdminOS jsme ho kompletně odstranili, už není potřeba!

Náš kernel nyní obsahuje patche, které vybrané soubory v /proc virtualizují na základě jmenných prostorů a limitů z cgroups. LXCFS muselo tyto hodnoty zjišťovat z userspace čtením parametrů ze /sys/fs/cgroup a k počítání load average dokonce muselo procházet všechny procesy patřící VPS přes /proc. Čtení těchto souborů zabírá sdílené zámky, což pak zdržuje ostatní přístupy.

LXCFS taktéž fungovalo jen tam, kde je připojeno. Pokud sami uvnitř VPS pouštíte kontejnery, museli jste LXCFS dovnitř buď ručně bind-mountovat, a nebo uvnitř těchto kontejnerů hodnoty v /proc virtualizovány nebyly. Některé utility zjišťují stav systému pomocí systémového volání sysinfo a to pak LXCFS kompletně obešlo. sysinfo používá např. Busybox, nebo i samotná glibc. Na Alpine Linuxu, kde je ve výchozím stavu Busybox, tak příkaz uptime ukazoval load average celého nodu.

Na nodech se starším kernelem se můžeme podívat, jaké soubory LXCFS v poslední fázi provozu virtualizuje:

$ grep lxcfs /proc/mounts
lxcfs /proc/cpuinfo fuse.lxcfs rw,nosuid,nodev,relatime,user_id=0,group_id=0,allow_other 0 0
lxcfs /proc/diskstats fuse.lxcfs rw,nosuid,nodev,relatime,user_id=0,group_id=0,allow_other 0 0
lxcfs /proc/loadavg fuse.lxcfs rw,nosuid,nodev,relatime,user_id=0,group_id=0,allow_other 0 0
lxcfs /proc/uptime fuse.lxcfs rw,nosuid,nodev,relatime,user_id=0,group_id=0,allow_other 0 0

V minulosti zde bylo také /proc/stat, /proc/meminfo, /proc/swaps a /sys/devices/system/cpu/online. Virtualizaci využití CPU z pohledu VPS na základě CFS kvót jsme tehdy do LXCFS sami dodělávali.

Historicky jsme začínali s jednou instancí LXCFS pro všechny VPS v rámci jednoho nodu, to fungovalo v pohodě na nodech do 100 VPS. Když jsme přecházeli na nody se 300/600 VPS, jeden LXCFS proces už přestával stíhat. Krátce jsme spouštěli každému VPS vlastní instanci LXCFS, ale to se ukázalo jako velmi špatný nápad, neboť se pak 300/600 LXCFS procesů pralo o čtení z /proc a cgroups parametrů. Nakonec jsme přešli na model LXCFS worker procesů, kdy jedna instance obsluhovala max 50 VPS a poté se dle potřeby spouštěly další instance:

[root@node24.prg.vpsfree.cz]
 ~ # osctl lxcfs worker ls
NAME             ENABLED   SIZE   MAX_SIZE   CPU_PACKAGE   LOADAVG   CFS
worker.0.cpu1    true      49     50         1             true      true
worker.1.cpu1    true      50     50         1             true      true
worker.2.cpu1    true      50     50         1             true      true
worker.3.cpu1    true      50     50         1             true      true
worker.4.cpu1    true      50     50         1             true      true
worker.5.cpu1    true      48     50         1             true      true
worker.6.cpu1    true      18     50         1             true      true
worker.7.cpu0    true      50     50         0             true      true
worker.8.cpu0    true      50     50         0             true      true
worker.9.cpu0    true      50     50         0             true      true
worker.10.cpu0   true      49     50         0             true      true
worker.11.cpu0   true      50     50         0             true      true
worker.12.cpu0   true      36     50         0             true      true

node23/24.prg mají dva CPU sokety a tam ještě řešíme i to, aby VPS používalo LXCFS instanci na stejném soketu. Kanál na přenos dat mezi sokety má totiž omezenou propustnost, takže se snažíme VPS držet v rámci jednoho soketu.

Nyní tohle všechno odpadá a LXCFS jsme z vpsAdminOS kompletně odstranili. Z VPS tak vždy uvidíte virtualizované hodnoty. Pokud uvnitř VPS sami LXCFS používáte, nic se pro vás nemění a můžete jej nadále provozovat. Naše změny vás pouze abstrahují od toho, že VPS je taky jen kontejner.

English

I’d like to inform you about a significant change we’ve implemented in vpsAdminOS concerning LXCFS, a filesystem based on FUSE (Filesystem in Userspace) used for virtualizing selected files in /proc and /sys for VPS as containers. Linux supports isolation through namespaces and cgroups limits, but without LXCFS in a VPS, you’d see all physical CPUs in top, free would show the memory usage of the entire node, and the load average would be higher than expected. Over time, we’ve been reducing our reliance on LXCFS and, as of kernel version 6.6.10 for vpsAdminOS, we’ve completely removed it - it’s no longer needed!

Our kernel now includes patches that virtualize selected files in /proc based on namespaces and cgroups limits. LXCFS had to calculate these values from userspace by reading parameters from /sys/fs/cgroup, and to calculate load average, it even had to go through all processes belonging to a VPS via /proc. Reading these files takes up shared locks, which then slow down other accesses.

LXCFS also only worked where it was actually mounted. If you run containers within a VPS, you had to either manually bind-mount LXCFS or the values in /proc inside these containers were not virtualized. Some utilities determine the system status using the sysinfo system call, which completely bypassed LXCFS. sysinfo is used, for example, by Busybox or even glibc. For instance, on Alpine Linux, which defaults to Busybox, the uptime command showed the load average of the entire node.

On nodes with older kernels, we can see which files LXCFS are virtualized in its final form:

$ grep lxcfs /proc/mounts
lxcfs /proc/cpuinfo fuse.lxcfs rw,nosuid,nodev,relatime,user_id=0,group_id=0,allow_other 0 0
lxcfs /proc/diskstats fuse.lxcfs rw,nosuid,nodev,relatime,user_id=0,group_id=0,allow_other 0 0
lxcfs /proc/loadavg fuse.lxcfs rw,nosuid,nodev,relatime,user_id=0,group_id=0,allow_other 0 0
lxcfs /proc/uptime fuse.lxcfs rw,nosuid,nodev,relatime,user_id=0,group_id=0,allow_other 0 0

In the past, there were also /proc/stat, /proc/meminfo, /proc/swaps, and /sys/devices/system/cpu/online. We’ve contributed the initial implementation of CPU usage view based on CFS quotas to LXCFS.

We initially started with one LXCFS instance per node for all VPS, which worked fine for nodes with up to 100 VPS. As we moved to nodes with 300/600 VPS, a single LXCFS process began to struggle. We briefly ran an individual LXCFS instance for each VPS, but this proved to be a very bad idea, as then 300/600 LXCFS processes were competing for reading /proc and cgroups parameters. Eventually, we switched to a model of LXCFS worker processes, where one worker served a maximum of 50 VPS, and additional workers were launched as needed:

[root@node24.prg.vpsfree.cz]
 ~ # osctl lxcfs worker ls
NAME             ENABLED   SIZE   MAX_SIZE   CPU_PACKAGE   LOADAVG   CFS
worker.0.cpu1    true      49     50         1             true      true
worker.1.cpu1    true      50     50         1             true      true
worker.2.cpu1    true      50     50         1             true      true
worker.3.cpu1    true      50     50         1             true      true
worker.4.cpu1    true      50     50         1             true      true
worker.5.cpu1    true      48     50         1             true      true
worker.6.cpu1    true      18     50         1             true      true
worker.7.cpu0    true      50     50         0             true      true
worker.8.cpu0    true      50     50         0             true      true
worker.9.cpu0    true      50     50         0             true      true
worker.10.cpu0   true      49     50         0             true      true
worker.11.cpu0   true      50     50         0             true      true
worker.12.cpu0   true      36     50         0             true      true

Nodes like node23/24.prg have two CPU sockets and there we also ensured that VPS use LXCFS worker running on the same socket. The data transfer channel between sockets has limited bandwidth, so we try to keep VPS pinned to individual sockets.

Now, all of this is no longer necessary, and we’ve completely removed LXCFS from vpsAdminOS. From within a VPS, you will always see virtualized values. If you are using LXCFS inside the VPS yourself, nothing changes for you, and you can continue to operate it. Our changes only abstract away the fact that a VPS is also a container.

2 Likes