Bog BOS: Squid (кеширующий прокси для http): установка, настройка и использование

Назначение

Представляет собой HTTP, FTP, gopher, SSL и WAIS proxy, кеширующий запросы. Также кеширует DNS. Один процесс на всех, неблокированный ввод/вывод, держит часто используемые объекты в виртуальной памяти. Бесплатен (GPL) в исходниках. Поддерживает иерархию серверов (ICP/UDP, HTCP/TCP, multicast). Откуда брать объект определяется так (в упрощенном виде): послать ICP/HTCP/multicast запросы ко всем подходящим соседям; подождать определенное время; загрузить с первого соседа, пославшего HIT; иначе загрузить с первого отца, ответившего MISS; иначе загрузить с первоисточника.

Делается различие между частными и общими объектами. Только общие объекты кешируются. Только метод GET дает общие объекты. Объекты, попавшие в стоп-лист есть частные объекты. Если запрос содержит аутентификационную информацию или ответ типа "401 Unauthorized" - это частный объект. Подробнее здесь.

cache digest - очень компактная форма представления какие объекты имеются в кеше. Кеши могут обмениваться этой информацией с соседями, чтобы избежать необходимости делать ICP-запросы. В качестве ключей объектов используется MD5.

Текущая стабильная версия: Squid 2.3-STABLE4 (18 июля 2000; отличия 2.3 от 2.2). Готовится к выходу 2.4 (отличия 2.4 от 2.3).

Установка

Полезные ключи ./configure (--help)

Ключи запуска squid

Формат squid.conf

Управление squid (Cache Manager)

Настройка прав доступа в squid.conf:

Управление squid с помощью программы client: /usr/local/squid/bin/client cache_object://localhost/команда

Управление squid с помощью cachemgr.cgi (лично я не пробовал и другим не советую):

Сколько надо оперативной памяти

Требования к памяти:

Причем большая часть используемой памяти не должна свопироваться (иначе работает безумно медленно). Например, при кеше в 7 ГБ (800 тысяч объектов) требуется 128 МБ оперативной памяти. При восстановлении оглавления кеша (после аварийной перезагрузки) требуется удвоенный объем.

Как уменьшить потребности:

Алгоритмы замещения кеша (LRU, GDSF, LFUDA)

Squid поддерживает размер кеша между low и high, регулярно запуская процедуру удаления объектов (чем ближе мы к high, тем агрессивнее очистка). Вместо удаления можно использовать очистку файлов (truncate) пока хватает inode. Удаление производится асинхронно внешней программой unlinkd. Если объект тянется в данный момент, то он не удаляется. Если объект "отрицательно кеширован", то он удаляется. Если объект частный, то он удаляется. Алгоритмы замешения:

Сравнение алгоритмов проводится в

Какие объекты кешируются

Кешируемость объектов в зависимости от кода возврата HTTP:

Caching  Code
        Successful 2xx
c        200 OK
         201 Created
         202 Accepted
c        203 Non-Authoriative Information *
E        204 No Content
         205 Reset Content *
?        206 Partial Content *
        Redirection 3xx
C        300 Multiple Choices
C        301 Moved Permanently
t        302 Moved Temporarily
-        303 See Other *
-        304 Not Modified
E        305 Use Proxy (proxy redirect) *
        Client Error 4xx
E        400 Bad Request
-        401 Unauthorized
         402 Payment Required *
E        403 Forbidden
E        404 Not Found
E        405 Method Not Allowed *
         406 Not Acceptable *
-        407 Proxy Authentication Required *
         408 Request Timeout *
         409 Confict *
C        410 Gone *
         411 Length Required *
         412 Precondition Failed *
         413 Request Entity To Large *
E        414 Request-URI Too Long *
         415 Unsupported Media Type
        Server Error 5xx
E        500 Internal Server Error
E        501 Not Implemented
E        502 Bad Gateway
E        503 Service Unavailable
E        504 Gateway Timeout *
         505 HTTP Version Not Supported *

Notes:
* HTTP 1.1
c Cached unless a query response without expiry information
C Cached
E Negatively cached if no expiry headers
t Cached only if expiry information
- Not cached
Unless other said, the response code is not cached.

Кешируемость в зависимости от заголовков:

Справедливый дележ канала (delay pool)

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

Определены 3 класса пулов:

  1. одно ведро на всех из этого класса
  2. одно общее ведро и 255 отдельных для каждого хоста из C-сетки
  3. 255 ведер для каждой сетки (класс B) и отдельное для каждого хоста

Пример конфигурации:

delay_pools 3     # 3 delay pools
delay_class 1 1   # pool 1 is class 1
delay_class 2 1   # pool 2 is class 1
delay_class 3 3   # pool 3 is class 3
delay_access 1 allow staff
delay_access 1 deny all
delay_access 2 allow students
delay_access 2 deny all
delay_access 3 allow college
delay_access 3 deny all
delay_parameters 1 640000/640000
delay_parameters 2 64000/64000
delay_parameters 3 64000/64000 32000/64000 6400/32000
                   # total_rest/total_max net_rest/net_max ind_rest/ind_max
где
  total - на всех
  net - на подсеть
  ind - на отдельный адрес
  rest - скорость заполнения (байт/сек)
  max - объем ведра (байт)

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

acl clients src адреса
delay_pools 1
delay_class 1 2
delay_access 1 allow clients
delay_access 1 deny all
delay_parameters 1 -1/-1 8000/4000

Прижать любителей MP3:

acl multimedia urlpath_regex -i \.mp3$ \.mpeg$ \.avi$ \.mov$
delay_pools 1
delay_class 1 1
delay_access 1 allow multimedia
delay_access 1 deny all
delay_parameters 1 16000/64000

quick abort д.б. установлен маленьким чтобы объект не качался на полной скорости после отпадения клиента (нет клиента для запроса - нет ограничений).

Squid как transparent proxy (только http)

В процессе участвуют маршрутизатор, Linux и Squid:

  1. http-пакеты, бегущие мимо, должн отлавливаться маршрутизаторо переадресовываться на proxy-сервер. Как понять, что это http-пакет? Будем считать, что все пакеты, направленные на tcp/80, являются запросами на HTTP. Возникал вопрос: а может кешировать и другие порты? После обработки статистики (большой объем - 422MB, в ситуации с добровольным использованием proxy) выяснилось, что всего обращений было 3120260, из них HTTP - 92%. Из них на нестандартные порты - 2.3% (3128 - 0.05%, 8000 - 0.09%, 8001 - 0.13%, 8080 - 1.06%, 8081 - 0.15%, 8100 - 0.11%, 8101 - 0.46%). Я не вижу смысла бороться за 1%, разве что найду готовое решение ;)
    • Proxy-сервер уже стоит на пути этих пакетов (например, squid установлен на маршрутизаторе или firewall).
    • Cisco отлавливает такие пакеты и с помощью route-map перенаправляет их на proxy-сервер.
  2. ОС proxy-сервера должна принимать эти пакеты, как родные, и перенаправлять к squid
    • linux 2.2.
      1. конфигурация ядра:
        CONFIG_EXPERIMENTAL=y # включен и так
        CONFIG_NET=y # само собой
        CONFIG_FIREWALL=y # чтобы иметь возможность тонко управлять маршрутизацией (несовместим с FAST_SWITCHING)
        # CONFIG_IP_ALIAS is not set # у меня включено и вроде работает (при нехватке ОП зависает драйвер eth0, если ALIAS выключить, то прикладная программа)
        CONFIG_INET=y # включен и так
        # CONFIG_IP_MULTICAST is not set # а почему нельзя?
        CONFIG_IP_FIREWALL=y # чтобы иметь возможность тонко управлять IP-маршрутизацией
        CONFIG_IP_MASQUERADE=y # а необходимо ли это для transparent proxy?
        CONFIG_IP_TRANSPARENT_PROXY=y
        CONFIG_IP_ROUTER=y # необязательно, но может ускорить обработку пакетов
      2. при загрузке включать ip_forward, ip_always_defrag (вставить в загрузочные скрипты: /etc/rc.d/init.d/network)
        echo 1 > /proc/sys/net/ipv4/ip_forward # ядро д.б. сконфигурировано поддержкой /proc и sysctl)
        echo 1 > /proc/sys/net/ipv4/ip_always_defrag # в ядре 2.2.12 указывается во время конфигурации
      3. ipchains (ipfwadm в ядре 2.2 работать не будет)
        • ipchains -A input -p TCP -d 127.0.0.1/32 www -j ACCEPT
        • ipchains -A input -p TCP -d адрес-хоста/32 www -j ACCEPT
        • ipchains -A input -p TCP -d 0/0 www -j REDIRECT 3128
        • дополнительные правила для firewall и др.
  3. squid д.б. скомпилирован так, чтобы он принимал эти пакеты, как родные.
    • linux 2.2 и squid 2.2. Происходит само собой.
  4. squid д.б. сконфигурирован так, чтобы обрабатывать эти пакеты соответствующим образом.
    • http_port 3128 # redirect д.б. на этот порт
    • httpd_accel_host virtual # имитирует акселератор http-сервера, но не совсе нормально
    • httpd_accel_port 80 # а если перенаправляли не только 80 порт?
    • httpd_accel_with_proxy on # иначе при работе в режиме акселератора отключается кеширование
    • httpd_accel_uses_host_header on # иначе виртуальные домены будут неправильно кешироваться (см. замечание про безопасность)

Проблема. дырка в firewall: запрос приходит на прокси-сервер, который от своего имени (уже изнутри) лезет куда не надо.

Ссылки:

Обработка статистики

В комплекте поставки идут access-extract.pl, (увеличено MaxEntries до ...)  получающая на стандартный ввод журнал access.log выдающая на стандартный выво промежуточный результат,  и access-summary.pl (убрал выдачу ICP, которой у меня нет, за счет чего увеличил ширину колонки с именами хостов; в xsort изменил сортировку с COUNT на BYTES), делающая из него красивый отчет. Внутри используется squid-logs.pl. Его надо расширить новыми суффиксами имен файлов (bmp - Image; rm, mid, mp3 - Audio; pl, cgi, shtml, php, php3, phtml, asp, dll - Dynamic; rpm, cab, avc - Bundle; css - HTML; koi - Text; js - Software). За день получается 60МБ в access.log, поэтому у меня не хватает терпения дождаться результатов работы других процедур из комплекта.

Формат access.log (запись делается, когда клиент закрывает socket; для наших 300 тысяч запросов в день получается 60 MB в день!):

  1. timestamp (unix time in ms)
  2. elapsed (ms)
  3. client IP address
  4. type/HTTP reply code, где type:
    • TCP_HIT (верная копия объекта нашлась в кеше)
    • TCP_MEM_HIT
    • TCP_NEGATIVE_HIT
    • TCP_MISS (запрашиваемый объект не был в кеше)
    • TCP_EXPIRED (объект был в кеше, но старый)
    • TCP_CLIENT_REFRESH (клиент запросил reload - no-cache pragma)
    • TCP_REFRESH_HIT (объект в кеше был старым, был сделан IMS-запрос к источнику и тот вернул "304 Not Modified")
    • TCP_REFRESH_MISS (объект в кеше был старым, был сделан IMS-запрос к источнику и тот вернул обновленное содержание)
    • TCP_IMS_HIT (клиент выдал IMS-запрос, объект оказался в кеше и свежим)
    • TCP_IMS_MISS (клиент выдал IMS-запрос для просроченного объекта)
    • TCP_REF_FAIL_HIT (объект в кеше староват, но запросить новую копию не удалось)
    • TCP_SWAPFAIL (объект д.б. в кеше, но не смогли извлечь)
    • TCP_DENIED
    • UDP_...
    • ERR_CLIENT_ABORT
    • ERR_NO_CLIENTS
    • ERR_READ_ERROR
    • ERR_CONNECT_FAIL
    • ERR_...
  5. size (bytes to client)
  6. method (GET, POST, ...)
  7. URL
  8. ident ("-", если недоступен)
  9. hierarhy data/Hostname
    • DEAD_NEIGHTBOR
    • DEAD_PARENT
    • LOCAL_IP_DIRECT
    • FIRST_PARENT_MISS
    • FIRST_UP_PARENT
    • PARENT_HIT (UDP-запрос к parent вернулся с подтверждением)
    • SINGLE_PARENT
    • PARENT_UDP_HIT_OBJECT (объект оказался у parent и поместился в UDP-ответе)
    • DIRECT (объект был запрошен с оригинального сервера)
    • ...
  10. тип содержимого (MIME тип/подтип)

Формат store.log:

  1. time (unix format with ms)
  2. action
    • RELEASE (удален из кеша)
    • SWAPOUT (сохранен на диск)
    • SWAPIN (был на диске, загружен в память)
  3. HTTP reply code
  4. HTTP Date: reply header
  5. HTTP Last-Modified: reply header
  6. HTTP Expires: reply header
  7. HTTP Content-Type: reply header
  8. HTTP Content-Length: reply header
  9. реально полученное число байт (если не совпадает с предыдущим числом, объект не хранится)
  10. HTTP метод (GET, POST, ...)
  11. ключ доступа (обычно URL, частные объекты еще и последовательный номер и метод)

Еще можно собирать useragent.log, все HTTP-заголовки и отладочную информацию. Я собираю только access.log и этого очень много.

Время в журналах записывается в Unix-формате (число милисекунд с 1 января 1970 года), что неудобно. Преобразовать в человеческий формат можно, например, с помощью gawk: awk '{print strftime("%Y%m%d%H%M%S",$1), $2, $3, $4, $5, $6, $7, $8, $9, $10, $11}' (printlog.sh) или perl: s/^\d+\.\d+/localtime $&/e;

Частота обращений к объектам извлекается из access.log с помощью (url_freq.sh)
awk '{print $7}' | sort | awk -f /usr/local/bin/count.awk | sort -nr +1
или без учета anchor и query-частей URL (url_freq_noq.sh)
awk '{print $7}' | awk -F'?' '{print $1}' | awk -F';' '{print $1}' | awk -F'#' '{print $1}' | sort | awk -f /usr/local/bin/count.awk | sort -rn +1

redirector, как средство оптимизации

redirector - внешняя программа (небуфферизованный в/в), в цикле читает с stdin URL и пишет на stdout преобразованный URL (или пустую строку, если нет преобразований). Преобразование происходит после проверки ACL, но до проверки на присутствие в кеше. Формат входной строки (поля, разделенные пробелами):

  1. URL
  2. ip-address/fqdn (если fqdn нет, то "-")
  3. ident ("-", если нет)
  4. method (GET, POST, ...)

В качестве примера использования редиректора для уменьшения трафика в поставке приводится редиректор, производящий нормализацию URL (приведение к стандартному виду), что увеличивает вероятность попадания в кеш. А также редиректор преобразующий запросы на загрузку всяких там IE, Netscape и пр. с различных зеркал в запросы к локальному www-серверу (автор клянется, что это дает 15%). См. также про борьбу с баннерами.

Примеры redirectors:

Борьба с баннерами

Только не будем спорить о морали :) Но и в transparent режиме этого делать не надо. Поставьте себе локальный прокси и радуйтесь. Кстати, теми же методами можно бороться со счетчиками и порно.

Превращение банеров-картинок в рваные прямоугольники:

  1. Насобирать "вредителей" (в виде регулярных выражений)
  2. Заводим файлы в /usr/local/squid/etc:
    • banners_path_regex - по одному регулярному выражению на строку
    • banners_regex - по одному регулярному выражению на строку
    • banners_exclusion (это строки, трактуемые в предыдущих файлах как баннеры, но их лучше не трогать - JavaScript всякие)
  3. В squid.conf
    acl banners_path_regex urlpath_regex "/usr/local/squid/etc/banners_path_regex"
    acl banners_regex url_regex "/usr/local/squid/etc/banners_regex"
    acl banners_exclusion url_regex "/usr/local/squid/etc/banners_exclusion"
    http_access deny banners_path_regex !banners_exclusion
    http_access deny banners_regex !banners_exclusion
    

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

  1. Насобирать "вредителей" (в виде регулярных выражений)
  2. На своем http-сервере завести "заменитель" рекламных картинок void.gif
  3. Настраиваем redirector в squid.conf (если он уже используется, то добавить к старому; м.б. еще настроить redirector_access):
    redirect_program /usr/local/squid/bin/banners.pl
  4. banners.pl (perl выбран для простоты демонстрации):
       #!/usr/bin/perl (или где perl живет)
       $|=1;
       while (<>) {
       [email protected]регулярное-выражение@http://www.nospam.org/[email protected];
       print;}
    

К сожалению, использование "http_access deny" для блокировки JavaScript программ не получается (броузер не показывает страницу совсем). Поэтому приходится заменять реальный скрипт через redirect (см. выше) на что-нибудь безобидное (и каждый случай обрабатывать отдельно :(. Например, если исходный скрипт открывал окно с рекламой (<script src=...>), то вместо него подсовываем

   <html><head>
   <script language="JavaScript"><!-- window.close(); //--></script>
   </head><body></body></html>

вместо tx3 (если он встроен страницу с помощью SSI, то не поможет, да и зачем тогда его резать?):

   document.write("&nbsp;");

Ссылки:

Приемы конфигурации

Простейшие случаи иерархии. Основной squid с консервативными настройками и вспомогательный с агрессивными настройками (антибаннер ;).

Консервативный: Агрессивный:

Закрыть доступ к какой-то информации для клиентов

badobjects - это произвольное имя ACL; ERR_ACCESS_DENIED - имя файла в /usr/lib/squid/errors/English или в подобном месте, вместо этого файла можно поставить для каждой ситуации свой файл; воздействие http_access зависит от места, в которое его поставить; если squid получает от parent TCP_DENIED, то он лезет напрямую:

Шаблон для борьбы с RealPlayer

http://[^/]+/SmpDsBhgRl

Как удалить объект из кеша

В squid.conf:
acl PURGE method purge
acl localhost src 127.0.0.1
http_access allow purge localhost
http_access deny purge
Теперь для каждого удаляемого объекта: client -m PURGE URI

Использование дополнительного канала для доступа в Интернет

Имеем маршрутизатор (Cisco) с двумя каналами доступа в Интернет через двух различных провайдеров. Клиентские компьютеры расположены в адресном пространстве одного из них, а хочется нагрузить оба канала на прием. Создаем файл as.list, содержащий список AS ,к которым мы хотим обращаться через второй канал. Конфигурируем squid:

Конфигурируем вспомогательный squid-сервер (мижно даже на том же хосте - специально выбран нестандартный порт; только прямого доступа к нему давать не надо):

Sergey E. Bogomolov