Влияние сетевых параметров на работу приложений: задержки, фрагментация, перегрузки и основы качества обслуживания.
Какие значения задержки и jitter допустимы для VoIP?
К чему приводит блокировка ICMP на маршрутизаторах?
Почему WRED предпочтительнее tail-drop на перегруженных интерфейсах?
Почему UDP-трафик вытесняет TCP при перегрузке очереди?
Что является последним защитным механизмом при несоответствии MTU на пути следования пакетов?
Продолжаем обсуждать общие вещи, связанные с сетями предприятия. Пока основного хардкора нет, просто говорим про то, как в приличном предприятии реализована связь. Давайте поговорим про то, что будет работать поверх той сети, которую мы когда-нибудь сможем построить в том самом гипотетическом предприятии, с которым мы работаем. Поверх того, что мы делаем с вами, будут бегать протоколы связи. Там, скорее всего, будет какой-нибудь IP. Поверх этого IP будут бегать какие-то TCP или UDP соединения. Надо понимать, что в TCP соединения есть, в UDP соединений нет. Но есть приложение, которое посылает трафик. И мы можем совокупность UDP называть словом «сессия». Это не «сессия» в классическом смысле, но тем не менее. И дальше уже эти данные будут приниматься приложением. И приложения, которые будут использоваться в сети предприятий, бывают самые разные.
Мы должны будем знать, что каждый конкретный протокол может сотворить с передаваемыми данными такого плохого, от чего приложение будет страдать. Для того чтобы понимать, что у нас всё работает хорошо, надо понимать, когда всё плохо. Что такое плохо для каждого конкретного приложения. Потому что если есть приложение А, которое может работать в ситуации с большим джиттером, и есть приложение Б, которое не может работать с большим джиттером, то мы должны будем понимать, что джиттер важен для некоторых приложений. Должны будем какой-то джиттер измерять. Должны будем знать, при каком джиттере приложение Б себя ведёт уже плохо и при каком ещё хорошо. Соответственно, для разных протоколов будут характерны разные проблемы. И мы должны будем знать и эти проблемы, и поведение, которое эти проблемы вызывают. Если говорить про Ethernet — в Ethernet, как правило, кроме петли особых проблем не бывает. Если вы просто поднимаетесь от Ethernet,
она там худо-бедно как-то работает, пить-есть не просит. И если она работает, то всё хорошо. Если она не работает, то всё очень плохо. Там нет такого промежуточного состояния, когда оно вроде есть, но как-то криво. В то же время, в других протоколах, которые мы с вами изучаем на CCNA и естественно на CCNP тоже, может быть такое, что сеть работает, но работает не до конца. Кое-что работает хорошо, а кое-что работает криво-косо. Например, с IP могут быть проблемы с тем, что пакеты проходят разными трассами. И как следствие, они страдают. Пакет по одной трассе проходит быстрее, чем по другой. Если у нас есть поток IP-пакетов, которые идут от одного и того же хоста до другого, и пакеты относятся к разным приложениям, то Cisco по умолчанию могут у вас перемешивать эти пакеты. Cisco устроена таким образом, чтобы пакеты одного и того же приложения всегда шли по одной и той же трассе.
Но если у вас пакеты относятся к разным приложениям, к разным потокам, они могут пойти по разным трассам. Оборудование других вендоров тоже может сделать так, что у вас пакеты, отправленные даже одним и тем же приложением, по факту пойдут по разным трассам. Как следствие, часть пакетов будет проходить чуть быстрее, часть пакетов — чуть медленнее. И в итоге вы получите ситуацию, когда пакеты по сети проходят за разное время и на получателя приходят в разном порядке, не в том, в котором они отправлялись изначально. Эта проблема будет называться resequencing. Если у нас есть пакеты, которые мы отправляем в одном порядке, мы ожидаем, что они приходят тоже в этом же порядке. Очень мало программистов, которые пишут свой софт, догадываются, что в сети могут такие проблемы возникнуть, что пакеты поменяют свой порядок. А они могут возникнуть. И если у вас программисты заложили в софт неявную предпосылку, что пакеты приходят в одном и том же порядке,
это может вызвать проблемы, если порядок следования этих пакетов будет нарушаться. Продемонстрировать этот пример можно очень просто. У вас есть, например, два роутера. Один роутер и второй роутер. И между ними два канала. В норме мы запускаем здесь какой-нибудь протокол типа OSPF. Он нам говорит: вижу два одинаковых канала, они оба одинаково хорошие, давайте использовать Equal Cost Multipath. ECMP. Equal Cost Multipath. Балансировать трафик по обоим интерфейсам, по обоим проводам, чтобы половину трафика посылать на один порт, половину — на другой. Прекрасная идея, но есть нюанс. Нюанс заключается в том, что если часть трафика мы пошлём через один интерфейс и часть через другой, очереди на этих интерфейсах могут загружаться не одинаково. Может быть такое, что в очереди на один интерфейс пакетов скопилось почему-то больше, чем на другой, потому что там, например, ещё что-то отправляется в этот же интерфейс, большое.
И очередь уже забилась. Поэтому к нам приходит пакет с номером 1, мы его отправляем на первый порт. Пакет приходит с номером 2, мы его отправляем на второй порт, и он там встаёт в свою очередь ожидания. Приходит номер 3, мы его снова отправляем наверх, снова в первый порт он уходит. И он отправляется уже быстрее, чем второй, который стоит всё ещё в очереди. Поэтому на получателя приходит первый, на получателя приходит третий, и пока они там идут, во втором канале бегает какая-то другая нагрузка, и только после этого приходит второй. Получатель получает пакеты 1, 3, 2. Такие штуки вполне возможны, если у вас есть балансировка трафика. В интернете балансировка в принципе может случиться — часть трафика пойдёт по одной трассе, часть по другой. Даже в сети предприятия такое может случиться. Каждый раз, когда у вас есть два варианта, как можно выйти из роутера, например, у вас есть два аплинка до вышестоящих distribution-свичей, на access-свиче вы получите с большой вероятностью
Equal Cost Multipath. Если ваше приложение страдает от этого, вы можете принять какие-то меры и, например, выключить Equal Cost Multipath, направить трафик только по одному маршруту. Но вы должны знать, что у вас есть приложение, которое от этого страдает, что ваши Cisco не различают трафик и не могут направить весь трафик одного и того же приложения по одному и тому же порту по какой-либо причине. И вы в этом случае должны будете принимать какие-то уже конкретные ответные меры, как сделать так, чтобы приложение себя хорошо чувствовало. Как это понять? Вы должны будете посмотреть, что приходит на ваше приложение. Вы здесь видите, что у вас есть, например, интерфейс, и здесь сидит конечный абонент. Запускайте на этом абоненте Wireshark и смотрите, что она видит. Если вы видите, что пакеты приходят в неправильном порядке, и видите, что когда это происходит, приложение начинает сбоить каким-то образом, можно сделать гипотезу, что виноват resequencing. Пока пакеты
приходят нормально — приложение работает. Как только пакеты начинают криво приходить — гипотеза, что проблема в resequencing. Можно автора приложения спросить, можно погуглить в интернете. Дальше уже можно будет предположить, что делать, если у нас такая проблема есть. Но в любом случае, это одна из проблем, которая в IP-сетях встречается, и знать про неё надо. Другая проблема может заключаться в том, что пакеты будут проходить двумя разными трассами, например, но они будут проходить не настолько за разное время, чтобы поменялся их порядок, но тем не менее за разное время. И может быть такое, что порядок-то у них сохранится, но время прохождения пакетов будет разное. И у нас получится, что пришёл первый пакет, в норме второй должен был прийти через 50 миллисекунд. И третий пакет, следующий за ним, тоже должен был прийти через 50 миллисекунд. И они так через 50 миллисекунд должны были проходить. Типичный сценарий с голосовыми пакетами — они реально через 50 миллисекунд вполне могут проходить,
20 пакетов в секунду у вас будет бегать. Но из-за того, что, например, третий пакет прошёл по сети по какой-то другой трассе, у вас получилось, что первый пакет пришёл условно вовремя, второй пакет пришёл условно вовремя, здесь действительно 50 миллисекунд получилось. Третий пакет почему-то пришёл чуть раньше, здесь, например, 20 миллисекунд. А четвёртый пакет тоже вовремя, но относительно третьего — 80 миллисекунд. Четвёртый пришёл вовремя, но относительно третьего он пришёл позже. Такие вещи бывают. И опять же, некоторые приложения требуют, чтобы пакеты приходили за одинаковое время, чтобы у них не было разброса. Чтобы вместо разницы в 50 миллисекунд между пакетами у нас не бегало от 20 до 80. Некоторым приложениям это будет не нравиться. В частности, вся голосовая телефония будет от этого страдать. Поэтому, если вы чувствуете, что у вас есть голос в IP-сети, и чувствуете, что голос начинает как-то нехорошо работать,
если есть какая-то статистика, то может быть конкретный узел, который плохо себя чувствует, он будет отдавать какую-то информацию, что, например, джиттер слишком большой или что-то ещё, то вы можете сделать предположение, что виновато то, что голосовые пакеты у вас не в приоритете. Где-то они могут задерживаться. Пакеты не могут пройти быстрее, чем по пустой трассе. Они могут только пойти по более загруженной трассе и, как следствие, притормозить. Например, мы можем сказать, что здесь первый пакет притормозил, здесь второй пакет притормозил, третий не притормозил и прошёл достаточно быстро, а четвёртый снова притормозил. И за счёт этого у нас получилось такое расхождение во времени прохождения пакетов. Это тоже проблема, тоже для некоторых приложений она будет актуальна. В частности, всё, что связано с реалтаймом: голос, видеокартинка и прочее. Лечится эта штука установкой QoS. Приоритизация: вы говорите, что трафик определённого приложения мы ставим в такую очередь, которая никогда не бывает загружена, и обрабатываем такой трафик всегда в приоритете по сравнению со всеми остальными.
Все остальные подождут, а этот ждать не может. Его всегда надо пропускать в первую очередь, потому что он чувствителен к джиттеру. Частая боль с реордерингом и ретрансмитом. Как лечить в случае с ECMP? Да, только QoS-ом. Если у вас TCP настолько страдает, что начинает ретрансмиты делать, то ничего с этим не сделаешь. Только QoS. Или выключать multipath. Что вы выключите multipath, что вы включите QoS — это всё геморрой, так или иначе. Просто разного характера. Для некоторых приложений будет актуально не просто то, что пакеты проходят за одинаковое время по сети, но им важно будет, что это время будет ещё достаточно небольшим. Опять же, голосовые всякие штуки. Здесь в игру вступает такой параметр, как latency.
Это время, которое требуется пакету для прохождения по сети. Если у вас есть голос, и этот голос претендует на то, чтобы быть голосом высокого качества, то тогда вы должны будете обеспечить со стороны IP-сети работу настолько хорошую, чтобы собеседник не замечал, что он на самом деле пользуется телефоном. Чтобы он, конечно, понимал, что держит в руке телефонную трубку, но при этом собеседник как будто бы находился рядом с ним. Это вы можете обеспечить, только если задержка при прохождении пакетов по сети будет весьма маленькая. Если говорить про конкретные цифры, то latency для голосовых пакетов не должна превышать примерно 100 миллисекунд для звука высокого качества. Лучше будет, если она будет примерно 40–50 миллисекунд. Если у вас такие цифры достигаются, то голос себя ведёт, как будто это действительно человек находится в одной комнате с вами. Почему нет смысла бороться за время меньше, чем 40–50 миллисекунд?
Дело в том, что время, в течение которого отсылается голосовой пакет, — он в себе несёт как раз примерно те самые 40–50 миллисекунд звука. Вы наговариваете что-то в микрофон, это всё оцифровывается, и каждый голосовой пакетик, который будет передавать информацию, он как раз несёт в себе приблизительно от 30 до 50 миллисекунд голосовых данных. И поэтому следующий пакет должен будет прийти не позже, чем через это же время. Если у вас время прохождения по сети немножко плавает, но плавает в пределах этих самых 40–50 миллисекунд, в принципе, ничего особенно плохого быть не должно. И latency, опять же, если все пакеты проходят одинаково, то те самые 40–50 миллисекунд будут таким хорошим показателем. Далее. Давайте со слайда всё сотру. Ещё одна проблема, которая в IP-сетях бывает, — это фрагментация. Заключается она в том, что у нас на некоторых интерфейсах есть ограничения на размер передаваемых данных сверху.
Например, классическое ограничение сверху в 1500 байт для IP-сетей. Если мы используем оборудование с настроенным по умолчанию Cisco, то у нас как раз MTU в IP будет 1500 байт. Это 1500 байт на размер пакета вместе с заголовками, вместе с полезной нагрузкой. IP-пакет у нас должен получиться не более чем такой. Если мы пытаемся отправить данных больше, чем влезет в 1500-байтовый пакет вместе с заголовками, например, мы хотим отправить 2 килобайта данных — UDP-шную датаграмму сделали, 2 килобайта, нам надо её отправить. В этом случае IP сделает следующее. Он сначала соорудит IP-пакет из 2 килобайт данных плюс своего заголовка, например, 20 байт, а потом скажет: я такой большой пакет не могу пропустить в интерфейс, у которого MTU 1,5 килобайта. Я его порежу на части. И он порежет его на части, 1500 байт каждая,
и будет отправлять это всё дело по частям. Именно IP-пакет. Кстати, в Ethernet термина MTU нет. Но мы про это говорим на свитчинге. Фрагментация есть в IP. У нас есть датаграмма, которую мы сделали, обернули её IP-заголовком и порезали IP-пакет на части, каждая не более чем 1,5 килобайта. Эта штука довольно простая. Про это говорится на CCNA. Но то, что не говорится на CCNA, — некоторые приложения не любят, чтобы их данные фрагментировали. Во-первых, фрагментация — это затратная операция. И она делается, как правило, на центральном процессоре. Очень мало аппаратных ускорителей умеет ускорять фрагментацию. Может быть такое, что если у вас обычный роутер пропускает через себя трафик, он пропускает его на скорости — то, что называется линейная скорость, line rate. Какой интерфейс у вас есть, 100 мегабит или гигабит, на такой скорости трафик он и форвардит. Но как только начинает прибегать трафик, который надо фрагментировать,
всё это дело перелезает на процессор, и производительность резко падает, прямо резко. И она снижается до, например, 40 мегабит в секунду. Абсолютно, кстати, реальная цифра. У меня был как раз пример, когда был роутер, который обслуживал гигабитный канал. Он номинально работал на гигабите и гигабит через себя пропускал. А потом кто-то догадался включить jumbo frames. И производительность этого роутера упала до реально 40 мегабит в секунду, потому что он начал всё пытаться расковырять на процессоре, и у него это не получалось. Ещё одна проблема будет заключаться в том, что, помимо того, что это просто затратно и что у нас производительность падает, операция разделения пакета на части затратна по времени. Если у вас есть пакет, который вы должны будете разрезать на части, это требует времени, и времени достаточно большого. Например, на операцию разделения пакета нам требуется 10 миллисекунд.
Когда у нас борьба идёт за то, чтобы обеспечить не больше 40 миллисекунд суммарную задержку, эти 10 миллисекунд начинают играть уже очень большую роль. Представьте себе, что у вас 5 роутеров в цепочке, и каждый из них будет что-нибудь такое делать. Один роутер 10 миллисекунд будет на фрагментации тратить, второй будет на чём-нибудь ещё, третий на чём-нибудь ещё. И получается, что в итоге у нас задержка становится большой. А кроме того, что она большая, она ещё и непредсказуемая. Потому что конкретно этот пакет порежется за 10 миллисекунд, но если перед ним в очереди ещё другие пакеты стояли, и каждый из них надо за 10 миллисекунд порезать, то роутер сначала один пакет сделает, потом второй, потом третий. Он не умеет их все одновременно делать. Он их параллельно не обрабатывает. Обрабатывает последовательно. Сначала один, потом второй, потом третий. И сколько пакетов в очереди перед конкретно данным конкретным пакетом будет, мы заранее не знаем. Может быть такое, что мы, как телефон, отправляем пакеты — отправляем 1-й пакет, 2-й пакет, 3-й пакет, 4-й пакет, 5-й пакет.
В телефоне такие большие пакеты обычно не отправляются, но тем не менее представим себе, что у нас есть какой-то протокол, который отправляет пакеты, чувствительные к задержке. И у нас есть роутер, который выполняет фрагментацию. Он первый пакет начал обрабатывать, а остальные поставил в очередь. И первый пакет он обработал быстро и сразу пропустил в сеть. А второй пакет сидел в очереди и ждал, пока первый освободится. И третий ждал, пока освободится второй. Время прохождения пакета по сети будет зависеть от загрузки очереди на фрагментацию на этом роутере. И все эти пакеты будут сидеть в буферах и эти буферы забивать. А если у нас достаточно большое количество трафика будет приходить, то в забитый буфер трафик будет дропаться. Поэтому фрагментация — штука довольно неприятная. И неприятная она именно потому, что, как правило, выполняется на центральном процессоре. Многие протоколы стремятся избегать фрагментации их данных.
В частности, из того, что приходит прямо сейчас в голову, — это NTP, Network Time Protocol. Протокол синхронизации времени, которому по служебной необходимости важно, чтобы прохождение NTP-пакетов по сети происходило за минимальное время. Если вы начинаете NTP-шные пакеты резать на части, у вас потом потребуется время на то, чтобы разрезать пакет изначально, потребуется время для того, чтобы потом его обратно собрать. Время это будет непредсказуемое, и NTP начнёт давать большую погрешность. Для него эти 10 миллисекунд погрешности будут уже очень серьёзную роль играть. Ещё из того, что не любит фрагментацию, — как ни странно, протокол TCP. Он тоже не любит того, чтобы данные фрагментировались. Не любит он по другой причине, правда. Хотя все эти задержки для TCP будут достаточно важны.
Он на задержках будет очень много всяких своих внутренних процессов строить и будет очень много таймеров запускать, которые должны будут сработать или не сработать за определённое время. Поэтому, если у вас где-то в сети начинает колбасить время прохождения пакетов, на таймеры TCP это может сказаться. Но, что более важно, у TCP есть штатный собственный механизм того, чтобы отправлять пакеты, которые не будут подлежать фрагментации. Он отправляет фактически все свои сегменты с флагом DF, Don't Fragment. Если такие пакеты будут в сети прибиваться, потому что они где-то не будут пролезать в сеть, — TCP говорит фактически: если не пролезает, то лучше его совсем прибить. Просто уведоми меня, что это случилось. TCP потом будет свои сегменты отправлять меньшего размера. Он всё равно переотправит эти данные. Просто он переотправит данные куском поменьше, который уже заведомо в сеть пройдёт. Поэтому да, IP тоже не любит фрагментацию. Многие IP-шные реализации прямо запрещают фрагментацию в явном виде,
они выставляют флаг DF, и если где-то что-то не пролезает, TCP уже под это будет адаптироваться. Но это опять же предполагает, что у вас везде корректно TCP будет узнавать про то, что его пакеты не пролезают в сеть. Что здесь может вас поджидать? Может поджидать такое, что не работает прохождение пакетов ICMP. В частности, пакетов ICMP, который называется Packet Too Big (PTB). Если у вас есть пакет, который прошёл по сети почти всё, а дальше его где-то кто-то пристрелил, потому что он слишком большой, TCP про это должен будет узнать следующим образом: ему встречный ICMP-шный Packet Too Big пакет должен прийти, И там должны будут сказать: «Я твой пакет не доставил, потому что он слишком жирный». И должны будут в явном виде сказать, какого размера пакеты будут пролезать. Дальше TCP уже будет пакет отправлять такого размера, который указан в Packet-to-Big. Если у вас Packet-to-Big не проходит, то TCP пытается что-то отправить, у него не пролезает.
Он про это не знает заранее. Он пытается это переотправить, он, может быть, будет отправлять данные в итоге поменьше. Но эти данные поменьше будет отправлять только спустя некоторое количество попыток, когда у него окно передачи будет сжиматься, сжиматься, сжиматься, и в итоге сожмётся до такого состояния, что он просто скажет: «Давайте я по одному байту буду отправлять, например». Не по одному байту, но смысл вы поняли. И TCP начнёт от этого реально очень сильно страдать. Эта штука, когда у вас IPv6-шное сообщение не проходит, и, в частности, не проходит Packet-to-Big сообщение, будет называться Path MTU Discovery Black Hole. PMTUD. У TCP, кроме того, что есть такая особенность с фрагментацией, есть ещё ряд своих особенностей, которые вы, опять же, должны будете знать для того, чтобы организовать хорошую передачу данных в сети.
Первое, что нужно будет знать, это MSS — максимальный размер сегмента, который TCP будет передавать для соседа. Когда у нас устанавливается TCP-шная сессия, там, среди прочего, можно будет согласовать так называемые опции. Когда TCP начинает передавать первые SYN-пакеты, эти SYN-пакеты могут быть не обязательно совсем пустые. Вы можете в опциях, например, сказать, как вы хотите работать. И одна из опций, которые в TCP есть, это как раз MSS, Maximum Segment Size. Если вы отправляете сообщение, что вы согласны отправлять сообщения не больше, чем определённого размера, и принимать сообщения не больше, чем определённого размера, то в этой ситуации сосед должен будет под вас адаптироваться и присылать вам сегменты не больше, чем эти определённые. Это опция. Если вы помните, в TCP опции достаточно широко используются, в отличие от протокола IP, где опции устроены очень похожим образом, но они там не взлетели.
Именно из-за того, что соседи в TCP поймут, что имеется в виду. Они могут на этапе передачи SYN-пакетов согласовать, какие опции они вообще понимают. А в IP транзитные роутеры могут не понимать, что имеется в виду в конкретной опции, поэтому трафик сбрасывать. В TCP опции все понимают. Поэтому, если вы заявляете, что вы хотите использовать максимальный размер сегмента какой-то определённый, то роутер соседа, роутер на другом конце сессии, он будет как раз присылать вам сегменты не более чем определённого размера. Опять же, если вдруг у вас есть где-то транзитный роутер IP, который пропускает через себя IP-пакеты не более чем определённого размера, он может просто взять и фрагментацию выполнить или прибить пакет, который через себя пропускает, с криком Packet to Big. А может, если он видит, что TCP-шная сессия идёт, и у него есть понимание, что интерфейс не совсем нормальный — например, не пропускает через себя 1500-байтовые пакеты —
в SYN-сегменты этот роутер может дополнительно вбросить информацию про максимальный размер сегмента MSS. Эту опцию может добавить не только отправитель SYN-пакета, но также и транзитный роутер. MSS хорош тем, что он будет очень легко выбираться: если у вас два роутера друг другу присылают разные MSS, то выбирается среди них наименьшее. И если где-то роутер в сети, который выбрасывает MSS-опцию, говорит, что пакеты через этот интерфейс не пройдут больше, чем определённые, то тогда оба узла будут знать, какого конкретно размера сегменты они могут посылать так, чтобы они по сети прошли. Это, опять же, фича TCP. Так, вопрос есть. Если есть два свитча с MTU 1601, соединённых через радиорелейку с MTU 1500, но при посылке ICMP с параметрами ping 1600 DF пакеты проходят, чем это объясняется?
Вендор заявил, что пакеты больше дропаются. Я думаю, что это объясняется тем, что ваши свитчи не генерируют пакеты больше, чем 1600 байт размером. Когда вы указываете ping 1111 3... А, там DF стоит. Я вижу. DF я не видел изначально. Тогда не знаю. Навскидку не скажу ничего. Давайте сейчас быстренько пробежимся по слайдам, которые здесь про всё это рассказывают. Потому что, на самом деле, я здесь рисунки рисовал, а у меня всё это дело на слайдах сделано. Первое, что мы обсуждали, это одновременно проблема со сменой порядка прохождения пакетов по сети и внесения каких-то неодинаковых задержек в пакеты, которые вызывают джиттер. Здесь сейчас мультик будет красивый, такое подобие мультика. Что к нам приходят пакеты в некотором порядке, некоторым образом пронумерованные.
И дальше эти пакеты начинают проходить разными трассами. Первый и третий пакет проходят по одной трассе, второй пакет проходит по другой трассе. И по какой-то причине здесь приходит, например, какой-то приоритетный пакет на нижний роутер, который обрабатывается перед пакетом с номером 2. Первый и третий пакеты проходят в нормальном порядке. Второй ждёт, пока пройдёт приоритетный пакет. И получается, что на узел назначения пакеты приходят в неправильном порядке. По сети они уходят: первый, потом какой-то важный, который тоже был получен в свою очередь, в важную очередь. Потом третий, потом второй. У нас и задержки выросли. Между первым и третьим, например, здесь видно, что задержка в размере как раз одного пакета. Если представить себе, что это были голосовые пакеты, то можно себе сказать, что здесь, например, разница между первым и вторым была 50 миллисекунд. Здесь тоже, между вторым и третьим тоже была 50 миллисекунд. Когда мы принимаем эти пакеты, мы говорим: первый пакет, и здесь через 50 миллисекунд должен быть второй, но его нет.
Ждём ещё 50 миллисекунд и говорим: пришёл третий, видимо, второй потерялся. И то, что потом второй придёт почти сразу после третьего, означает, что по факту его уже выкинули. Когда у вас есть голосовое соединение, вы не можете ждать бесконечно долго того, чтобы вам пакеты какие-то недостающие потом доставили. Поэтому голосовые соединения не используют, например, TCP, именно потому, что нет смысла что-то перезаказывать или ожидать того, что не вовремя пришло. Если что-то не пришло вовремя, это просто выкидывается, вместо этого идёт какой-то серый шум, буль или розовый шум. И дальше голосовой поток идёт, как ни в чём не бывало. То, что здесь у нас первый пакет пришёл, второго нету вовремя, третий пакет пришёл и потом отдельно пришёл второй, означает только то, что второй пакет по факту мог бы не приходить. Он зря занимал ресурсы сети. В каких ситуациях эта штука может происходить? Если у вас есть балансировка или если у вас есть какой-то странный QoS, который не совсем корректно настроен, потому что при корректной настройке такого не бывает.
Про latency уже рассказывал, что если у вас задержка большая, то это может вызвать проблемы. Я уже говорил про рекомендованные задержки для звука высокого качества. На всякий случай здесь повторю. Совсем хорошая задержка — это задержка порядка 50 миллисекунд. Задержка приличная — порядка 100 миллисекунд. Это ещё не будет ощущаться как какое-то нездоровое поведение. Может быть, не будет это восприниматься как звук супервысокого качества, потому что всё-таки задержку 100 миллисекунд не то чтобы можно ощутить, но это не так же ощущается, как если человек с вами в одной комнате находится. Но тем не менее это всё равно всё ещё звук хороший, звук всё ещё высокого качества. Если задержки начинают расти до порядка 150–200 миллисекунд, то вы это начинаете слышать. Это реально уже воспринимается психологически, как что-то нездоровое происходит. И всё, что больше чем 200 миллисекунд, это уже прямо некомфортно. Если вы можете вспомнить, например, кто-то из вас, может быть, достаточно старый, чтобы помнить, как работала междугородная и международная связь в Советском Союзе,
когда вы разговаривали с другим городом, и вы говорили там «Алло» в трубку, вы должны были ждать, потому что ваше «Алло» по сети передавалось в течение длительного времени. И ваше «Алло» собеседник слышал там через секунду, и в ответ тоже своё «Алло» говорил, и его «Алло» вы в свою очередь слышали тоже через секунду. То есть вы сказали «Алло» и дальше 2 секунды молчите. И только в этот момент к вам придёт ответ собеседника. Если бы вы начинали вести себя как при обычном телефонном разговоре, когда вы фактически ожидаете, что собеседник находится как бы с вами рядом, вы говорите «Алло» и ожидаете немедленный ответ. А его нету. Вы снова говорите: «Алло, алло, ты меня слышишь вообще? Алло». И только в этот момент до вас приходят первые сообщения. Если у вас задержка будет слишком большой, то вы будете испытывать такого же рода проблемы. И да, опорные цифры, на которые ориентируются телефонисты, это цифры задержки не больше, чем 100–150 миллисекунд. Если задержка меньше 150 миллисекунд, в принципе, телефония работает хорошо.
Если задержка меньше 50 миллисекунд, телефония работает, как правило, замечательно. Так, фрагментация — уже всё рассказал. В IPv6 фрагментации на транзитных узлах нету. Мы с вами это обсуждали на CCNA, но тем не менее я повторюсь. В IPv4 фрагментация может выполняться транзитными узлами. И весь смысл как раз был в том, что вы заранее не знаете, где что порежется на части, и поэтому вы отправляете всё как есть, а дальше там, где оно не пролезает, оно там и фрагментируется. Это вызывает нагрузку на транзитные маршрутизаторы, это вызывает непредсказуемый рост задержки, это вызывает проблемы, если у вас из нескольких кусочков потеряется только какой-то один кусочек, то вы должны будете собирать целиковый пакет и ждёте все-все-все отдельные фрагменты. В IPv6 эту проблему — почти все из этих проблем — решили. IPv6 будет отправлять только пакеты, которые не подлежат фрагментации.
Транзитные узлы в IPv6 не могут резать пакеты на части. Если у вас есть пакет, который не пролезает где-то в сеть, то его там могут только прибить. Но когда вам прибивают пакет, вам присылают сообщение Packet Too Big, Destination Unreachable, и там указывается, какого размера пакеты могли бы пролезть. И если вы захотите, вы можете в следующий раз этому абоненту посылать пакеты с меньшим содержимым. Если вы сами хотите, вы можете сами порезать пакет на части. Фрагментация в IPv6 есть, но выполняет её только сам отправитель. Или вы можете сказать, что если там, например, TCP, то: дорогой TCP, пожалуйста, отправляй сегменты поменьше. Эта штука будет называться Path MTU Discovery. И ваш стек IPv6 должен хранить Path MTU для каждого конкретного своего отдельного получателя, с которым он будет вести взаимодействие. Когда вы в IPv6 начинаете обмениваться трафиком с каждым конкретным узлом, вы будете знать, какого размера пакеты до этого узла нормально проходят.
Если вдруг где-то у вас пакеты в сети не пролезают, вам придёт сообщение: «Давай, присылай поменьше». Если вы можете, вы присылаете поменьше. Если вы не можете, вы режете пакет на части сами и уже фрагменты отправляете в сеть. По поводу TCP-шной фрагментации уже сказал, что обычно реализация TCP заказывает флаг DF, и фактически фрагментация в TCP полностью отсутствует. За счёт того, что если что-то где-то TCP-шное не пролезает, вам приходит отбойник Packet Too Big, вы начинаете отправлять сегменты поменьше. Это даже не фрагментация на отправителе, это просто отправка сегментов меньшего размера. Ещё одна интересная штука, которая в TCP есть. Опять же, вы должны будете про это знать. Не то чтобы это какая-то критично важная штука для сетей небольшого размера, но если у вас есть какая-то крупная сеть, в которой используются TCP-шные сессии, в которых передаётся много трафика —
типичный пример это iSCSI — вы должны будете знать некоторую штуку. И эта штука заключается в том, что у вас отдельная TCP-шная сессия в принципе не может пропускать через себя трафика больше, чем определённое значение. Даже если у вас есть супербыстрые каналы, даже если у вас всё супер-мега-круто, TCP-шная сессия не предназначена для того, чтобы по ней посылать много данных. Она будет ограничена по природе своей неким числом. И это число будет называться Bandwidth Delay Product. Смотрите, что это такое. Когда у вас есть узел-отправитель и узел-получатель, они друг другу будут отправлять какие-то данные. Я постараюсь сейчас вам нарисовать диаграмму времени, как смогу, так и нарисую. В ту сторону у нас откладывается время, а здесь по горизонтали я буду пытаться нарисовать прохождение пакетов между двумя узлами. Вот тут узел-отправитель, давайте sender, и узел-получатель, receiver. У нас один узел посылает другому какие-то данные и ожидает на них подтверждения.
Данные идут в течение некоторого времени, подтверждение на эти данные идёт тоже в течение некоторого времени. Если вдруг у вас достаточно большая полоса пропускания доступна, bandwidth, то вы посылаете данные все, которые у вас есть в окне. Вы отправляете данные все, которые были в окне, и до тех пор, пока к вам подтверждение не приходит, вы ничего больше не отправляете. Вы начали посылать данные, и эти данные из окна отправились. Это ваш размер окна, вы всё своё окно отправили, и до тех пор, пока подтверждение на какие-то данные к вам не пришло, вы не можете больше ничего отправлять. Отправили какие-то данные и молчите. Молчите, ждёте подтверждения. Пришло какое-то подтверждение, вы начинаете снова какие-то данные отправлять, снова отправляете и снова ждёте подтверждения на то, что к вам уже пришло. Получится, что TCP-шная сессия не может, в принципе, физически отправлять данные всё время, если полоса между этими двумя узлами достаточно быстрая, а время прохождения пакетов туда-обратно достаточно большое.
Представьте себе, что у вас есть, например, какой-нибудь спутниковый канал. Канал весьма быстрый, но с большой задержкой. В этой ситуации получится, что у вас есть какое-то окно передачи. Вы его начинаете отправлять и отправляете очень быстро, потому что bandwidth у вас очень большой. А дальше сидите и ждете, потому что delay у вас тоже очень большой. И чем больше delay, тем дольше сидите и ждете. И если взять и перемножить bandwidth и delay, то вы получите некоторое значение. Это значение указывает, насколько вы подвержены этой проблеме. Если у вас получается число больше чем 12500, эта штука измеряется в килобитах в секунду, а delay измеряется в секундах, то вы получаете килобайты. Строго говоря, байты или килобайты. Если у вас получается значение больше чем 12500, то считается, что ваша сеть будет подвержена такой проблеме.
Сети, которые будут иметь этот параметр больше чем 12500, будут называться Long Fat Network или Elephant Network. Как бороться с этой ситуацией? Потому что на самом деле 12500 — это вообще ни о чем. Представьте себе, что у вас bandwidth, например, 100 мегабит в секунду. Не самый быстрый канал на сегодня, правда? А delay, например, 1 миллисекунда. Опять же, не самая большая задержка, которую вы можете встретить. В этой ситуации 100 мегабит в секунду — это 10 в восьмой степени бит в секунду, и 1 миллисекунда — это 10 в минус третьей степени. У вас получается произведение 10 в пятой степени. 100-мегабитный канал с задержкой в одну миллисекунду уже получается этой проблеме подвержен.
Что делать в такой ситуации? Есть специальная опция, которая называется Window Scale. Вы можете её согласовать на этапе установления соединения и можете сказать, что когда вы передаёте сегменты в TCP, вы указываете в поле Window Size не размер окна в байтах, а специальный мультипликатор. Соответственно, умножаете двойку на соответствующее число, которое там есть, и эта умноженная двойка будет указывать на то, какой на самом деле размер окна у нас есть. И за счёт того, что мы увеличиваем размер окна, у нас получается, что произведение bandwidth-delay может расти больше. Размер окна в обычном смысле, в смысле RFC 793, который является классическим RFC, у нас ограничен 65 килобайтами. И поэтому, когда мы начинаем отправлять это окно в канале с определённым bandwidth, мы отправляем 64 килобайта, и это окно заканчивается достаточно быстро.
Если мы используем опцию Window Scale, то мы можем увеличить размер окна практически до одного гигабайта. И соответственно, мы в течение того же самого времени delay на таком же канале с тем же bandwidth можем отправить уже не 64 килобайта, а гигабайт. И это намного упрощает нашу жизнь. Но всё равно, даже если вы используете этот Window Scale, и даже используете максимальные мультипликаторы, и раскачиваете окно до гигабайтного значения, на определённых каналах TCP в своём нынешнем виде всё равно будет давать сбой. Не в том смысле сбой, а в том смысле, что у него всё равно будет ограничение по производительности сверху. Но это ограничение уже будет вырождаться не в мегабитах или не в мегабайтах. Если мне память не изменяет, что-то порядка 20 гигабит в секунду — у TCP на современных каналах уже будут начинаться лёгкие проблемы. Опять же, чем больше задержка, тем меньше bandwidth будет вызывать проблему, и чем больше bandwidth, тем меньше delay будет нужно.
На классических каналах, например, два Ethernet соединения, связанные с помощью EtherChannel, TCP будет вызывать проблемы. Нет, про Ethernet я, наверное, зря сказал. Про Ethernet всё у нас по одному каналу идёт. 20 гигабит — это цифра, которую я помню из интернета, что на 20 гигабит в секунду и на задержках порядка одной сотой миллисекунды, то есть 10 микросекунд, у TCP будут возникать уже проблемы с произведением bandwidth-delay. Ещё одна проблема, которая в TCP есть, — это синхронизация. Синхронизация заключается в следующем. Если у нас есть интерфейс, мы через этот интерфейс должны отправлять какие-то данные, и мы начинаем отправлять данные, и у нас случается перегрузка.
В чём это выражается? В том, что мы кладём пакеты в буфер для отправки данных, пакеты этот буфер забивают, и новые пакеты, которые мы собираемся положить в буфер, туда не влезают, потому что там уже всё занято. У нас размер буфера ограничен, и мы его не успеваем разбирать. Соответственно, пакеты, которые будут дропаться, будут дропаться по правилу, которое называется tail drop. Всё, что мы не можем поставить в конец очереди, будет отбрасываться, потому что а что с ним ещё делать? Если у вас есть TCP-шные сессии, которые идут через один и тот же интерфейс, и у вас, давайте попробуем рисовать, есть роутер, у него есть выходной интерфейс, и есть несколько TCP-шных сессий, которые приходят и которые надо пропустить через один и тот же интерфейс. В выходном интерфейсе у нас забита вся очередь. Соответственно, приходит один пакетик из одной сессии, из другой, из третьей, и все они одновременно будут сбрасываться, потому что у нас очередь перегружена. В этой ситуации, если у нас несколько сессий одновременно будут сбрасываться, получится, что все сессии одновременно получают потерю данных.
А когда TCP начинает терять данные, он начинает играть окном. Он начинает сжимать окно и отправляет меньше данных. У него эффективная полоса пропускания, которую он занимает, становится меньше. В одной сессии потеряли пакет — начали отправлять меньше данных. В другой сессии потеряли пакет — отправляем меньше данных. В третьей сессии потеряли пакет — отправляем меньше данных. Несколько сессий одновременно, которые шли через интерфейс и вызывали его перегрузку, теперь начали одновременно потреблять меньше трафика и одновременно снизили свою требуемую полосу пропускания. У вас в какой-то момент времени возникла перегрузка, потому что много трафика через интерфейс посылалось, а потом в следующий квант времени трафик начинает резко меньше через интерфейс проходить. Интерфейс был загружен на 100%, потому что была перегрузка. Потом несколько сессий получили tail drop'ы, и средняя загрузка интерфейса снизилась до 70%.
А потом эти сессии начинают видеть, что всё, что по сети бегает, хорошо проходит, снова начинают раскачиваться, раскачиваются, раскачиваются, доходят до 100%, снова видят tail drop'ы, снова падают до 70%, и у вас получается загрузка интерфейса вот такой пилой. Эта пила по факту означает, что у вас одновременно все сессии начинают проседать по производительности. И именно это возникает потому, что все сессии одновременно начинают страдать от tail drop'ов. Логика, которая может помочь предотвратить такое поведение, заключается в том, что можно заставлять страдать эти сессии в разное время. Да, нам всё равно математика не позволяет пропихать через один интерфейс много разных данных, но по крайней мере мы можем сделать так, чтобы у нас не пила была 100–70, 100–70, а чтобы у нас интерфейс был загружен плюс-минус равномерно. Тогда мы сможем больше трафика отправить, пусть не очень много, но всё равно больше. И в этой ситуации можно использовать механизмы, которые называются RED или WRED — Random Early Detection или Weighted Random Early Detection.
WRED — это цисковская фирменная штука, RED — это стандартная вещь. Заключается она в том, что мы отбрасываем пакеты, когда у нас очередь ещё не заполнена до конца. Когда пакет просто приходит, интерфейс свободен, мы его кладём в буфер и дальше из этого буфера он сразу забирается. Если очередь начинает расти, это значит, что интерфейс отправляет данные всё время и он не успевает разбирать эту очередь до того, как она заполняется. Начинает очередь расти, расти, расти, расти. Это значит, что интерфейс загружен на 100%. И если очередь доходит, например, до 50% своего объёма, то мы сбрасываем пакеты с вероятностью до 10%. Приходит новый пакет, а мы говорим: ты неудачник, мы тебя расстреливаем. А другие 9 пакетов нормально проходят при этом. Этот механизм RED как раз позволяет отбрасывать TCP-шные сегменты не одновременно во всех сессиях, а в разное время. Сегодня потерялся пакет из одной сессии, завтра из другой, через два дня из третьей.
Не с разницей в день, конечно, с разницей в миллисекунды. Но тем не менее у вас не одновременно начинают эти дропы происходить для всех сессий. А в один момент времени для одной сессии дроп, в другой момент времени для другой сессии дроп, в третий — для третьей. И получается, пила у нас уже не такая большая, как была, 100–70, а там 99–91, 99–91. Это более эффективное использование полосы пропускания. И если вы знаете, что у TCP есть такая проблема, которая называется TCP synchronization, что TCP-шные сессии имеют склонность одновременно все проседать по производительности, а потом все одновременно раскачиваться по производительности, то лечить её можно как раз этим самым RED. Опять же, вы должны будете знать, что у вас есть много TCP-шных сессий, которые проходят через интерфейс. Должны будете пронаблюдать эту пилу и сказать, что да, мы знаем, что пила вызывается очень часто синхронизацией TCP.
Ну и ещё одна штука, которую нужно знать про трафик, — это UDP Starvation. С TCP-проблемами мы более-менее обсудили, которые бывают. У UDP проблем нет. UDP всегда хорошо себя чувствует. UDP отправляет свои данные, и как только он отправил датаграмму в сеть, он про неё забывает. Дошло, не дошло — UDP вообще по барабану. Соответственно, если у вас есть приложение, которое хочет одновременно отправить много данных по UDP, оно отправляет эти данные — сколько в интерфейс вошло, столько отправит. Никто вообще не проверяет, что с этими данными дальше случилось. Доставились они получателю, не доставились. Забили они где-нибудь буфер, не забили, потерялись, не потерялись. Естественно, если вы отправили данные — классический пример: у нас есть узел. Он отправляет данные на свитч, интерфейс, который у него есть, 100 мегабит, допустим. Дальше это всё дело втыкается в роутер. И роутер дальше всё это дело должен отправить в канал.
Здесь у нас тоже 100 мегабит, а здесь у нас какой-нибудь условный ADSL с производительностью 8 мегабит в секунду. И UDP отправил 100-мегабитный поток в сторону свитча. Свитч переправил это всё в сторону роутера. Роутер получил 100-мегабитный поток и сложил всё в буфер на отправку в 8-мегабитный канал. Естественно, у него этот буфер очень быстро забился. К нему приходит поток 100 мегабит, он отправляет поток 8 мегабит. Математика запрещает такое взаимодействие в течение долгого времени, пока у вас буферы все не заполнятся. Это ещё как-то можно пережить. Но дальше все буферы UDP забивает и начинаются дропы. А что ещё делать? UDP в принципе вообще никаким образом не переживает, когда случаются дропы. Но вы должны понимать, что, во-первых, приложение, которое будет использовать этот трафик, оно, возможно, каким-то образом пострадает. Если у вас в UDP никаких гарантий доставки нет, то приложение, которое использует UDP, может потерять трафик, а для него это будет нормой.
А во-вторых, что будет, если вы в один и тот же буфер будете складывать TCP-шные и UDP-шные данные? Это происходит, кстати, по умолчанию. Если вы QoS нигде не настраиваете, то у вас все данные смешиваются в одной и той же исходящей очереди. UDP забивает все эти буферы, и TCP говорит: ой, а у меня tail drop. И начинает сжимать свою полосу пропускания, которую он требует. У вас был интерфейс. Этот интерфейс был загружен, например, на 50% TCP, а остальные 50% были свободны. И все были счастливы. Пришла одна UDP-шная сессия, забила все буферы. Оставшиеся 50% забила. Все TCP-сессии сказали: ой, у нас не хватает полосы, мы начинаем сжимать окно. Они сжались вдвое, например, до 25 мегабит. UDP сказал: о, прикольно, у меня освободилась полоса, я её тоже сожру. И он сжирает ровно столько, сколько вы ему даёте. А TCP стесняется отбирать своё и начинает сжиматься, сжиматься, сжиматься. И дожимает себя до того состояния, при котором нормальная работа уже невозможна.
Поэтому, если у вас есть TCP и UDP, которые вы смешиваете в одной очереди, а это происходит по умолчанию, то UDP может эту очередь забить целиком. И для TCP просто не останется места. Эта штука называется UDP Starvation. Как с этим бороться? Настраивать QoS и не смешивать TCP и UDP в одной очереди. Это просто ошибка дизайна.