ip route replace default scope global
nexthop via 11.22.33.1 dev eth0 weight 1
nexthop via 55.66.77.1 dev eth1 weight 1
(Примерная инструкция здесь)
Проблема заключается в следующем — соединения периодически падают, причём никакой системы нет. Может простоять несколько часов, может упасть через 5-10 минут. Всяким http и torrent'ам это не мешает. В первом случае сессии обычно достаточно короткие, во втором реконнект проходит незаметно и без последствий. Но если мы работаем с ssh?
Разъяснение для тех, кто не знает, как работает такая схема маршрутизации.
Для каждого соединения, идущего через шлюз по умолчанию, выбирается один из имеющихся в наличии каналов, с вероятностью, соответствующей параметру weight.
Затем запись о маршруте для этого соединения заносится в кеш, и все пакеты между этими ip-адресами далее ходят по этому маршруту. Если какое-то время пакетов по этому маршруту нет — запись из кеша удаляется. По умолчанию на это требуется около пяти минут. В этом есть уже как минимум одна проблема — если ваше соединение долго не передаёт никаких данных, запись из кеша маршрутов будет вытерта, хотя из кеша соединений, возможно, ещё и не будет. По умолчанию в модуле nf_conntrack выставлено какое-то очень большое время жизни для tcp-соединений. Что случится? При следующем прохождении пакета в этом соединении, которое ещё считается несброшенным, будет выбран новый маршрут, как если бы оно было вновь установлено. Если повезет — тот же, что и был. Тогда всё продолжит работать. А вот если другой — ничего никуда не пойдёт.
Но на практике это проблема невеликая, довольно мало ситуаций, в которых соединение стоит без дела столько времени, а даже если это, скажем, ssh-сессия, то в ней можно включить keep-alive пакеты. Как и в большинстве других практических случаев. По идее такая ситуация возможна ещё с ftp, но я им давно не пользуюсь, и вам не советую. Да и большинство ftp-клиентов тоже умеют keep-alive.
Хуже другое — в такой схеме падали даже сессии с непрерывным потоком данных. И вот это оказалось непонятно.
Простой обход проблемы, слепленный на скорую руку, оказался в прописывании статических маршрутов для наиболее нужных удалённых хостов, так чтобы маршрут к ним лежал строго через один интерфейс. Но некрасиво же. Неуниверсально и ломает идею connection-failover.
То, что это помогло, и навело меня на мысль, что проблема где-то именно в роутинге. Трёхчасовые раскопки выявили следующее: в ядрах до 2.6.35 (а систем на них очень и очень немало) в настройках роутинга есть параметр net.ipv4.route.secret_interval, в секундах, по умолчанию 600. Отвечает за принудительный сброс кеша маршрутов во избежание его переполнения. В дальнейшем от него было решено отказаться — https://github.com/torvalds/linux/commit/3ee943728fff536edaf8f59faa58aaa1aa7366e3
Таким образом, раз в 10 минут ваш кеш сбрасывается, и маршруты выбираются заново. И не всегда так, как хотелось бы.
Поэтому для стабильной работы policy-routing на системах с несколькими аплинками я рекомендую выставлять этот параметр в 0.
sysctl -w net.ipv4.route.secret_interval=0
Можно конечно поставить патч ядра, устраняющий это поведение целиком, но это уже решение не для всех.
Отключать этот сброс вполне безопасно, поскольку в более поздних ядрах никакого дополнительного механизма защиты от переполнения кеша маршрутов введено не было, имеющиеся были сочтены достаточно надёжными.
Комментарии (21)
amarao
22.09.2015 00:32А можно я задам простой вопрос: а почему надо использовать недетерминистический алгоритм выбора аплинка? Используйте либо хэш от пары ip-адрес порт («последний бит хеша 0 — налево, 1 — направо»), либо, вообще, последний бит IP-адреса. То есть маршрутизировать исходя из маски 0.0.0.1.
Автоматически снимает все вопросы со «странными contracker'ами», которые вообще можно отключить. Пакет с чётным dst в IP? Аплинк 1. С нечётным? Аплинк 2.Ilandslc
22.09.2015 00:36+1Идея мне нравится. Не подскажете, как реализовать +- штатными средствами, скажем в CentOS?
А хотя есть тут один подводный камень. Но надо померять предварительно. А чтоб померять — реализовать.demon_odinok
22.09.2015 09:32iptables -t mangle -A PREROUTING -i $dev_in -d 0.0.0.0/0.0.0.1 -j MARK --set-mark 0x1
iptables -t mangle -A PREROUTING -i $dev_in ! -d 0.0.0.0/0.0.0.1 -j MARK --set-mark 0x2
Ilandslc
22.09.2015 00:40А собственно, отвечая на ваш вопрос: использовать не надо. Использовать можно. Более менее популярное и документированное решение, без изобретения велосипедов. Как часто случается — не без граблей. Ваш вариант, если он так же решается штатными средствами, наверно просто менее популярен и документирован.
Ilandslc
22.09.2015 01:21Забыл уточнить — в моём практическом случае полосы пропускания аплинков соотносятся 1:2. Соот-но, и weight у меня стоит 1 и 2. То есть вариант чёт/нечет уже не очень хорош.
alex_www
22.09.2015 04:27+11) Матчим в iptables любые биты как хотим, например как тут www.stearns.org/doc/iptables-u32.current.html
2) Создаем IP рулы: ip rule add fwmark
3) Указываем соответвущие gw для созданных рулов
lorc
Хм, а почему изменение роутинга должно приводить к разрыву соединения? Разве хосту не пофиг, каким маршрутом пришел пакет? Главное, что бы исходящий адрес не менялся.
Ilandslc
Так он изменится — у другого аплинка внешний ip другой.
mureevms
Это же только для новых соединений и после сброса кеша. Если соединение активно маршрут не изменится. Тут нет вариантов.
Ilandslc
Пост не читали? Речь и идёт о проблемах с самопроизвольным сбросом кеша
mureevms
Конечно читал. Не припомню, чтобы на Debian была подобная ситуация. Возможно это дистрибутивозависимо.
Ilandslc
Это ядрозависимо. ЕМНИП, у дебиана ядра новее
blind_oracle
Да, это вам, батенька, не BGP…
lorc
Ну да, действительно, если во владении нету своей сетки, то по другому и не выкрутишься.
Ilandslc
Ну решение-то в общем изначально наколеночное, для домашнего сервера разве что.
blind_oracle
Угу.
Я ещё наблюдал странное поведение, даже на более или менее современных ядрах — когда кеш не соответствует таблице маршрутов.
То есть, всё, вроде бы красиво — дефолтный маршрут на хосте указывает на роутер, который всех в инет выпускает, но произвольные адреса в интернете не пингуются, либо весь интернет недоступен.
Делаем ip route flush cache — и всё поехало. Проблема вылезала на 3.14, кажется, где-то раз в месяц. Сейчас, на 3.18, не видел пока, может пофиксили.
Ilandslc
С таким не сталкивался. На 2.6.32 предложенный способ отлично работает. Принудительно сбрасывать не приходилось никогда. Насколько велик у вас кеш?
cruxacrux
Начиная с ядер 3.6 ipv4 кеш вообще выпилен, т.е. вывод команды ip route cache пустой и flush cache ничего не делает. Тут pdf-презентация с оправданиями, почему так сделали.
Multipath на таких ядрах совместно с nat уже мало пригоден для практического применения, что особенно заметно на https-сессиях.
wmlex
А не подскажите, чем тогда реализовать multipath, например в Centos 7, с ядром 3.10?
cruxacrux
Есть пример решения, где помимо правил маршрутизации используется ещё ipset и iptables. Выглядит устрашающе, не знаю, возможно есть другие решения.
blind_oracle
Угу, я про это тоже читал в процессе решения проблемы. Но факт остаётся фактом — только ip route flush cache помогал восстановить связь. Ядро было точно не старее 3.14