Сравнение производительности HashiCorp Vault с разными бэкендами

e247445b8e4135726fd7dc62de1c84b1.png

Vault — Open Source-решение от HashiCorp для управления секретами. Его изначальная ориентированность на модульность и масштабируемость позволяет запускать как небольшой dev-сервер Vault на своем ноутбуке, так и полноценный HA-кластер для production-сред.

Начиная работать с Vault, мы задались двумя вопросами:

  1. Какой бэкенд (т.е. место, где физически хранятся секреты; им может быть локальная файловая система или решение на основе реляционных БД и других хранилищ) использовать и что по этому поводу говорят сами авторы?

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

По первому вопросу все сначала кажется предельно простым: HashiCorp рекомендует единственный правильный вариант — Consul. Не сказать, что удивительно, если знать/посмотреть на авторов этого продукта. А вот со вторым вопросом все сложнее. Но почему в принципе нас это волновало?

В проекте, где активно используется Vault, мы изначально запустили его на основе Consul, доверившись выбору по умолчанию. И вскоре увидели, что сам факт наличия и обслуживания Consul«а добавляет существенные накладные расходы: десятки репозиториев, десятки окружений, множество задач по обслуживанию эксплуатации (обновления, реконфигурация и т.д.). Посовещавшись с коллегами, мы приняли решение упростить себе жизнь и мигрировать на GCS (Google Cloud Storage), т.к. это полностью снимает головную боль от поддержки бэкенд-решения как self-hosted.

Однако со временем проявилась одна неожиданность, о которой сразу не задумывались: бэкенды рассчитаны на совершенно разную нагрузку. Если GCS выдерживал «спокойный» трафик от разработки, то во время массовых тестов и активных демо-показов заказчикам начинались неприятности. Это естественным образом натолкнуло на мысль: «Что же будет на проде? Раз мы столкнулись с такой проблемой, надо бы протестировать нагрузочные способности различных бэкендов, сравнить их».

Итак, есть много параметров, от которых зависит производительность Vault: задержка сети, выбранный бэкенд, количество узлов в кластере Vault и нагрузка на них… При изменении даже одного из них цифры сильно меняются. В рамках этой статьи мы рассмотрим только часть этих факторов — «базовую» (т.е. не включающую в себя оптимизацию) производительность различных бэкендов.

Хорошая новость в том, что в Git есть огромное количество репозиториев с готовыми скриптами для тестирования. Если вы захотите провести испытания на своем Vault«е, можно воспользоваться, например, Vault Benchmarking Scripts и Load Tests for Vault. Или же дочитайте статью и попробуйте мой метод тестирования, основанный на скриптах от HashiCorp.

Вводные для тестирования

Требования

Чтобы проводить описанные ниже тесты, нам понадобятся:

  • Уже работающий в режиме HA Vault. Его запуск я буду производить в отдельном Kubernetes-кластере, где будут разворачиваться 3 варианта кластеров Vault (с разными бэкендами).

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

Участники 

Мы будем тестировать три популярных бэкенда:

  1. Consul;

  2. PostgreSQL;

  3. GCS (Google Cloud Storage).

Главным критерием при их выборе была поддержка HA (по этой причине есть GCS, но нет AWS S3), а также старались не брать концептуально одинаковые виды, чтобы сравнение было более интересным и показательным.

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

План

Как будет происходить тестирование?

  1. В несколько потоков записываем 10000 секретов. У этого количества нет строгого обоснования: значение выбрано для достаточного объёма, «сложности» при тестировании бэкенда.

  2. Аналогично считываем их.

  3. Замеряем полученные данные для каждого бэкенда.

Технически нам в этом поможет специализированная утилита для тестирования нагрузки — wrk. Именно она используется в benchmarking-наборе от самой HashiCorp.

Тестируем производительность

1. Consul

Итак, сначала протестируем Vault с бэкендом Consul. По плану у нас два теста: на запись и на чтение секретов.

Запускаем кластер Consul из узлов в 3 pod«ах и указываем его как backend для Vault, тоже запущенного на 3 узлах.

6d7df159815baa6f84b6d47c9069a110

Мы готовы начинать!

# Подготавливаем переменные окружения:
export VAULT_ADDR=https://vault.service.consul:8200
export VAULT_TOKEN=YOUR_ROOT_TOKEN

# Включаем авторизацию в Vault для простого тестирования
vault auth enable userpass
vault write auth/userpass/users/loadtester password=benchmark policies=default

# Так как Vault запущен не в dev-моде,
# в пути `secret` сейчас ничего не должно быть
vault secrets enable -path secret -version 1 kv

git clone https://github.com/hashicorp/vault-guides.git
cd vault-guides/operations/benchmarking/wrk-core-vault-operations/

# Будем одновременно записывать рандомные секреты, используя:
# 6 потоков
# 16 соединений на поток
# 30 секунд на выполнение теста
# 10000 секретов для записи

nohup wrk -t6 -c16 -d30s -H "X-Vault-Token: $VAULT_TOKEN" -s write-random-secrets.lua $VAULT_ADDR -- 10000 > prod-test-write-1000-random-secrets-t6-c16-30sec.log &

Получаем такие данные:

Number of secrets is: 10000
thread 1 created
Number of secrets is: 10000
thread 2 created
Number of secrets is: 10000
thread 3 created
Number of secrets is: 10000
thread 4 created
Number of secrets is: 10000
thread 5 created
Number of secrets is: 10000
thread 6 created
Running 30s test @ http://vault:8200
  6 threads and 16 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   134.96ms   68.89ms 558.62ms   84.21%
    Req/Sec    16.31      6.62    50.00     87.82%
  2685 requests in 30.04s, 317.27KB read
Requests/sec:     89.39
Transfer/sec:     10.56KB
thread 1 made 446 requests including 446 writes and got 443 responses
thread 2 made 447 requests including 447 writes and got 445 responses
thread 3 made 447 requests including 447 writes and got 445 responses
thread 4 made 459 requests including 459 writes and got 457 responses
thread 5 made 450 requests including 450 writes and got 448 responses
thread 6 made 449 requests including 449 writes and got 447 responses

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

# Запишем 1000 секретов:

wrk -t1 -c1 -d5m -H "X-Vault-Token: $VAULT_TOKEN" -s write-secrets.lua $VAULT_ADDR -- 1000

# И проверим, что тысячный секрет создался:

vault read secret/read-test/secret-1000

Key                 Value
---                 -----
refresh_interval    768h
extra               1xxxxxxxxx2xxxxxxxxx3xxxxxxxxx4xxxxxxxxx5xxxxxxxxx6xxxxxxxxx7xxxxxxxxx8xxxxxxxxx9xxxxxxxxx0xxxxxxxxx
thread-1            write-1000


# Пробуем одновременное чтение 1000 секретов в 4 потока
nohup wrk -t4 -c16 -d30s -H "X-Vault-Token: $VAULT_TOKEN" -s read-secrets.lua $VAULT_ADDR -- 1000 false > prod-test-read-1000-random-secrets-t4-c16-30s.log &

Результат:

Number of secrets is: 1000
thread 1 created with print_secrets set to false
Number of secrets is: 1000
thread 2 created with print_secrets set to false
Number of secrets is: 1000
thread 3 created with print_secrets set to false
Number of secrets is: 1000
thread 4 created with print_secrets set to false
Running 30s test @ http://vault:8200
  4 threads and 16 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     2.25ms    2.88ms  54.18ms   88.77%
    Req/Sec     2.64k   622.04     4.81k    65.91%
  315079 requests in 30.06s, 130.08MB read
Requests/sec:  10483.06
Transfer/sec:      4.33MB
thread 1 made 79705 requests including 79705 reads and got 79700 responses
thread 2 made 79057 requests including 79057 reads and got 79053 responses
thread 3 made 78584 requests including 78584 reads and got 78581 responses
thread 4 made 77748 requests including 77748 reads and got 77745 responses

2. PostgreSQL

Проводим аналогичный тест для PostgreSQL 11.7.0 в качестве бэкенда. SSL выключен, все настройки выставлены по умолчанию.

ee967f79ea15429f7494298a0964aafd

Результат записи:

Number of secrets is: 10000
thread 1 created
Number of secrets is: 10000
thread 2 created
Number of secrets is: 10000
thread 3 created
Number of secrets is: 10000
thread 4 created
Number of secrets is: 10000
thread 5 created
Number of secrets is: 10000
thread 6 created
Running 30s test @ http://vault:8200
  6 threads and 16 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    33.02ms   14.74ms 120.97ms   67.81%
    Req/Sec    60.70     15.02   121.00     69.44%
  10927 requests in 30.03s, 1.26MB read
Requests/sec:    363.88
Transfer/sec:     43.00KB
thread 1 made 1826 requests including 1826 writes and got 1823 responses
thread 2 made 1797 requests including 1797 writes and got 1796 responses
thread 3 made 1833 requests including 1833 writes and got 1831 responses
thread 4 made 1832 requests including 1832 writes and got 1830 responses
thread 5 made 1801 requests including 1801 writes and got 1799 responses
thread 6 made 1850 requests including 1850 writes and got 1848 responses

Результат чтения:

Number of secrets is: 1000
thread 1 created with print_secrets set to false
Number of secrets is: 1000
thread 2 created with print_secrets set to false
Number of secrets is: 1000
thread 3 created with print_secrets set to false
Number of secrets is: 1000
thread 4 created with print_secrets set to false
Running 30s test @ http://vault:8200
  4 threads and 16 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     2.48ms    2.93ms  71.93ms   89.76%
    Req/Sec     2.11k   495.44     3.01k    65.67%
  251861 requests in 30.02s, 103.98MB read
Requests/sec:   8390.15
Transfer/sec:      3.46MB
thread 1 made 62957 requests including 62957 reads and got 62952 responses
thread 2 made 62593 requests including 62593 reads and got 62590 responses
thread 3 made 63074 requests including 63074 reads and got 63070 responses
thread 4 made 63252 requests including 63252 reads and got 63249 responses

3. GCS

Теперь для бэкенда GCS (настройки тоже по умолчанию):

d630ca3051989a1dbaa4bc05e1285ffe

Запись:

Number of secrets is: 10000
thread 1 created
Number of secrets is: 10000
thread 2 created
Number of secrets is: 10000
thread 3 created
Number of secrets is: 10000
thread 4 created
Number of secrets is: 10000
thread 5 created
Number of secrets is: 10000
thread 6 created
Running 30s test @ http://vault:8200
  6 threads and 16 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   209.08ms   42.58ms 654.51ms   91.96%
    Req/Sec     9.89      2.45    20.00     87.21%
  1713 requests in 30.04s, 202.42KB read
Requests/sec:     57.03
Transfer/sec:      6.74KB
thread 1 made 292 requests including 292 writes and got 289 responses
thread 2 made 286 requests including 286 writes and got 284 responses
thread 3 made 284 requests including 284 writes and got 282 responses
thread 4 made 289 requests including 289 writes and got 287 responses
thread 5 made 287 requests including 287 writes and got 285 responses
thread 6 made 288 requests including 288 writes and got 286 responses

Чтение:

Number of secrets is: 1000
thread 1 created with print_secrets set to false
Number of secrets is: 1000
thread 2 created with print_secrets set to false
Number of secrets is: 1000
thread 3 created with print_secrets set to false
Number of secrets is: 1000
thread 4 created with print_secrets set to false
Running 30s test @ http://vault:8200
  4 threads and 16 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     4.40ms    1.72ms  53.59ms   92.11%
    Req/Sec     0.93k   121.29     1.19k    71.83%
  111196 requests in 30.02s, 45.91MB read
Requests/sec:   3704.27
Transfer/sec:      1.53MB
thread 1 made 31344 requests including 31344 reads and got 31339 responses
thread 2 made 26639 requests including 26639 reads and got 26635 responses
thread 3 made 25690 requests including 25690 reads and got 25686 responses
thread 4 made 27540 requests including 27540 reads and got 27536 responses

Сводные результаты

Consul

PostgreSQL

GCS

write (RPS/thread)

16.31

60.70

9.89

write (total)

2685

10927

1713

read (RPS/thread)

2640

2110

930

read (total)

315079

251861

111196

Показатели в таблице — это количество секретов:

  • записываемых в секунду на 1 поток;

  • записанных в общей сложности (во всех потоках) за 30 секунд;

  • читаемых в секунду на 1 поток;

  • прочитанных в общей сложности (во всех потоках) за 30 секунд.

Прочитать полученные результаты можно так:

  • PostgreSQL показал себя с самой лучшей стороны в плане записи. Он в 4 раза производительнее Consul, и в 10 раз производительнее GCS.

  • В плане чтения лучший показатель у Consul.

Более общие выводы:

  • При ожидаемом очень большом количестве записей можно рекомендовать использовать PostgreSQL. В остальных случаях — Consul, который официально советуют использовать в Hashicorp.

  • По нашему опыту, GCS наиболее удобен, если мы не хотим сами разворачивать и поддерживать бэкенд. Вдобавок, у него есть удобный Auto Unseal и встроенный сервис для создания бэкапов (с ними есть свои сложности, но об этом — в другой статье).

Примечание по результатам

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

При тестировании мы использовали токен $VAULT_TOKEN, не учитывая процесс авторизации. Результаты будут разниться в зависимости от разных параметров. Среди них:

  • количество одновременных клиентов;

  • какой метод аутентификации используете;

  • как часто клиенты будут аутентифицироваться;

  • какой тип секретов будет использоваться;

  • откуда будут приходить запросы (из локальной сети или через интернет);

Заключение

Надеюсь, что проведенное испытание поможет ответить на вопрос о производительности бэкендов в Vault, который возник и у нас. Представленную здесь последовательность действий рассматривайте как основу для проведения собственных экспериментов. И уже такое внутреннее тестирование поможет получить более точное представление, как будет себя вести Vault при реальных нагрузках в вашем случае. Для этого могут потребоваться изменения в конфигурации бэкендов (и их выбор вообще), а также модификация Lua-скриптов и/или параметров их вызова.

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

© Habrahabr.ru