Серверок у меня небольшой, маломощный. Бюджетнее не бывает, так сказать. И как следствие, одно ядро, немного памяти, постоянно не хватает ресурсов. А тут ещё и Zabbix прикрутил туда же, стало как-то неуютно. Последней каплей оказался, как это ни странно, скрипт бэкапирования. Скрипт просто делает архив и отсылает его на ftp-сервер. Ну просто, дальше некуда. Но… Архивчик *.tar.bz2 пакуется командой
 tar -cvjpf *.tar.bz2 .....
Всё бы хорошо, но в момент архивации нагрузка на процессор около 100%, падают запущенные сервисы, и далеко не всё поднимается обратно. Надо что-то делать, решил я. Покопавшись в сети, нашёл утилитку cpulimit, попытался вставить её в скрипт архивации, но ни фига вот. Не завершается утилитка, а как следствие не завершается и скрипт. Тогда я пошёл искать дальше. Нашёл в сети статью, как сделать cpulimit сервисом. Основный моменты там
1. Ставим cpulimit и gawk
2. Качаем два скрипта.
2.1 Первый cpulimit_daemon.sh В нем несколько папаметров можно править (цитирую):

  • CPU_LIMIT — это максимальное значение CPU-ресурса, допустимое для каждого приложения. Значение по умолчанию: 20%.
  • DAEMON_INTERVAL — это интервал времени, через который скрипт будет проверять систему. По умолчанию 3 секунды.
  • BLACK_PROCESS_LIST — этот список содержит приложения, которые нужно контролировать. Необходимо использовать символ “|” для добавления сразу нескольких программ. Для примера: “mysql|firefox|gedit“.
  • WHITE_PROCESSES_LIST — этот список содержит приложения, которые НЕ нужно контролировать. Необходимо использовать символ “|” для добавления сразу нескольких программ. Для примера: “mysql|firefox|gedit“.

Примечание: один из этих двух списков BLACK_PROCESS_LIST или WHITE_PROCESSES_LIST должен быть обязательно пуст, так как вы не можете иметь оба списка одновременно.
Выставляем ему права 700 и копируем в директорию /usr/bin/
2.2 Второй cpulimit копируется в /etc/init.d/ , владелец root, группа root,  исполняемый и суём в автозагрузку:

cp ~/cpulimit/cpulimit /etc/init.d/
chown root:root /etc/init.d/cpulimit
chmod +x /etc/init.d/cpulimit
update-rc.d cpulimit defaults

По идее, всё должно бы стартовать, но выползает ньюанс. Если запускать вручную, всё работает, после перезагрузки сервис стоит. Опять нужно стартовать вручную. Начинаем копать. Оказывается, со времени написания статьи изменился сам подход к автозапуску. Из-за этого просто так ни хрена не запустишь. Начинаем делать службу. По пути /etc/systemd/system/ создаём файл cpulimit.service, с правами 644 и следующим содержанием:

[Unit]
Description=cpulimit
After=syslog.target network.target

[Service]
Type=idle
ExecStart=/usr/bin/cpulimit_daemon.sh

[Install]
WantedBy=multi-user.target

Потом перезагружаем службы и смотрим:

systemctl enable cpulimit
systemctl daemon-reload

Выхлоп отвратительный:

Synchronizing state of cpulimit.service with SysV service script with /lib/systemd/systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install enable cpulimit
update-rc.d: error: cpulimit Default-Start contains no runlevels, aborting.

После некоторой возни до меня наконец дошло, уже пару лет твердят, что всё теперь решается через systemd, по строгим правилам и мне нужно поправить сами скрипты. Окончательно cpulimit_daemon.sh выглядит вот так:

cpulimit_daemon.sh
#!/bin/bash
### BEGIN INIT INFO
# Provides: cpulimit
# Required-Start: $all
# Required-Stop:
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Limit CPU...
### END INIT INFO
# ==============================================================
# CPU limit daemon - set PID's max. percentage CPU consumptions
# ==============================================================

# Variables
CPU_LIMIT=50 # Maximum percentage CPU consumption by each PID
DAEMON_INTERVAL=3 # Daemon check interval in seconds
BLACK_PROCESSES_LIST="tar|bzip2|cp" # Limit only processes defined in this variable. If variable is empty (default) all violating processes are limited.
WHITE_PROCESSES_LIST= # Limit all processes except processes defined in this variable. If variable is empty (default) all violating processes are limited.

# Check if one of the variables BLACK_PROCESSES_LIST or WHITE_PROCESSES_LIST is defined.
if [[ -n "$BLACK_PROCESSES_LIST" && -n "$WHITE_PROCESSES_LIST" ]] ; then # If both variables are defined then error is produced.
echo "At least one or both of the variables BLACK_PROCESSES_LIST or WHITE_PROCESSES_LIST must be empty."
exit 1
elif [[ -n "$BLACK_PROCESSES_LIST" ]] ; then # If this variable is non-empty then set NEW_PIDS_COMMAND variable to bellow command
NEW_PIDS_COMMAND="top -b -n1 -c | grep -E '$BLACK_PROCESSES_LIST' | gawk '\$9>CPU_LIMIT {print \$1}' CPU_LIMIT=$CPU_LIMIT"
elif [[ -n "$WHITE_PROCESSES_LIST" ]] ; then # If this variable is non-empty then set NEW_PIDS_COMMAND variable to bellow command
NEW_PIDS_COMMAND="top -b -n1 -c | gawk 'NR>6' | grep -E -v '$WHITE_PROCESSES_LIST' | gawk '\$9>CPU_LIMIT {print \$1}' CPU_LIMIT=$CPU_LIMIT"
else
NEW_PIDS_COMMAND="top -b -n1 -c | gawk 'NR>6 && \$9>CPU_LIMIT {print \$1}' CPU_LIMIT=$CPU_LIMIT"
fi

# Search and limit violating PIDs
while sleep $DAEMON_INTERVAL
do
NEW_PIDS=$(eval "$NEW_PIDS_COMMAND") # Violating PIDs
LIMITED_PIDS=$(ps -eo args | gawk '$1=="cpulimit" {print $3}') # Already limited PIDs
QUEUE_PIDS=$(comm -23 <(echo "$NEW_PIDS" | sort -u) <(echo "$LIMITED_PIDS" | sort -u) | grep -v '^$') # PIDs in queue

for i in $QUEUE_PIDS
do
cpulimit -p $i -l $CPU_LIMIT -z & # Limit new violating processes
done
done