Cgroups là cơ chế đặc biệt được cung cấp bởi kernel Linux cho phép chúng ta cấp phát các kiểu tài nguyên như processor time, số processes cho mỗi group, lượng memory cho mỗi group hay kết hợp các tài nguyên đó cho một process hoặc một tập các processes. Cgroups được tổ chức theo dạng phân cấp và cơ chế này cũng tương tự như các process thông thường, vì chúng cũng được tổ chức theo dạng phân cấp và các child cgroups (hay còn được gọi là cgroups con) kế thừa một tập các tham số nhất định từ parent cgroup (cgroups cha). Nhưng thực sự thì chúng không giống nhau. Sự khác biệt chính giữa cgroups và các process thông thường đó là có rất nhiều phân cấp khác nhau của controls groups có thể tồn tại đồng thời tại một thời điểm, trong khi tree các process thông thường luôn chỉ có một phân cấp duy nhất. Phân cấp này không phải ngẫu nhiên vì mỗi phân cấp của control group đều được gắn vào tập các control group subsystems.
Các loại control groups subsystems
Một control group subsystem đại diện cho một kiểu tài nguyên như processor time hoặc số pid – hay nói cách khác là số các process dành cho một control group. Linux kernel hỗ trợ cung cấp 12 control group subsystems như sau:
- cpuset: gán bộ vi xử lý riêng và các memory nodes cho các tác vụ trong một group.
- cpu: sử dụng scheduler để cung cấp quyền truy cập các tác vụ cgroup tới tài nguyên vi xử lý.
- cpuacct: tạo báo cáo về processor usage bởi một group
- io: đặt giới hạn read/write từ/tới để chặn các thiết bị.
- memory: đặt giới hạn cho việc sử dụng memory bởi các tác vụ từ một group.
- devices: cho phép truy cập tới các thiết bị bởi các tác vụ từ một group.
- freezer: cho phép tạm dừng/tiếp tục các tác vụ từ một group.
- net_cls: cho phép đánh dấu các gói tin mạng từ các tác vụ trong một group.
- net_prio: cung cấp một cách tự động đặt mức độ ưu tiên lưu lượng mạng cho mỗi network interface từ một group.
- perf_event: cung cấp quyền truy cập perf_event cho một group.
- hugetlb: kích hoạt hỗ trợ các pages lớn cho một group.
- pid: đặt giới hạn số lượng process cho một group.
Mỗi control group subsystem phụ thuộc vào các tùy chọn cấu hình liên quan. Ví dụ, cpuset subsystem nên được kích hoạt thông qua tùy chọn cấu hình kernel CONFIG_CPUSETS, io subsystem thông qua tùy chọn cấu hình kernel CONFIG_BLK_CGROUP,… Tất cả những thông số cấu hình kernel có thể được tìm thấy trong General setup -> Control Group như hình bên dưới.
Bạn có thể thấy các control groups được kích hoạt trên máy tính thông qua filesystem proc.
$ cat /proc/cgroups
#subsys_name hierarchy num_cgroups enabled
cpuset 8 6 1
cpu 6 150 1
cpuacct 6 150 1
blkio 12 150 1
memory 3 198 1
devices 9 150 1
freezer 11 6 1
net_cls 5 6 1
perf_event 4 6 1
net_prio 5 6 1
hugetlb 2 6 1
pids 10 158 1
rdma 7 1 1
Hoặc thông qua sysfs
$ ls -l /sys/fs/cgroup/
total 0
dr-xr-xr-x 6 root root 0 Thg 3 5 08:58 blkio
lrwxrwxrwx 1 root root 11 Thg 3 5 08:58 cpu -> cpu,cpuacct
lrwxrwxrwx 1 root root 11 Thg 3 5 08:58 cpuacct -> cpu,cpuacct
dr-xr-xr-x 6 root root 0 Thg 3 5 08:58 cpu,cpuacct
dr-xr-xr-x 3 root root 0 Thg 3 5 08:58 cpuset
dr-xr-xr-x 6 root root 0 Thg 3 5 08:58 devices
dr-xr-xr-x 3 root root 0 Thg 3 5 08:58 freezer
dr-xr-xr-x 3 root root 0 Thg 3 5 08:58 hugetlb
dr-xr-xr-x 6 root root 0 Thg 3 5 08:58 memory
lrwxrwxrwx 1 root root 16 Thg 3 5 08:58 net_cls -> net_cls,net_prio
dr-xr-xr-x 3 root root 0 Thg 3 5 08:58 net_cls,net_prio
lrwxrwxrwx 1 root root 16 Thg 3 5 08:58 net_prio -> net_cls,net_prio
dr-xr-xr-x 3 root root 0 Thg 3 5 08:58 perf_event
dr-xr-xr-x 6 root root 0 Thg 3 5 08:58 pids
dr-xr-xr-x 2 root root 0 Thg 3 5 08:58 rdma
dr-xr-xr-x 6 root root 0 Thg 3 5 08:58 systemd
dr-xr-xr-x 5 root root 0 Thg 3 5 08:58 unified
Như bạn đã có thể đoán được rằng, cơ chế control groups không phải là cơ chế chỉ được phát minh trực tiếp cho Linux kernel, mà hầu như chủ yếu dành cho user space. Để sử dụng một control group, bạn nên tại nó lúc đầu. Chúng ta có thể tạo một cgroup bằng hai cách như sau:
- Cách đầu tiên là tạo thư mục con trong bất kỳ subsystem nào từ /sys/fs/cgroup và thêm một pid của tác vụ tới file tasks – là file sẽ được tạo tự động sau khi chúng ta tạo thư mục con.
- Cách thứ hai là tạo/hủy/quản lý cgroups với các tiện ích từ thư viện libcgroup.
Chúng ta sẽ xem xét một ví dụ đơn giản. Dòng lệnh bash dưới đây sẽ in một dòng tới thiết bị /dev/tty
#!/bin/bash
while :
do
echo "print line" > /dev/tty
sleep 5
done
Vậy nếu chúng ta chạy đoạn script này thì sẽ thấy kết quả như sau:
$ sudo chmod +x cgroup_test_script.sh
~$ ./cgroup_test_script.sh
print line
print line
print line
...
...
...
Tiếp theo đi đến vị trí cgroupfs được gắn kết trên máy tính. Như bạn đã thấy, đây là thư mục /sys/fs/cgroup, nhưng bạn có thể gắn kết nó bất cứ khi nào bạn muốn.
$ cd /sys/fs/cgroup
Và bây giờ tiếp tục đi đến thư mục con devices đại diện cho kiểu tài nguyên mà cho phép hoặc từ chối quyền truy cập tới các thiết bị bởi các tác vụ trong một cgroup:
# cd devices
Và tạo thư mục cgroup_test_group tại đây
# mkdir cgroup_test_group
Sau khi tạo thư mục cgroup_test_group, những sau đây sẽ được tạo tự động
/sys/fs/cgroup/devices$ ll cgroup_test_group/
total 0
drwxr-xr-x 2 root root 0 Thg 3 7 12:17 ./
dr-xr-xr-x 8 root root 0 Thg 3 5 08:58 ../
-rw-r--r-- 1 root root 0 Thg 3 7 12:17 cgroup.clone_children
-rw-r--r-- 1 root root 0 Thg 3 7 12:17 cgroup.procs
--w------- 1 root root 0 Thg 3 7 12:17 devices.allow
--w------- 1 root root 0 Thg 3 7 12:17 devices.deny
-r--r--r-- 1 root root 0 Thg 3 7 12:17 devices.list
-rw-r--r-- 1 root root 0 Thg 3 7 12:17 notify_on_release
-rw-r--r-- 1 root root 0 Thg 3 7 12:17 tasks
Tại đây, mình chỉ quan tâm đến file tasks và file devices.deny. File tasks đầu tiên phải chứa pid của các process sẽ được gắn kết tới cgroup_test_group. File thứ hai là devices.deny chứa danh sách các thiết bị bị cấm. Mặc định, một group mới được tạo sẽ không có bất kỳ giới hạn truy cập tới bất kỳ thiết bị nào. Để cấm một thiết bị (trong tường hợp này là /dev/tty) chúng ta sẽ ghi vào file devices.deny với nội dung như sau:
# echo "c 5:0 w" > devices.deny
Giờ ta sẽ phân tích ý nghĩa của dòng này. Chữ c đầu tiên đại diện cho loại thiết bị. Trong trường hợp mình ví dụ là /dev/tty. Chúng ta có thể xác minh từ output của câu lệnh ls:
$ ls -l /dev/tty
crw-rw-rw- 1 root tty 5, 0 Thg 3 7 12:17 /dev/tty
Thấy ký tự c đầu tiên trong danh sách permission ở ouput bên trên. Phần thứ hai là 5:0 là số chính và số phụ của thiết bị. Bạn có thể thấy những số này trong output của câu lệnh ls. Và ký tự w cuôí cùng dùng để cấm các tác vụ ghi vào thiết bị này. Bây giờ giờ mình sẽ chạy file scrip cgroup_test_script.sh có kết quả như sau:
~$ ./cgroup_test_script.sh
print line
print line
print line
...
...
Lưu ý: ở trên là kết quả chạy script ghi vào thiết bị khi chưa sử dụng cgroups cho process này.
Sau đó thêm pid của process này tới file devices/cgroup_test_group/tasks của group.
# echo $(pidof -x cgroup_test_script.sh) > /sys/fs/cgroup/devices/cgroup_test_group/tasks
Kết quả của hành động chạy script này sẽ tương tự như sau:
~$ ./cgroup_test_script.sh
print line
print line
print line
print line
print line
print line
./cgroup_test_script.sh: line 5: /dev/tty: Operation not permitted
Tình huống tương tự cũng sẽ xảy ra khi bạn chạy docker, như ví dụ container dưới đây chẳng hạn:
~$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
fa2d2085cd1c mariadb:10 "docker-entrypoint..." 12 days ago Up 4 minutes 0.0.0.0:3306->3306/tcp mysql-work
~$ cat /sys/fs/cgroup/devices/docker/fa2d2085cd1c8d797002c77387d2061f56fefb470892f140d0dc511bd4d9bb61/tasks | head -3
5501
5584
5585
...
...
...
Vậy nên trong quá trình khởi tạo docker container, docker sẽ tạo một cgroup cho các process trong container này.
$ docker exec -it mysql-work /bin/bash
$ top
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 1 mysql 20 0 963996 101268 15744 S 0.0 0.6 0:00.46 mysqld
71 root 20 0 20248 3028 2732 S 0.0 0.0 0:00.01 bash
77 root 20 0 21948 2424 2056 R 0.0 0.0 0:00.00 top
Và chúng ta có thể thấy cgroup này trên host
$ systemd-cgls
Control group /:
-.slice
├─docker
│ └─fa2d2085cd1c8d797002c77387d2061f56fefb470892f140d0dc511bd4d9bb61
│ ├─5501 mysqld
│ └─6404 /bin/bash
Bây giờ chúng ta đã biết một chút về cơ chế control groups, cách sử dụng nó một cách thủ công và mục đích của cơ chế này là gì rồi đấy.