Для того чтобы использовать cgroups вовсе необязательно использовать для этого systemd, docker или k8s. Для этого достаточно использовать набор команд, для ubuntu находящийся в пакете `cgroup-tools
` и виртуальную файловую систему /sys
.
Создадим cgroup с ограничением по памяти в 10Мб используя следующую команду (для cgroups v1):
cgcreate -g memory:cg1
echo 10M > /sys/fs/cgroup/memory/cg1/memory.limit_in_bytes
В случае cgroups v2:
cgcreate -g memory:cg1
echo 10M > /sys/fs/cgroup/cg1/memory.max
Для того чтобы протестировать ограничение по выделению памяти, написал небольшую программу на golang. Это оказалось не совсем тривиально, поскольку простой вызов make не даст сразу выделения памяти, поскольку сработает оптимизация, поэтому присваивается значение в ячейку памяти. Более того, чтобы оптимизация не сработала нужно, чтобы компилятор не знал заранее (на этапе компиляции) о том, в какую именно ячейку будет происходить присвоение. Поэтому индекс этой ячейки вычисляется как произвольное число.
package main
import (
"math/rand"
"time"
)
const k = 1024
const m = k * 1024
func main() {
N := 20_000
time.Sleep(3 * time.Second)
a := make([]byte, N * m)
a[rand.Intn(N * m)] = 1
}
go mod init a
go build
Такого рода игрушечные программы полезны при тестировании systemd файлов, чтобы проверить, что они корректно рестартуют исполняемый файл при достижении лимита по памяти. Так же, можно эмулировать поведение программы при старте, используя timer.Sleep(ms)
, что довольно удобно.
Теперь применим созданную cgroup к тестовой программке
sudo cgexec -g memory:cg1 ./a
В результате получите ошибку "out of memory" и завершение программы
В syslog вы увидите работу oom киллера
tail -n 100 /var/log/syslog
Так же, можно посмотреть, насколько снизится производительность программки при достижении лимита cpu.
Создадим еще одну небольшую программу для создания нагрузки (есть специальные бенчмарки для нагрузки CPU, но тут я решил размять руки):
Данная программа при запуске создает некоторое количество блоков памяти, соединенных связным списком и заполняет их случайными байтами. Затем в 10 горутинах происходит вычисление simhash хеша (он довольно сильно нагружает процессор). В основном потоке происходит раз в 2 секунды выводится на экран информация о количестве операций в секунду.
Создадим еще один cgroup slice cg2
с параметрами cpu и переопределим cpu.max
для использования 0.5 CPU
cgcreate -g cpu:cg2
echo '50000 100000' > /sys/fs/cgroup/cg2/cpu.max
Для запуска команды так же, как и в первом случае используем cgexec
:
sudo cgexec -g cpu:cg2 ./readInf
На моем лаптопе (8 logical cpu cores) результаты выполнения программы следующие:
- До применения ограничения
- После применения
Можно поиграться с этим параметром, добиваясь оптимальных показателей производительности и потребления ресурсов (зависимость даже в этом простейшем случае нелинейная).