LXC - Get memory estimate inside container

angus.dickey

New Member
Jan 26, 2023
3
0
1
We have a few LXCs (Proxmox 7.2-11) that have memory restrictions set using the normal method available in the Proxmox console. Generally things work great but there is one application we run in the containers that keep consuming memory until the container hits the limit and the OOM killer tries to free some.

We figured out that the application is overestimating the available memory by using the total host memory instead of the container memory. The host has 768GB and the containers have 2GB, so it is a big difference. Inside the containers some utilities (free & /proc/meminfo) show the expected memory:

Code:
$ free -h
               total        used        free      shared  buff/cache   available
Mem:           2.0Gi       152Mi       1.1Gi       0.0Ki       755Mi       1.8Gi
Swap:          256Mi          0B       256Mi
$ cat /proc/meminfo | grep MemTotal
MemTotal:        2048000 kB

and some don't (sysinfo) :

C:
#include <stdio.h>
#include <sys/sysinfo.h>

/*
get_memory.c
*/
int main(void) {
   struct sysinfo si;
   sysinfo(&si);
   printf("sysinfo() thinks it has %ld bytes of physical memory\n", si.totalram);
   return 0;
}

Code:
$ ./get_memory
sysinfo() thinks it has 135083474944 bytes of physical memory

The application code is open source so we can see it tries to adjust its memory estimate by looking at the cgroup v2 files inside the container. It finds the user "slice" (I think it is called) and then tries to read from /sys/fs/cgroup/<slice>/memory.max. The problem is this file is always set to max in the container, in fact they are all set to max:

Code:
$ find /sys/fs/cgroup -type f -name memory.max -exec sh -c "cat '{}'" \;
max
max
max
... all max ...
max

If I try the same thing on the host I actually find it is set to the expected value:

Code:
$ cat /sys/fs/cgroup/lxc/901/memory.max
2097152000

So, my questions are: Does Proxmox handle limiting memory the same way LXD does? Is it through cgroups v2? What is the best way for a program running in a Proxmox LXC to get the amount of memory available to the container?

Thanks for any insight.

Angus
 
Is it through cgroups v2?
Yes (if the PVE is recent, previously this was with v1 obviously)

What is the best way for a program running in a Proxmox LXC to get the amount of memory available to the container?
Using a modern syscall/API interface that is able to do it correctly. This depends on what you want and what your programming lanugage is.
 
Thank you for the response, I hear what you are saying.

In Python psutil reports the right physical memory. Do you happen to know an equivalent for the C/C++ world that works with cgroup v2?
 
Do you happen to know an equivalent for the C/C++ world that works with cgroup v2?
I do know that tools like free report it correctly in e.g. the last 6 Debian versions or so, so there was a change in there. Alpine Linux with it's musl c library still has this problem. So it must be therein somewhere.

I just set up a Debian Bullseye container and tried a couple of solutions from SO and the only reliable option is to read /proc/meminfo. This is exactly what free (meminfo.c) does. Neither sysconf nor sysinfo were able to read the correct value. Hopefully this helps!
 
That does help thank you.

I did something similar, although restricted to the newer Ubuntu versions, and found the same thing. Reading /proc/meminfo is what the project decided to go with as a fix.

Thanks again for your time,

Angus
 
This problem has bitten me with respect to how JVM ergonomics chooses its heap size by default in proxmox LXC (5.15.85-1-pve).

When running under proxmox container, all of the "memory.max" are set to "max", and the JVM ergonomics uses other means to determine system memory:

Code:
# pct config 100
arch: amd64
cores: 4
hostname: deep3
memory: 4096
mp0: local-zfs:subvol-100-disk-1,mp=/data,backup=1,size=32G
net0: name=eth0,bridge=vmbr0,hwaddr=52:37:86:0C:62:4F,ip=dhcp,type=veth
ostype: ubuntu
rootfs: local-zfs:subvol-100-disk-0,size=8G
swap: 512
unprivileged: 1

# pct exec 100 -- java -XshowSettings:system -version
Operating System Metrics:
    Provider: cgroupv2
    Effective CPU Count: 4
    CPU Period: -1
    CPU Quota: -1
    CPU Shares: -1
    List of Processors: N/A
    List of Effective Processors: N/A
    List of Memory Nodes: N/A
    List of Available Memory Nodes: N/A
    Memory Limit: Unlimited
    Memory Soft Limit: 0.00K
    Memory & Swap Limit: Unlimited
    Maximum Processes Limit: Unlimited

openjdk version "17.0.6" 2023-01-17 LTS
OpenJDK Runtime Environment Zulu17.40+19-CA (build 17.0.6+10-LTS)
OpenJDK 64-Bit Server VM Zulu17.40+19-CA (build 17.0.6+10-LTS, mixed mode, sharing)

# pct exec 100 -- java -Xlog:os+container=trace -XshowSettings:system -version
[0.000s][trace][os,container] OSContainer::init: Initializing Container Support
[0.000s][debug][os,container] Detected optional pids controller entry in /proc/cgroups
[0.001s][debug][os,container] Detected cgroups v2 unified hierarchy
[0.001s][trace][os,container] Path to /cpu.max is /sys/fs/cgroup/.lxc/cpu.max
[0.001s][debug][os,container] Open of file /sys/fs/cgroup/.lxc/cpu.max failed, No such file or directory
[0.001s][trace][os,container] CPU Quota is: -2
[0.001s][trace][os,container] Path to /cpu.max is /sys/fs/cgroup/.lxc/cpu.max
[0.001s][debug][os,container] Open of file /sys/fs/cgroup/.lxc/cpu.max failed, No such file or directory
[0.001s][trace][os,container] CPU Period is: -2
[0.001s][trace][os,container] OSContainer::active_processor_count: 4
[0.001s][trace][os,container] CgroupSubsystem::active_processor_count (cached): 4
[0.001s][trace][os,container] total physical memory: 33522720768
[0.001s][trace][os,container] Path to /memory.max is /sys/fs/cgroup/.lxc/memory.max
[0.001s][trace][os,container] Raw value for memory limit is: max
[0.001s][trace][os,container] Memory Limit is: Unlimited
[0.001s][debug][os,container] container memory limit unlimited: -1, using host value 33522720768
[0.002s][trace][os,container] CgroupSubsystem::active_processor_count (cached): 4
[0.011s][trace][os,container] CgroupSubsystem::active_processor_count (cached): 4
[0.022s][trace][os,container] total physical memory: 33522720768
[0.022s][trace][os,container] Path to /memory.max is /sys/fs/cgroup/.lxc/memory.max
[0.022s][trace][os,container] Raw value for memory limit is: max
[0.022s][trace][os,container] Memory Limit is: Unlimited
[0.022s][debug][os,container] container memory limit unlimited: -1, using host value 33522720768
[0.042s][trace][os,container] total physical memory: 33522720768
[0.042s][trace][os,container] Path to /memory.max is /sys/fs/cgroup/.lxc/memory.max
[0.042s][trace][os,container] Raw value for memory limit is: max
[0.042s][trace][os,container] Memory Limit is: Unlimited
[0.042s][debug][os,container] container memory limit unlimited: -1, using host value 33522720768
Operating System Metrics:
    Provider: cgroupv2
[0.050s][trace][os,container] Path to /cpu.max is /sys/fs/cgroup/.lxc/cpu.max
[0.050s][debug][os,container] Open of file /sys/fs/cgroup/.lxc/cpu.max failed, No such file or directory
[0.050s][trace][os,container] CPU Quota is: -2
[0.050s][trace][os,container] Path to /cpu.max is /sys/fs/cgroup/.lxc/cpu.max
[0.050s][debug][os,container] Open of file /sys/fs/cgroup/.lxc/cpu.max failed, No such file or directory
[0.050s][trace][os,container] CPU Period is: -2
[0.050s][trace][os,container] OSContainer::active_processor_count: 4
    Effective CPU Count: 4
    CPU Period: -1
    CPU Quota: -1
    CPU Shares: -1
    List of Processors: N/A
    List of Effective Processors: N/A
    List of Memory Nodes: N/A
    List of Available Memory Nodes: N/A
    Memory Limit: Unlimited
    Memory Soft Limit: 0.00K
    Memory & Swap Limit: Unlimited
    Maximum Processes Limit: Unlimited

openjdk version "17.0.6" 2023-01-17 LTS
OpenJDK Runtime Environment Zulu17.40+19-CA (build 17.0.6+10-LTS)
OpenJDK 64-Bit Server VM Zulu17.40+19-CA (build 17.0.6+10-LTS, mixed mode, sharing)

From the logging above, you can see that java is using a different API to determine physical memory when "memory.max" is set to "max". Is there documentation aroung the cgroups v2 api around the semantics of "max"? Or, should the proxmox cgroups controller be setting this value to the container's memory limit?

When running in a docker container, "memory.max" is set to the limits of the docker container, and java is able to correctly establish the cgroup memory limit:

Code:
$ docker run --rm --memory 4g eclipse-temurin:17 java -XshowSettings:system -version                        
Operating System Metrics:
    Provider: cgroupv2
    Effective CPU Count: 24
    CPU Period: 100000us
    CPU Quota: -1
    CPU Shares: -1
    List of Processors: N/A
    List of Effective Processors, 24 total:
    0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
    List of Memory Nodes: N/A
    List of Available Memory Nodes, 1 total:
    0
    Memory Limit: 4.00G
    Memory Soft Limit: 0.00K
    Memory & Swap Limit: 8.00G
    Maximum Processes Limit: 154434

openjdk version "17.0.5" 2022-10-18
OpenJDK Runtime Environment Temurin-17.0.5+8 (build 17.0.5+8)
OpenJDK 64-Bit Server VM Temurin-17.0.5+8 (build 17.0.5+8, mixed mode, sharing

$ docker run --rm --memory 4g eclipse-temurin:17 cat /sys/fs/cgroup/memory.max    
4294967296