Объединение Санкт-Петербурга и Ленинградской области


Яндекс цитирования
Ремонт сайтов

[на страницу публикаций>>]

Ограничение, учёт ресурсов и борьба с DoS-атаками в условиях массового виртуального хостинга

Олег Петрачёв // Cronfy,
системный администратор Peterhost.ru

Введение

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

Одним из источников экстремальной нагрузки являются мощные DoS-атаки, противодействие которым невозможно обеспечить, ограничиваясь одними лишь техническими методами.

В статье идёт речь о возможностях и комплексных методах распределения, учёта и ограничения ресурсов в условиях взаимосвязанной системы. Также рассматриваются вопросы поиска и устранения причин появления направленных на систему атак.

Распределение ресурсов

Концепция виртуального хостинга и требования к ресурсам

В условиях массового виртуального хостинга сотни и даже тысячи сайтов располагаются на одном физическом сервере и разделяют между собой его ресурсы. Их одновременное сосуществование при приемлемой скорости работы возможно потому, что, во-первых, большая часть сайтов вообще не потребляет сколько-нибудь заметное относительно мощности сервера количество ресурсов, а во-вторых, посещаемость сайтов более-менее равномерно распределена по времени, и ресурсы в каждый момент требуются не всем сразу, а лишь нескольким сайтам. Дополнительный плюс виртуального хостинга заключается в том, что за небольшие деньги каждый сайт может успешно выдерживать кратковременные пиковые нагрузки, получая в распоряжение все свободные вычислительные ресурсы мощного сервера. Всё это позволяет сохранять низкой стоимость предоставления услуги, а также позволяет использовать ресурсы очень эффективно: сервер всегда занят полезным делом, обладая при этом некоторым запасом мощности для обслуживания пиковых нагрузок.Итак, по концепции массового виртуального хостинга, ресурсов одного большого сервера должно хватить для нормальной работы всем сайтам, которые на нём размещены. Однако эта идиллия возможна только при том условии, что использование сервера каждым сайтом является не слишком агрессивным и не превышает определённого предела. Если какой-либо сайт запрашивает ресурсы слишком активно, он отнимает ресурсы у других, в результате чего другие сайты начинают обслуживаться хуже. И если для работы сайта в момент кратковременной пиковой посещаемости эти ресурсы могут быть предоставлены, то долговременное превышение предела использования ресурсов неприемлемо.

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

Планировщик операционной системы

В первую очередь распределением ресурсов в многозадачной операционной системе занимается планировщик (scheduler). Планировщик отвечает за расстановку приоритетов выполнения между процессами, и за то, какой процесс должен выполняться в текущий момент. В многопроцессорных системах планировщик также разносит выполнение задач на разные процессоры для наиболее эффективного их использования. Также существуют планировщики ввода-вывода, определяющие, каким образом между процессами будут распределяться имеющиеся мощности дисковой подсистемы.

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

В разных операционных системах используются разные планировщики, иногда даже предоставляется возможность выбора между ними. Так, во FreeBSD имеется старый планировщик 4BSD, но есть возможность использовать более новый, хотя и не настолько тщательно протестированный и менее стабильный SCHED_ULE, уже за первую половину 2007 года сменивший версию сначала на 2.0, а потом, объединённый с ранее отделённым в параллельную ветвь SCHED_SMP, на 3.0. Ожидается, что во FreeBSD 7.0 можно будет безопасно использовать новую улучшенную версию этого планировщика. В ядре Linux дела тоже не стоят на месте: из разрабатывавшихся в последнее время планировщиков SD (Staircase Deadline) и CFS (Completely Fair Scheduler) в ядро 2.6.23 был включён CFS, пришедший на смену старому планировщику O(1).

Разработка планировщиков продолжается из-за недостатков алгоритмов расстановки приоритетов, из-за необходимости более полноценного учёта работы в многопроцессорных системах и улучшения масштабируемости. При этом планировщик сам должен использовать по возможности как можно меньше ресурсов и времени для обсчёта приоритета и переключения между выполняемыми процессами. Например, из-за недостатков алгоритма, старые планировщики при попытках масштабирования в условиях многоядерных систем начинают вести себя довольно неэффективно, затрачивая много ресурсов на диспетчеризацию. Даже O(1) не оправдывает своё название, так как при переключении контекста M раз между N процессами общий алгоритм всей диспетчеризации - O(N*M). Новые же разрабатываемые планировщики сейчас довольно близки в среднем к O(log n).

Казалось бы, отлично! Даже если в прошлом это было сложно, то в ближайшем будущем планировщик операционной системы будет на ура справляться с распределением ресурсов автоматически, честно и с учётом характера задач разделяя ресурсы сервера. Но, увы, это не так.

В идеальном мире планировщик операционной системы можно было бы сконфигурировать для честного долговременно равномерного распределения ресурсов между процессами с возможностью динамической переконфигурации. Но пока что, из-за несовершенства планировщиков даже в их базовом состоянии, о возможности настройки под нужды конкретной системы не идёт даже речи. И несколько больший упор разработчики планировщиков сейчас делают на десктопное применение систем, в условиях небольшого количества запущенных процессов, считая это направление наиболее востребованным.

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

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

Ограничение ресурсов

Сервер виртуального хостинга - это система, состоящая из физического сервера и работающих на нём служб. Помимо физических ресурсов самого сервера (например, дисковое пространство) есть ещё ограниченные ресурсы, принадлежащие службам системы (например, количество одновременных соединений). Учитывать необходимо все доступные для учёта ресурсы, это поможет сделать более правильные выводы о нагрузке, создаваемой аккаунтом. Некоторые из ресурсов можно ограничивать прямо, для других возможности явного ограничения не существует. Тем не менее, так как речь идёт о взаимосвязанной системе, мы имеем возможность замерять использование одних ресурсов, и на основании этих данных принимать решения о необходимости ограничения других ресурсов, что, в конечном счёте, приводит к выравниванию нагрузки. Более того, нецелесообразно ограничивать ресурсы в любом месте, где стало известно имя пользователя и факт превышения им допустимой нагрузки, или создавать изолированные системы учёта/ограничения ресурсов для каждого сервиса - скорее всего это будет менее эффективно, чем применение системного подхода.

Сами службы системы нельзя ограничивать в ресурсах, от которых зависит скорость их работы, можно лишь ограничивать влияние пользователей на систему. В наших интересах добиться максимальной производительности, поэтому применять ограничения к службам неприемлемо - нужно искать точки входа, порождающие использование ресурсов, и устанавливать ограничения на них, аккуратно выбирать место, где это можно сделать без серьёзных потерь производительности.

Например, нет смысла понижать приоритет обработчику веб-сервера Apache, который обслуживает пользователя, превысившего свой лимит по нагрузке. Число обработчиков Apache конечно, и чем дольше будет выполняться запущенный, например, php-скрипт пользователя, тем дольше этот обработчик будет недоступен для обслуживания других соединений. По тем же причинам нецелесообразно ограничивать MySQL-сервер - он нужен нам для поддержки баз данных, а не для замедления работы. Наиболее дешёвым решением здесь будет ограничить количество одновременных соединений с сайтом, задерживая их поступление от frontend'a (например, nginx) к backend'у (Apache), потому что nginx, по сравнению с Apache, может одновременно обрабатывать гораздо большее количество соединений.

Типы ресурсов, возможности их использования и ограничения Система виртуального хостинга обладает несколькими различными типами ресурсов, некоторые из которых можно ограничивать, некоторые же можно только учитывать:
  • процессорное время: каждое процессорное ядро обладает 60 секундами времени в минуту (расчёты времени выполнения скриптов и запросов к MySQL производятся раздельно);
  • реальное время выполнения процесса;
  • количество оперативной памяти, выделяемой процессу;
  • интеграл использования памяти по времени;
  • количество одновременно запущенных процессов;
  • количество одновременно открытых файлов;
  • дисковая подсистема - количество дисковых операций за определённый интервал времени;
  • доступное дисковое пространство/максимальное количество файлов пользователя;
  • количество соединений с сайтом: единовременное и за какой-либо промежуток времени;
  • количество одновременных соединений по ftp;
  • почтовая система: количество писем, отправленных за определённый промежуток времени.
У пользователя есть несколько возможностей эти ресурсы использовать:
  • через обращения посетителей к сайту;
  • через crontab;
  • через шелл, получив доступ по ssh;
  • через ftp.
У нас же есть несколько возможностей ресурсы ограничивать:
  • через pam_limits или /etc/limits в Linux или через /etc/login.conf во FreeBSD;
  • через конфигурацию служб и подключение к службам дополнительных модулей ограничения;
  • через установку дисковых квот.
Тривиальные ограничения

Самые простые ограничения, как правило, накладываемые постоянно, - это ограничение на дисковое пространство и количество файлов, устанавливаемое через quota, а также ограничение числа одновременных ftp-сессий, настраиваемое через конфигурацию ftp-сервера.

Ulimit

Ограничения на максимальное процессорное время, которое процесс может использовать, максимальный выделяемый объем оперативной памяти, количество одновременно запущенных процессов, приоритет выполняемых процессов (nice), максимальное количество открытых файлов выставляются с помощью лимитов ресурсов, поддерживаемых операционной системой. Linux позволяет ограничивать ресурсы для групп пользователей с помощью модуля pam_limits через конфигурационный файл /etc/security/limits.conf, а FreeBSD позволяет каждому пользователю присвоить login class, установив ограничения для каждого класса в /etc/login.conf.

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

Нужно отметить, что практически все настраиваемые таким образом ограничения не влияют на скорость выполнения процесса, а лишь не дают ему сделать что-то лишнее и проработать больше, чем нужно. Превышение лимита обычно заканчивается неудачным выполнением, что даже сокращает время работы. Исключение составляет параметр приоритета (nice), установка которого может замедлить работу веб-сервера при обслуживании сайтов аккаунта, если обслуживание происходит с переключением в контекст пользователя. Поэтому этот параметр нужно использовать с осторожностью.

Реальное время выполнения процесса

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

Штатного средства для установки верхнего предела времени реального выполнения процесса нет. Однако можно написать скрипт, который, запускаясь раз в несколько минут, будет вычищать подобные зависшие процессы (и, возможно, вносить каким-либо данные об этом в статистику о нагрузке пользователя, от чьего имени был запущен процесс).

Ограничение количества обработчиков веб-сервера

В работе веб-сервера с переключением в контекст пользователя есть один нюанс - невозможно с помощью login class ограничить количество одновременно обслуживающих пользователя обработчиков веб-сервера. В самом деле, в таком случае веб-сервер изначально работает от пользователя root, и порождение нового процесса с последующим переключением в пользователя инициируется им же, что снимает пользовательские ограничения.

Для веб-сервера Apache (впрочем, другие в этом плане ничем не отличаются) эта проблема решается с помощью специального патча, который позволяет запретить переключение в пользователя при опасности превышения количества запущенных пользователем процессов. Эта модификация также требует накладывания патча на ядро, который позволит определить точное количество процессов, работающих от имени конкретного пользователя в данный момент.

Ограничение количества соединений

Обычно система виртуального хостинга состоит из backend'а (как правило, это Apache), который обладает широкой функциональностью, но при этом довольно жадно использует память и плохо масштабируется при увеличении количества соединений, и frontend'a (зачастую это nginx или какой-либо прокси), который умеет дёшево обслуживать большое количество сессий и берёт на себя отдачу контента медленному клиенту, как можно быстрее забирая выдачу backend'а и освобождая его для обслуживания дальнейших запросов.

В этом случае можно регулировать нагрузку, создаваемую аккаунтом при обращении посетителей к сайтам, пропуская ограниченное количество соединений от frontend'а к backend'у. Для этого необходимо научить frontend по имени запрашиваемого хоста определять, к какому аккаунту идёт запрос, и задерживать его пересылку backend'у при наличии предельного количества соединений с backend'ом для этого аккаунта.

Реализация ограничения возможна с помощью написания специального модуля для frontend'а, либо через специального "демона", который вкладывается дополнительной прослойкой между frontend'ом и backend'ом и контролирует количество соединений.

Этот модуль/демон можно даже научить узнавать запросы, создающие большую нагрузку, и избирательно затормаживать именно их, если, конечно, имеется такая статистика по запросам.

Ограничение mysql

По причинам невозможности замедления работы служб системы, о которых говорилось выше, мы не можем ни затормаживать запросы, идущие к MySQL, ни пытаться ограничить выдачу ресурсов MySQL-серверу снижением приоритета даже в пределах одного пользователя.

Однако мы можем ограничить процессорное время для пользователя MySQL так же, как ядро ограничивает процессорное время для пользователя системы. Для этого нужно модифицировать MySQL таким образом, чтобы сервер учитывал процессорное время выполнения запроса и возвращал ошибку в случае превышения лимита.

Кроме того, использование MySQL может быть снижено косвенным путём через ограничение входящих соединений для сайта. И всё-таки планировщик?

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

Однако к этому вопросу необходимо подходить очень осторожно как из-за требований к эффективности работы служб системы вне зависимости от того, под каким пользователем они выполняются, так и из-за опасности нарушить заложенные алгоритмы работы планировщика, что может привести к сильному снижению качества работы всей системы в целом.

Ограничение для отправки почты

Ограничение почты по большей части необходимо для предотвращения рассылок спама, однако ресурсы почтовой системы также не безграничны, и необходимо контролировать поток писем, идущих от одного пользователя. Для ограничения можно использовать встроенные средства почтового сервера. Например, exim предлагает ratelimit с широкими возможностями, позволяя подсчитывать и ограничивать количество отправляемых писем, используя в качестве ключа любую содержащуюся в письме информацию (к примеру, заголовок, содержащий имя пользователя). При превышении лимита возвращается ошибка со статусом 4XX, предписывающая отправляющему серверу повторить попытку позже.

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

К сожалению, возможности ratelimit в exim всё же недостаточно широки. Например, можно ограничивать только количество писем, но невозможно ограничить количество уникальных получателей в сутки, как это сделано, например, у Google. Поэтому ведутся работы по созданию собственного универсального ratelimit-демона, хранящего информацию в базе данных и позволяющего ограничивать поток писем (впрочем, не обязательно писем), по любым критериям, которые описываются языком SQL.

Неограничиваемые ресурсы

К неограничиваемым ресурсам относятся операции ввода-вывода. Впрочем, статистика показывает, что довольно редко одним аккаунтом исчерпывается именно этот вид ресурсов.

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

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

Внешние источники нагрузки

В основном, нагрузка, создаваемая аккаунтом, является следствием нормальной его работы. Однако бывают случаи, когда причиной повышенной нагрузки является чётко определяемый внешний источник, агрессивно взаимодействующий с сайтами аккаунта. В этом случае требуется не ограничение ресурсов аккаунта, а ограничение взаимодействия с источником нагрузки, например, сокращение принимаемых от него одновременных входящих соединений. Часто устанавливаются глобальные ограничения для всех источников, например, ограничение количества соединений с одного IP-адреса, однако при наличии соответствующей статистики можно создавать персональные ограничения и для известных внешних источников.

Одним из ярких примеров внешних источников нагрузки являются поисковые роботы. Многие из них ведут себя довольно спокойно, но встречаются экземпляры, не желающие себя ограничивать и создающие серьёзную нагрузку одновременно на множество аккаунтов одного сервера. С таким поведением приходится бороться ограничением полосы пропускания или количества запросов в минуту для зафиксированных роботов.

Динамическое изменение лимитов

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

В идеальном случае можно было бы отследить, каким образом аккаунтом создаётся нагрузка на систему (через веб-сервер, через cron или через ssh). Однако имеющиеся средства мониторинга не позволяют сделать это безошибочно. Тем не менее, можно определить среднюю нагрузку пользователя на всю систему, и с учётом полученного значения провести изменение всех лимитов сразу. Исключение составляет только почтовая система, на нагрузку которой сложно повлиять уменьшением количества соединений с сайтом. Для почтовой системы из-за её изолированности нагрузка учитывается и регулируется отдельно.

Необходимо помнить, что пользователь должен получать необходимые ресурсы в момент кратковременных пиковых нагрузок, и недолгое время уровень нагрузки может быть высоким. Однако за большой промежуток времени средний уровень нагрузки должен быть ниже. Поэтому среднее значение нагрузки пользователя рассчитывается и сравнивается с допустимым в нескольких временных интервалах, например: за сутки, за 8 часов, за час, за 30 минут, за 10 минут, за 5 минут и за 2 минуты. Чем меньше временной интервал, тем больше будет допустимое значение. В зависимости от того, насколько оно было превышено, для каждого интервала рассчитывается уровень жёсткости лимитов, из всех полученных значений уровня выбирается максимальное.

В зависимости от полученного уровня жёсткости выставляются текущие лимиты для аккаунта по всей системе в целом:
  • для изменения количества входящих соединений динамически настраивается frontend или демон-прослойка между ним и backend'ом;
  • для изменения ограничений ulimit для пользователей создаётся несколько классов (или групп) различной степени жёсткости ограничений, и осуществляется переключение между ними;
  • при нагрузке на почтовую систему динамически настраивается почтовый сервер или ratelimit-демон.

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

Человеческие методы снижения нагрузки

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

Также очень важно не оставлять пользователя один на один с сайтом, создающим нагрузку. Зачастую пользователь не обладает достаточной квалификацией для диагностирования и устранения проблемы. Поэтому крайне необходима информационная поддержка и снабжение пользователя диагностическими средствами, позволяющими ему быстро определить источник нагрузки. Диагностические средства - это и статистика по нагрузке, и анализатор логов, и добавление в логи информации об использовании процессорного времени (с помощью специальных патчей, разумеется), и, что немаловажно, доброжелательное объяснение ситуации по телефону.

Последний шанс

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

Учёт ресурсов

Обозначив широкие возможности по ограничению ресурсов, по динамическому изменению лимитов на основании статистики поведения аккаунта, необходимо рассказать и о том, как собирается эта статистика, какие средства используются для учёта ресурсов и какие соображения при этом применяются.

Статистика по использованию системы собирается соответствующими утилитами, форматируется и регулярно добавляется в базу данных для последующего анализа. Регулярность обновления базы зависит от того, какую скорость реакции системы необходимо получить.

Вся информация без исключения

При сборе данных часто возникает вопрос: а какая же информация значима для того, чтобы быть учтённой в статистике? На самом деле, всю доступную информацию необходимо заносить в базу данных. Вряд ли вычёркивание значений ниже "порога значимости" позволит сильно сэкономить на вычислениях, зато при выборе неверного порога статистика может быть сильно искажена. Более того, порог значимости определяют сами данные, поэтому нельзя установить правильный порог, обладая лишь их частью.

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

Системный аккаунтинг

Для получения информации о процессорном времени, памяти и дисковых операциях пользователя можно воспользоваться аккаунтингом операционной системы. Например, во FreeBSD есть встроенные средства аккаунтинга. Ядро Linux можно скомпилировать с поддержкой аккаунтинга BSD и воспользоваться соответствующими утилитами для вывода собранной статистики.

Вывод собранной информации выглядит примерно так:
admin $ sa -im

root                    46         0.69cpu          110tio      325823k*sec
sshd                     2         0.00cpu            0tio           0k*sec
www                      2         0.00cpu            0tio        2908k*sec
admin                    6         0.00cpu            3tio           0k*sec
user_acc1                2         0.03cpu          122tio       65394k*sec
user_acc2                1         0.00cpu            0tio           0k*sec
user_acc3               15         0.00cpu            2tio      173238k*sec
...
Аккаунтинг MySQL

Стандартный MySQL не содержит средств для сбора информации о нагрузке пользователей, за исключением лога медленных запросов. Возможно, разработчики MySQL просто не догадываются об использовании своего продукта в многопользовательских проектах.

Поэтому для сбора данных необходимо применять модификации MySQL. Одна из возможных модификаций разработана Google, и одним из её улучшений является отображающая статистику аккаунта команда SHOW USER_STATISTICS. Подробнее об этих патчах можно почитать на сайте code.google.com.

Другая модификация, разработанная в компании Peterhost, добавляет аналог BSD-аккаунтинг'а для MySQL. При выполнении запросов сохраняется информация о том, сколько процессорного времени было использовано для выполнения запроса, а также собирает другую статистику по запросам пользователя.

Модификация добавляет или изменяет несколько команд:

SHOW [FULL] PROCESSLIST

В вывод команды добавляется информация о процессорном времени, которое было использовано в течение выполнения запроса. Позволяет в реальном времени наблюдать использование процессорного времени сервера пользователями MySQL.

Эта модификация требует также накладывания на ядро патча, который позволяет запрашивать информацию о соседних тредах.

Вывод выглядит так:
+-----+-------+----------------+-----+---------+------+----------+----------------+------------+
| Id  | User  | Host           | db  | Command | Time | CPU      | State          | Info       |
+-----+-------+----------------+-----+---------+------+----------+----------------+------------+
| 781 | user1 | 10.0.0.2:64329 | db1 | Sleep   |   14 | 0.000000 |                | NULL       |
| 916 | user2 | 10.0.0.2:49478 | db2 | Query   |    1 | 0.010000 | closing tables | SELECT ... |
| 022 | user3 | 10.0.0.4:58751 | db3 | Query   |    1 | 0.000000 | Opening tables | select ... |
| 193 | user4 | 10.0.0.5:55501 | db4 | Query   |    3 | 0.070000 | closing tables | SELECT ... |
| 580 | user1 | 10.0.0.2:50008 | db1 | Sleep   |    3 | 0.000000 |                | NULL       |
| 581 | user7 | 10.0.0.4:50003 | db7 | Query   |    1 | 0.010000 | closing tables | select ... |
| 584 | user2 | 10.0.0.2:53437 | db2 | Query   |    0 | 0.000000 | Opening tables | select ... |
| 586 | user1 | 10.0.0.2:50037 | db1 | Query   |    2 | 0.040000 | closing tables | SELECT ... |
...
SHOW ACCOUNTING
Команда выводит накопленную информацию об использовании ресурсов, о количестве медленных запросов, FULL JOIN'ов, временных таблиц и т.д. по каждому аккаунту:
+------+----------+----------+----------+------------+---------+-----------+-------------+-
| User | Host     | stime    | utime    | conn_count | updates | questions | S_full_join | ...
+------+----------+----------+----------+------------+---------+-----------+-------------+-
| u5   | 10.0.0.2 | 0.950000 | 1.930000 |         54 |      44 |       800 |           2 | ...
| u12  | 10.0.0.2 | 0.420000 | 1.270000 |         54 |     705 |      2517 |           0 | ...
| u15  | 10.0.0.2 | 0.250000 | 0.720000 |        715 |     302 |      1347 |           0 | ...
| u26  | 10.0.0.2 | 0.460000 | 1.640000 |        121 |     480 |      2521 |           0 | ...
| u29  | 10.0.0.2 | 0.360000 | 1.650000 |         20 |      36 |      2515 |           0 | ...
| u31  | 10.0.0.2 | 1.170000 | 3.850000 |        219 |       2 |       790 |           4 | ...
| u36  | 10.0.0.2 | 0.180000 | 1.230000 |         61 |      39 |       415 |           0 | ...
...
Значения, составляющие различные колонки вывода SHOW ACCOUNTING, в основном представляют собой счётчики, идентичные по названию счётчикам SHOW STATUS, но пересчитанные в контексте пользователя, а не сервера или соединения. Полный список всех учитываемых параметров таков:
User: имя пользователя;
Host: хост подключения;
stime: системное процессорное время, затраченное на аккаунт;
utime: пользовательское процессорное время;
conn_count: количество соединений;
updates: количество модификаций таблиц;
questions: общее количество запросов;
S_full_join: количество объединений без использования индексов;
S_full_range_join: количество объединений с ограниченным поиском по вторичной таблице;
S_range: количество объединений с ограниченным поиском по первой таблице;
S_range_check: количество объединений без ключа с проверкой ключа после каждого ряда;
S_scan: количество полных проходов первой таблицы;
Slow_queries: количество медленных запросов;
locks_waited: количество блокировок таблиц с предварительным ожиданием;
Sort_merge: количество проходов, потребовавшихся для сортировки (указывает на необходимость увеличения буфера сортировки);
Sort_range: количество сортировок с ограниченным поиском по таблице;
Sort_rows: количество отсортированных рядов;
Sort_scan: количество сортировок со сканированием таблицы;
FLUSH ACCOUNTING

Вывод накопленной статистики в файл, указанный в my.cnf, и сброс счётчиков.

Внедрение этой модификации позволяет полноценно учитывать ресурсы и нагрузку, создаваемую аккаунтом на сервер MySQL. Собранная с помощью аккаунтинга статистика даёт возможность вести мониторинг и даже с довольно высокой точностью определять причину повышенной нагрузки, создаваемой аккаунтом.

Анализ логов

Анализ логов используется для глубокого изучения ситуации и не годится для ведения статистики, потому что логи обычно содержат довольно малый процент полезной для статистики по нагрузке информации от их общего объёма, либо требуют сложного парсинга для получения примерного представления о создаваемой нагрузке.

Тем не менее, снабдив логи дополнительной информацией о нагрузке, полученной из других источников, можно получать данные о наиболее вероятных причинах создаваемой нагрузки. Например, с помощью специальной модификации можно заставить Apache сохранять в логи замеры процессорного времени, использованного для обслуживания запроса, что сразу даёт возможность определить, какие скрипты на сайте являются наиболее требовательными к ресурсам.

Наличие таких дополнительных данных позволяет повысить точность применения ограничений, например, вместо понижения лимитов целиком для аккаунта можно ограничить скорость приёма только тех запросов, которые действительно создают нагрузку. Это очень сильно смягчает эффект применения лимитов и позволяет пользователю чувствовать себя более спокойно при изучении ситуации с нагрузкой, не волнуясь за то, что его сайт целиком работает с применением ресурсных ограничений.

Подобным образом можно экспериментировать с другими точками входа нагрузки (шелл, cron), пытаясь более точно определить, что именно порождает повышенное потребление ресурсов. И если даже и не применять автоматическое регулирование на основании этих данных, то хотя бы включать их в статистику, видимую пользователем и системными администраторами.

DoS-атаки

Существуют случаи, когда любые имеющиеся методы ограничения нагрузки не могут быть применены - это DoS-атаки. DoS-атака - это умышленное злонамеренное создание кем-либо такого количества бессмысленных запросов, которое во много раз превышает возможности обслуживания системы. В результате сервис теряет работоспособность, растрачивая все ресурсы на обработку этих запросов. Некоторые DoS-атаки бывают нацелены на переполнение канала, что создаёт проблемы для прохождения полезного трафика.

DoS-атака не проблема, а симптом

DoS-атаки бывают простые, например, когда сервер атакуется с нескольких IP-адресов. В таких случаях удаётся вычислить параметры идущей атаки: например, это может быть постоянный размер пакетов, или какая-то постоянная строка в заголовке пакета или запроса, или IP-адреса, с которых обращение к серверу происходит наиболее часто. В таком случае атаку можно отфильтровать, например, на маршрутизаторе.

Бывают сложные случаи - когда атакующий имеет возможность менять адреса и условия атаки, постоянно обходя созданные фильтры. Бывают также мощные распределённые DDoS-атаки, ведущиеся с использованием ботнетов, которые практически невозможно заблокировать техническими методами.

Но на самом деле, техническими методами пытаться справиться с атакой бессмысленно. DoS-атака является не проблемой, а лишь её симптомом. У атакующего обычно есть вполне явно выраженные мотивы: например, он пытается добиться прекращения работоспособности какого-либо сайта. И даже если атака была каким-либо образом отфильтрована, у атакующего всегда есть возможность это обнаружить, и в этом случае он, скорее всего, будет пытаться возобновить блокирование сервиса, например, изменив параметры атаки для обхода фильтрации. Это может привести к постоянной гонке, в процессе которой сервис длительное время будет работать с перебоями.

Для успешной борьбы с атаками (любого рода) важно всегда искать их причину и устранять именно её, в таком случае атакующий самостоятельно прекратит свои действия. Даже если удалось снизить парализующий эффект атаки, необходимо как можно быстрее определить, на кого она направлена и почему. Поэтому в этой статье основной упор делается не на технические методы борьбы с атаками, а на необходимость поиска и устранения её причины.

Поиск причины

Есть несколько способов определить причину атаки.

Анализ пакетов. Если атака идёт http-запросами, то из заголовков наиболее часто встречающихся пакетов можно определить атакуемый сайт. Эти же данные могут присутствовать в логах, но на сервере может не быть общего лога запросов, или запросы могут быть составлены некорректно и не попадают в лог, или они могут не попадать в лог из-за загруженности сервера - в любом случае, первоисточником данных являются именно пакеты и их анализ часто более успешен.

Проверка жалоб. Часто атакуемый сайт раздражает не только автора атаки. Поэтому разбор пришедших на abuse@ жалоб за последнюю неделю может помочь определить потенциальные атакуемые сайты.

Как ни странно, звонок или письмо от атакуемого. Владелец атакуемого сайта может быть в курсе того, что и почему с ним происходит, и при достаточной адекватности он постарается об этом сообщить своему провайдеру.

Размещение информации, адресованной атакующему. В первые минуты атаки, во время фазы её изучения, сервис в любом случае недоступен для пользователей. Ситуацией можно воспользоваться для вывода при обращении к любому сайту информации о том, что сервер находится под атакой. Это шанс начать общение с наводящим атаку (скорее всего, в первые минуты он будет следить за производимым эффектом) и сообщить ему адреса для контакта, по которым он может сообщить цели своих действий. Это также даёт возможность сообщить пользователям причину недоступности сервиса, иначе же непонимание ими ситуации может привести к формированию плохого мнения о качестве предоставляемых услуг.

Возможное противодействие

До того, как причина атаки будет установлена, может пройти довольно большое количество времени. Естественно, необходимо в это время стараться каким-либо способом хотя бы частично восстановить работу сервиса. Есть несколько возможностей для этого.

Фильтрация. При известных параметрах атаки можно отфильтровать принадлежащие ей пакеты на маршрутизаторе, либо, если мощности маршрутизатора не хватает, на специальном фильтрующем и анализирующем сервере. Фильтрацию можно также проводить по географическому признаку. Ботнеты, используемые для атаки, часто оказываются большей частью расположенными за рубежом, поэтому сильно снизить эффект атаки может отключение зарубежного канала для атакуемого IP-адреса.

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

Изменение IP-адреса атакуемого сайта. Для успешной атаки атакующему необходимо следить за тем, чтобы атака шла в нужном направлении. Этим можно воспользоваться, и, если известен домен атакуемого сайта, изменить A-запись для него. Через некоторое время информация об этом достигнет либо ботов, либо направляющего атаку, и она будет перенаправлена, что позволит отфильтровать её без ущерба для остальных сайтов, находящихся на сервере.

При первой возможности контакта с владельцем атакуемого сайта необходимо выяснять всю возможную информацию, касающуюся причин атаки и требовать решения ситуации. Владелец сайта и только владелец сайта может предпринять какие-либо действия для того, чтобы атака не повторилась в будущем. Если DoS-атаки являются неотъемлемым свойством сайта, и его владелец отказывается что-либо менять, с таким пользователем, к сожалению, приходится расставаться, потому что подобные проекты не укладываются в рамки виртуального хостинга.

Если же владелец сайта проявляет адекватность и идёт на сотрудничество, то его первым действием в большинстве случаев должно быть обращение в профильные органы. Специалисты по безопасности рекомендуют обращаться в прокуратуру района, где совершается преступление, в данном случае по адресу расположения дата-центра, в который помещён сервер, обслуживающий сайт.

Таким образом, отражение DoS-атаки часто сводится к "борьбе с атакуемым", а не с тем, кто направляет атаку. Серьёзные DoS-атаки всё-таки стоят заказчику денег и не начинаются просто так. Только владелец атакуемого сайта несёт ответственность за появление атаки и очень важно явно и прямолинейно в деловом разговоре донести эту информацию до пользователя, причастного к проблеме.

Профилактика

О снижении ущерба от потенциальных атак в условиях виртуального хостинга можно заботиться заранее. Для этого необходимо выделять для каждого сервера несколько IP-адресов и равномерно распределять их между сайтами, находящимися на сервере. При таком разделении можно будет свести негативный эффект начинающейся атаки к блокированию только тех сайтов, которые находятся на атакуемом IP, остальные же сайты будут нормально работать.

Для снижения же вероятности появления атак на сайты, необходимо уделять должное внимание приходящим жалобам и своевременно на них реагировать. Контактная информация для подачи жалоб должна быть доступна на сайте и в описании блока адресов (запись abuse-mailbox), адрес должен быть действующим и регулярно проверяться.

Заключение

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

Операционная система представляет широкие возможности для ограничения ресурсов. Однако не все виды воздействия на систему можно ограничивать этими средствами. Для внедрения дополнительных способов контроля может потребоваться модификация существующего программного обеспечения, создание дополнительных модулей служб, либо написание собственных вспомогательных средств регулирования нагрузки.

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

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

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

Для отражения DoS-атак недостаточно только технических методов. Необходимо предпринимать всё для определения причины атаки: общаться с атакуемым, сотрудничать с отделом безопасности провайдера трафика, обращаться в профильные органы. Важно также по возможости устанавливать контакт с ведущим атаку либо просматривая жалобы, поступавшие за последнее время, либо размещая контактную и разъясняющую информацию на атакуемом сервере.

Использование накопленного опыта, о котором рассказано в этой статье, поможет повысить бесперебойность функционирования сервиса и более равномерно распределить имеющиеся возможности обслуживания между пользователями, уделяя должное внимание каждому клиенту.

Благодарности

Горячую благодарность за поддержку в процессе написания статьи хочется выразить Людмиле Крыловой (Lu), Владиславу Зитикису (sandsnake), Филу Кулину (schors), Андрею Грибкову, Антону Воршевскому (gbdj), Ольге Евелинской (Snowhite), Лёше Блышко (mr.nekt). Спасибо вам за ваши советы и комментарии.

Отдельное спасибо проекту Грамота.Ру.


[в начало >> | на страницу публикаций>>]