Навожу еще один простой способ обезопасить сервер от сканирования. В данном случаи будем использовать только iptables. В зависимости от того, какие у нас работают сервисы на серверах, набор портов будет разный. Для начала создадим свою цепочку, в которую добавим список IP адресов, которым позволено/запрещено иметь доступ к списку портов.

1
2
3
4
root@srv-19:~# iptables -N antiscan
root@srv-19:~# iptables -A antiscan -s 195.34.25.11 -j ACCEPT
root@srv-19:~# iptables -A antiscan -s 195.34.25.12 -j ACCEPT
root@srv-19:~# iptables -A antiscan -j DROP

В данном случае разрешено доступ только для двух IP-адресов. Теперь нужно добавить в цепочку INPUT список портов, к которым ограничивается доступ. Для начала добавим TCP порты.

1
2
3
4
root@srv-19:~# iptables -I INPUT -m tcp -p tcp --dport 25 -j antiscan
root@srv-19:~# iptables -I INPUT -m tcp -p tcp --dport 82 -j antiscan
root@srv-19:~# iptables -I INPUT -m tcp -p tcp --dport 3306 -j antiscan
root@srv-19:~# iptables -I INPUT -m tcp -p tcp --dport 8083 -j antiscan

Далее добавим UDP порты.

1
2
root@srv-19:~# iptables -I INPUT -m udp -p udp --dport 111 -j antiscan
root@srv-19:~# iptables -I INPUT -m udp -p udp --dport 161 -j antiscan

Теперь можно добавить правила в автозагрузку.

1
2
3
4
5
root@srv-19:~# iptables-save > /etc/iptables.rules
root@srv-19:~# cat > /etc/network/if-pre-up.d/iptablesup << EOF
#!/bin/bash
iptables-restore </etc/iptables.rulesexit 0 EOF
root@srv-19:~# chmod +x /etc/network/if-pre-up.d/iptablesup

Можно написать маленький скрипт, который добавлять все нужные цепочки и правила.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
root@srv-19:~# cat antiscan.sh
#!/bin/bash
WHITEIP=( `cat /opt/cdn/utils/iptables/whitelist.ip` )
PORTS_TCP=(
25
82
3306
8083 )
PORTS_UDP=(
111
161 )
if [ ! -f/opt/cdn/utils/iptables/whitelist.ip ]
then
        echo "Please add white list /opt/cdn/utils/iptables/whitelist.ip"
        exit 1
fi
iptables -N antiscan &&echo "Antiscan chain was added" ||echo "Failed to create Antiscan chain"
echo "============== Adding white list ==============="
for IPsin "${WHITEIP[@]}"
do
        iptables -A antiscan -s ${IPs} -j ACCEPT &&echo "${IPs} was added to Antiscan chain" ||echo "Failed to add ${IPs} to Antiscan chain"
done
echo "============== Adding TCP ports ==============="
for TCPPORTin "${PORTS_TCP[@]}"
do
        iptables -I INPUT -m tcp -p tcp --dport ${TCPPORT} -j antiscan &&echo "Adding Antiscan chain for ${TCPPORT} port to INPUT chain" ||echo "Failed to add Antiscan chain for ${TCPPORT} port to INPUT chain"
done
echo "============== Adding UDP ports ==============="
for UDPPORTin "${PORTS_UDP[@]}"
do
        iptables -I INPUT -m udp -p udp --dport ${UDPPORT} -j antiscan &&echo "Adding Antiscan chain for ${UDPPORT} port to INPUT chain" ||echo "Failed to add Antiscan chain for ${UDPPORT} port to INPUT chain"
done
iptables -A antiscan -j DROP &&echo "Adding DROP rule for Antiscan chain" ||echo "Failed to add DROP rule to Antiscan chain"
echo "================= ADD rules to autostart ==================="
iptables-save |grep -v fail2ban >/etc/iptables.rules
cat >/etc/network/if-pre-up.d/iptablesup << EOF
#!/bin/bash
iptables-restore </etc/iptables.rules
exit 0
EOF
chmod +x/etc/network/if-pre-up.d/iptablesup
echo "================= DONE ==================="
exit 0

/opt/cdn/utils/iptables/whitelist.ip – это список IP адресов, которые имеют доступ к портам.
Так же можно написать маленький скрипт для добавления нового IP в белый список.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
root@srv-19:~# cat antiscan_append.sh
#!/bin/bash
usage() {
         echo -e"nUsage: $0 IP-address"
         echo -e"Example:n $0 82.50.92.45"
         exit 1
}
if [ $# -lt 1  ]
        then
                echo "Please enter IP to add to white list"
                usage
fi
echo "============== Check if exist ========================"
iptables -C antiscan -s $1 -j ACCEPT &&echo "Already exist" &&exit 1
echo "============== Adding IP to white list ==============="
iptables -I antiscan 1 -s $1 -j ACCEPT &&echo "$1 was added to Antiscan chain" ||echo "Failed to add $1 to Antiscan chain"
echo "================= Saving rules to autostart ==================="
iptables-save |grep -v fail2ban >/etc/iptables.rules
echo "================= DONE ==================="
exit 0

Так же можно написать маленький скрипт для добавления нового IP в белый список.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
root@srv-19:~# cat antiscan_clean.sh
#!/bin/bash
PORTS_TCP=(
25
82
3306
8083 )
PORTS_UDP=(
111
161 )
iptables -F antiscan &&echo "Antiscan chain was flushed" ||echo "Failed to flush antiscan chain"
for TCPPORTin "${PORTS_TCP[@]}"
do
        iptables -D INPUT -m tcp -p tcp --dport ${TCPPORT} -j antiscan &&echo "Delete Antiscan chain for ${TCPPORT} port to INPUT chain" ||echo "Failed to del Antiscan chain for ${TCPPORT} port to INPUT chain"
done
for UDPPORTin "${PORTS_UDP[@]}"
do
        iptables -D INPUT -m udp -p udp --dport ${UDPPORT} -j antiscan &&echo "Delete Antiscan chain for ${UDPPORT} port to INPUT chain" ||echo "Failed to del Antiscan chain for ${UDPPORT} port to INPUT chain"
done
iptables -X antiscan &&echo "Deleting antiscan chain" ||echo "Failed to delete antiscan chain"
echo "================= Deleting autostart rules ==================="
rm -f/etc/iptables.rules &&echo "Deleting /etc/iptables.rules" ||echo "Failed to delete /etc/iptables.rules"
rm -f/etc/network/if-pre-up.d/iptablesup  &&echo "Deleting /etc/network/if-pre-up.d/iptablesup" ||echo "Failed to delete /etc/network/if-pre-up.d/iptablesup"
exit 0

Также добавим скрипт для удаления и чистки всех правил

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
root@srv-19:~# cat antiscan_clean.sh
#!/bin/bash
PORTS_TCP=(
25
82
3306
8083 )
PORTS_UDP=(
111
161 )
iptables -F antiscan &&echo "Antiscan chain was flushed" ||echo "Failed to flush antiscan chain"
for TCPPORTin "${PORTS_TCP[@]}"
do
        iptables -D INPUT -m tcp -p tcp --dport ${TCPPORT} -j antiscan &&echo "Delete Antiscan chain for ${TCPPORT} port to INPUT chain" ||echo "Failed to del Antiscan chain for ${TCPPORT} port to INPUT chain"
done
for UDPPORTin "${PORTS_UDP[@]}"
do
        iptables -D INPUT -m udp -p udp --dport ${UDPPORT} -j antiscan &&echo "Delete Antiscan chain for ${UDPPORT} port to INPUT chain" ||echo "Failed to del Antiscan chain for ${UDPPORT} port to INPUT chain"
done
iptables -X antiscan &&echo "Deleting antiscan chain" ||echo "Failed to delete antiscan chain"
echo "================= Deleting autostart rules ==================="
rm -f/etc/iptables.rules &&echo "Deleting /etc/iptables.rules" ||echo "Failed to delete /etc/iptables.rules"
rm -f/etc/network/if-pre-up.d/iptablesup  &&echo "Deleting /etc/network/if-pre-up.d/iptablesup" ||echo "Failed to delete /etc/network/if-pre-up.d/iptablesup"
exit 0

Ну и маленький скрипт для удаления определенного IP со списка позволенных.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
root@srv-19:~# cat antiscan_del_ip.sh
#!/bin/bash
echo "============== Check if exist ========================"
iptables -C antiscan -s $1 -j ACCEPT
if [ $? -eq 0 ]
then
        echo "Entry exist"
        echo "Deleting..."
        iptables -D antiscan -s $1 -j ACCEPT &&echo "$1 was deleted from Antiscan chain" ||echo "Failed to delete $1 from Antiscan chain"
        echo "================= Saving rules to autostart ==================="
        iptables-save |grep -v fail2ban >/etc/iptables.rules
        echo "================= DONE ==================="
        exit 0
else
        echo "Entry doesn't exist"
        exit 1
fi

Пробуйте, может кому-то понадобится.

Для работы нам понадобятся:
1. x264, для кодирования видеопотока.
2. ffmpeg, будем использовать только для кодирования звука, так на мой взгляд проще.
3. mkvtoolnix, для сборки этого чуда в контейнер mkv.

Так как серий у нас много, напишем несложный батник, который пригодится для последующих рипов.

Видео

Сначала приведу код, потом поясню параметры:

"...x264-10b.exe" "...имя файла источника.m2ts" --input-res 1920x1080 --fps 23.976 --profile high10 --preset medium --tune animation --crf=15.5 --me=umh --ref=9 --deblock=-1,-1 --merange=24 --bframes=12 --trellis=1 --video-filter resize:width=854,height=480,method=lancoz --output "...имя выходного файла.264"

 

Параметры

— input-res 1920×1080 --fps 23.976

Подсказываем x264 разрешение и частоту кадров источника. (можно подсмотреть в MediaInfo)

--profile high10

Варианты: baseline, main, high, high10, high422, high444.
Указываем профиль. В данном случае кодируем в 10 бит, поэтому high10, если в 8 — high.

--preset medium

Варианты: ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow, placebo.
Баланс скорости кодирования и качества. Т.е. выше скорость — хуже качество.
В основном использую medium, slow и иногда veryslow.

--tune animation

Варианты: film, animation, grain, stillimage, psnr, ssim, fastdecode, zerolatency.
Предустановки, зависящие от входного видео. Для фильмов одно, для аниме другое.

--crf=15.5

Диапазон: 1-50.
Режим постоянного качества и его уровень. Чем меньше — тем лучше качество. Позволяет каждому кадру использовать собственный QP, основанный на сложности кадра.
Подробнее на английском можно почитать тут.
Это лучший метод для однопроходного кодирования.
Возможные варианты на замену: --bitrate или --qp, рассматривать их не будем.

Далее переопределим некоторые параметры, т.к. не все они из пресета medium нам подходят:

--me=umh

Варианты: dia, hex, umh, esa, tesa.
Метод оценки движения полного пикселя. Рекомендую umh.

--ref=9

Диапазон: 1-16.
Количество референсных кадров. Чем больше, тем медленнее будет кодировать. Если следовать спецификациям для поддержки бытовой техники, 4 — максимум для 1080p, и 9 — максимум для 720p.
При 6 и выше особой разницы в качестве не увидите, а скорость кодирования сильно упадет.

--deblock=-1,-1.

Деблокинг, в формате сила:порог. Если коротко, то чем выше сила деблокинга, тем сильнее он применяется, чем выше порог, тем больше блоков ему попадается.
Так как мы кодируем BDRemux аниме, силу и порог желательно снизить, чтобы уменьшить размытие линий. Я использую -1:-1, но встречались рипы и с -2:-2.

--merange=24

Диапазон: 4-64.
Определяет максимальное количество попыток нахождения оптимального варианта при поиске вектора движения макроблока. Чем больше, тем лучше качество.
Не имеет особого смысла ставить больше 24.

--bframes=12

Диапазон: 1-16.
Устанавливает максимальное число параллельных B-фреймов. Большое значение может привести к значительному улучшению эффективности степени сжатия.

--trellis=1

Диапазон: 0-2.
Треллис квантование для повышения эффективности сжатия. 0 — отключено. 1 — Вариант «на макроблоках». 2 — «везде».
1 — хороший компромисс между потерей скорости и эффективностью сжатия. Лучше всего 2, но совместно с --psy-rd, иначе замылит мелкие детали.

Ресайз

 

--video-filter resize:width=854,height=480,method=lancoz

Указываем ширину и высоту, а так же метод. Я использую lancoz.
Варианты: fastbilinear, bilinear, bicubic, experimental, point, area, bicublin, gauss, sinc, lanczos, spline.

Аудио

Для кодирования будем использовать ffmpeg.

"...ffmpegbinffmpeg.exe" -i "...имя файла источника.m2ts" -vn -c:a libvorbis -qscale:a 6 "...имя выходного файла.ogg"

Тут всё просто:

-vn

Отключаем кодирование видео.

-c:a libvorbis -qscale:a 6

Указываем кодер и качество.
Диапазон: 0-10.
Чем больше, тем лучше качество.
6 это ~192 Кбит/с.

Сборка:

Кодируем первую серию.

Открываем GUI mkvtoolnix, выбираем получившиеся видео и аудио, задаем параметры (например, для видео полезно будет задать аспект и частоту кадров, а для аудио — язык дорожки) и жмем скопировать в буфер обмена, получаем что-то вроде:

"...mkvtoolnixmkvmerge.exe" -o "...имя выходного файла.mkv"  "--default-track" "0:yes" "--forced-track" "0:no" "--aspect-ratio" "0:16/9" "--default-duration" "0:23.976fps" "-d" "0" "-A" "-S" "-T" "--no-global-tags" "--no-chapters" "(" "...имя перекодированного видео.264" ")" "--language" "0:jpn" "--default-track" "0:yes" "--forced-track" "0:no" "-a" "0" "-D" "-S" "-T" "--no-global-tags" "--no-chapters" "(" "...имя перекодированного аудио.ogg" ")" "--track-order" "0:0,1:0"

Полный код для 25 серий:

@echo off
for /L %%i in (1,1,9) do (
"...ffmpegbinffmpeg.exe" -y -i "...имя 0%%i.m2ts" -vn -map 0:2 -c:a libvorbis -qscale:a 6 "...имя 0%%i.ogg"
"...x264x264-10b.exe" "...имя 0%%i.m2ts" --input-res 1920x1080 --fps 23.976 --profile high10 --preset medium --tune animation --crf=15.5 --me=umh --ref=9 --deblock=-1,-1 --merange=24 --bframes=12 --trellis=1 --video-filter resize:width=854,height=480,method=lancoz --output "...имя 0%%i.264"
"...mkvtoolnixmkvmerge.exe" -o "...имя 0%%i.mkv"  "--default-track" "0:yes" "--forced-track" "0:no" "--aspect-ratio" "0:16/9" "--default-duration" "0:23.976fps" "-d" "0" "-A" "-S" "-T" "--no-global-tags" "--no-chapters" "(" "...имя 0%%i.264" ")" "--language" "0:jpn" "--default-track" "0:yes" "--forced-track" "0:no" "-a" "0" "-D" "-S" "-T" "--no-global-tags" "--no-chapters" "(" "...имя 0%%i.ogg" ")" "--track-order" "0:0,1:0"
)
for /L %%i in (10,1,25) do (
"...ffmpegbinffmpeg.exe" -y -i "...имя %%i.m2ts" -vn -map 0:2 -c:a libvorbis -qscale:a 6 "...имя %%i.ogg"
"...x264x264-10b.exe" "...имя %%i.m2ts" --input-res 1920x1080 --fps 23.976 --profile high10 --preset medium --tune animation --crf=15.5 --me=umh --ref=9 --deblock=-1,-1 --merange=24 --bframes=12 --trellis=1 --video-filter resize:width=854,height=480,method=lancoz --output "...имя %%i.264"
"...mkvtoolnixmkvmerge.exe" -o "...имя %%i.mkv"  "--default-track" "0:yes" "--forced-track" "0:no" "--aspect-ratio" "0:16/9" "--default-duration" "0:23.976fps" "-d" "0" "-A" "-S" "-T" "--no-global-tags" "--no-chapters" "(" "...имя %%i.264" ")" "--language" "0:jpn" "--default-track" "0:yes" "--forced-track" "0:no" "-a" "0" "-D" "-S" "-T" "--no-global-tags" "--no-chapters" "(" "...имя %%i.ogg" ")" "--track-order" "0:0,1:0"
)
pause

Поскольку у меня две кривые руки, один и тот же код написан дважды.

Всем спасибо за внимание.

UPD:

--input-res 1920x1080 --fps 23.976

Можно не указывать. Это необходимо только если вы скармливаете x264 чистый raw поток.

--profile high10

Так же можно не указывать, т.к.

Битность определяется на этапе компиляции и скодировать в 8 и 10 одним бинарником не выйдет. Так что и смысла выставлять профиль в принципе нет (если только не надо 4:2:2 или 4:4:4).

Спасибо tp7.

Также, на мой взгляд, имеет смысл поднять --subme до 9.

Когда-нибудь вам может потребоваться сервисы на компьютере за маршрутизатором или же вы просто хотите сделать жизнь проще, подключаясь к локальной сети из интернета. Возможно, вы захотите запустить серверы FTP, HTTP, SSH или VNC на одном или нескольких компьютерах за вашим маршрутизатором и подключаться к ним всем. Есть только одно ограничение — вы можете запускать только по одному сервису на компьютере на каждый порт. Например, невозможно настроить три FTP-сервера за вашим маршрутизатором и попробовать подключиться ко всем трем из интернета через порт 21; только один из них будет на порту 21, когда как остальные будут использовать, скажем, порты 123 и 567.

Все правила проброса портов имеют вид iptables -t nat -A PREROUTING [-p протокол] --dport [внешний порт маршрутизатора] -i ${WAN} -j DNAT --to [IP-адрес/порт для проброса]. К сожалению, iptables не принимает имена узлов при пробросе портов. Если вы пробрасываете внешний порт на порт с тем же номером для внутреннего компьютера, то вы можете опустить порт назначения. См. страницу помощи iptables (8) для большей информации.

Листинг 6.1: Выполнение команд iptables

Скопируйте и вставьте для примера...
# export LAN=eth0
# export WAN=eth1

Пробрасывание порта 2 на ssh внутреннего хоста
# iptables -t nat -A PREROUTING -p tcp --dport 2 -i ${WAN} -j DNAT --to 192.168.0.2:22

Пробрасывание FTP-трафика на внутренний компьютер
# iptables -t nat -A PREROUTING -p tcp --dport 21 -i ${WAN} -j DNAT --to 192.168.0.56

Пробрасывание HTTP-трафика на внутренний компьютер
# iptables -t nat -A PREROUTING -p tcp --dport 80 -i ${WAN} -j DNAT --to 192.168.0.56

Пробрасывание VNC-трафика для внутренних компьютеров
# iptables -t nat -I PREROUTING -p tcp --dport 5900 -i ${WAN} -j DNAT --to 192.168.0.2
# iptables -t nat -I PREROUTING -p tcp --dport 5901 -i ${WAN} -j DNAT --to 192.168.0.3:5900
Теперь, если вам необходимо подключиться к VNC по адресу 192.168.0.3, то просто добавьте «:1» к имени маршрутизатора

Проброс трафика Bittorrent
# iptables -t nat -A PREROUTING -p tcp --dport 6881:6889 -i ${WAN} -j DNAT --to 192.168.0.2

Проброс трафика eDonkey/eMule
# iptables -t nat -A PREROUTING -p tcp --dport 4662 -i ${WAN} -j DNAT --to 192.168.0.55

Поддержка Game Cube Warp Pipe
# iptables -t nat -A PREROUTING -p udp --dport 4000 -i ${WAN} -j DNAT --to 192.168.0.56

Поддержка Playstation 2 Online
# iptables -t nat -A PREROUTING -p tcp --dport 10070:10080 -i ${WAN} -j DNAT --to 192.168.0.11
# iptables -t nat -A PREROUTING -p udp --dport 10070:10080 -i ${WAN} -j DNAT --to 192.168.0.11

Xbox Live
# iptables -t nat -A PREROUTING -p tcp --dport 3074 -i ${WAN} -j DNAT --to 192.168.0.69
# iptables -t nat -A PREROUTING -p udp --dport 3074 -i ${WAN} -j DNAT --to 192.168.0.69
# iptables -t nat -A PREROUTING -p udp --dport 88 -i ${WAN} -j DNAT --to 192.168.0.69

В данной статье хотелось бы собрать некоторые виды атак на сервера и средства защиты сервера от хакеров. На тему безопасности написано немерено книг и статей. Упор данной статьи сделан на базовые ошибки администраторов и решения по их устранению. После прочтения этой статьи и проверки собственного сервера администратор так же не сможет спать спокойно, он сможет только сказать я сдал «кандидатский минимум».
Continue Reading

Ext4.

Я выбрал всё таки файловую систему ext4, хотя многие тесты упорно рекомендуют ext3. Журналирование в файловых системах, естественно, не прибавляет скорости в I/O, но с ним можно разобраться, а сама ext4, помимо журнала, тоже получила множество улучшений и жаль было бы их терять. Ведь Google не зря перешёл с ext3 на ext4 без журнала!

К этому времени мне уже было известно о благотворном влиянии отключения шлагбаумов barrier=0 (или nobarrier).

Что за шлагбаумы? Код файловой системы обязан перед созданием записи фиксации [журнала] быть абсолютно уверенным, что вся информация о транзакции помещена в журнал. Просто делать запись в правильном порядке недостаточно; современные диски имеют кэш большого объёма и меняют порядок записи для оптимизации производительности. Поэтому файловая система обязана явно сообщить диску о необходимости записать все журнальные данные на носитель перед созданием записи фиксации; если сначала будет создана запись фиксации, журнал может быть повреждён. Блокирующая система ввода-вывода ядра предоставляет такую возможность благодаря использованию механизма ? «шлагбаумов» (barriers); проще говоря, «шлагбаум» запрещает запись любых блоков, посланных после него, до того момента, как всё, что было прислано перед «шлагбаумом», будет перенесено на носитель. При использовании «шлагбаумов» файловая система может гарантировать, что всё, что находится на диске, целостно в любой момент времени. Отключая шлагбаум barrier=0, мы ускоряем операции записи на разделы ext4.

В одной из англоязычных статей, её автор приводил тесты для файловых систем для MySQL сервера и с barrier=0 ext4 была не хуже ext3.

Кроме barrier=0, добавлены такие опции как relatime и commit=600.
relatime — атрибут времени доступа (atime) обновляется, но только в том случае, если изменились данные файла (атрибут mtime) или его статус (атрибут ctime).
commit – время между сбросами буферов на диск.

Естественно, такие манипуляции проводятся, так как точно будет использоваться источник бесперебойного питания. То есть UPS это один из ускоряющих элементов сервера.

Планировщик.

Где-то в глубине души я знал, что с планировщиком CFQ нам не по пути, он сделан под углом честного дележа доступа к диску и пытается сортировать очередь к диску, зная про головки HDD и минимизацию их перемещения. Но имеет ли смысл что-то сортировать у виртуального диска?

Вначале я выставил планировщик noop, который является простейшим планировщиком и ничего не сортирует. Но статьи уважаемой IBM по лучшему использованию KVM утверждают, что лучше использовать планировщик deadline и что так поступает сама IBM. Да и Canonical вместо CFQ стала использовать deadline.

Пока решил довериться IBM и Canonical и выставить deadline по умолчанию.

Советы IBM по вопросам работы памяти.

IBM считает, и наверное не безосновательно, что для бо́льшой производительности нужно отключить zone_reclaim_mode и выставить swappiness=0.

NUMA (Non-Uniform Memory Access — неравномерный доступ к памяти или Non-Uniform Memory Architecture — Архитектура с неравномерной памятью) — схема реализации компьютерной памяти, используемая в мультипроцессорных системах, когда время доступа к памяти определяется её расположением по отношению к процессору.

numa
Оборудование большинства операционных систем — это системы NUMA, со своей штрафом-платой (NUMA penalty) при удалённом доступе к памяти.

NUMA penalty = CPIremote / CPIlocal , где CPI — cycles per instruction.

Если NUMA penalty становится высокой, то операционная система осуществляет очистку (zone reclaim).

Для примера, операционная система выделяет память в ноде NUMA, но нода NUMA полна. В таком случае, операционная система начинает очистку памяти локальной ноды NUMA, а не выделяет немедленно память на удалённой ноде NUMA. Выигрыш в выделении памяти на локальной ноде перевешивает недостаток скорости при очистке памяти (reclaiming memory).

Однако, в некоторых ситуациях очистка памяти (reclaiming memory) снижает производительность до такой степени, что верно и обратное. Иными словами, выделение памяти на удалённой ноде NUMA будет быстрее, чем очищение памяти на локальной ноде.

Гостевые операционные системы могут вызывать zone reclaim в следующих случаях:

  • гостевая операционная система использует гигантские страницы (huge pages).
  • используется Kernel SamePage Merging (KSM) для слияния одинаковых страниц памяти гостевых операционных систем.

Настройка huge pages, использование KSM и отключение zone reclaim эти шаги IBM считает хорошей практикой при использовании виртуализации KVM.

zone_reclaim_mode.

Для отключения zone_reclaim_mode на KVM хосте, нужно убедиться в текущем состоянии дел.

cat /proc/sys/vm/zone_reclaim_mode

Моя Proxmox VE нода выдала 0 и значит разработчики Proxmox VE уже решили нашу задачу. Если у вас не ноль, то в /etc/sysctl.conf нужно указать vm.zone_reclaim_mode=0 и рестарт.

swappiness.

Дефолтное значение swappiness = 60. Можно выставить от 0 до 100. Когда выставлен swappiness = 0 на системах Intel Nehalem, то менеджер виртуальной памяти удаляет страничный кеш (page cache) и кеш буфера (buffer cache), а не скидывает программу из памяти в swap.

Платформа Intel Nehalem использует расширенные таблицы страниц Extended Page Table (EPT). EPT не устанавливает бит доступа к страницам, которые используются гостевыми операционными системами. Таким образом, менеджер виртуальной памяти Linux не может использовать свой алгоритм LRU (least recently used), чтобы точно определить какие страницы не нужны. Точнее сказать, алгоритм LRU не может точно определить какие страницы в гостевых операционных системах являются лучшими кандидатами на вытеснение в swap. В это ситуации, во избежание ненужного своппинга лучше выставить swappiness=0.

Системы с процессорами Advanced Micro Devices (AMD) и с технологией Nested Page Table (NPT) не имеют описанной выше проблемы.

Узнайте текущее значение cat /proc/sys/vm/swappiness

Для KVM хоста в /etc/sysctl.conf нужно указать vm.swappiness=0.

huge pages.

Приложения, которые используют много непрерывных участков памяти, получат максимальную выгоду от huge pages, поскольку будет генерироваться меньше промахов Translation Lookaside Buffer (TLB).

Процессор чаще находит нужное отображение в TLB и реже сканирует таблицу страниц.

Таким образом, huge pages увеличивает производительность приложений, которые используют большие и непрерывные участки памяти.

Приложения, которые используют маленькие и непрерывные блоки памяти, не получат от huge pages выгоды.

Huge pages нужно настраивать в ручную и знать необходимость приложения в огромных блоках памяти. По этой причине, пока не стал связываться с ними.

Быстродействие MySQL сервера.

Исторически так сложилось, что каждый из физических серверов под конкретную задачу обладал установленным на нём сервере MySQL для хранения данных. Чтобы удобно было администрировать задачу через веб интерфейс нужен веб сервер Apache. Другими словами сложилась ситуация — всё свое ношу с собой.

Так было с DHCP сервером — самописная веб морда, записи MAC и IP в MySQL, PHP, Perl, Apache. Такая же участь у почтовика — Postfix, учётные записи в MySQL, PostfixAdmin, PHP, Apache. Биллинг Интернет трафика — Squid, Squid2Mysql, учётные записи в MySQL, PHP, Apache.

Тяжело вырваться из этого круга, но хотелось сделать единый MySQL сервер с единым Apache и местом обитания всех веб морд. Создал виртуальную машину mysqlserver, которая обладала сервером MySQL и веб сервером Apache.

Единый MySQL позволит легко манипулировать доступом к базам данным. Но с другой стороны, я опасался эффекта «все яйца в одной корзинке». Зависимость от отдельного MySQL сервера могла сделать неработоспособной какую-либо службу при недоступности MySQL сервера.

Частично негативный эффект от единой точки отказа был сглажен. Например, DHCP сервер берёт записи из базы MySQL и формирует свой dhcpd.conf при изменениях в записях MAC и IP адресов. При отказе MySQL физически не изменить базу данных через веб интерфейс и, следовательно, DHCP сервер не увидит изменений и будет работать с ранее сформированным dhcpd.conf.

Новый Squid 3 изменил представление о сохранении своих логов вместо файлов в базу данных MySQL.

logformat squid_mysql %ts.%03tu %6tr %>a %Ss %03Hs %<st %rm %ru %un %Sh %<A %mt
access_log daemon:/etc/star_scripts/squid/log_mysql_daemon.conf squid_mysql
logfile_daemon /etc/star_scripts/squid/log_mysql_daemon.pl

Пришлось по другому запихивать логи в базу, нежели как раньше скриптом из проекта squid2mysql. Нашёл perl скрипт труд итальянца Marcello Romani, но у него своя схема базы данных и шикарные представления view, которые создают отличные запросы на все случаи жизни прокси сервера. Чтобы веб морда Squid2Mysql ничего не заметила и продолжала нормально свою работу, не смотря на новую схему от итальянца, я просто сделал нужный view и сопоставил новые поля из новой таблицы в старые поля старой таблицы. При недоступности MySQL скрипт от итальянца работает идеально и Squid продолжает работу и предоставляет доступ в Интернет. Единственное, что трафик не учитывается, но это лучший расклад при гипотетической неработоспособности отдельного сервера MySQL. Лучше терять логи, но дать доступ в Интернет.

То есть DHCP и Squid не смертельно связаны с MySQL и могут некоторое время прожить и без него. Но Postfix с MySQL связаны более жёстко. В MySQL хранятся почтовые пользователи и Postfix при своей работе постоянно обращается к базе данных. Для защиты от спама и вирусов, Postfix связан с Amavis + SpamAssassin, которые частично используют базу данных, чтобы получать белые списки людей, которых нужно исключить из защиты.

DELAY_KEY_WRITE.

Решил поиграть в великого оптимизатора MySQL и получить порцию знаний по этой теме. Почитал замечательную книгу авторства Бэрон Шварц, Петр Зайцев, Вадим Ткаченко, Джереми Заводны, Арьен Ленц, Дерек Боллинг «MySQL. Оптимизация производительности». Для себя открыл, неведомый мне прежде, режим отлаженной записи ключей DELAY_KEY_WRITE. Это ускоряет многочисленные INSERT в таблицу, так как с этой опцией индексы хранятся только в памяти и записываются на диск в последнюю очередь, по команде FLUSH TABLES или по завершению mysqld.

Для таблицы access_log, в которую записываются огромные логи Squid 3 скриптом итальянца, я сделал ALTER TABLE access_log DELAY_KEY_WRITE = 1, помня об недостатках DELAY_KEY_WRITE и обязательному наличию UPS.

Сегментированные таблицы.

Прочёл на Хабре срач про сегментированные таблицы, которые калькой с английского часто называют партицированием. Кто-то говорил о сырости и глюкавости данного решения, дескать написанное криворукими индийцами за горстку риса. Кто-то утверждает, что это бред сивой кобылы.

Смысл в том, что сегментированная таблица позволит призрачно разделить таблицу по какому-либо критерию на призрачные таблицы с меньшим количеством строк. Меньше количество строк = меньше нагрузка при запросах. Меня бы устроило разделение access_log по дате месяца, так как жизнь показала, что база очень быстро распухает и работа с ней замедляется.

Профи советуют сегментировать примерно так.

ALTER TABLE access_log
PARTITION BY RANGE( TO_DAYS(date_day) )
(
PARTITION y2013m01 VALUES LESS THAN( TO_DAYS('2013-01-01') ),
PARTITION y2013m02 VALUES LESS THAN( TO_DAYS('2013-02-01') ),
...
PARTITION y2020m11 VALUES LESS THAN( TO_DAYS('2020-11-01') ),
PARTITION y2020m12 VALUES LESS THAN( TO_DAYS('2020-12-01') ),
PARTITION pcatchall VALUES LESS THAN (MAXVALUE)
);

Не большой совет от гуру — создать секции заранее, чтобы потом было проще. Вместо ... много много строк по аналогии от 2013 до 2020 года и все их месяцы.
Теперь EXPLAIN PARTITIONS SELECT покажет, что использование WHERE date_day будет затрагивать определённую секцию, что благотворно влияет на быстродействие.

Но принявшись за сегментирование, меня ждал fail. Как я понял, банально попал под ограничения секционированных таб­лиц и запрос ALTER TABLE access_log PARTITION BY RANGE ( TO_DAYS (date_day) ) выдал A PRIMARY KEY must include all columns in the table's partitioning function.

Гуру SQL объяснили: ограничение, которое встретилось вам, звучит примерно так: каждый уникальный ключ (включая PK, который является UK NOT NULL) должен содержать все колонки, используемые в выражении, по которому идет секционирование. Вызвано это тем, что в MySQL отсутствуют глобальные индексы, так как поддержание подобных индексов достаточно дорогая операция. Иными словами, имея PK id и секционирование по полю ddate, MySQL бы пришлось каким-то образом гарантировать, что id уникален во всех партициях сразу. Для этого нужен индекс, проходящий сквозь все секции. MySQL такого не умеет. А вот если PKем станет пара id, ddate, то достаточно, чтобы внутри каждой секции эти значения были уникальными. Так как ddate в разных секциях будет разный, то это автоматически гарантирует уникальность пары id, ddate на всей таблице.

Получается или нужно сегментирование или детально разбираться в не в своей схеме таблицы и грамотно изменить индексы. Решил пока повременить с сегментацией таблицы.

Оптимизация запросов.

Решил в скриптах Perl и PHP, которые за многие годы до меня администраторы на создавали для упрощения администрирования, разобраться и оптимизировать запросы в них.

Начал с простого. Ищем все SELECT FROM WHERE и анализируем колонки таблиц, участвующие в запросе. Банальное создание индексов на такие поля и EXPLAIN SELECT показывает не множество задетых строк, а единицы. Вроде мелочь, а проектировщик в прошлом не удосужился сделать такую отличную вещь, как индекс для поля. Минус у индекса, если можно назвать это минусом, можно считать необходимость в дополнительном обновлении индекса при операциях INSERT, UPDATE в таблице. Но кроме access_log все остальные таблицы не испытывают мощных INSERT, UPDATE и можно смело навешивать index налево и направо.

В поиске столбцов, которые участвуют в запросах и не имеют индекса, очень помогает параметр log-queries-not-using-indexes в /etc/mysql/my.cnf. Чтение лога mysql помогает найти виновника и устранить недоразумение. В логе сразу видно кто, откуда и каким запросом умудряется цеплять множество строк таблиц, не используя индексы.

Из книги «MySQL. Оптимизация производительности» узнал, что поле с индексом в запросе с WHERE должен стоять левее и когда встречал сложное условие, то переписывал его так, чтобы сначала было поле, а потом уже больше-меньше и выражение.

Потом пошла оптимизация посложнее. Автор Squid2MySQL в веб админке постоянно использовал запросы к «тяжёлой» таблице squidlog, хотя часто нужные данные лежали в более «лёгкой» таблице rdnload, в которой уже лежали пред вычисленные значения скаченного трафика. Логику автора не понять. Как писал выше, данные по скачанному через Squid теперь заносились скриптом итальянца, а не родным скриптом проекта, поэтому squidlog стал представлением (VIEW) для таблицы access_log. Но скрипты веб интерфейса Squid2Mysql я всё равно изменил, чтобы запросы больше обращались к таблице rdnload, чем через представление squidlog к access_log.

Оптимизация Apache.

Самая лучшая оптимизация Apache — это использовать что-то полегче, чем Apache . Но известность и популярность Апача немаловажные вещи. Решено поставить именно его, но настроить его под низкие требования малочисленных администраторов.

У нас не высоконагруженный проект, а сайт с вебмордами админок. В общем, параметры такие:

StartServers 1
MinSpareServers 1
MaxSpareServers 2
MaxClients 5
MaxRequestsPerChild 300

StartServers определяет количество процессов сервера, запускаемых изначально, сразу после его старта. MinSpareServers и MaxSpareServers определяют минимальное и максимальное количество дочерних «запасных» процессов Apache. Такие процессы находятся в состоянии ожидания входящих запросов и не выгружаются, что даёт возможность ускорить реакцию сервера на новые запросы. MaxClients определяет максимальное количество параллельных запросов, одновременно обрабатываемых сервером. Когда количество одновременных соединений превысит это количество, новые соединения будут поставлены в очередь на обработку. MaxClients определяет максимально-допустимое число дочерних процессов Apache, запущенных одновременно. MaxRequestsPerChild определяет количество запросов, которое должен обработать дочерний процесс Apache, прежде чем завершить своё существование. Когда обработанных запросов станет больше MaxRequestsPerChild, то дочерний процесс будет перезапущен и это помогает при возможных утечках кривых скриптов.