How to limit physical memory RAM allocation for a particular user in Linux using cgroups ?

We have seen many posts how to limit memory or cpu for a user/group using /etc/security/limits.conf file.

However when we limit memory usage by referencing AS(address space) in /etc/security/limits.conf file, it restricts overall VSZ or the total virtual memory a user/group can take.

Suppose you want to restrict only the physical memory/RAM i.e. RSS for a user/group, then we have an interesting feature of cgroups.

What is cgroup ?
cgroup is the acronym for control groups.

Control group is a feature of Linux kernel introduced from RHEL 6/CentOS 6/NGELinux 1.0 to provide a new way of setting limits to system resources for processes/users.

We can create our own cgroups, manage the impact of cgroups we configure, make cgroups that can deny access to certain resources, and even re-configure our cgroups dynamically on a live system.

Now let us take an example to understand how to setup cgroup.

Limit a user saket on your system with maximum 40MB RAM usage.

1. Calculate the bytes of memory you want to allocate for a user.

40 MB Memory Limit for a User.
40*1024*1024 Bytes
41943040 Bytes

2. Make sure cgroup package is installed on your system.

[root@nglinux ~]# rpm -qf /etc/cgconfig.conf 
libcgroup-0.40.rc1-24.el6_9.i686

### If not installed, then install it using: yum install libcgroup

3. Make a memlimit group in /etc/cgconfig.conf file with maximum 40MB memory.

[root@nglinux ~]# tail /etc/cgconfig.conf 
	
group memlimit {
    memory {
        memory.limit_in_bytes = 41943040;
    }
}
[root@nglinux ~]# 

4. Now make a rule in /etc/cgrules.conf to restrict the user at a defined memory usage in memlimit group.

[root@nglinux ~]# cat /etc/cgrules.conf 
# /etc/cgrules.conf
#The format of this file is described in cgrules.conf(5)
#manual page.

saket		memory		memlimit/

# End of file
[root@nglinux ~]# 

5. Now restart cgconfig and cgred services:

[root@nglinux ~]# service cgconfig start
Starting cgconfig service:                           [ OK ]
[root@nglinux ~]# chkconfig cgconfig on

[root@nglinux ~]# service cgred restart
Stopping CGroup Rules Engine Daemon...                     [  OK  ]
Starting CGroup Rules Engine Daemon:                       [  OK  ]
[root@nglinux ~]# chkconfig cgred on
[root@nglinux ~]# 

6. Now lets check saket user current memory usage.
It is zero at the moment since the user is not logged in and no process is running.

[root@nglinux ~]# user=saket; echo -e "\n############ \n Memory usage of user $user \n RSS \t VSZ"; ps -U $user -o rss,vsz --no-headers | awk '{rss+=$1; vmem+=$2} END{print rss" "vmem}' | awk '{rss2=$1/1024; vmem2=$2/1024} END{print " "rss2" "vmem2}'; echo -e "#############";
############ 
 Memory usage of user saket 
 RSS 	 VSZ
 0 0
#############

7. Lets login from saket user and check if it blocks the RAM usage.
Try to block 100MB memory and see the command if it runs successfully.

[saket@nglinux ~]$ dd if=/dev/zero of=/dev/null bs=100M
^CKilled

Ohh !! what happened, does cgroup work ? Lets check the RSS and VSZ usage.

[root@nglinux ~]# user=saket; echo -e "\n############ \n Memory usage of user $user \n RSS \t VSZ"; ps -U $user -o rss,vsz --no-headers | awk '{rss+=$1; vmem+=$2} END{print rss" "vmem}' | awk '{rss2=$1/1024; vmem2=$2/1024} END{print " "rss2" "vmem2}'; echo -e "#############";

############ 
 Memory usage of user saket 
 RSS 	 VSZ
 41.8711 109.164
#############

So it is clear, that RAM usage is restricted to 41.87MB and rest space is used from virtual memory.

Lets try to increase this memory usage to 200MB.

### From saket user:
[saket@nglinux ~]$ dd if=/dev/zero of=/dev/null bs=200M
^CKilled

### From root user:
[root@nglinux ~]# user=saket; echo -e "\n############ \n Memory usage of user $user \n RSS \t VSZ"; ps -U $user -o rss,vsz --no-headers | awk '{rss+=$1; vmem+=$2} END{print rss" "vmem}' | awk '{rss2=$1/1024; vmem2=$2/1024} END{print " "rss2" "vmem2}'; echo -e "#############";

############ 
 Memory usage of user saket 
 RSS 	 VSZ
 41.875 209.164
#############

Now also, 200 MB virtual memory is used, and physical memory is restricted to 41.87 MB.

Lets try to increase the usage to 400MB.

### From saket user:
[saket@nglinux ~]$ dd if=/dev/zero of=/dev/null bs=400M
^CKilled
[saket@nglinux ~]$ 

### From root user:
[root@nglinux ~]# user=saket; echo -e "\n############ \n Memory usage of user $user \n RSS \t VSZ"; ps -U $user -o rss,vsz --no-headers | awk '{rss+=$1; vmem+=$2} END{print rss" "vmem}' | awk '{rss2=$1/1024; vmem2=$2/1024} END{print " "rss2" "vmem2}'; echo -e "#############";

############ 
 Memory usage of user saket 
 RSS 	 VSZ
 41.875 409.164
#############
[root@nglinux ~]# 

Still the physical memory usage is restricted to 41MB and virtual memory occupies more.

Lets try to increase it beyond virtual memory.

[saket@nglinux ~]$ dd if=/dev/zero of=/dev/null bs=700M
^CKilled
[saket@nglinux ~]$ dd if=/dev/zero of=/dev/null bs=800M
dd: memory exhausted
[saket@nglinux ~]$ 

Finally, in the end, if it exceeds more than virtual memory, then it throws an error of memory exhaust.

Seems interesting to use cgroups.

However since the feature in introduced recently and is less used so you will not get much help of this article online.

Also there are few bugs identified with cgroup and systemd compatibility so you might face issues with RHEL7 if you configure cgroups.

Feel free to post any of your comments/suggestions.

Leave a Reply

avatar
  Subscribe  
Notify of