Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Алексей Лавренюк (Яндекс) - Методика нагрузочного тестирования

Алексей Лавренюк (Яндекс) - Методика нагрузочного тестирования

Доклад с конференции Moscow Python Conf 2016 (http://conf.python.ru)
Видео: https://conf.python.ru/metodika-nagruzochnogo-testirovaniya/

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

Moscow Python Meetup

October 12, 2016
Tweet

More Decks by Moscow Python Meetup

Other Decks in Programming

Transcript

  1. Кадр из фильма "Марсианин" Предположим, объем шлюза составляет два кубических

    метра. Надутый скафандр, вероятно, занимает половину. Требуется минута, чтобы добавить 0.2 атм к 1 кубическому метру. Это 285 грамм воздуха… (тут еще два абзаца подобных вычислений) …когда я выйду из шлюза, этот скафандр продержится всего четыре минуты.
  2. Back-of-the-envelope вычисления Задача (от Google): сервис картинок, нужно спроектировать страницу

    с результатами поиска 〉 есть хранилище фотографий, по 256 КБ каждая 〉 страница результатов содержит 30 превьюшек по 24 КБ 〉 у нас есть серверы с 24 ГБ RAM, 8-ю ядрами, 2x2TB hdd, 1Gbps ethernet С чего начать? 7
  3. Back-of-the-envelope вычисления 8 Задача (от Google): сервис картинок, нужно спроектировать

    страницу с результатами поиска 〉 есть хранилище фотографий, по 256 КБ каждая 〉 страница результатов содержит 30 превьюшек по 24 КБ 〉 у нас есть серверы с 24 ГБ RAM, 8-ю ядрами, 2x2TB hdd, 1Gbps ethernet С чего начать: оценить нагрузку! (хотим 100 000 RPS)
  4. Подход "в лоб" Делаем все на одной машинке. 30 картинок

    / 2 диска = 15 чтений (256 KB/1 MB) * (время чтения 1 MB с диска) ms + (время поиска на диске) ms = ? ms чтение одной картинки 15 чтений * ? ms = ? ms время генерации страницы 9
  5. "Latency numbers every programmer should know" L1 cache reference 0.5

    ns Branch mispredict 5 ns L2 cache reference 7 ns 14x L1 cache Mutex lock/unlock 25 ns Main memory reference 100 ns 20x L2 cache, 200x L1 cache Compress 1K bytes with Zippy 3,000 ns Send 1K bytes over 1 Gbps network 10,000 ns 0.01 ms Read 4K randomly from SSD* 150,000 ns 0.15 ms Read 1 MB sequentially from memory 250,000 ns 0.25 ms Round trip within same datacenter 500,000 ns 0.5 ms Read 1 MB sequentially from SSD* 1,000,000 ns 1 ms 4X memory Disk seek 10,000,000 ns 10 ms 20x datacenter roundtrip Read 1 MB sequentially from disk 20,000,000 ns 20 ms 80x memory, 20X SS Send packet CA->Netherlands->CA 150,000,000 ns 150 ms By Jeff Dean: http://research.google.com/people/jeff/ Originally by Peter Norvig: http://norvig.com/21-days.html#answers 10
  6. Подход "в лоб" Делаем все на одной машинке. 30 картинок

    / 2 диска = 15 чтений (256 KB/1 MB) * 30 ms + 10 ms seek = 17.5 ms чтение одной картинки 15 чтений * 17.5 ms = 362 ms время генерации страницы Долго. 11
  7. Параллельность Фронтенды читают с нескольких бэкэндов. (30 * 24 KB

    * 10 ms) / 1 MB = около 7 ms на чтение 30 превьюшек по сети 17.5 ms + 0.5 ms (rtt внутри сети) + 7 ms = около 25 мс на генерацию страницы Ускорили сервис более чем в 10 раз, не написав ни одной строки кода! 12
  8. Теперь можно помечтать Сколько бэкэндов нужно, чтобы держать 100 000

    показов страниц с результатами? 100 000 * 30 = 3 000 000 превьюшек нужно в секунду (1000 ms / 17.5 ms) * 2 диска = около 120 генерится одним сервером 3 000 000 / 120 = 25 000 бэкэндов нужно 13
  9. Подтверждаем нагрузочными тестами Проверяем гипотезы: 〉 времена ответов и максимальную

    производительность компонентов 〉 масштабируемость 〉 сложность алгоритмов Уточняем модель (чем раньше — тем дешевле ошибки) 15
  10. Проверяем как можно раньше 〉 Бенчмарки: синтетическая нагрузка на минимально

    функционирующем коде. (timeit, pytest-benchmark) 〉 Ранние тесты новых реализаций на имеющихся данных. 16
  11. Yandex.Tank: 10 лет истории Better software is produced by those

    forced to operate it* phantom — очень быстрый вебсервер phantom-benchmark плагин для фантома, предназначенный для тестирования. Тоже очень быстрый Yandex.Tank построен вокруг phantom- benchmark. Сейчас появилось много нового * из анонса доклада Theo Schlossnagle's "Operational Software Design" 19
  12. Yandex.Tank сегодня Yandex.Tank — проект с открытыми исходниками Основной язык

    — Python Генератор нагрузки по умолчанию
 phantom (C++) Поддержка JMeter Пушки на Go и Python 20
  13. Внутренняя архитектура танка Yandex.Tank — это метаинструмент Танк предоставляет общий

    фреймворк для разных генераторов нагрузки Генератору остается только отправить запросы и аккуратно замерить время их выполнения Танк состоит из модулей 21 By Dave Hakkens [CC BY-SA 3.0], via Wikimedia Commons
  14. Модули танка В модулях содержится вся функциональность 〉Поддержка генераторов 〉Сбор

    и анализ данных 〉Мониторинг 〉Автостопы 〉Отправка результатов тестов 22
  15. Первый тест Конфигурация в .ini-файлах Предусмотренны хорошие дефолтные значения Их

    можно переопределять на разных уровнях довольно просто использовать Танк в автоматизированных системах 24
  16. Указываем запросы Патроны в одном из возможных форматов 27 [phantom]

    address = my.service.com uris = / /mypage.html /clck/page?data=hello headers = [Host: example.org] [Accept-Encoding: gzip,deflate]
  17. Задаем расписание Для начала — попроще лента генерируется заранее 28

    [phantom] address = my.service.com uris = / /mypage.html /clck/page?data=hello headers = [Host: example.org] [Accept-Encoding: gzip,deflate] rps_schedule = const(1, 40s)
  18. А как же мониторинг? 29 [monitoring] config_contents=<Monitoring> <Host address="my_tank" />

    <Host address="my_backend"> <Custom measure="call" label="db size"> stat -c%s /usr/mysql/data </Custom> </Host> <Host address="my_frontend" /> </Monitoring>
  19. Подключаем Overload Сервис от Яндекса (public beta): overload.yandex.net залогиньтесь с

    помощью GitHub или Yandex и скачайте токен 30 [tank] plugin_overload=yandextank.plugins.Overload [overload] token_file = ~/token.txt
  20. Результаты тестов Итоговые цифры за весь тест — этого недостаточно.

    Нужно наблюдать систему в динамике. Нужны графики. Графики времен ответа не сильно лучше. Каждую секунду может прилетать несколько тысяч ответов. Много шума. Графики средних времен за каждую секунду? Они не дают представления о характере распределения. 33
  21. Открытые и закрытые системы 38 Закрытая система Открытая система В

    закрытых системах есть обратная связь, которая не дает "добить" сервис. Пользователи ждут ответа, если сервис перегружен В открытых системах обратной связи нет. Интернет — открытая система
  22. Universal Scalability Law 40 sigma — contention, непараллельные части kappa

    — coherency delay, межпроцессное взаимодействие
  23. Плохой параллелизм vs. плохое IPC 42 В десять раз улучшили

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

    пользователь шлет запросы один за другим. overload.yandex.net/1763 43 yandex-tank -c ./load.ini -o "phantom.instances_schedule=line(1, 8, 4m)" -o "phantom.rps_schedule=" -o "phantom.loop=100000"
  25. Ищем точку разладки Открытая система, строгое расписание, эмуляция открытой системы

    с помощью большого числа пользователей overload.yandex.net/1764 45 yandex-tank -c ./load.ini -o "phantom.rps_schedule=line(1, 600, 5m)" -o "phantom.instances=10000"
  26. Смотрим в мониторинг Во что "уперлись"? Полностью ли утилизировали процессор?

    Соответствует ли картина нашим ожиданиям? (например, линеен ли рост загруженности CPU) 47
  27. Замеряем времена ответов Открытая система, постоянная нагрузка. Уровень нагрузки из

    SLA или на основании предыдущих тестов не забываем прогревать систему 48 yandex-tank -c ./load.ini -o "phantom.rps_schedule=line(1, 300, 30s) const(300, 5m)" -o "phantom.instances=10000"
  28. Причины пиков "Тяжелый" запрос в патронах. Частота пиков зависит от

    уровня нагрузки Периодический процесс на сервере. Cron job или синхронизация кэша. Garbage collector Кто-то еще приходит на сервер и что-то скачивает 50
  29. Ищем утечки ресурсов Открытая система, постоянная нагрузка, стреляем подольше, смотрим

    в мониторинги уровень нагрузки на 80-90% от максимально возможного 52 yandex-tank -c ./load.ini -o "phantom.rps_schedule=line(1, 700, 30s) const(700, 1h)" -o "phantom.instances=10000"
  30. Методология тестирования 〉 Смоук-тест. Убедиться, что все работает и мы

    собираем все необходимые метрики 〉 На максимальную производительность. Закрытая модель 〉 На разладку. Открытая модель, жесткое расписание 〉 Тайминги. Открытая модель, постоянный уровень (SLA) 〉 Утечки. Нагрузку повыше, стреляем подольше 〉 Любой тест, который позволит больше узнать о сервисе 〉 Автоматизация и регрессионные тесты 53
  31. BFG: генератор нагрузки BFG стреляет кодом на Python До 10

    000 RPS Можно делать нагрузочные тесты из функциональных сценариев Можно вообще все =) 55 BFG9000, оружие из игры Doom (id Software)
  32. BFG Стенд на YaC 2014 〉«Ручное» нагрузочное тестирование 〉MIDI →

    ØMQ → BFG (Tank) → сервис В реальной жизни: тесты Thrift и LDAP клиента, XML RPC, развесистые сценарные тесты, тесты мобильных приложений с Appium. 56
  33. Пример сценария 62 def case1(self, missile): # используем контекст measure,

    чтобы замерять время. # поле gun проинициализировано в __init__() # Результаты собираются автоматически: with self.gun.measure("case1"): log.info("Shoot case 1: %s", missile) # в одном сценарии может быть несколько шагов: with self.gun.measure("case1_step2") as sample: log.info("Shoot case 1, step 2: %s", missile) # можно самим проставить коды ответов: sample["proto_code"] = 500
  34. О чем мы сегодня поговорили 〉Оценка, проектирование, эксперимент 〉Яндекс.Танк и

    Overload 〉Методика нагрузочного тестирования 〉Стрельбы Python-скриптами 64
  35. Albert Einstein* Once we accept our limits, we go beyond

    them. * некоторые утверждают, что Эйнштейн этого не говорил, но фраза все равно классная