Yandex.Tank: нагрузочное тестирование

Автор: | 09/02/2021

Кроме горячо любимых Apache Bench и JMeter имеется интересная утилита Yandex Tank.

Ей давно пользуются наши QA, пришло время и мне его потрогать для проверки одной проблемы с Kubernetes, о которой поговорим в следующем посте.

В этом – посмотрим на базовые возможности Yandex.Tank.

Из особенно приятного – в отличии от Apache Bench выводит статистику по кодам ответов, и проще в настройке и запуске, чем JMeter, плюс гибкая настройка автостопа на случай, если “Что-то пошло не так” (с)

Компоненты

См. Modules.

Сам Yandex Tank написан Python.

Для выполнения тестов имеется несколько модулей Load generators, по-умолчанию используется Phantom на С++.

Telegraf – для мониторинга тестируемого хоста – подключается по SSH, запускает свой агент и снимает метрики CPU/mem/etc.

Overloader – используется для загрузки полученных данных в Yandex Overloader или InfluxDB, но мы им не воспользуемся. См. Artifact uploaders.

Кроме того, в примерах ниже не рассматривается создание отдельных “патронов” (ammo), т.к. мы не QA, и мне пока достаточно будет простых GET на какие-нибудь URI. Still, см. документацию по ним в Preparing requests.

Запуск

Создадим минимальный конфиг для запуска Phantom:

phantom:
  address: rtfm.co.ua:443
  header_http: "1.1"
  headers:
    - "[Host: rtfm.co.ua]"
  uris:
    - /
  load_profile:
    load_type: rps
    schedule: const(1,30s)
  ssl: true
console:
  enabled: true
telegraf:
  enabled: false

Тут:

  • phantom:
    • address: адрес и порт тестируемого хоста
    • header_http: версия HTTP для тестов, зададим 1.1, что бы использовать одно соединение на запросы, а не открывать новое каждый раз (см. HTTP persistent connection)
    • headers: заголовки, передаваемые на тестируемый сервер
    • uris: список URI, к которым выполняем запросы
    • load_profile:
      • load_type: может быть rps или instances:
        • rps: requests per second – указываем желаемое количество запросов в секунду
        • instances: указываем желаемое количество активных тредов, которые будут генерировать столько rps, сколько смогут, см. Dynamic thread limit
      • schedule: может быть const, line или step (или все три вместе) – определяет характер генерируемой нагрузки, см. Tutorials:
        • const: указывается в виде (load,dur), где load – кол-во rps, dur – время для выполнение тестирования, в примере выше – выполняем 1 запрос в секундв течении 30 секунд
        • line: указывается в виде (a,b,dur), где a – начальное кол-во rps, b – конечное, dur – время для выполнение тестирования, в кечении котрого значение будет линейно увеличиваться от a до b
        • step: указывается в виде (a,b,step,dur), где a – начальное кол-во rps, b – конечное, step – на сколько rps увеличивать, dur – время между каждым step
    • ssl: включаем поддержку запросов по HTTPS (не забываем указать 443 в address)
  • console: включаем вывод результатов в консоль
  • telegraf: агент мониторинга для сбора метрик с тестируемого хоста, рассмотрим в Monitoring (Telegraf)

И запускаем Танк из Docker-образа:

[simterm]

$ docker run --rm -v $(pwd):/var/loadtest -it direvius/yandex-tank

[/simterm]

Результат:

Monitoring (Telegraf)

С помощью Telegraf можно по SSH подключиться к тестируемом хосту, и с нимать метрики с него, после чего отображать их в результатах тестирования в режиме реального времени.

Включаем его в load.yaml:

...
telegraf:
  enabled: true
  package: yandextank.plugins.Telegraf

Настройки метрик описываются в отдельном файле, создаём monitoring.xml, см. Configuration file format:

<Monitoring>
  <Host address="rtfm.co.ua" interval="1" username="root">
    <CPU />
    <Kernel />
    <Net />
    <System />
    <Memory />
    <Disk />
    <Netstat/>
  </Host>
</Monitoring>

Тут в address – сервер, с которого будем собирать метрики, interval – интервал сбора метрик, username – пользователь, под которым Telegraf будет подключаться на сервер.

При этом у этого пользователя на тестируемом хосте в ~/.ssh/authorized_keys должна быть записана публичная часть SSH-ключа для аутентификации (см. SSH: авторизация по ключам).

Приватную часть передадим в контейнер  исмонтируем в контейнер в файл /root/.ssh/id_rsa, т.к. процессы в контейнере запускаются под рутом:

[simterm]

$ docker run --rm -v $(pwd):/var/loadtest -v /home/setevoy/.ssh/setevoy-do-nextcloud-production-d10-03-11:/root/.ssh/id_rsa -it direvius/yandex-tank

[/simterm]

Paramiko: SSHException: not a valid RSA private key file

При первом запуске Telegraf выдал ошибку:

[simterm]

16:32:54 [ERROR] Failed to install monitoring agent to rtfm.co.ua
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/yandextank/plugins/Telegraf/client.py", line 209, in install
    out, errors, err_code = self.ssh.execute(cmd)
  File "/usr/local/lib/python2.7/dist-packages/yandextank/common/util.py", line 72, in execute
    with self.connect() as client:
  File "/usr/local/lib/python2.7/dist-packages/yandextank/common/util.py", line 42, in connect
    timeout=self.timeout, )
  File "/usr/local/lib/python2.7/dist-packages/paramiko/client.py", line 437, in connect
    passphrase,
  File "/usr/local/lib/python2.7/dist-packages/paramiko/client.py", line 749, in _auth
    raise saved_exception
SSHException: not a valid RSA private key file

[/simterm]

Возникает она из-за формата ключа – на DigitalOcean он по-умолчанию в PEM/OpenSSH.

Проверяем:

[simterm]

$ file /home/setevoy/.ssh/setevoy-do-nextcloud-production-d10-03-11
/home/setevoy/.ssh/setevoy-do-nextcloud-production-d10-03-11: OpenSSH private key

[/simterm]

Конвертируем его в RSA:

[simterm]

$ ssh-keygen -p -m PEM -f /home/setevoy/.ssh/setevoy-do-nextcloud-production-d10-03-11

[/simterm]

Проверяем снова:

[simterm]

$ file /home/setevoy/.ssh/setevoy-do-nextcloud-production-d10-03-11
/home/setevoy/.ssh/setevoy-do-nextcloud-production-d10-03-11: PEM RSA private key

[/simterm]

Запускаем тесты повторно – Telegraf выведет собираемые и игнорируемые метрики и вообще свою конфигурацию:

[simterm]

$ docker run --rm -v $(pwd):/var/loadtest -v /home/setevoy/.ssh/setevoy-do-nextcloud-production-d10-03-11:/root/.ssh/id_rsa -it direvius/yandex-tank
...
16:36:38 [INFO] Detected monitoring configuration: telegraf
16:36:38 [INFO] Preparing test...
16:36:38 [INFO] Telegraf Result config {'username': 'root', 'comment': '', 'telegraf': '/usr/bin/telegraf', 'python': '/usr/bin/env python2', 'host_config': {'Kernel': {'fielddrop': '["boot_time"]', 'name': '[inputs.kernel]'}, 'Netstat': {'name': '[inputs.netstat]'}, 'System': {'fielddrop': '["n_users", "n_cpus", "uptime*"]', 'name': '[inputs.system]'}, 'Memory': {'fielddrop': '["active", "inactive", "total", "used_per*", "avail*"]', 'name': '[inputs.mem]'}, 'Net': {'interfaces': '["eth0","eth1","eth2","eth3","eth4","eth5"]', 'fielddrop': '["icmp*", "ip*", "udplite*", "tcp*", "udp*", "drop*", "err*"]', 'name': '[inputs.net]'}, 'Disk': {'name': '[inputs.diskio]', 'devices': '["vda0","sda0","vda1","sda1","vda2","sda2","vda3","sda3","vda4","sda4","vda5","sda5"]'}, 'CPU': {'fielddrop': '["time_*", "usage_guest_nice"]', 'name': '[inputs.cpu]', 'percpu': 'false'}}, 'startup': [], 'host': 'rtfm.co.ua', 'telegrafraw': [], 'shutdown': [], 'port': 22, 'interval': '1', 'custom': [], 'source': []}
16:36:38 [INFO] Installing monitoring agent at [email protected]...
16:36:38 [INFO] Creating temp dir on rtfm.co.ua
16:36:38 [INFO] Execute on rtfm.co.ua: /usr/bin/env python2 -c "import tempfile; print tempfile.mkdtemp();"
...

[/simterm]

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

И работа агента на тестируемом сервере:

[simterm]

root@rtfm-do-production-d10:~# ps aux | grep tele
root      4580  0.5  0.4 309992  9436 pts/1    Ssl+ 15:38   0:00 python2 /tmp/tmpZez6yJ/agent.py --telegraf /tmp/telegraf --host rtfm.co.ua
root      4582  7.1  1.5 851256 31896 ?        Ssl  15:38   0:01 /tmp/telegraf -config /tmp/tmpZez6yJ/agent.cfg

[/simterm]

Autostop

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

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

Добавляем:

...
autostop:
  autostop:
    - http(2xx,100%,1s)

Тут для примера – остановить выполнение, если в течении 1 секунды 100% ответов будут с кодом 2хх.

Запускаем, и:

[simterm]

$ docker run --rm -v $(pwd):/var/loadtest -v /home/setevoy/.ssh/setevoy-do-nextcloud-production-d10-03-11:/root/.ssh/id_rsa -it direvius/yandex-tank
...
16:56:24 [INFO] Monitoring received first data.
16:56:24 [WARNING] Autostop criterion requested test stop: http(2xx,100%,1s)
16:56:24 [WARNING] Autostop criterion requested test stop: 2xx codes count higher than 100.0% for 1s, since 1612889780
16:56:24 [INFO] Finishing test...
16:56:24 [INFO] Stopping load generator and aggregator
...

[/simterm]

Тест сразу остановился.

Больше примеров – в документации тут>>>.

Полность файл load.yaml получился таким:

phantom:
  address: rtfm.co.ua:443
  header_http: "1.1"
  headers:
    - "[Host: rtfm.co.ua]"
  uris:
    - /
  load_profile:
    load_type: rps
    schedule: const(1,30s)
  ssl: true
console:
  enabled: true
telegraf:
  enabled: true
  package: yandextank.plugins.Telegraf
  config: monitoring.xml
autostop:
  autostop:
    - http(2xx,100%,1s)

В целом, на этом всё.

Возможностями порадовал, можно придумывать запуск автоматических load-тестов для приложений в Kubernetes-кластере.

Ссылки по теме