CPU 100% на VPN-сервере: причины
Процессор на VPN-сервере упёрся в 100%, туннель тормозит или рвётся, а обычный сайт на этом же железе работал бы без проблем. У VPN своя специфика нагрузки: каждый байт трафика нужно зашифровать и расшифровать, и это чисто процессорная работа, которая не спрячется за кэшем или диском. Причин обычно три: шифрование идёт в один поток и не использует остальные ядра, протокол или шифр работает без аппаратного ускорения, либо на порт VPN льётся лавина подключений, похожая на DDoS. Ниже — как быстро отличить одно от другого и что с этим делать.
Содержание
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Как понять, где искать: одно ядро или все сразу
Первый шаг — не гадать, а посмотреть на нагрузку по ядрам отдельно, а не в среднем. Общий процент CPU в top смазывает картину: если у вас 4 ядра и одно из них забито под завязку, средняя загрузка покажет скромные 25%, хотя туннель уже задыхается. Смотрите так:
mpstat -P ALL 1
Если не установлен — apt install sysstat или dnf install sysstat. В htop то же самое видно наглядно: вверху экрана отдельная шкала на каждое ядро, и картина сразу говорит, куда копать.
- Одно ядро на 100%, остальные почти простаивают — типичный признак однопоточного шифрования: весь трафик туннеля гонит один процесс или поток, и он упёрся в потолок одного ядра, сколько бы их ни было на сервере.
- Загружены все ядра примерно поровну, но нагрузка растёт с числом клиентов быстрее, чем растёт трафик — похоже на шифр без аппаратного ускорения: каждый мегабит обходится процессору дороже, чем должен.
- Все ядра забиты, число соединений и наполовину открытых сессий зашкаливает — это уже не про шифрование трафика, а про лавину подключений. Проверьте счётчики:
ss -s
ss -tn state syn-recv | wc -l
Дальше разберём каждую из трёх причин по отдельности — и что с ней делать.
Причина 1: шифрование идёт через одно ядро
Классическая связка OpenVPN устроена так, что один процесс сервера обрабатывает шифрование и расшифровку трафика туннеля по сути последовательно, в одном потоке. На сервере с 8 ядрами это означает, что весь тяжёлый крипто-цикл упирается в производительность одного ядра — остальные семь просто не участвуют в работе с этим туннелем. Для одного клиента с несколькими мегабитами трафика разницы не видно, но на сервере, где через один и тот же процесс идёт трафик десятков пользователей, это ядро становится узким местом раньше, чем сервер исчерпает реальные ресурсы.
Проверить гипотезу просто: если mpstat -P ALL 1 стабильно показывает одно ядро около 100% при почти простаивающих остальных — совпадает с PID вашего OpenVPN-процесса, дело в этом:
ps -eo pid,pcpu,comm --sort=-pcpu | head -5
Решений два, в порядке предпочтения:
- Перейти на WireGuard. Его крипто-стек (ChaCha20-Poly1305 или AES при поддержке AES-NI) реализован в виде модуля ядра и естественным образом распределяется по нескольким ядрам через механизм multiqueue сетевых интерфейсов — узкое место в одно ядро здесь не возникает в принципе. Если менять протокол пока не вариант — см. следующий пункт.
- Разнести клиентов по нескольким процессам OpenVPN. Поднимите два-три независимых сервера OpenVPN на разных портах (1194, 1195, 1196) с разными конфигами и подсетями туннеля, и распределите клиентов между ними — вручную или через балансировку на уровне DNS. Каждый процесс — это отдельный поток ядра планировщика, и они естественным образом разъедутся по разным ядрам:
# /etc/openvpn/server1.conf
port 1194
dev tun1
server 10.9.1.0 255.255.255.0
# /etc/openvpn/server2.conf
port 1195
dev tun2
server 10.9.2.0 255.255.255.0
systemctl enable --now openvpn@server1 openvpn@server2
Полное сравнение протоколов и когда что имеет смысл — в статье WireGuard или OpenVPN: что выбрать для сервера.
Арендуйте сервер под свои задачи!
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверПричина 2: устаревший протокол без аппаратного ускорения
Современные процессоры умеют шифровать и расшифровывать AES напрямую в железе — набор инструкций AES-NI ускоряет эту операцию на порядок по сравнению с программной реализацией. Если протокол или выбранный шифр эти инструкции не использует, каждый мегабит трафика обходится процессору кратно дороже — и именно поэтому нагрузка растёт непропорционально росту трафика.
Проверьте, поддерживает ли процессор AES-NI:
grep -m1 -o aes /proc/cpuinfo
Если команда ничего не вывела — аппаратного ускорения AES на этом CPU нет (бывает на бюджетных ARM-сборках и старых виртуальных ядрах), и любой AES-шифр там будет считаться программно. В этом случае разумнее выбрать шифр, который не завязан на AES: ChaCha20-Poly1305 (в WireGuard он используется по умолчанию, в OpenVPN 2.5+ тоже доступен) спроектирован для быстрой работы на процессоре без специальных инструкций и часто оказывается быстрее программного AES именно на таком железе.
Если AES-NI на сервере есть, а нагрузка всё равно высокая — вероятно, конфиг тянет устаревший или программный шифр по историческим причинам. Загляните в конфиг OpenVPN:
grep -E "cipher|ncp-ciphers" /etc/openvpn/server.conf
Blowfish-CBC (старый шифр по умолчанию до OpenVPN 2.4), любые варианты *-CBC без указания GCM, а тем более PPTP с шифрованием MPPE — всё это либо не использует AES-NI, либо устарело и по безопасности, и по эффективности. Замените на:
cipher AES-256-GCM
ncp-ciphers AES-256-GCM:AES-128-GCM
auth SHA256
GCM-режим не только безопаснее CBC (защищает от подмены данных без отдельного HMAC), но и лучше ложится на аппаратное ускорение. Оцените разницу до и после на своём железе:
openssl speed -evp aes-256-gcm
openssl speed -evp chacha20-poly1305
Конкретные цифры пропускной способности у вас будут свои — зависят от модели процессора и загрузки сервера в моменте, ориентируйтесь на порядок разницы между шифрами на вашей машине, а не на чужие бенчмарки из интернета. Про PPTP отдельно: протокол устарел, его шифрование MPPE взламывается и не имеет никакого аппаратного ускорения — если он у вас ещё где-то жив, это повод для миграции в первую очередь, а не для оптимизации.
Причина 3: лавина подключений маскируется под DDoS
TLS-рукопожатие OpenVPN — это операции с асимметричными ключами (RSA или EC), а они на порядки дороже симметричного шифрования самого трафика. Именно поэтому даже без успешной авторизации боты, сканирующие интернет в поисках открытых VPN-портов, или целенаправленный флуд подключений способны положить процессор в потолок: сервер честно выполняет дорогое рукопожатие для каждой попытки, прежде чем её отклонить. Внешне это выглядит как обычная DDoS-атака, только цель — не полоса канала, а именно CPU.
Проверьте, не это ли ваш случай:
journalctl -u openvpn@server --since "10 min ago" | grep -c "TLS Error\|VERIFY ERROR"
tcpdump -ni any port 1194 -c 200
Резкий рост числа неудачных TLS-рукопожатий с разных IP за короткое время — характерный признак. Если так, помогает несколько мер, от простой к более радикальной:
- fail2ban на неудачные попытки авторизации. Готовый джейл для OpenVPN блокирует IP после нескольких неудачных подключений подряд, и повторные попытки с того же адреса больше не доходят до дорогого TLS-рукопожатия.
- Ограничение скорости новых подключений на уровне firewall. Правило nftables/iptables, которое рубит частоту новых соединений на порт VPN, снимает основную массу автоматического флуда ещё до того, как он дойдёт до демона:
iptables -A INPUT -p udp --dport 1194 -m hashlimit \
--hashlimit-above 20/sec --hashlimit-burst 30 \
--hashlimit-mode srcip --hashlimit-name vpnflood -j DROP
- Перейти на WireGuard. Здесь разница принципиальная: WireGuard проверяет подлинность пакета дешёвым криптографическим механизмом (cookie) ещё до того, как выполнить дорогие вычисления по протоколу Diffie-Hellman. Пакет без валидного ключа отбрасывается почти бесплатно для CPU — сканеры и флудеры не могут заставить сервер тратить ресурсы на рукопожатие с чужаком, в отличие от классической схемы TLS-рукопожатия OpenVPN.
Что делать в первые минуты, если нагрузка резко подскочила и похоже на настоящую атаку, а не на фоновый шум сканеров — подробный порядок действий в статье DDoS-атака: первые действия.
Сравнение протоколов по нагрузке на CPU
Для ориентира — как разные протоколы ведут себя именно с точки зрения процессора, без учёта прочих критериев вроде маскировки трафика или удобства настройки:
| Протокол | Многопоточность | Шифр по умолчанию | Ускорение AES-NI | Устойчивость к флуду подключений |
|---|---|---|---|---|
| OpenVPN (классика) | один процесс = одно ядро на туннель | зависит от конфига, часто AES-CBC | использует, если указан GCM-шифр | низкая, TLS-рукопожатие дорогое |
| WireGuard | естественно многоядерный (kernel + multiqueue) | ChaCha20-Poly1305 | не обязательно, но использует при наличии | высокая, cookie-фильтр до рукопожатия |
| IPsec/IKEv2 | зависит от реализации, обычно лучше OpenVPN | AES-GCM в современных настройках | использует | средняя |
| PPTP | один поток, устарел | MPPE (слабый, программный) | не использует | низкая, протокол устарел и небезопасен |
| SoftEther | многопоточный | AES-GCM в современных версиях | использует | средняя |
Таблица — ориентир для диагностики, а не вердикт «что выбрать»: у протоколов есть и другие плюсы-минусы, не связанные с CPU. Общий гид по выбору под сценарий использования — в статье какой протокол VPN выбрать.
Что делать: пошаговый план
Соберём диагностику в порядок действий:
- Посмотрите нагрузку по ядрам через
mpstat -P ALL 1. Одно горячее ядро — смотрите в сторону однопоточного шифрования и смены протокола или разнесения по нескольким процессам. - Проверьте поддержку AES-NI и актуальность шифра в конфиге. Программный AES или PPTP — переходите на AES-GCM с аппаратным ускорением или на ChaCha20-Poly1305, если ускорения на CPU нет.
- Проверьте количество TLS-ошибок и новых подключений за последние минуты. Всплеск — включайте fail2ban и ограничение скорости на firewall, при серьёзной атаке — переходите к мерам из статьи про первые действия при DDoS.
- Если нагрузка растёт стабильно и все три пункта уже приведены в порядок, а процессору всё равно тесно — это честный сигнал, что трафик и число клиентов переросли текущую конфигурацию, а не признак поломки.
- При смене протокола с OpenVPN на WireGuard не обязательно рвать доступ разом — поднимите оба туннеля параллельно и переводите клиентов постепенно. Порядок такого перехода без простоя — в статье миграция между VPN-протоколами без даунтайма.
Если после всех оптимизаций процессор всё равно упирается в потолок при реальном, легитимном трафике — это уже не вопрос настройки, а вопрос мощности: нужен сервер с большим числом ядер и обязательно с поддержкой AES-NI, без перепроданных ресурсов, где ваши ядра делят с соседями по железу.
Арендуйте сервер под свои задачи!
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Один процесс OpenVPN грузит одно ядро на 100%, а остальные семь простаивают — это нормально?
Да, это ожидаемое поведение классической схемы OpenVPN: шифрование туннеля идёт в одном потоке. Дополнительные ядра сами по себе эту нагрузку не разгрузят — нужно либо разнести клиентов по нескольким процессам, либо перейти на протокол с естественной многопоточностью, например WireGuard.
Как отличить нагрузку от шифрования и нагрузку от флуда подключений?
Смотрите на количество активных сессий и частоту новых TLS-рукопожатий. Ровная нагрузка от небольшого числа постоянных клиентов — это шифрование трафика. Всплеск числа коротких неудачных подключений с разных IP за минуты — это флуд, а не обычный трафик.
WireGuard правда меньше грузит процессор, чем OpenVPN?
В большинстве сценариев да, за счёт более лёгкой криптографии и того, что обработка пакетов распределяется по нескольким ядрам, а не упирается в один процесс. Насколько именно меньше — зависит от вашего трафика и железа, точных цифр без замера на конкретном сервере лучше не обещать.
AES-NI есть на процессоре, а нагрузка всё равно высокая — почему?
Проверьте, какой шифр реально согласовывается в конфиге: если там старый AES-CBC или тем более Blowfish, аппаратное ускорение может использоваться не полностью. Переключитесь на AES-256-GCM и сверьте разницу через openssl speed.
Стоит ли просто добавить ядер, не разбираясь в причине?
Не в первую очередь. Если проблема в однопоточном шифровании или устаревшем шифре, лишние ядра не помогут — упирается конкретный поток, а не сервер целиком. Сначала диагностика, апгрейд — когда оптимизация исчерпана.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →