среда, 15 ноября 2017 г.

Load balancing

         В очередной раз убедился что механизм балансировки основанный на RTT(round trip time) это не самая удачная идея. Когда мы его внедряли мы получили довольно не плохой выигрыш в производительности - порядка 20%. Но потом выяснилось что это была не самая удачная идея. Почему:
          - Сетевые флуктуации случайным образом влияют на распределение нагрузки. То есть распределение запросов между экземплярами приложения определяется (или как минимум сильно зависит от сетевых флуктуаций). Фактически мы приходим к ситуации когда небольшие сетевые проблемы могут привезти к тому что отдельные экземпляры отхватывают порцию траффика не совместимую с жизнью и уходят в себя/убиваются OOM.
      - В контексте перехода на Kubernetes выяснилась еще одна интересная особенность. Экземпляры приложения запущенные в Kubernetes стабильно получали больше запросов чем аналогичные инстансы  запущенные вне Kubernetes. Объясняется это просто. Все Kubernetes сервера приехали в последней партии железа и соответсвенно были смонтированы в несколько рядом стоящих стоек. Соответсвенно RTT внутри Kubernetes облака были в среднем в 2 раза меньше пингов между старыми серверами, что приводило к соответсвующему распределению нагрузки в пользу K8s экземпляров. 
     -  При RTT-based балансировке ты не можешь пропорционально балансировать нагрузку между экземплярами приложения имеющими разное количество аппаратных ресурсов. То есть если один экземпляр запущен на сервере с 24 ядрами а другой на сервере с 40 ядрами - то они все равно будут получать примерно равное количество запросов. Все потому что RTT до сервера с 24 ядрами ничем не отличается от RTT до сервера с 40 ядрами. Вроде бы очевидный факт, но почему-то в тот момент когда мы это реализовывали мне это в голову не пришло. За что собственно и поплатился.
            Собственно вывод который я сделал для себя: балансировка должна основываться именно на ресурсах доступных экземпляру приложения (количеству/качеству CPU, RAM). То есть должен высчитываться некий индекс производительности экземпляра приложения и на его основе уже будет происходить балансировка нагрузки.           
      Еще один извечный вопрос load balancing-а: как собственно балансировать трафик ? Централизовано, через какой-нибудь прокси или через алгоритм "зашитый в клиента". Под алгоритмом зашитым в клиента я подразумеваю некий "smart connector" который сам решает к какому серверу нужно обратится на основе неких доступных ему данных (service discovery + какие-то метрики/данные).
        В первом случае мы получаем проблему в виде SPOF (single point of failure) со всей вытекающей головной болью(как обеспечить ее доступность, мониторинг и тд), нужно дополнительное железо именно под балансировщик/прокси. Именно поэтому я всегда был поклонником второго варианта. Но выяснилось что не все так просто в реальной жизни. У второго варианта также есть  весьма серьезные недостатки: невозможность быстро и единообразно поменять алгоритм балансировки, необходимость поддерживать "smart connector" для всех используемых языков/платформ.  Если у тебя 5 компонентов которые написаны на одном языке - то все более менее терпимо. Но у нас то микросервисы, блять! Это прдразумевает что: 1.  их много, 2. Они поддерживаются разными командами и за всеми хер уследишь, 3. Каждый суслик - агроном и может писать на чем ему вздумается.  В общем менять алгоритм балансировки в таких условиях - это боль и печаль.