среда, 14 ноября 2012 г.

Ваш Squid может работать быстрее

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

Позволю себе не согласиться с этим доводом. 

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

Поясню свою позицию. Допустим, у вас в наличии локальная сеть. Из 10-100-1000-или более станций. С одной точкой доступа в интернет, скоростью, допустим, 1 Гбит (хотя это канал, скорее, провайдерского уровня. Но допустим). В локальной сети у вас 6я кабельная категория и сетевая активка 1 Гбит. Как вы думаете, какой процент одинаковых интернет-запросов у пользователей подобной сети - это раз, и два - насколько велик сейчас в относительном исчислении интернет-контент по отношению, скажем, к 1996му году?

Считать не надо. Просто прикиньте - ваши 1000 пользователей в 9 утра одновременно, придя на работу, ломятся в социалки. 1000 гигабит вливаются в канал 1 Гбит внешнего доступа. Как вы считаете, они вот так вот, без тормозов, попадут туда? Это помимо того факта, что фактически будут качаться 1000 копий одного и того же контента.

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

Как?

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

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

Давайте попробуем обобщить эти подходы.

Выбор оборудования

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

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

Второе. Диски важно иметь серверного класса - для надежности (мы говорим об интенсивной круглосуточной работе), и, вне зависимости от используемой платформы, собранными в зеркало - как по соображениям высокой отказоустойчивости, так и по соображениям скорости. 

Важно - про RAID, кроме 10, забудьте.

Третье. Операционная система может быть любой, при условии, что это не Windows.

По моему опыту, сервер 1RU с 4 процессорами/ядрами и 4 Гб памяти, 1-2 сетевыми интерфейсами 1 Гбит, с парой дисков, подключенных к двум контроллерам ввода-вывода (то есть способными работать одновременно) вполне удовлетворителен в качестве кэширующего прокси для сети до 1000 рабочих станций без сколько-нибудь выраженной перегрузки. Если мы говорим о Squid.

Настройки OS

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

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

Всегда помните о законах сохранения! - они действуют в IT! Процессы выполняются не на Великом Небесном Сервере, а здесь и сейчас - на ЭТОМ процессоре, размещаются в ЭТОЙ оперативной памяти, обращаются к ЭТОМУ диску.

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

Следует также позаботиться о достаточном количестве файловых дескрипторов для пользователя, от которого мы намерены исполнять наш прокси-сервер. В противном случае мы получим узкое место, созданное собственными руками. А также об адекватных настройках IPC, если таковые имеются в наличии на выбранной платформе. Конкретно - нас интересует достаточная степень параллелизма процессов на нашем сервере. Если допустимо использовать термин из РСУБД в такой области.

Конкретный пример в случае Solaris 10.

Параметры /etc/system:

set zfs:zfs_arc_max=1073741824 
set zfs:zfs_immediate_write_sz=1000000000 
set zfs:zvol_immediate_write_sz=1000000000 

set maxphys=1048576 

set rlim_fd_max=65536 
set rlim_fd_cur=4096 
set pidmax=65536 set 
max_nprocs=65536 
set maxuprc=32767

Обратите внимание на ограничение размеров ARC (Да, мы используем ZFS). Что касается параметров собственно ZFS для кэша, то они следующие:

root @ ktulhu / # zfs get all data/cache
NAME        PROPERTY              VALUE                  SOURCE
data/cache  type                  filesystem             -
data/cache  creation              Thu Aug 18 14:29 2011  -
data/cache  used                  22.5G                  -
data/cache  available             77.5G                  -
data/cache  referenced            37K                    -
data/cache  compressratio         1.00x                  -
data/cache  mounted               yes                    -
data/cache  quota                 100G                   local
data/cache  reservation           none                   default
data/cache  recordsize            4K                     local
data/cache  mountpoint            /data/cache            default
data/cache  sharenfs              off                    default
data/cache  checksum              on                     default
data/cache  compression           off                    default
data/cache  atime                 off                    inherited from data
data/cache  devices               on                     default
data/cache  exec                  off                    inherited from data
data/cache  setuid                off                    inherited from data
data/cache  readonly              off                    default
data/cache  zoned                 off                    default
data/cache  snapdir               hidden                 default
data/cache  aclinherit            restricted             default
data/cache  canmount              on                     default
data/cache  shareiscsi            off                    default
data/cache  xattr                 on                     default
data/cache  copies                1                      default
data/cache  version               5                      -
data/cache  utf8only              off                    -
data/cache  normalization         none                   -
data/cache  casesensitivity       sensitive              -
data/cache  vscan                 off                    default
data/cache  nbmand                off                    default
data/cache  sharesmb              off                    default
data/cache  refquota              none                   default
data/cache  refreservation        none                   default
data/cache  primarycache          all                    default
data/cache  secondarycache        none                   inherited from data
data/cache  usedbysnapshots       0                      -
data/cache  usedbydataset         37K                    -
data/cache  usedbychildren        22.5G                  -
data/cache  usedbyrefreservation  0                      -
data/cache  logbias               latency                inherited from data
data/cache  sync                  standard               default
data/cache  rstchown              on                     default

Обратите внимание на размер записи и параметры primarycache и secondarycache. Дабы не разжевывать подробно, отсылаю интересующихся к документации, а здесь лишь скажу - так надо.

Настройки Squid

Большинство настроек Squid, которыми ньюфаги обмениваются в интернете, выглядят так, будто их выкопали из криокамеры и взяли с машинки на Pentium-I с 512 мегабайтами оперативной памяти. 

Вместе с тем, поскольку оперативная память сейчас стоит практически как лопата дерьма, никто не мешает использовать ее более продуктивно, нежели пытаться кэшировать контент Squid средствами ОС, или, тем более, дисков.

Я не буду приводить полный конфиг продуктивного Squid, а выделю лишь ключевые параметры, непосредственно касающиеся производительности.

# -------------------------------------
# Store parameters
# -------------------------------------
# Uncomment and adjust the following to add a disk cache directory.

cache_dir diskd /data/cache/d1 16384 16 256 Q1=72 Q2=64
cache_dir diskd /data/cache/d2 16384 16 256 Q1=72 Q2=64
cache_dir diskd /data/cache/d3 16384 16 256 Q1=72 Q2=64
cache_dir diskd /data/cache/d4 16384 16 256 Q1=72 Q2=64

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

Следующая группа параметров относится к памяти. Поскольку на дворе не каменный век, а времена Web 1.0 с крошечными объектами безвозвратно канули в Лету, с учетом имеющейся в наличии оперативной памяти, установим следующие параметры прокси:

# ------------------------------------- 
# Memory parameters 
# ------------------------------------- 
cache_mem 512 Mb 
#memory_pools off 
memory_pools_limit 100 MB 
maximum_object_size 128 Mb 
maximum_object_size_in_memory 2 Mb 

Давайте не будем жлобиться и учтем вышесказанное.

Следующая группа параметров прямо влияет на производительность:

# ------------------------------------- 
# Tuning parameters 
# ------------------------------------- 
ipcache_size 8192 
ipcache_low 95 
ipcache_high 98 

fqdncache_size 8192 

buffered_logs on 

memory_replacement_policy heap GDSF 
cache_replacement_policy heap LFUDA 


cache_swap_high 98 
cache_swap_low 95 

negative_ttl 15 minutes 
positive_dns_ttl 15 hours 
negative_dns_ttl 15 minutes 

store_avg_object_size 64 KB 

pipeline_prefetch on 

shutdown_lifetime 1 second 

#offline_mode off 

# Turn it on till cache populated 
# Default is off 
ignore_ims_on_miss on 

zero_buffers off 

Прошу обратить внимание, что данные настройки целиком нацелены на  минимизацию накладных расходов, повышение коэффициента кэш-попаданий, устранение ненужных операций (zero_buffers) и задержек (shutdown_lifetime).

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

# Common captcha acl 
acl captcha urlpath_regex -i ^captcha 

# Media and video acls 
acl youtube dstdomain .youtube.com 
acl media rep_mime_type -i video/flv 
acl mediapr urlpath_regex \.flv(\?.*)?$ 
acl media rep_mime_type -i ^application/x-shockwave-flash$ 
acl mediapr urlpath_regex \.swf(\?.*)?$ 
# Real audio acls acl RealAudio_mime 
req_mime_type application/x-pncmd 
# The keyword for all youtube video files are "get_video?", "videodownload?" and "videoplayback" plus the id 
acl store_rewrite_list urlpath_regex \/(get_video\?|videodownload\?|videoplayback.*id) 

....

# Cache directives 
cache allow youtube all 
cache allow media all 
cache allow mediapr all 
cache allow POST RealAudio_mime all 
cache deny localapache all 
cache deny captcha all 

.... 

# ------------------------------------- 
# Content parameters 
# ------------------------------------- 
# Break HTTP standard for flash videos. Keep them in cache even if asked not to. refresh_pattern -i \.flv$ 10080 90% 999999 ignore-no-cache override-expire ignore-private 
# Apparently youtube.com use 'Range' requests 
# - not seen, but presumably when a video is stopped for a long while then resumed, (or fast-forwarded). 
# - convert range requests into a full-file request, so squid can cache it 
# NP: BUT slows down their _first_ load time. 
quick_abort_min -1 KB 

# Youtube's videos 
refresh_pattern (get_video\?|videoplayback\?|videodownload\?) 5259487 99999999% 5259487 override-expire ignore-reload ignore-private negative-ttl=0 

# Add any of your own refresh_pattern entries above these. 
refresh_pattern -i \.js(\?.*)?$ 43200 100% 43200 
refresh_pattern -i (/cgi-bin/|\?) 0 0% 0 
refresh_pattern . 43200 100% 43200 override-expire override-lastmod reload-into-ims ignore-no-cache ignore-private ignore-auth 

.... 

# ------------------------------------- 
# Rewriter parameters 
# ------------------------------------- 
# Storeurl settings 
storeurl_access allow store_rewrite_list 
storeurl_access deny all 
storeurl_rewrite_program /usr/local/squid/bin/storeurl.pl storeurl_rewrite_children 16 
storeurl_rewrite_concurrency 10 
# SquidGuard rewriter 
#url_rewrite_program /usr/local/bin/squidGuard -c /usr/local/squidGuard/squidGuard.conf 
# SquidClamAV rewriter 
url_rewrite_program /usr/local/bin/squidclamav 
url_rewrite_children 96

Мы сознательно нарушаем ряд стандартов HTTP для повышения коэффициента кэш-попаданий. А также используем функциональность storeurl для эффективного кэширования роликов Ютуба по самоочевидной причине (если мы договорились не блокировать их). Большинство вышеприведенных параметров не нуждаются в комментировании. Мы молча кэшируем на 1 месяц большую часть контента, включая JS (в том числе намеренно оформленные для обхода кэширования), переписываем динамические ссылки Ютуба в статические и сохраняем их в кэше.

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

Заключение

Рассмотренные в статье подходы позволили добиться весьма эффективной работы кэширующего прокси с минимальной латентностью. Фактически пользователи субъективно не ощущают присутствия кэша. Объективные показатели следующие. Для сети из 50 высокоактивных пользователей, при скорости внешнего канала 2 Мбит, и внутренней сети 1 Гбит и прогретом кэше, максимальная латентность полного соединения не превышает 0,1-0,2 сек. Собственная латентность кэша составляет 0,02-0,05 сек. Коэффициент кэш-попаданий колеблется в диапазоне 48-64%.