Пользователь
0,0
рейтинг
29 октября 2013 в 13:38

Администрирование → Девятилетняя оптимизация маршрутизатора из песочницы

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

Сеть

Сама сеть существует с 1997г — это дата, когда все общежития были объединены в единую сеть и получили доступ в Интернет. До 2004 года кампусная сеть была построена полностью на меди, между общежитиями линки были проброшены кабелем П270 (благо, расстояние между общежитиями не превышало 350м, и линк, при использовании карт 3c905 поднимался «на сотке»). В каждом здании стоял свой сервер, в котором стояло по 3 сетевых карты. Две из них «смотрели» на соседние сервера, к третьей подключалась «локалка» общежития. Итого, все шесть (а столько было общаг в нашем университете) были замкнуты в кольцо, а маршруты между ними строились по протоколу OSPF, что позволяло, при обрыве линии запустить трафик в обход выпавшего звена. А обрывы случались часто: то гроза разразится и линк сляжет, то электрики нахимичат. Да и обслуживать сами сервера было не очень удобно, тем более они все были разношерстные: в разные годы от 486DX4 (да-да, с 8Мб ОЗУ, на ядре 2.0.36 и картах 3com она тянула два линка 100Мбит, правда загрузка была в потолок на голом роутинге без всяких ipfwadm) до AMD K6-2 и даже P4 2.8Ghz.
Минусы такой организации (помимо уж очень ненадежного по сегодняшним меркам линка) налицо: уж очень неудобно управлять пользовательской базой. Адреса белые, их количество ограничено. Договор привязывался к IP «пожизненно», то есть на весь срок обучения студента. Сеть нарезана на сегменты, размеры которых изначально были сделаны с учетом численности студентов в каждом общежитии. Но то студенты переезжают из общаги в общагу, то факультету Автоматики понадобилось заметно больше адресов, чем филологам — в общем жуть.
Ограничения доступа были по паре MAC-IP, ибо в времена «зачатия» сети сменить MAC-адрес сетевой карты, не имея под рукой программатора (а то и паяльника) было очень проблематично, а то и невозможно. Поэтому было достаточно держать актуальным файлик /etc/ethers и спасаться от 99% любителей халявы. О управляемых коммутаторах в те времена только мечтали, и уж точно ставить их в качестве абонентского оборудования было не по карману (так как сеть развивалась 100% на деньги самих студентов, а студенты, как известно, народ небогатый)

Звезда

В 2004 году подвернулась хорошая возможность: один из городских провайдеров предложил в обмен на пиринг между своей сетью и сетью кампуса безвозмездно соединить все здания по оптике. Ну как соединить — непосредственно монтажом оптики занималась инициативная группа студентов, а технари провайдера ее только разварили. В итоге, с использованием этой оптики удалось построить не кольцо, а звезду!
И тут родилась идея — поставить один хороший сервер, с несколькими гигабитными сетевыми картами, связать все линки в один bridge и сделать одну плоскую сеть, что позволило бы избавиться от головной боли с нарезанным на подсети адресным пространством, а так же позволило управлять доступом из одного места.
Так как шина PCI не смогла бы прокачать такой трафик, да и требуемых 6-8 гигабитных портов нельзя было получить за отсутствием такого количества PCI-разъемов на материнских платах, то было решено брать 2x Intel Quad Port Server Adapter на шине PCI-X 133Mhz. К таким сетевкам пришлось взять материнскую плату Supermicro X6DHE-XG2 по причине наличия аж трех PCI-X 133, ну и процессоры на нее, Xeon 3Ghz 2 шт (это те, что можно найти на ark.intel.com в разделе Legacy Xeon)
И понеслось: на сервер устанавливается RHELAS 2.1, заводится bridge, сеть склеивается в одну большую /22. И тут выясняется, что если ограничивать доступ паре сотен адресов с помощью правил типа:
iptables -A FORWARD -s a.b.c.d -j REJECT
то загрузка на сервере подпрыгивает до неприличных значений. Сервер не справляется?

Оптимизация 1

Поиск в Интернете подсказывает только появившийся тогда проект — ipset. Да, оказалось что это именно то, что нужно. В дополнение к тому, что можно было избавиться от большого количества однотипных записей в iptables, появилась возможность сделать привязку IP-MAC с использованием macipmap.
Одной из особенностей бриджа было то, что пакет, проходящий через бридж в каких-то случаях попадал в цепочку FORWARD, а в каких-то — нет. Оказалось, что в FORWARD попадают «маршрутизирующиеся» между интерфейсами пакеты, а «бриджующиеся» (то есть, входящие в br0 тут же и выходящие из br0) — не попадают.
Решением стало использование таблицы mangle вместо filter.
Так же получилось сделать привязку конкретного адреса не только к MAC, но и к общежитию, в котором проживал пользователь сети. Сделано было с использованием модуля iptables physdev и выглядело примерно так:
iptables -t mangle -A PREROUTING -m physdev --physdev-in eth1 -m set --set IPMAC_H1 src -j ACCEPT
iptables -t mangle -A PREROUTING -m physdev --physdev-in eth2 -m set --set IPMAC_H2 src -j ACCEPT
...
iptables -t mangle -A PREROUTING -i br0 -j DROP

Так как оптическая «звезда» была построена с помощью оптоконвертеров, то на каждое здание «смотрела» своя сетевая карта. И нужно было добавить всего лишь в сет IPMAC_H1 пары MAC-IP пользователей первого общежития, в сет IPMAC_H2 — второго общежития и так далее.
Порядок самих правил внутри iptables попытался сделать таким, чтобы выше были те правила, описывающие общежития, где пользователи более активные, что позволило пакетам быстрее проходить цепочки.

Оптимизация 2

Так как в итоге весь межобщажный и внешний трафик в итоге стал проходить через сервер то появилась идея в случае, если абонент отключен, или в случае несовпадения пары IP-MAC — отображать пользователю некую страничку с информацией, в которой бы разъяснялось, почему, собственно, не работает сеть. Показалось, что это не сложно. Нужно было вместо DROP пакетов, идущих на 80-й порт, сделать MARK пакета, а потом перенаправить помеченные пакеты с помощью DNAT на локальный веб-сервер.
Первая проблема оказалась в том, что если просто редиректить пакеты на вебсервер — вебсервер в 99% случаях отвечает, что страница не найдена. Потому как, если пользователь шел на ark.intel.com/products/27100, а вы завернули его на свой веб сервер, то маловероятно, что там будет страница products/27100, и вы в лучшем случае получите ошибку 404. Поэтому был написан простенький демон на С, который на любой запрос выдавал Location: myserverru
В последствии этот костыль был заменен на более красивое решение с mod_rewrite.
Вторая, и наиболее существенная, проблема была в том, что как только в ядро был загружен модуль nat, то нагрузка опять подпрыгнула. Виновата конечно же таблица conntrack, а при таком количестве соединений и pps существующее железо не вывозило в часы максимальной нагрузки.
Сервер не справляется?
Начинаем думать. Поставленная цель довольно интересна, но на существующем железе не работает. Использование -t raw -j NOTRACK помогало, но не сильно. Решение была найдено такое: NAT-ить пакеты не на центральном маршрутизаторе, а на одной из старых машинок, которые еще оставались и использовались для различных сервисов типа p2p-сервера, игрового сервера, jabber-сервера, а то и просто стоящих без дела. В случае всплеска нагрузки на этом сервере в худшем случае абонент бы не получил сообщение в окне браузера, что он отключен (или что его IP не соответствует зарегистрированному MAC), и это не повлияло бы на работу остальных пользователей сети. А для того, чтобы доставить трафик пользователя на этот сервер с NAT использовалась команда:
iptables -t mangle -A POSTROUTING -p tcp --dport 80 -j ROUTE --gw a.b.c.d
которая просто подменяла адрес шлюза и отправляла пакет дальше в обход остальных цепочек.
Вообще, очень удобно было посылать «неугодные» пакеты таким образом на обработку на сторонний сервер, не заботясь о прохождении этим пакетом остальных цепочек типа filter, но с изменением архитектуры ядра этот патч из patch-o-matic стал не поддерживаемым.
Решение: маркировать нужные пакеты маркой 0x1, потом, с помощью ip rule fw посылать пакет в «другую» таблицу маршрутизации, где единственный маршрут — это наш сервер с NAT
iptables -t mangle -A PREROUTING -p tcp --dport 80 -j MARK --set-mark 0x1
ip route flush table 100
ip route add via a.b.c.d table 100
ip rule add fwmark 0x1 lookup 100

В итоге «хороший» трафик пропускался, а «плохим» пользователям показывалась страничка с информацией о блокировке. А так же, в случае несовпадения IP-MAC пользователь мог введя логин/пароль провести перепревязку на свой текущий МАС.

Оптимизация 3

Действие происходит во времена помегабайтного трафика, в общежитии. То есть в безденежной, интернетоактивной и IT-продвинутой среде пользователей. Значит простой привязки IP-MAC уже недостаточно, а случаи хищения интернет-трафика становятся повсеместными.
Единственный вменяемый по затратам вариант — vpn. Но, учитывая то, что к тому времени у сети кампуса появился бесплатный пиринг с полудюжиной городских операторов, гонять пиринговый трафик через vpn-сервер — не получится, он просто не вывезет. Конечно был возможен получивший распространение метод: в интернет — через vpn, в пиринг и локалку — батник с маршрутами. Но мне батник казался уж очень некрасивым решением. Рассматривался вариант с RIPv2, который на тот момент был «встроен» в большинство используемых ОС, но там оставался открытый вопрос с подлинностью анонсов. Без дополнительной настройки кто угодно мог рассылать маршруты, а в популярной тогда WindowXP и ее «Слушателе RIP» вообще никакой настройки не было.
Тогда был «придуман» «ассиметричный VPN». Клиент для выхода в интернет устанавливает обычное vpn-pptp соединение к серверу с логином/паролем, при этом снимая в настройках галочку «Использовать шлюз в удаленной сети». На клиентский конец тоннеля выдавался адрес 192.0.2.2, причем всем клиентам одинаковый, и как будет показано далее, вообще не имевший никакого значения.
На стороне VPN-сервера был модифицирован скрипт /etc/ppp/ip-up, выполняемый после аутентификации и поднятия интерфейса
PATH=/sbin:/usr/sbin:/bin:/usr/bin
export PATH

LOGDEVICE=$6
REALDEVICE=$1

[ -f /etc/sysconfig/network-scripts/ifcfg-${LOGDEVICE} ] && /etc/sysconfig/network-scripts/ifup-post  ifcfg-${LOGDEVICE}

[ -x /etc/ppp/ip-up.local ] && /etc/ppp/ip-up.local "$@"

PEERIP=`/usr/local/bin/getip.pl $PEERNAME`

if [ $LOGDEVICE == $PEERIP ] ; then
    ip ro del $PEERIP table vpn > /dev/null 2>/dev/null&
    ip ro add $PEERIP dev $IFNAME table 101
else
    ifconfig $IFNAME down
    kill $PPPD_PID
fi
exit 0


То есть в переменную PEERIP из базы скриптом выдергивался IP-адрес, который должен быть у пользователя с PEERNAME (логином, под которым он подключился), и если этот адрес совпадает с IP, с которого произошло подключение (LOGDEVICE) к VPN-серверу, то весь трафик до этого IP маршрутизируется в интерфейс IFNAME посредством таблицы 101. Так же в таблице 101 шлюзом по умолчанию установлен 127.0.0.1
Весь маршрутизируемый трафик заворачивается в таблицу 101 правилом
ip ru add iif eth0 lookup 101
В итоге получаем, что трафик, пришедший на vpn-сервер, и следующий НЕ на vpn сервер (тот пойдет в таблицу local, которая есть по умолчанию) уходит в таблицу 101. А там «разбредется» по ppp-тоннелям. А если не найдет нужного, то просто дропнется.
Пример того, что получается в итоге в табличке 101 (ip r sh ta 101)
[root@vpn ~]# ip route show table 101
a.b.c.d dev ppp2  scope link
a.b.c.e dev ppp6  scope link
a.b.c.f dev ppp1  scope link
default via 127.0.0.1 dev lo


Теперь всего лишь остается завернуть на центральном маршрутизаторе весь трафик с «интернетного» интерфейса на vpn-шлюз, и без подключения к VPN интернета у пользователей не будет. Причем остальной трафик (пиринговый) будет бегать IPoE (то есть «обычным» способом), и не будет нагружать VPN-сервер. При появлении дополнительных пиринговых сетей пользователю не придется править никакие bat-файлы. Опять же, доступ до некоторых ресурсов внутренних, хоть IP, хоть портов, можно сделать через VPN, достаточно завернуть пакет на VPN-сервер.
При использовании данной методики злоумышленник конечно может путем подмены IP-MAC послать в интернет трафик, но не может получить ничего обратно, так как не поднят vpn-тоннель. Что практически напрочь убивает смысл подмены — «посидеть в интернете» с чужого IP теперь нельзя.
Для того, чтобы клиентские компьютеры могли получать пакеты через vpn-тоннель, нужно было в Windows выставить в реестре ключ IPEnableRouter=1, а в linux — rp_filter=0. Иначе ОС не принимали ответы не с тех интерфейсов, куда отправляли запросы.
Затраты на реализацию почти нулевые, на ~700 одновременных подключений к vpn хаватло сервера уровня celeron 2Ghz, так как интернет трафик внутри ppp во времена помегабайтных тарифов был не очень большой. При этом пиринговый трафик бегал на скоростях до 6Гбит/с суммарно (через Xeon на S604)

Работает

Проработало все это чудо лет 8. Году в 2006 RHELAS 2.1 был заменен на свежевышедший CentOS 4. Центральные коммутаторы в зданиях поменяны на DES-3028, абонентскими остались DES-1024. Управление доступом на DES-3028 сделать не получалось толком. Для того, чтобы сделать привязку ip-mac к порту с использовании ACL не хватало 256 записей, ведь в некоторых общежитиях было более 300 компьютеров. Менять оборудование стало проблемой, так как университет «легализовал» сеть, и теперь за нее нужно было платить в кассу университета, при обратно денег на оборудование не выделялось, а если и выделялось, то очень скупо, через год и через конкурс (когда покупают не то что нужно, а то, что дешевле, или где откат больше).

Сервер сломался

И тут сервер сломался. Вернее сгорела материнская плата (по заключению из мастерской — умер северный мост). Нужно собрать что-то на замену, денег нет. То есть неплохо бы бесплатно. И чтобы можно было вставить сетевки PCI-X. Благо знакомый отдавал списанный из банка сервер, в нем как раз было пару слотов PCI-X 133. Но материнка однопроцессорная, и в ней не Xeon, а Socket 478 Pentium 4 3Ghz
Перекидываем винты, сетевые карты. Стартуем — вроде работает.
Но softirq «съедает» 90% в сумме от двух псевдоядер (в процессоре одно ядро, и включен гипертрединг), пинг подскакивает до 3000, даже консоль «тупит» до невозможности.
Стало плохо
Казалось бы вот оно, сервер устарел, пора на покой.

Оптимизация 4

Вооружаюсь oprofile, начинаю «выкашивать лишнее». Вообще oprofile в процессе «общения» с этим сервером пользовался довольно часто, и не раз он выручал. К примеру, даже используя ipset стараюсь пользоваться ipmap, а не iphash (если это возможно), так как с oprofile действительно видно НАСКОЛЬКО разительна разница в производительности. По моим данным получилось на два порядка, то есть плавало от 200 до 400 раз. Так же при подсчете трафика в разное время переходил с ipcad-ulog на ipcad-pcap, и далее на nflow, ориентируясь на профайлинг. ipt_NETFLOW уже не использовал, так вошли в век «безлимитного интернета», а пишет там наш провайдер верхнего уровня netflow для СОРМ или нет — его проблемы. Собственно, используя oprofile выявил, что ip_conntrack был главным пожирателем ресурсов при включении nat.
В общем oprofile в этот раз говорит мне, что 60% циклов процессора занимает модуль ядра e1000 (сетевой карты). Ну что с ним делать? Рекомендованные в e1000.txt
options e1000 RxDescriptors=4096,4096,4096,4096,4096,4096,4096,4096,4096,4096 TxDescriptors=4096,4096,4096,4096,4096,4096,4096,4096,4096,4096 InterruptThrottleRate=3000,3000,3000,3000,3000,3000,3000,3000,3000,3000
вписаны еще в 2005 году.
Беглый просмотр git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git на предмет сколько нибудь значимых изменений в e1000 не дал результатов (то есть изменения конечно есть, но либо исправление ошибок, либо расстановка пробелов в коде). На всякий случай ядро все-таки обновил, но результатов это не дало.
В ядре так же стоит CONFIG_HZ_100=y, при большем значении результаты еще хуже.
Еще Oprofile заявляет, что довольно большую долю циклов занимает модуль bridge. И, казалось бы, без него уже никуда, так как IP-адреса размазаны в беспорядке по нескольким зданиям, и снова разбить их на сегменты уже не возможно (вариант — объединить все в один сегмент без сервера не рассматривается, так как теряется управление)
Подумываю «разбить» бридж, и использовать proxy_arp. Тем более давно хотелось это сделать, после обнаружения баги в DES-3028 с flood_fdb. В принципе есть возможность загрузить все адреса в таблицу маршрутизации в виде:
ip route add a.b.c1.d1 dev eth1 src 1.2.3.4
ip route add a.b.c1.d2 dev eth1 src 1.2.3.4
...
ip route add a.b.c2.d1 dev eth2 src 1.2.3.4
ip route add a.b.c2.d2 dev eth2 src 1.2.3.4
...

потому как известно, какой абонент где должен быть (хранится в базе)
Но так же давно хотелось реализовать привязку IP-MAC не только к зданию, но и к порту узлового свича на здании (повторюсь, на абонентов стоят неуправляшки типа DES-1024)
И тут доходят руки поразбираться с dhcp-relay и dhcp-snooping.
На свичах включил:
enable dhcp_relay
config dhcp_relay option_82 state enable
config dhcp_relay option_82 check enable
config dhcp_relay option_82 policy replace
config dhcp_relay option_82 remote_id default
config dhcp_relay add ipif System 10.160.8.1

enable address_binding dhcp_snoop
enable address_binding trap_log
config address_binding ip_mac ports 1-28 mode acl stop_learning_threshold 500
config address_binding ip_mac ports 1-24 state enable strict allow_zeroip enable forward_dhcppkt enable
config address_binding dhcp_snoop max_entry ports 1-24 limit no_limit

config filter dhcp_server ports 1-24 state enable
config filter dhcp_server ports 25-28 state disable
config filter dhcp_server trap_log enable
config filter dhcp_server illegal_server_log_suppress_duration 1min

На сервере интерфейсы вывел из бриджа, убрал IP-адреса на них (интерфейсы без IP), включил arp_proxy

Настройка isc-dhcp
log-facility local6;
ddns-update-style none;
authoritative;
use-host-decl-names on;

default-lease-time 300;
max-lease-time 600;
get-lease-hostnames on;

option domain-name              "myserver.ru";
option ntp-servers              myntp.ru;
option domain-name-servers      mydnsp-ip;

local-address 10.160.8.1;
include "/etc/dhcp-hosts"; #здесь лежат привязки MAC-IP в виде "host  hostname    {hardware ethernet AA:BB:CC:55:92:A4; fixed-address w.x.y.z;}"


on release {
    set ClientIP = binary-to-ascii(10, 8, ".", leased-address);
    log(info, concat("***** release IP " , ClientIP));
    execute("/etc/dhcp/dhcp-release", ClientIP);
}
on expiry {
    set ClientIP = binary-to-ascii(10, 8, ".", leased-address);
    log(info, concat("***** expiry IP " , ClientIP));
    execute("/etc/dhcp/dhcp-release", ClientIP);
}

on commit {
if exists agent.remote-id {
      set ClientIP = binary-to-ascii(10, 8, ".", leased-address);
      set ClientMac = binary-to-ascii(16, 8, ":", substring(hardware, 1, 6));
      set ClientPort = binary-to-ascii(10,8,"",suffix(option agent.circuit-id,1));
      set ClientSwitch = binary-to-ascii(16,8,":",substring(option agent.remote-id,2,6));
      log(info, concat("***** IP: " , ClientIP, " Mac: ", ClientMac, " Port: ",ClientPort, " Switch: ",ClientSwitch));
      execute("/etc/dhcp/dhcp-event", ClientIP, ClientMac, ClientPort, ClientSwitch);
}
}

option space microsoft; #не нужена нам микрософ-сеть
option microsoft.disable-netbios-over-tcpip code 1 = unsigned integer 32;
if substring(option vendor-class-identifier, 0, 4) = "MSFT" {
    vendor-option-space microsoft;
}

shared-network HOSTEL {

    subnet 10.160.0.0 netmask 255.255.248.0 {
        range 10.160.0.1 10.160.0.100; #пул для неизвестных хостов
        option routers  10.160.1.1;
        option microsoft.disable-netbios-over-tcpip 2;
    }
    subnet a.b.c.0 netmask 255.255.252.0 {
        option routers  a.b.c.d;
        option microsoft.disable-netbios-over-tcpip 2;
    }
    subnet 10.160.8.0 netmask 255.255.255.0 { #из этой подсети запросы dhcp-relay со свичей
    }

}


В файле dhcp-event производится проверка agent.circuit-id, agent.remote-id, IP и MAC на валидность, и если все ок, то добавляется машрут до этого адреса через нужный интерфейс
Примитивный пример dhcp-event:
#hostel 1
if ($ARGV[3] eq '0:21:91:92:d7:55') {
    system "/sbin/ip ro add $ARGV[0] dev eth1 src a.b.c.d";
}
#hostel 2
if ($ARGV[3] eq '0:21:91:92:d4:92') {
    system "/sbin/ip ro add $ARGV[0] dev eth2 src a.b.c.d";
}

здесь проверяется только $ARGV[3] (то есть agent.remote-id, или МАС свича, через который получен DHCP-запрос), но можно проверять так же все остальные поля получая их валидные значения, к примеру, из базы

В итоге получаем:
1) клиент, не запросивший адрес по DHCP — не выходит дальше своего неуправляемого свича, его не пропускает IP-MAC-PORT-BINDING;
2) клиент, у которого МАС известен (есть в базе), но запрос не соответствует порту или коммутатору — получит IP привязанный к этому МАС, но маршрут до него не будет добавлен, соответственно proxy_arp «ответит», что адрес уже занят, и адрес будет тут же освобожден;
3) клиент, у которого МАС не известен, получит адрес из временного пула. С этих адресов происходит перенаправление на страничку с информацией, здесь же можно перерегистрировать свой МАС использую логин/пароль;
4) и наконец клиент, у которого МАС известен, и совпадает привяка к коммутатору и порту — получит свой адрес. Dhcp-snooping добавит динамическую привязку в таблицу impb на коммутаторе, сервер добавит маршрут до этого адреса через нужный интерфейс бывшего бриджа.

При окончании аренды или освобождении адреса вызывается скрипт /etc/dhcp/dhcp-release, содержание которого донельзя примитивно:
system "/sbin/ip ro del $ARGV[0]";

Есть небольшой изъян в безопасности, конкретно в п.2. Если использовать нестандартный dhcp-client, который не проверяет, занят ли выданный dhcp-сервером адрес, то освобождения адреса не произойдет. Конечно выхода во внешнюю сеть, дальше маршрутизатора, у пользователя не появится, так как сервер не добавит маршрут на этот адрес через нужный интерфейс, но коммутатор разблокирует эту пару MAC-IP на своем порту.
Обойти этот изъян можно, используя классы в dhcpd.conf, но это заметно усложняет конфигурационный файл, и, соответственно, увеличивает нагрузку на наш старенький сервер. Потому как для каждого абонента придется создать свой класс, с довольно сложным условием попадания в него, а потом свой пул. Хотя в планах попробовать на практике, как сильно это увеличит нагрузку.

Таким образом получилось, что за соответствием пары IP-MAC теперь «следит» DHCP при выдаче адреса, доступ с «невалидных» MAC-IP ограничивает коммутатор. С сервера теперь можно было убрать не только bridge, но и macipmap, заменив 6 сетов (из «Оптимизации 1») на один ipmap, в котром содержатся все открытые IP-адреса. А так же удалив -m physdev.
Еще интерфейсы сервера перешли из promisc mode в обычный, что тоже несколько снизило нагрузку.

А именно, вся эта процедура с разборкой бриджа снизила общую нагрузку на сервер почти в 2 раза! Softirq теперь не 50-100%, а 25-50%. При этом контроль доступа к сети стал только лучше.

Оптимизация 5

После проведения последней оптимизации, хотя нагрузка заметно упала, была замечена странность: возрос iowait. Не то чтобы сильно, с 0-0.3% до 5-7%. Это с учетом того, что на данном сервере вообще каких бы то ни было дисковых операций практически нет — он просто перекидывает пакетики.
iowait
(синий юзертайм — компиляция ядра)
iostat показывал, что есть постоянная нагрузка на диск в 800-820 Blk_wrtn/s
Начал поиск процессов, которые могли бы заниматься записью. Выполнение
echo 1 > /proc/sys/vm/block_dump

дало странный результат: виновниками оказались
kjournald(483): WRITE block 76480 on md0 
и 
md0_raid1(481): WRITE block 154207744 on sdb2
md0_raid1(481): WRITE block 154207744 on sda3

Ext3 находится в режиме data=writeback, noatime, да и на диск ничего не пишется, разве что кроме логов. Но логи какие писались вчера — такие и пишутся сегодня, их объем не возрос, то есть iowait тоже не дожен был возрасти.
Начал по шагам прокручивать в голове, что я делал, и что могло повлиять на iowait. В итоге остановил syslog — и iowait резко упал до ~0%
Для того, чтобы dhcp не захламял messages своими сообщениями, я отправил их в log-facility local6, а в syslog.conf вписал:
*.info;mail.none;authpriv.none;cron.none;local6.none            /var/log/messages
local6.info                                             /var/log/dhcpd.log

оказалось, что при записи через syslog для каждой строки делается sync. Запросов к dhcp-серверу довольно много, генерируется много событий, которые попадают в лог, и вызывается много sync.
Исправление на
local6.info                                             -/var/log/dhcpd.log

снизило iowait в моем случае 10-100 раз, вместо 5-7% стало 0-0.3%
Результат оптимизации:
Результат оптимизации

К чему эта статья.
Во-первых, возможно кто-то почерпнет из нее полезные для себя решения. Америку я тут не открыл, хотя бОльшая часть описанных тут событий была в дохабровую эпоху, и «гугление рецептов» мало помогало, доходил до всего своей головой.
Во-вторых, постоянно приходится сталкиваться с тем, что вместо оптимизации кода разработчики занимаются наращиванием вычислительных мощностей, и эта статья может быть примером того, что при желании можно сколь угодно долго находить ошибки в своем, казалось бы много раз проверенном, коде и справлять их. 90% разработчиков сайтов проверяют у себя на машине работу сайта, выкладывают в продкашн. Потом все это дело тормозит под нагрузкой. Теребят админа сервера. Оптимизацией сервера в этому случае мало что можно добиться, если код написан изначально не оптимально. И покупается новый сервер, или еще один сервер и балансировщик. Потом балансировщик балансировщика и так далее. А код внутри как был неоптимальным, так и остается навеки. Я конечно понимаю, что в нынешних реалиях оптимизация кода стоит дороже, чем экстенсивное наращивание мощностей, но с появлением огромного числа «доморощенных» айтишников объемы «плохого кода» приобретют серьезные масштабы.
Из ностальгического: имею на полке старый ноутбук, Р3-866 2001гв (Panasonic CF-T1 если кому-то это что-то скажет), но сейчас на нем невозможно даже сайты смотреть, хотя смысла на этих сайтах за 10 лет больше не стало. С любовью вспоминаю интересные игрушки на ZX-Spectrum по геймплею не уступающим сегодняшним монстрам, требующим 4 ядра/4гига
@trublast
карма
21,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Администрирование

Комментарии (22)

  • +2
    Потрясающе. Как бывший админ сетки в общаге КПИ очень хорошо вас понимаю :).
  • +3
    Больше всего толку (для мозгов!) именно когда гугление не помогает а делать чтото надо
  • +2
    Я рад, что еще есть люди, которые имеют время на такие оптимизации. Но сегодняшние реалии таковы, что чаще дешевле купить новое оборудование, чем стоимость труда инженера. Хотя, наверное, может быть иначе, раз в вашем случае это работает.
  • 0
    А я вот не очень понял. Зачем нужно было бриджом объединять на сервере? Неужели не было подходящих свитчей в то время? Я как помню — были. Как дальше бороться с халявщиками — другой вопрос. На самом деле, наверное в этом стыдно признаться, но в сети на 1000 клиентов, где я админ, есть простая привязка по dhcp ip-mac и никакого контроля, в случае если абонент ip пропишет чужой. Случаи появления халявщиков бывают — но с помощью управляемых свитчей на раз вычисляется кто халявит и применяются административные наказания типа отключения от сети и платного подключения. Если всё же требуется 100% защиты от халявщиков — можно было поставить vpn (сейчас более модно pppoe).
    Ещё интересно, какие порядки трафика были, когда сервера не справлялись?
    • +1
      Бридж был интересен тем, что создавалась «плоская» сеть, и большое количество ПО (игры, «сетевое окружение», бродкаст-чаты, и тому подобное, что использует бродкаст-пакеты для обнаружения) работало нормально. При этом можно было ограничивать доступ заблокированным абонентам до некоторых выборочных ресурсов сети (или наоборот, оставлять выборочные) которые находятся в его «псевдосегменте» сети.
      К примеру:
      -у шлюза адрес a.b.c.1/24
      -у вас адрес a.b.c.2/24
      -у вебсервера1 адрес a.b.c.3/24
      -у вебсервера2 адрес a.b.c.4/24
      В можете попасть на вебсервер2, а при попытке зайти на вебсервер1 попадаете на «страничку-информатор». При этом вроде как трафик даже через шлюз не проходил с точки зрения клиентской ОС.
      Управляшки в то время были, но 300$ стоила управляшка с RS232+WEB (без snmp и telnet/ssh) которая умела поднимать/опускать порты, показывать статистику на портах и ТОЛЬКО port-based VLAN (без 802.1q). То есть по сегодняшним меркам даже недолевел2. Ни о каком ограничении доступа на уровне этих железок речи идти не могло, во всяком случае за вменяемые деньги.
      • 0
        да управляшки и не сильно нужны (хотя они конечно очень помогли). у вас проблема халявщиков решалась для 1/6 сети всё равно. т.е. не полностью. чем 1/6 сети отличается от единой сети, в случае даже тупого свитча, если проблему всё равно не решить полностью? и я не понял, какие всё таки порядки трафика были?
        • 0
          и да. тупым свитчём можно было сделать плоскую сеть. и интересно, сколько вы платили за такой канал, как вы описали в то врёмя?
        • 0
          Вообще-то не для 1/6, а для 5/6. То есть злоумышленник имел возможность подставить только примерно 16% МАС из общего числа, причем он в точности не знал, какие пары MAC-IP относятся к его сегменту. А тут уже была возможность мониторить, на каком порту бриджа вдруг стали появляться нелигитимные МАСи (brctl showmacs br0) и принимать какие-либо административные меры.
          После установки свичей, которые могли слать SNMP-traps этот функционал был расширен таким образом:
          в случае, если МАС «перепрыгнул» с порта на порт, то адрес блокировался, и клиентов заворачивало на спец-страничку, где для разблокировки предлагалось ввести логин/пароль. В этом случае получалось уже не 5/6 отсекалось, а выбор у злоумышленника оставался примерно в 5-7 МАС-адресов (соседи по неуправляемому свичу, компьютеры которых в данный момент выключены) из 1000 клиентов, или менее 1%. И такой подход очень сужал территориальное место поиска злоумышленника, единственное, что нужно было дойти ногами до конкретного свича и по очереди выключать кабели с активным линком. А иногда удавалось и без этого: пропал один МАС на порту — и тут же появился другой (у которого «пропажа трафика»). Что наводило на вполне обоснованные подозрения, и отключение кабеля было уже только для того, чтобы окончательно убедиться.
  • +1
    Привет Максу от Онибаки 8)
    • 0
      принято
      встречный привет Роману от Трубласта
  • +2
    Нескромный вопрос — и сколько там трафика при изображенной на графичках нагрузке?
    • +3
      Тяжело сказать, «сколько трафика», потому как не ясна методика подсчета :)
      Начать с того, что в данной ситуации влияние оказывает не столько трафик в мегабитах, сколько пакетрейт (pps), так как именно пакетики каждый раз генерировали прерывания, и проходили цепочки iptables и других структур ядра. Особенно портили жизнь кольца на свичах, так как loopdetect отрабатывал не сразу (а порой вообще не отрабатывал) и весь закольцованный трафик, перекидываемый быстрыми специализированными микрухами коммутаторов, летел через многострадальный бридж.
      Статистики по пакетрейту, к сожалению, у меня нет. Зато есть данные по трафику в мегабитах, в rrd.
      Методику подсчета «общего трафика» использовал такую: брал для каждого физического интерфейса среднее значение входящих байт за час, и прибавлял к нему среднее значение исходящих байт за час. За час — потому что так ужимает rrd, пиковые безусловно могли быть заметно больше, но данных не осталось. Потом сложил получившиеся значения для всех интерфейсов.
      В итоге получилось 3.1Гбит/с
      Посмотрев текущую загрузку на загруженном порту одного из коммутаторов, выявил, что средний размер пакета где-то 900 байт плюс-минус.
      Следовательно, при 3.1Гбит/с и размере пакетика около 900байт получаем пакетрейт 450 тысяч пакетов в секунду.
      Наверное, правильнее было бы его разделить на 2, так как при суммарном битрейте использовался входящий и исходящий трафик, а сам сервер пакеты практически не генерирует, то есть весь трафик — транзитный. Значит средний пакетрейт за час выходит около 250 тысяч пакетов в секунду, без учета возможных кратковременных всплесков.
      • 0
        что-то у вас не здоровое число трафика для студентов, которые по идее должны пользоваться научным контентом.у нас от тысячи абонентов с безлимитами меньше трафика в разы. какое количество пользователей у вас было?
        • +1
          В самом начале статье есть упоминание про кол-во пользователей:
          сервер до сих пор живет и обслуживает сетку из 1000 машин
          Насколько мне известно, в разные времена оно плавало вокруг этой цифры: было больше во времена мегабайтного трафика, стало меньше сейчас во времена анлима. Раньше довольно крупную часть трафика генерили пиринг с локалкой, щас в пиринг летит малая часть (около 15% по nsk-ix), остальное внешка. То есть, несмотря на добавление в пиринг новых операторов, студентам стало проще находить нужный «научный контент» на ютубе и торрентах ;-)
        • 0
          что-то у вас не здоровое число трафика для студентов, которые по идее должны пользоваться научным контентом.


          Хорошая шутка. По-вашему, студенты не играют в игры, не смотрят видео? А если учесть, что интернет помегабайтный, то локальный p2p становится только важнее.

          Например, в Харьковском Политехническом в 2009-м году до сих пор была жива помегабайтная оплата и невероятно популярен DC++ с локальными серверами.
          • 0
            Первый безлимитный тариф в стенах общаг вышеописанного универа появился в апреле 2010 емнип, у него даже название было «весенний» и народ шутил, что к лету отменят и вернут мегабайты. Не вернули.
            Правда Макс вполне может возразить, что анлим был на заре описанной в статье эпохи. Я те времена не застал.
  • +1
    Сел читать статью и с каждой строкой глаза все больше лезли на лоб! Я уж было подумал, что кто-то из старой гвардии (green, madmax, abs) решил тряхнуть стариной и рассказать как это все было в НГУ. Всё было точно так же — и воздушки П270, и сервера с OSPF и тремя сетевухами, и грозди неуправляемых свичей и тд. А потом настал 2004 год, когда университет по проложенной еще при царе горохе на деньги Сороса оптике стал организовывать нормальную сеть в общежитиях.

    Надо будет как-нибудь собраться с духом и написать историю про наш вуз, благо еще не все забыто и не все контакты потеряны.
  • 0
    очень интересно. а можно вопрос: local6.info -/var/log/dhcpd.log — эта строчка отключает лог dhcpd?
    • 0
      Нет. Указание минуса перед именем файла отключает sync на диск при каждой записи в лог, что повышает производительность, но может привести к тому, что при сбое данные могут оказаться фактически не записанными на диск.
      Выдержка из man syslog.conf на эту тему:
      You may prefix each entry with the minus ''-'' sign to omit syncing the file after every
      logging. Note that you might lose information if the system crashes right behind a write
      attempt. Nevertheless this might give you back some performance, especially if you run
      programs that use logging in a very verbose manner.
  • 0
    Странная идея использовать user-space утилиты подсчёта трафика, игнорируя ipt_netflow на основании того, что «Netflow и так использует верхний провайдер для СОРМ».
    • +1
      Поподробнее, пожалуйста, про ipt_netflow в 2004-2009 гг
      Ну либо Вы невнимательно читали статью. Имелось ввиду, что необходимость в учете трафика на описываемой машине пропала к моменту выхода Ipt_NETFLOW, а до этого приходилось пользоваться юзерспейс утилитами за неимением ничего другого. В данный момент на этом сервере сбор netflow не ведется.
      • 0
        Да, так понятнее.

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