Русский
Русский
English
Статистика
Реклама

Балансировка нагрузки

Динамическая балансировка нагрузки в pull-схеме

01.09.2020 22:17:56 | Автор: admin
В прошлой новости про принципы работы коллекторов логов PostgreSQL я упомянул, что одним из недостатков pull-модели является необходимость динамической балансировки нагрузки. Но если делать ее аккуратно, то недостаток превращается в достоинство, а система в целом становится гораздо более устойчивой к изменениям потока данных.


Давайте посмотрим, какие решения есть у этой задачи.

Распределение объектов по мощности


Чтобы не углубляться в неинтересные абстракции, будем рассматривать на примере конкретной задачи мониторинга. Соотнести предлагаемые методики на свои конкретные задачи, уверен, вы сможете самостоятельно.

Равномощные объекты мониторинга


В качестве примера можно привести наши коллекторы метрик для Zabbix, которые исторически имеют с коллекторами логов PostgreSQL общую архитектуру.

И правда, каждый объект мониторинга (хост) генерирует для zabbix практически стабильно один и тот набор метрик с одной и той же частотой все время:


Как видно на графике, разница между min-max значениями количества генерируемых метрик не превышает 15%. Поэтому мы можем считать все объекты равными в одинаковых попугаях.

Сильный дисбаланс между объектами


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

Например, база рабочего один хост может генерировать в лог миллион планов за сутки, другой десятки тысяч, а какой-то и вовсе единицы. Да и сами эти планы по объему и сложности и по распределению во времени суток сильно отличаются. Так и получается, что нагрузку сильно качает, в разы:


Ну, а раз нагрузка может меняться настолько сильно, то надо учиться ей управлять

Координатор


Сразу понимаем, что нам явно понадобится масштабирование системы коллекторов, поскольку один отдельный узел со всей нагрузкой когда-то точно перестанет справляться. А для этого нам потребуется координатор тот, кто будет управлять всем зоопарком.

Получается примерно такая схема:


Каждый worker свою нагрузку в попугаях и в процентах CPU периодически сбрасывает master'у, те коллектору. А он, на основании этих данных, может выдать команду типа новый хост посадить на ненагруженный worker#4 или hostA надо пересадить на worker#3.

Тут еще надо помнить, что, в отличие от объектов мониторинга, сами коллекторы имеют вовсе не равную мощность например, на одном у вас может оказаться 8 ядер CPU, а на другом только 4, да еще и меньшей частоты. И если нагрузить их задачами поровну, то второй начнет затыкаться, а первый простаивать. Отсюда и вытекают

Задачи координатора


По сути, задача всего одна обеспечивать максимально равномерное распределение всей нагрузки (в %cpu) по всем доступным worker'ам. Если мы сможем решить ее идеально, то и равномерность распределения %cpu-нагрузки по коллекторам получим автоматом.

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

Динамическая балансировка


Простую задачу (zabbix) мы можем решить достаточно банально:

  • вычисляем относительную мощность каждого коллектора в задачах
  • делим все задачи между ними пропорционально
  • между worker'ами распределяем равномерно



Но что делать в случае сильно неравных объектов, как для коллектора логов?..

Оценка равномерности


Выше мы все время употребляли термин "максимально равномерное распределение", а как вообще можно формально сравнить два распределения, какое из них равномернее?

Для оценки равномерности в математике давно существует такая вещь как среднеквадратичное отклонение. Кому лениво вчитываться:
S[X] = sqrt( sum[ ( x - avg[X] ) ^ 2 of X ] / count[X] )

Поскольку количество worker'ов на каждом из коллекторов у нас тоже может отличаться, то нормировать разброс по нагрузке надо не только между ними, но и между коллекторами в целом.

То есть распределение нагрузки по worker'ам двух коллекторов [ (10%, 10%, 10%, 10%, 10%, 10%) ; (20%) ] это тоже не очень хорошо, поскольку на первом получается 10%, а на втором 20%, что как бы вдвое больше в относительных величинах.

Поэтому введем единую метрику-расстояние для общей оценки равномерности:
d([%wrk], [%col]) = sqrt( S[%wrk] ^ 2 + S[%col] ^ 2 )
То есть величины среднеквадратичного отклонения для наборов величин нагрузки по всем worker'ам и по всем коллекторам воспринимаем как координаты вектора, длину которого будем стараться минимизировать.

Моделирование


Если бы объектов у нас было немного, то мы могли бы полным перебором разложить их между worker'ами так, чтобы метрика оказалась минимальной. Но объектов у нас тысячи, поэтому такой способ не подойдет. Зато мы знаем, что коллектор умеет перемещать объект с одного worker'а на другой давайте этот вариант и смоделируем, используя метод градиентного спуска.

Понятно, что идеальный минимум метрики мы так можем и не найти, но локальный точно. Да и сама нагрузка может изменяться во времени настолько сильно, что искать за бесконечное время идеал абсолютно незачем.

То есть нам осталось всего лишь определить, какой объект и на какой worker эффективнее всего переместить. И сделаем это банальным переборным моделированием:

  • для каждой пары (целевой host, worker) моделируем перенос нагрузки
  • нагрузку от host внутри исходного worker'а считаем пропорционально попугаям
    В нашем случае за попугая оказалось вполне разумно взять объем получаемого потока логов в байтах.
  • относительную мощность между коллекторами считаем пропорциональной суммарным попугаям
  • вычисляем метрику d для перенесенного состояния

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

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

Если же уменьшать некуда вот он локальный минимум!

Пример на картинке:


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

Микро-оптимизации


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

Нулевые попугаи


Если на замеренном интервале объект/задача/хост сгенерировал нагрузку 0 штук, то его не то что перемещать куда-то его даже рассматривать и анализировать не надо.

Самоперенос


При генерации пар нет необходимости оценивать эффективность переноса объекта на тот же самый worker, где он и так находится. Все-таки уже будет T x (W - 1) мелочь, а приятно!

Неразличимая нагрузка


Поскольку мы моделируем все-таки перенос именно нагрузки, а объект всего лишь инструмент, то пробовать переносить одинаковый %cpu нет смысла значения метрик останутся точно те же, хоть и для другого распределения объектов.

То есть достаточно оценить единственную модель для кортежа (wrkSrc, wrkDst, %cpu). Ну, а одинаковыми вы можете считать, например, значения %cpu, совпадающие до 1 знака после запятой.

Пример реализации на JavaScript
var col = {  'c1' : {    'wrk' : {      'w1' : {        'hst' : {          'h1' : 5        , 'h2' : 1        , 'h3' : 1        }      , 'cpu' : 80.0      }    , 'w2' : {        'hst' : {          'h4' : 1        , 'h5' : 1        , 'h6' : 1        }      , 'cpu' : 20.0      }    }  }, 'c2' : {    'wrk' : {      'w1' : {        'hst' : {          'h7' : 1        , 'h8' : 2        }      , 'cpu' : 100.0      }    , 'w2' : {        'hst' : {          'h9' : 1        , 'hA' : 1        , 'hB' : 1        }      , 'cpu' : 50.0      }    }  }};// вычисляем опорные метрики и нормализуем по "мощности"let $iv = (obj, fn) => Object.values(obj).forEach(fn);let $mv = (obj, fn) => Object.values(obj).map(fn);// initial reparsefor (const [cid, c] of Object.entries(col)) {  $iv(c.wrk, w => {    w.hst = Object.keys(w.hst).reduce((rv, hid) => {      if (typeof w.hst[hid] == 'object') {        rv[hid] = w.hst[hid];        return rv;      }      // нулевые значения ничего не решают, поэтому сразу отбрасываем      if (w.hst[hid]) {        rv[hid] = {'qty' : w.hst[hid]};      }      return rv;    }, {});  });  c.wrk = Object.keys(c.wrk).reduce((rv, wid) => {    // ID воркеров должны быть глобально-уникальны    rv[cid + ':' + wid] = c.wrk[wid];    return rv;  }, {});}// среднеквадратичное отклонениеlet S = col => {  let wsum = 0    , wavg = 0    , wqty = 0    , csum = 0    , cavg = 0    , cqty = 0;  $iv(col, c => {    $iv(c.wrk, w => {      wsum += w.cpu;      wqty++;    });    csum += c.cpu;    cqty++;  });  wavg = wsum/wqty;  wsum = 0;  cavg = csum/cqty;  csum = 0;  $iv(col, c => {    $iv(c.wrk, w => {      wsum += (w.cpu - wavg) ** 2;    });    csum += (c.cpu - cavg) ** 2;  });  return [Math.sqrt(wsum/wqty), Math.sqrt(csum/cqty)];};// метрика-расстояниеlet distS = S => Math.sqrt(S[0] ** 2 + S[1] ** 2);// выбираем оптимальный перенос и моделируем егоlet iterReOrder = col => {  let qty = 0    , max = 0;  $iv(col, c => {    c.qty = 0;    c.cpu = 0;    $iv(c.wrk, w => {      w.qty = 0;      $iv(w.hst, h => {        w.qty += h.qty;      });      w.max = w.qty * (100/w.cpu);      c.qty += w.qty;      c.cpu += w.cpu;    });    c.cpu = c.cpu/Object.keys(c.wrk).length;    c.max = c.qty * (100/c.cpu);    qty += c.qty;    max += c.max;  });  $iv(col, c => {    c.nrm = c.max/max;    $iv(c.wrk, w => {      $iv(w.hst, h => {        h.cpu = h.qty/w.qty * w.cpu;        h.nrm = h.cpu * c.nrm;      });    });  });  // "текущее" среднеквадратичное отклонение  console.log(S(col), distS(S(col)));  // формируем набор хостов и воркеров  let wrk = {};  let hst = {};  for (const [cid, c] of Object.entries(col)) {    for (const [wid, w] of Object.entries(c.wrk)) {      wrk[wid] = {        wid      , cid      , 'wrk' : w      , 'col' : c      };      for (const [hid, h] of Object.entries(w.hst)) {        hst[hid] = {          hid        , wid        , cid        , 'hst' : h        , 'wrk' : w        , 'col' : c        };      }    }  }  // реализация переноса нагрузки на целевой worker  let move = (col, hid, wid) => {    let w = wrk[wid]      , h = hst[hid];    let wsrc = col[h.cid].wrk[h.wid]      , wdst = col[w.cid].wrk[w.wid];    wsrc.cpu -= h.hst.cpu;    wsrc.qty -= h.hst.qty;    wdst.qty += h.hst.qty;    // перенос на другой коллектор с "процентованием" нагрузки на CPU    if (h.cid != w.cid) {      let csrc = col[h.cid]        , cdst = col[w.cid];      csrc.qty -= h.hst.qty;      csrc.cpu -= h.hst.cpu/Object.keys(csrc.wrk).length;      wsrc.hst[hid].cpu = h.hst.cpu * csrc.nrm/cdst.nrm;      cdst.qty += h.hst.qty;      cdst.cpu += h.hst.cpu/Object.keys(cdst.wrk).length;    }    wdst.cpu += wsrc.hst[hid].cpu;    wdst.hst[hid] = wsrc.hst[hid];    delete wsrc.hst[hid];  };  // моделирование и оценка переноса для пары (host, worker)  let moveCheck = (orig, hid, wid) => {    let w = wrk[wid]      , h = hst[hid];    // тот же воркер - ничего не делаем    if (h.wid == w.wid) {      return;    }    let col = JSON.parse(JSON.stringify(orig));    move(col, hid, wid);    return S(col);  };  // хэш уже проверенных переносов (hsrc,hdst,%cpu)  let checked = {};  // перебираем все возможные пары (какой хост -> на какой воркер)  let moveRanker = col => {    let currS = S(col);    let order = [];    for (hid in hst) {      for (wid in wrk) {        // нет смысла пробовать повторно перемещать одну и ту же (с точностью до 0.1%) "мощность" между одной парой воркеров        let widsrc = hst[hid].wid;        let idx = widsrc + '|' + wid + '|' + hst[hid].hst.cpu.toFixed(1);        if (idx in checked) {          continue;        }                let _S = moveCheck(col, hid, wid);        if (_S === undefined) {          _S = currS;        }        checked[idx] = {          hid        , wid        , S : _S        };        order.push(checked[idx]);      }    }    order.sort((x, y) => distS(x.S) - distS(y.S));    return order;  };  let currS = S(col);  let order = moveRanker(col);  let opt = order[0];  console.log('best move', opt);  // реализуем перенос  if (distS(opt.S) < distS(currS)) {    console.log('move!', opt.hid, opt.wid);    move(col, opt.hid, opt.wid);    console.log('after move', JSON.parse(JSON.stringify(col)));    return true;  }  else {    console.log('none!');  }  return false;};// пока есть что-куда переноситьwhile(iterReOrder(col));

В результате, нагрузка по нашим коллекторам распределяется практически одинаково в каждый момент времени, оперативно нивелируя возникающие пики:

Подробнее..

Готовим видеосервис к нагрузке в сотни Гбитс. Доклад Яндекса

30.09.2020 12:15:28 | Автор: admin
Классический CDN anycast, GeoDNS, веб-сервер с кешем отлично работает с простыми файлами и небольшим количеством пользователей. Но если возникает необходимость раздавать потоковое видео, всё становится намного интереснее. Вместо одного короткого запроса появляется сессия, которая длится десятки минут. Без правильной балансировки пользователей и контента уже не прожить: кеша на всё не хватает, а когда Россия играет против Испании, это хотят смотреть сразу все. Руководитель разработки платформы видеостриминга Андрей Василенков рассказал, благодаря чему наш CDN позволяет обслуживать сотни тысяч пользовательских сессий одновременно и переживать отключения серверов и дата-центров. А в качестве бонуса показал на примере, как современная поп-культура мешает обучению.


Всем привет! Расскажу, какие вопросы приходится решать, когда вам нужно подготовить ваш сервис к нагрузкам в несколько сотен гигабит, а то и терабит в секунду. Впервые мы с такой проблемой столкнулись в 2018 году, когда готовились к трансляциям чемпионата мира по футболу.

Начнем с того, что вообще такое стриминговые протоколы и как они устроены самый поверхностный обзорный вариант.



В основе любого стримингового протокола лежит манифест [manifest] или плейлист [playlist]. Это небольшой текстовый файл, который содержит метаинформацию о контенте. Там описан тип контента live-трансляция или VoD-трансляция (video on demand). Например, в случае live это футбольный матч или онлайн-конференция, как у нас сейчас с вами, а в случае VoD ваш контент заранее подготовлен и лежит на ваших серверах, готовый к раздаче в сторону пользователей. В этом же файле описана длительность контента, информация о DRM.



Там же описаны вариации контента видеодорожки, аудиодорожки, субтитры. Видеодорожки могут быть представлены в разных кодеках. Например, универсальный H.264 поддерживается на любом устройстве. С его помощью вы сможете проиграть видео на любом утюге у вас дома. Или есть более современные и более эффективные кодеки HEVC и VP9, которые позволяют вам передавать картинку 4K с поддержкой HDR.

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



Что со всем этим делает плеер? Задача плеера в первую очередь выбрать те вариации контента, которые он может проиграть, просто потому что не все кодеки универсальны, не все могут быть проиграны на определенном устройстве.

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

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

После этого он начинает формировать ссылки на видео- и аудиосегменты. На самом деле это обычные HTTP-ссылки, такие же, как во всех остальных сценариях в интернете. И он начинает скачивать видео- и аудиосегменты, складывать их в буфер друг за другом и бесшовно проигрывать. Такие видеосегменты, как правило, имеют длительность 2, 4, 6 секунд, может быть, 10 секунд в зависимости от вашего сервиса.



Какие тут есть важные моменты, про которые нам нужно думать, когда мы проектируем наш CDN? В первую очередь, у нас появляется сессия пользователя.

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

Тут важно понимать, что время ответа сервера тоже имеет значение. Если мы показываем какую-нибудь live-трансляцию в реальном времени, то не можем делать большой буфер просто потому, что пользователь хочет смотреть видео настолько близко к реальному времени, насколько возможно. Ваш буфер в принципе не может быть большим. Соответственно, если сервер не успевает отвечать за то время, пока пользователь успевает просматривать контент, видео в какой-то момент просто зависнет. К тому же контент довольно тяжеловесный. Стандартный битрейт для Full HD 1080p 3-5 Мбит/с. Соответственно, на одном гигабитовом сервере вы не сможете обслужить больше 200 пользователей одновременно. И это идеальная картинка, потому что пользователи, как правило, равномерно по времени со своими запросами не ходят.



В какой момент пользователь вообще взаимодействует с вашим CDN? Взаимодействие происходит в основном в двух местах: когда плеер скачивает манифест (плейлист), и когда он скачивает сегменты.

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

Задача всей нашей системы сводится к тому, что мы хотим сформировать правильную ссылку на эти сегменты и подставить туда правильный домен какого-нибудь нашего CDN-хоста. В этом месте мы пользуемся следующей стратегией: сразу в плейлисте отдаем нужный CDN-хост, куда пользователь пойдет. Этот подход лишен многих недостатков, но обладает одним важным нюансом. Вам нужно гарантировать, что у вас есть механизм, который позволит увести пользователя с одного хоста на другой бесшовно в процессе проигрывания, не прерывая просмотр. На самом деле такая возможность есть у всех современных стриминговых протоколов, и HLS, и DASH такое поддерживают. Нюанс: довольно часто даже в очень популярных опенсорс-библиотеках такая возможность не реализована, хоть и существует по стандарту. Нам самим приходилось посылать пачки в опенсорс-библиотеку Shaka, она джаваскриптовая, используется для веб-плеера, для проигрывания DASH.

Есть еще одна схема anycast-схема, когда вы используете один единый домен и отдаете во всех ссылках именно его. В этом случае вам не нужно думать ни про какие нюансы, отдаете один домен, и все счастливы. (...)



Теперь давайте поговорим про то, как мы будем формировать наши ссылки.

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

Этот набор серверов мы будем называть локацией. И в каждой такой локации у нас есть уникальный идентификатор. Мы его будем использовать в составе доменного имени хостов на этой площадке и просто чтобы однозначно ее идентифицировать.

Таких площадок в Яндексе несколько десятков, серверов на них несколько сотен, и в каждую локацию приходят линки от нескольких операторов, поэтому линков у нас тоже порядка нескольких сотен.

Как мы будем выбирать, на какую локацию отправить конкретного пользователя?



На этом этапе вариантов не очень много. Мы можем использовать только IP-адрес, чтобы принимать решения. С этим нам помогает отдельная команда Яндекса Traffic Team, которая знает все про то, как устроен трафик и сеть в компании, и именно она собирает маршруты других операторов, чтобы мы использовали эти знания в процессе балансировки пользователей.

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

Мы получаем от команды Traffic Team набор IP-сетей и линков, через которые мы можем обслуживать клиентов. Дальше нам нужно понять, какая же IP-подсеть подходит конкретному пользователю.



Делаем это довольно простым способом строим префиксное дерево. А дальше наша задача используя в качестве ключа IP-адрес пользователя, найти, какая подсеть наиболее точно соответствует этому IP-адресу.



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



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

Обязательно нужно смотреть, как происходит реальное скачивание данных пользователями. Это можно делать и на серверной, и на клиентской стороне. На сервере мы активно собираем в логах TCP info пользовательских соединений, смотрим на round-trip time. С пользовательской стороны мы активно собираем perf-логи браузера и плеера. В этих perf-логах есть подробная информация о том, как происходило скачивание файлов с нашего CDN.

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



Допустим, мы выбрали линк. Можем ли мы прямо на этом этапе отправлять туда пользователей? Не можем, потому что вес довольно статичен на протяжении долгого периода времени, и он не учитывает никакую реальную динамику нагрузки. Мы хотим в реальном времени определять, можем ли мы сейчас использовать линк, загруженный, скажем, на 80%, когда рядом есть чуть менее приоритетный линк, загруженный всего на 10%. Скорее всего, в этом случае мы как раз захотим использовать второй.



Что еще нужно принимать во внимание в этом месте? Мы должны учитывать пропускную способность линка, понимать его текущий статус. Он может работать или быть технически неисправным. Или, может быть, мы его хотим увести в сервис, чтобы не пускать туда пользователей и обслуживать его, расширить, например. Мы всегда должны учитывать текущую нагрузку этого линка.

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

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

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

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



Допустим, мы посчитали метрики, всё собрали. Теперь нужен алгоритм принятия решения при балансировке. Он должен обладать четырьмя важными свойствами:

Обеспечивать пропускную способность линка.
Предупреждать перегрузку линка, просто потому что если вы загрузили линк на 95% или 98%, то у вас начинают переполняться буферы на сетевом оборудовании, начинаются дропы пакетов, ретрансмиты, и пользователи ничего хорошего от этого не получают.
Предупреждать пил нагрузки, про это поговорим чуть позже.
В идеальном мире было бы здорово, чтобы мы научились утилизировать линк до определенного уровня, который мы считаем правильным. Например, 85% загрузки.



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

Что мы с ними будем делать? Введем по одной вероятностной величине на каждый такой класс сессии. У нас будет величина под названием Slowdown, определяющая процент новых сессий, которые мы не будем пускать на этот линк. Если Slowdown равен нулю, то мы все новые сессии принимаем, а если он равен 50%, то каждую, грубо говоря, вторую сессию мы отказываемся обслуживать на этом линке. При этом наш алгоритм балансировки на более высоком уровне будет проверять альтернативные варианты для этого пользователя. Drop то же самое, только для текущих сессий. Мы можем часть пользовательских сессий увести с площадки куда-нибудь в другое место.



Как мы будем выбирать, каким будет значение наших вероятностных метрик? В качестве основы возьмем процент загрузки линка, а дальше первая наша идея была такой: давайте воспользуемся кусочно-линейной интерполяцией.

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

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

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

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



Даже невооруженным глазом сторонний наблюдатель может понять, что тут что-то, наверное, не так, и спросить меня: Андрей, все ли у вас хорошо?. А если бы вы были моим начальником, вы бы бегали по комнате и кричали: Андрей, боже мой! Откатывайте все назад! Верните все как было! Давайте расскажу, что тут происходит.

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

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

На втором линке повторяется та же картинка. Мы резко повышаем нагрузку, замечаем, что линк перегружен, снимаем нагрузку, и эти два линка оказываются в противофазе по уровню нагрузки.



Что можно сделать? Мы можем проанализировать динамику системы, при большом росте нагрузки замечать это и немного ее демпфировать. Именно это мы и сделали. Мы взяли текущий момент, взяли окно наблюдений в прошлое за несколько минут, например 2-3 минуты, и посмотрели, насколько сильно меняется загрузка линка на этом интервале. Разницу между минимальным и максимальным значением мы будем называть интервалом колебаний этого линка. И если этот интервал колебаний большой, мы добавим демпфирование, таким образом увеличим наш Slowdown и станем пускать меньше сессий.



Выглядит эта функция примерно так же, как и прошлая, с чуть меньшим числом переломов. Если у нас интервал загрузки колебаний небольшой, то никакого extra_slowdown мы добавлять не будем. А если интервал колебаний начинает расти, то extra_slowdown принимает ненулевые значения, позже мы будем прибавлять его к основному Slowdown.



Такая же логика работает и при низких значениях интервала колебаний. Если у вас колебания на линке минимальные, то вы, возможно, наоборот, хотите пустить туда немножко больше пользователей, снизить Slowdown и тем самым лучше утилизировать ваш линк.



Эту часть мы тоже внедрили. Итоговая формула имеет примерно следующий вид. При этом мы гарантируем, что обе эти величины extra_slowdown и reduce_slowdown никогда не имеют ненулевого значения одновременно, поэтому эффективно работает только одна из них. Именно в таком виде эта формула балансировки пережила все топовые матчи чемпионата мира по футболу. Даже на самых популярных матчах она работала довольно хорошо: Это Россия Хорватия, Россия Испания. Во время этих матчей мы раздавали рекордные для Яндекса объемы трафика 1,5 терабита в секунду. Мы спокойно это пережили. С тех пор формула никак не менялась, потому что такого трафика на нашем сервисе с тех пор не было до определенного момента.

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



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



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

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

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



Нам обязательно нужны быстрые метрики. Без них единственное, что вы можете сделать, избежав страданий пользователя, недоутилизировать сеть. Но это нас тоже не устраивает.

Если посмотреть на нашу систему высокоуровнево, станет понятно, что наша система динамическая система с обратной связью. У нас есть пользовательская нагрузка, которая является входящим сигналом. Люди приходят и уходят. У нас есть контрольный сигнал те самые две величины, которые мы можем менять в реальном времени. Для таких динамических систем с обратной связью уже давно, несколько десятилетий, разрабатывается теория автоматического управления. И именно ее компоненты мы бы хотели применить для того, чтобы стабилизировать нашу систему.



Мы посмотрели на фильтр Калмана. Это такая крутая штука, которая позволяет построить математическую модель системы и при зашумленных метриках или при отсутствии некоторых классов метрик улучшить модель с помощью вашей реальной системы. И потом принимать решение о реальной системе, основываясь на математической модели. К сожалению, оказалось, что у нас не так много классов метрик, которые мы можем использовать, и не получается этот алгоритм применить.



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

Со дня на день мы попробуем этот PID-регулятор в продакшене. Возможно, через несколько месяцев сможем рассказать вам о результатах.

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



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

У меня есть еще одна интересная история. Весной, как вы знаете, начался карантин. У Яндекса давно есть образовательная платформа Яндекс.Учебник, которая позволяет учителям загружать ролики, уроки. Школьники туда приходят и смотрят контент. Во время пандемии Яндекс начал поддерживать школы, активно звать их на свою платформу, чтобы школьники могли обучаться удаленно. И мы в какой-то момент увидели довольно хороший рост трафика, довольно стабильную картинку. Но в один из апрельских вечеров мы увидели на графиках примерно следующее.



Снизу картинка трафика на образовательном контенте. Мы увидели, что он в какой-то момент резко упал. Начали паниковать, выяснять, что вообще происходит, что у нас сломалось. Потом мы заметили, что общий трафик на сервис при этом начал расти. Очевидно, случилось что-то интересное.

На самом деле в этот момент произошло следующее.



Вот как быстро танцует человечек.

Начался концерт Little Big, и все школьники ушли смотреть его. Но после окончания концерта они вернулись и с успехом продолжили обучаться дальше. Такие картинки мы довольно часто наблюдаем на нашем сервисе. Поэтому, думаю, работа у нас довольно интересная. Всем спасибо! На этом про CDN я, наверное, закончу.
Подробнее..

Как не держать лишнее железо и справляться с ростом нагрузки внедрение graceful degradation в Яндекс.Маркете

14.01.2021 12:05:51 | Автор: admin

Привет, меня зовут Евгений. Я разрабатываю инфраструктуру поиска Яндекс.Маркета. Хочу рассказать, как graceful degradation помогает нам обрабатывать больше запросов, чем физически могут выдержать наши сервера, и что происходит с поиском в Маркете, если один из дата-центров отключается.

Проблема

Все критичные сервисы в Яндексе должны переживать отключение одного из дата-центров (ДЦ), поэтому обычно они размещаются минимум в трёх ДЦ.

Обычное состояниеОбычное состояние

Но когда все ДЦ работают, каждый из них оказывается задействован лишь на 60%. Ещё 10% резервируются для экспериментов и непредвиденного роста, а оставшиеся 30% используются только в случае, если один из ДЦ отключается, и нужно перераспределить запросы.

ДЦ 2 отключёнДЦ 2 отключён

Если сервис работает на сотнях серверов, 30% это огромное количество железа. А поскольку отключаются ДЦ очень редко, резервные мощности почти всегда простаивают.

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

ДЦ 1 и ДЦ 3 не справляются с нагрузкойДЦ 1 и ДЦ 3 не справляются с нагрузкой

Применяем graceful degradation

Graceful degradation или изящная деградация это подход к построению систем, при котором система продолжает выполнять свою основную функцию, даже если отдельные её элементы вышли из строя.

Мой коллега из Яндекс.Такси уже рассказывал про graceful degradation в случае, когда ломается часть сервиса. Мы же применили этот подход на бэкендах для ситуации, когда у нас отключается значительное количество серверов.

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

Чтобы запустить graceful degradation, нам надо было решить две задачи:

  1. Разработать механизм уменьшения нагрузки.

  2. Сделать автоматизацию включения механизма.

Механизм уменьшения нагрузки

Уменьшить нагрузку можно по-разному. Один из вариантов отвечать ошибкой на некоторые запросы. Но тогда надо понять, на что можно так отвечать, а на что нет. То есть нужен какой-то параметр важность, которого мы не знаем. Если спросить у наших клиентов, что важно, они ответят, что важны все запросы, и ничего нельзя отбрасывать. Поэтому этот путь нам не подошёл.

Другой вариант использовать облегчённый алгоритм ранжирования. Минус в том, что это долго и сложно новый алгоритм надо обучить и потом постоянно переобучать. Кроме того, состояние нашей системы может быть только вкл. или выкл., а нам хотелось бы иметь механизм плавного уменьшения нагрузки.

Таким образом, мы выбрали третий вариант снижать качество. Основное преимущество этого подхода возможность уменьшать нагрузку на 5%, 10% и так далее.

Допустим, вы ищете себе новый телефон и заходите в категорию Смартфоны на Яндекс.Маркете. Я уже подробно описывал, как работает наш поиск: запрос отправляется во фронтенд, надо показать пользователю 48 лучших предложений из категории с телефонами для конкретного региона. Фронтенд делает свой запрос в поисковый бэкенд.

Бэкенд получает запрос и раcпределяет его на 8 серверов с шардами. В шардах хранятся предложения от магазинов.

Общая схема обработки поискового запросаОбщая схема обработки поискового запроса

На каждом шарде поиск проходит несколько стадий. На стадии фильтрации ищется примерно 50000 предложений, это число зависит от категории. На этапе ранжирования для каждого предложения вычисляется релевантность, учитывается цена, рейтинг товара, рейтинг магазина и ещё более 2000 факторов. ML по факторам вычисляет вес каждого предложения. Затем берётся только 48 лучших. Meta Search получает эти 48 предложений с каждого шарда, то есть всего 48*8=384 предложения. Предложения снова ранжируются, опять берётся 48 лучших. Последние 48 уже показываются пользователю. То есть чтобы показать нашим пользователям 48 телефонов, мы обрабатываем 400 000 предложений.

Количество обрабатываемых документов без graceful degradationКоличество обрабатываемых документов без graceful degradation

В случае с graceful degradation, когда надо уменьшить нагрузку, мы можем скомандовать: теперь обрабатывай 95% документов, а теперь 90% или 80%. Если обрабатывать 95%, то есть 400000*0.95=380 000 документов, то из них всё равно выбираются 48 лучших предложений для выдачи. И в среднем только 2 предложения будут отличаться от изначальной выдачи без снижения качества. При таком маленьком изменении большинство пользователей даже не заметят разницы.

Количество обрабатываемых документов при включении механизма уменьшения нагрузкиКоличество обрабатываемых документов при включении механизма уменьшения нагрузки

Автоматизация включения механизма

Автоматизация работает за счёт постоянного мониторинга загрузки CPU. Если нагрузка становится выше 90%, автоматика начинает снижать качество. На 90% снижение небольшое, но если нагрузка продолжает расти, процент деградации повышается линейно и доходит до максимума при 100% загрузки CPU. Такой подход позволяет снижать качество минимально.

Общий алгоритм выглядит так:

При выключении ДЦ: балансеры перераспределяют запросы в оставшиеся ДЦ => нагрузка на CPU повышается => при превышении порогового значения происходит снижение качества по заданной формуле.

При включении ДЦ: балансеры перераспределяют запросы на все ДЦ => нагрузка на CPU снижается => понижение качества прекращается.

Повышение нагрузки при выключении ДЦ. Линии на верхнем графике показывают загрузку CPU в отдельных ДЦ. Нагрузка выросла с 82% до 98%. Нижний график показывает процент срезанных документов. Повышение нагрузки при выключении ДЦ. Линии на верхнем графике показывают загрузку CPU в отдельных ДЦ. Нагрузка выросла с 82% до 98%. Нижний график показывает процент срезанных документов.

Внедрение

Для внедрения мы провели несколько экспериментов и понижали качество на небольшом проценте пользователей. В течение недели мы постоянно отслеживали уменьшение нагрузки и одновременно количество переходов на магазины-партнёры. Имея на руках цифры, мы договорились с бизнесом на максимальное снижение кликов в 1% при снижении качества. Исходя из этого мы посчитали, сколько серверов можем отдать другим проектам или выключить для резерва. У нас получилось 15% всех мощностей поиска.

В конце, когда всё было готово, мы провели эксперимент с отключением дата-центра, чтобы убедиться, что всё работает как надо.

Выводы

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

Подробнее..

Перевод Устанавливаем балансировщик нагрузки HAProxy на CentOS

23.07.2020 18:11:16 | Автор: admin

Перевод статьи подготовлен в преддверии старта курса Администратор Linux. Виртуализация и кластеризация




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


HAProxy стремится оптимизировать использование ресурсов, максимизировать пропускную способность, минимизировать время отклика и избежать перегрузки каждого отдельно взятого ресурса. Она может быть установлена на множестве дистрибутивов Linux, таких как CentOS 8, на котором мы остановимся в этом руководстве, а также в системах Debian 8 и Ubuntu 16.



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


В качестве необходимого условия для достижения наилучших результатов у вас должно быть как минимум два веб-сервера и сервер для балансировки нагрузки. На веб-серверах должна быть запущена хотя бы базовая веб-служба, такая как nginx или httpd, для того чтобы можно было проверить балансировку нагрузки между ними.


Установка HAProxy на CentOS 8


В силу того, что HAProxy быстро развивающееся приложение с открытым исходным кодом, дистрибутив, доступный вам в стандартных репозиториях CentOS, может оказаться не самой последней версией. Чтобы узнать актуальную версию, выполните следующую команду:


sudo yum info haproxy

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


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


sudo yum install gcc pcre-devel tar make -y

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


wget http://www.haproxy.org/download/2.0/src/haproxy-2.0.7.tar.gz -O ~/haproxy.tar.gz

После завершения загрузки распакуйте файлы с помощью приведенной ниже команды:


tar xzvf ~/haproxy.tar.gz -C ~/

Перейдите в распакованный каталог с исходниками:


cd ~/haproxy-2.0.7

Затем скомпилируйте программу под вашу систему:


make TARGET=linux-glibc

И, наконец, установите саму HAProxy:


sudo make install

Теперь HAProxy установлена, но для ее работы требуются некоторые дополнительные манипуляции. Продолжим настройку программного обеспечения и сервисов ниже.


Настройка HAProxy под ваш сервер


Теперь добавьте следующие каталоги и файл статистики для записей HAProxy:


sudo mkdir -p /etc/haproxysudo mkdir -p /var/lib/haproxy sudo touch /var/lib/haproxy/stats

Создайте символьную ссылку для двоичных файлов, чтобы вы могли запускать команды HAProxy от имени обычного пользователя:


sudo ln -s /usr/local/sbin/haproxy /usr/sbin/haproxy

Если вы хотите добавить прокси-сервер в систему в качестве службы, скопируйте файл haproxy.init из examples в свой каталог /etc/init.d. Отредактируйте права доступа к файлу, чтобы скрипт выполнялся, а затем перезагрузите демон systemd:


sudo cp ~/haproxy-2.0.7/examples/haproxy.init /etc/init.d/haproxysudo chmod 755 /etc/init.d/haproxysudo systemctl daemon-reload

Вам также необходимо разрешить службе автоматическую перезагрузку при запуске системы:


sudo chkconfig haproxy on

В целях удобства также рекомендуется добавить нового пользователя для запуска HAProxy:


sudo useradd -r haproxy

После этого вы можете еще раз проверить номер установленной версии с помощью следующей команды:


haproxy -vHA-Proxy version 2.0.7 2019/09/27 - https://haproxy.org/

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


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


sudo firewall-cmd --permanent --zone=public --add-service=httpsudo firewall-cmd --permanent --zone=public --add-port=8181/tcpsudo firewall-cmd --reload

Настройка балансировщика нагрузки


Настройка HAProxy довольно простой процесс. По сути, всё, что вам нужно сделать, это сообщить HAProxy, какие соединения он должен прослушивать и куда следует их ретранслировать.


Это делается путем создания файла конфигурации /etc/haproxy/haproxy.cfg с определяющими настройками. Вы можете почитать о параметрах конфигурации HAProxy на странице документации, если хотите узнать об этом больше.


Балансировка нагрузки на транспортном уровне (layer 4)


Начнем с базовой настройки. Создайте новый файл конфигурации, например, используя vi с командой ниже:


sudo vi /etc/haproxy/haproxy.cfg

Добавьте в файл следующие разделы. Замените server_name тем, что должно вызывать ваши сервера на странице статистики, а private_ip приватными IP-адресами серверов, на которые вы хотите направлять веб-трафик. Вы можете проверить приватные IP-адреса на панели управления UpCloud и на вкладке Private network в меню Network.


global   log /dev/log local0   log /dev/log local1 notice   chroot /var/lib/haproxy   stats timeout 30s   user haproxy   group haproxy   daemondefaults   log global   mode http   option httplog   option dontlognull   timeout connect 5000   timeout client 50000   timeout server 50000frontend http_front   bind *:80   stats uri /haproxy?stats   default_backend http_backbackend http_back   balance roundrobin   server server_name1 private_ip1:80 check   server server_name2 private_ip2:80 check

Это определяет балансировщик нагрузки транспортного уровня (layer 4) с внешним именем http_front, прослушивающий порт 80, который затем направляет трафик к бэкенду по умолчанию с именем http_back. Дополнительная статистика /haproxy?stats подключает страницу статистики по указанному адресу.


Различные алгоритмы балансировки нагрузки.


Указание серверов в разделе бэкенда позволяет HAProxy использовать эти серверы для балансировки нагрузки в соответствии с алгоритмом циклического перебора, когда это возможно.


Алгоритмы балансировки используются для определения того, на какой сервер в бэкенде передается каждое соединение. Вот некоторые из полезных опций:


  • Roundrobin: каждый сервер используется по очереди в соответствии со своим весом. Это самый плавный и честный алгоритм, когда время обработки серверами остается равномерно распределенным. Этот алгоритм является динамическим, что позволяет регулировать вес сервера на лету.


  • Leastconn: выбирается сервер с наименьшим количеством соединений. Циклический перебор выполняется между серверами с одинаковой нагрузкой. Использование этого алгоритма рекомендуется для длинных сеансов, таких как LDAP, SQL, TSE и т. д., но он не очень подходит для коротких сеансов, таких как HTTP.


  • First: первый сервер с доступными слотами для подключения получает соединение. Серверы выбираются от самого низкого числового идентификатора до самого высокого, который по умолчанию соответствует положению сервера в ферме. Как только сервер достигает значения maxconn, используется следующий сервер.


  • Source: IP-адрес источника хешируется и делится на общий вес запущенных серверов, чтобы определить, какой сервер будет получать запрос. Таким образом, один и тот же IP-адрес клиента будет всегда доставаться одному и тому же серверу, в то время как серверы остаются неизменными.



Настройка балансировки нагрузки на прикладном уровне (layer 7)


Еще одна доступная возможность настроить балансировщик нагрузки для работы на прикладном уровне (layer 7), что полезно, когда части вашего веб-приложения расположены на разных хостах. Это может быть достигнуто путем регулирования передачи соединения, например, по URL.


Откройте файл конфигурации HAProxy с помощью текстового редактора:


sudo vi /etc/haproxy/haproxy.cfg

Затем настройте сегменты фронтенд и бэкенд в соответствии с примером ниже:


frontend http_front   bind *:80   stats uri /haproxy?stats   acl url_blog path_beg /blog   use_backend blog_back if url_blog   default_backend http_backbackend http_back   balance roundrobin   server server_name1 private_ip1:80 check   server server_name2 private_ip2:80 checkbackend blog_back   server server_name3 private_ip3:80 check

Фронтенд объявляет правило ACL с именем url_blog, которое применяется ко всем соединениям с путями, начинающимися с /blog. Use_backend определяет, что соединения, соответствующие условию url_blog, должны обслуживаться бэкендом с именем blog_back, а все остальные запросы обрабатываются бэкендом по умолчанию.


Со стороны бэкенда конфигурация устанавливает две группы серверов: http_back, как и раньше, и новую, называемую blog_back, которая обслуживает соединения с example.com/blog.


После изменения настроек сохраните файл и перезапустите HAProxy с помощью следующей команды:


sudo systemctl restart haproxy

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


Тестирование настройки


Когда HAProxy настроен и запущен, откройте публичный IP-адрес сервера балансировщика нагрузки в браузере и проверьте, правильно ли вы подключились к бэкенду. Параметр stats uri в конфигурации создает страницу статистики по указанному адресу.


http://load_balancer_public_ip/haproxy?stats

Когда вы загружаете страницу статистики, если все ваши серверы отображаются зеленым, то настройка прошла успешно!



Страница статистики содержит некоторую полезную информацию для отслеживания ваших веб-хостов, включая время работы/простоя и количество сеансов. Если сервер помечен красным, убедитесь, что сервер включен и что вы можете пропинговать его с машины балансировки нагрузки.


Если ваш балансировщик нагрузки не отвечает, убедитесь, что HTTP-соединения не блокируются файрволом. Также убедитесь, что HAProxy работает с помощью команды ниже:


sudo systemctl status haproxy

Защита страницы статистики с помощью пароля


Однако, если страница статистики просто указана во фронтенде, то она открыта для всеобщего обозрения, что может оказаться не очень хорошей идеей. Вместо этого вы можете назначить ей собственный номер порта, добавив приведенный ниже пример в конец вашего файла haproxy.cfg. Замените username и password на что-нибудь безопасное:


listen stats   bind *:8181   stats enable   stats uri /   stats realm Haproxy\ Statistics   stats auth username:password

После добавления новой группы слушателей удалите старую ссылку на stats uri из фронтенд группы. Когда закончите, сохраните файл и перезапустите HAProxy.


sudo systemctl restart haproxy

Затем снова откройте балансировщик нагрузки с новым номером порта и войдите в систему с именем пользователя и паролем, которые вы указали в файле конфигурации.


http://load_balancer_public_ip:8181

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


http://load_balancer_public_ip/

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


Заключение: балансировщик нагрузки HAProxy


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


Используя несколько хостов для защиты вашего веб-сервиса с помощью запаса мощности, сам балансировщик нагрузки все равно может представлять точку отказа. Вы можете еще больше повысить высокую доступность, установив плавающий IP между несколькими балансировщиками нагрузки. Вы можете узнать больше об этом в нашей статье о плавающих IP-адресах на UpCloud.




Подробнее о курсе Администратор Linux. Виртуализация и кластеризация***



Подробнее..

Перевод Как спокойно спать, когда у вас облачный сервис основные архитектурные советы

19.08.2020 18:13:30 | Автор: admin
LOST by sophiagworld

В этой статей собраны некоторые общие шаблоны, помогающие инженерам работать с масштабными сервисами, к которым делают запросы миллионы пользователей.

По опыту автора, это не исчерпывающий список, но действительно эффективные советы. Итак, начнем.

Переведено при поддержке Mail.ru Cloud Solutions.

Начальный уровень


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

Инфраструктура как код


Первая часть советов заключается в том, чтобы реализовать инфраструктуру как код. Это означает, что у вас должен быть программный способ развертывания всей инфраструктуры. Звучит замысловато, но на самом деле мы говорим о следующем коде:

Развертывание 100 виртуальных машин

  • с Ubuntu
  • 2 ГБ RAM на каждой
  • у них будет следующий код
  • с такими параметрами

Вы можете отслеживать изменения в инфраструктуре и быстро возвращаться к ним с помощью системы управления версиями.

Модернист во мне говорит, что можно использовать Kubernetes/Docker, чтобы сделать всё выше перечисленное, и он прав.

Кроме того, обеспечить автоматизацию, можно с помощью Chef, Puppet или Terraform.

Непрерывная интеграция и доставка


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

Каждый раз на этом этапе вы отвечаете на вопрос: будет ли моя сборка компилироваться и проходить тесты, валидна ли она? Это может показаться низкой планкой, но решает множество проблем.


Нет ничего прекраснее, чем видеть эти галочки

Для этой технологии можете оценить Github, CircleCI или Jenkins.

Балансировщики нагрузки


Итак, мы хотим запустить балансировщик нагрузки, чтобы перенаправлять трафик, и обеспечить равную нагрузку на всех узлах или работу сервиса в случае сбоя:


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

Обычно балансировщики нагрузки настраиваются в том облаке, которым вы пользуетесь.

RayID, сorrelation ID или UUID для запросов


Вам когда-нибудь встречалась ошибка в приложении с сообщением вроде такого: Что-то пошло не так. Сохраните этот id и отправьте его в нашу службу поддержки?


Уникальный идентификатор, correlation ID, RayID или любой из вариантов это уникальный идентификатор, который позволяет отслеживать запрос в течение его жизненного цикла. Это позволяет отследить весь путь запроса в логах.


Пользователь делает запрос к системе A, затем А связывается с B, та связывается с C, сохраняет в X и затем запрос возвращается в A

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

Средний уровень


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

Централизованное ведение журналов


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

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


Функциональность стека ELK

Агенты мониторинга


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

На этом этапе вы проверяете, что запущенная сборка хорошо себя чувствует и нормально работает.

Для небольших и средних проектов я рекомендую Postman для мониторинга и документирования API. Но в целом просто следует убедиться, что у вас есть способ узнать, когда произошел сбой, и получить своевременное оповещение.

Автомасштабирование в зависимости от нагрузки


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


В большинстве облачных служб вы можете настроить автоматическое масштабирование, используя большее количество серверов или более мощные серверы.

Система экспериментов


Хорошим способом безопасно развернуть обновления станет возможность протестировать что-то для 1% пользователей в течение часа. Вы, конечно, видели такие механизмы в действии. Например, Facebook показывает части аудитории другой цвет или меняет размер шрифта, чтобы посмотреть, как пользователи воспринимают изменения. Это называют A/B-тестированием.

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

Продвинутый уровень


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

Сине-зеленые развертывания


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

Этот шаг зависит от наличия балансировщика нагрузки. Представим, что у вас версия N вашего программного обеспечения, а затем вы хотите развернуть версию N+1.

Вы могли бы просто остановить службу и развернуть следующую версию в то время, которое считаете удобным для ваших пользователей, и получить некоторое время простоя. Но предположим, что у вас действительно строгие условия SLA. Так, SLA 99,99% означает, что вы можете уходить в офлайн только на 52 минуты в год.

Если вы действительно хотите достичь таких показателей, нужно два деплоя одновременно:

  • тот, который есть прямо сейчас (N);
  • следующая версия (N+1).

Вы указываете балансировщику нагрузки перенаправить процент трафика на новую версию (N+1), в то время как сами активно отслеживаете регрессии.


Здесь у нас есть зеленый деплой N, который нормально работает. Мы пытаемся перейти к следующей версии этого деплоя

Сначала мы посылаем действительно небольшой тест, чтобы посмотреть, работает ли наш деплой N+1 с небольшим количеством трафика:


Наконец, у нас есть набор автоматических проверок, которые мы в конечном итоге запускаем до тех пор, пока наше развертывание не будет завершено. Если вы очень-очень осторожны, также можете сохранить свое развертывание N навсегда для быстрого отката в случае плохой регрессии:


Если хотите перейти на еще более продвинутый уровень, пусть всё в сине-зеленом деплое выполняется автоматически.

Обнаружение аномалий и автоматическое смягчение последствий


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


С обнаружением аномалий вы начинаете изучать некоторые подсказки, которые выдает сервис. Например, всплеск нагрузки на CPU может подсказать, что жесткий диск выходит из строя, а всплеск количества запросов означает, что нужно масштабироваться. Такого рода статистические данные позволяют сделать сервис проактивным.

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

Вот и всё!


Этот список приоритетов избавит вас от многих проблем, если вы поднимаете облачный сервис.

Автор оригинальной статьи приглашает читателей оставлять свои комментарии и вносить изменения. Статья распространяется как open source, пул-реквесты автор принимает на Github.

Что еще почитать по теме:

  1. Go и кэши CPU.
  2. Kubernetes в духе пиратства с шаблоном по внедрению.
  3. Наш канал Вокруг Kubernetes в Телеграме.
Подробнее..

Антистресс для твоего сервера. Тестируем балансировщик нагрузки от Timeweb

07.06.2021 16:20:09 | Автор: admin

Привет, Хабр!


Сегодня мы расскажем сообществу о запуске нашего нового сервиса балансировщик нагрузки.

Сейчас услуга доступна для всех желающих в режиме открытого бета-тестирования в личном кабинете VDS. Это, конечно, бесплатно, а мы будем благодарны вам за комментарии и замечания.

Ребята, почему только сейчас?


Можете вполне резонно спросить вы. Мы, как и все, привыкаем к новой, постпандемийной (или еще нет?), реальности и отвечаем запросам наших клиентов.

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

Особенно актуальна услуга будет, когда одна VDS уже не справляется с нагрузкой или вы просто хотите повысить отказоустойчивость приложения.

Как это работает?


Балансировщик переправляет клиентские запросы на доступные рабочие серверы из группы. Регулярно опрашивая состояние серверов, балансировщик понимает, какие серверы активны и доступны для обработки клиентских запросов.

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

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

Окей, это всё понятно, а как попробовать?


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

В правилах переадресации задаем параметры проброса трафика, указываем входящий и исходящий порт, а также протокол трафика из доступных: tcp, https, http, http2.



Далее вы можете выбрать один из двух доступных алгоритмов балансировки: Round Robin или Least Connections.

Какой алгоритм выбрать?


Round Robin алгоритм, при котором серверы перебираются по кругу: первый запрос передается на первый сервер, следующий на второй сервер, и так далее до последнего сервера, после чего цикл начинается заново.

Least Connections алгоритм, при котором каждый новый запрос передается на сервер с меньшим количеством активных подключений.

Для автоматического контроля доступности ваших серверов можно указать параметры в разделе Проверка доступности: просто задайте порт и протокол, а также интервалы для фонового мониторинга доступности серверов.

Теперь остается только выбрать ваши VDS или указать IP-адреса серверов. Кстати, вы можете использовать балансировщик не только с нашими VDS. Мы будем только за!

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

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

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

Да начнется беспощадный бета-тест!



Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru