Комментарии 18
Вот это я понимаю статья. Плотно, четко, по делу. Профессионально
Непонятно, почему разный траффик нельзя распределять по разным instance, чтобы не городить таких сложностей
В нашем облаке мы запускаем контейнеры на своих собственных железных серверах. VM instances (если вы про них) мы не используем – они медленнее и мы все равно "платим" за весь сервер, а не за instances, как в публичных облаках. Совмещение задач prod и nonprod на одном сервере дает существенную экономию ресурсов – на наших нагрузках она выливается в миллионы долларов. Про причины экономии рассказывалось в разделе про приоритеты задач, также подробнее можно почитать в предыдущей статье про one-cloud.
Я не про виртуалки. Не знаю, используете ли кубернетец, но его средствами можно легко распределять трафик на разные сервисы (допустим) и эффективно и независимо масштабировать. Ваш вариант, когда важный трафик ограничен по пропускной способности может приводить к вредному простою в случае, когда фондовых задач нет или мало
вам стоит прочитать предыдущую статью статью про one-cloud тут, там есть ответы на эти вопросы. дублировать ее в комментах думаю не стоит.
Я прочитал, но ответа на вопрос не вижу. Что мешает разный трафик направлять в разные контейнеры или даже машины?
Если направлять разный трафик на разные машины, то вам понадобится 2 или больше машин вместо 1. 2 и более машин дороже в 2 и более раз, чем 1, потребляют электричества больше чем 1 и занимают в 2 и более раз больше места в датацентре. Это может быть неважно, если продакшен состоит из 1 машины, но если из тысячи, то уже важно иметь 1 тысячу машин, а не 2.
Если же направлять прод и нонпрод трафик в разные контейнеры, расположенные на 1 машине ( а если контейнеров больше чем машин, то это произойдет даже в кубере ), то в момент, когда нонпрод контейнер нагрузит сетевую карту, прод контейнер ( low latency, оно же задача с низкой задержкой ) будет работать с более высокими задержками ( в обоих статьях есть иллюстрирующие графики с задержками с серверов продакшена ), что опять же может быть неважно, если на контейнер приходят 1, 2 и даже 3 запроса в день, но становится важным, когда прод контейнер должен обслуживать тысячи запросов в секунду, тк увеличение задержки обслуживания запросов выше его SLA приведет к отказу в обслуживании. Опять же, в этом случае можно скалировать такой контейнер в 2 и более раз, заняв больше машин в 2 и более раз, но - см 1 абзац.
А зачем вы шейпите входящий трафик?
Причины те же – избежать взаимного влияния задач вследствие перегрузки сетевой карты. Под входящим трафиком имеется в виду не только внешний трафик, но и внутренний трафик между задачами, которого в наших ДЦ во много раз больше. Без шейпинга какая-то расчетная nonprod задача, например, может забить входящую полосу, повлияв на сетевую задержку соседней prod задачи. Хотя, в случае входящего трафика, мы не можем влиять на его скорость напрямую (пакеты уже прошли сетевую карту), шейпинг позволяет делать это косвенно, влияя на алгоритм congestion control отправителя через увеличение времени round-trip или дропы пакетов при превышении лимита задачей. Разумеется, это возможно только при условии, что congestion control работает правильно. Т.е. трафик внутренний или от правильно настроенных клиентов (от DoS-атаки шейпер конечно не спасет).
Хабр иногда все еще торт!
В порядке умозрительного эксперимента: чтобы избежать тормозов на одном интерфейсе, можно каждому контейнеру выдать отдельный ifb (или даже veth), там делать шейпинг, и копировать (mirred) в выходной интерфейс, где mqprio точно также раскидает по очередям. Правда, это сработает, если аппаратные очереди имеют приоритеты. Не очень понял, так ли это в случае автора.
Да, действительно, у нас была идея сделать настроить шейпинг на каждый контейнер. В таком случае проблема единой блокировки остается на уровне контейнера. Плюс сильно усложнилась бы конфигурация сети – у нас несколько veth на контейнер (для разных VLAN), которые имеют общий лимит. По результатов предварительных тестов, производительность такого решения была намного хуже, чем вариант с использованием BPF, к которому мы пришли.
Ох уже эти BPF, которые решают все проблемы. Скоро весь линукс превратится в одну большую BPF-машину. Точнее, в 200 маленьких BPF-машин на все случаи жизни.
Спасибо за интересную статью, Леонид. Достаточно глубокий, не повседневный топик, при этом легко читать. Надеюсь, видеть побольше таких статей на хабре.
Мне тоже приходилось сталкиваться с похожей проблемой, когда сетевой трафик начинал шейпится, и это вызывало скачки CPU. perf показывал тот же стек, что в этой статье с native_queued_spin_lock_slowpath. Однако в моем случае, это была mq qdisc, и как я узнал, из данной статься, она должна быть не блокирующей (v4.14.198 версия ядра, на всякий случай), т.е. CPU от spinlock не должно быть? Перечитывая статью и, взглянув на упомянутый __dev_xmit_skb, мне стало не понятно, почему не блокирующая qdisc должна помочь, ведь `spinlock_t *root_lock = qdisc_lock(q);` берется на уровень выше и его тип всегда spinlock_t, независимо от типа qdisc. Или дело в том, что не блокирующая qdisc дает то, что внутри критического блока, поток будет проводить много меньше времени, таким образом другим потокам не придется жечь CPU? Вообщем, очень интересно Ваше мнение на этот счет.
Спасибо за отзыв, рад что статья понравилась :)
Насчет mq – блокировка может присутствовать в дочерних qdisc. В __dev_xmit_skb в параметре q передается не сама mq, а дочерняя дисциплина, соответственно, qdisc_lock(q) – блокировка для дочерней дисциплины. Надо смотреть, какая дисциплина стоит под mq. По умолчанию mq настраивается в связке с pfifo_fast, которая lockless – в этом случае блокировка дисциплины не будет браться (сработает проверка q->flags & TCQ_F_NOLOCK).
Но независимо от qdisc есть еще spinlock у буферов сетевой карты, которые берутся при отправке из qdisc в сетевую карту в методе sch_direct_xmit (см. слайд про блокировки). Поэтому от блокировок полностью избавиться не получится, даже если использовать noqueue.
Также стоит проверить, равномерно ли нагружаются очереди сетевой карты. Если, например, весь трафик идет через одну очередь (например, все пакеты принадлежат одному flow), то получится та же глобальная блокировка на уровне отдельной очереди.
Спасибо за ответ и указание на TCQ_F_NOLOCK. A нашел что в 4.14 версии ядра, на котором мы видели эту проблему, нет никакой TCQ_F_NOLOCK, поэтому возможно стоит поновее ядро взять, хотя не понятно на сколько это поможет, так как в sch_direct_xmit все равно spinlock. Ну и да, у нас были pfifo_fast под mq, а так же не сбалансированная нагрузка по очередям, с которой тоже пришлось повозиться.
Как мы оптимизировали сетевой шейпер Linux в облаке с помощью eBPF