Рубрики
Uncategorized

Nginx зеркалирующие советы и хитрости

В последнее время я играл с Nginx и его относительно новым зеркальным модулем, который появился в 1.13.4. T… Tagged with nginx, DevOps.

В последнее время я играл с Nginx и его относительно новым Зеркало модуль который появился в 1.13.4. Модуль зеркала позволяет копировать запросы на другой бэкэнд, игнорируя из него ответы. Пример пример использования для этого:

  • ПРЕДВАРИТЕЛЬНЫЕ ТЕСТРЕНИЯ, наблюдая, как ваша новая система обрабатывает реальный производственный трафик
  • Регистрация запросов на анализ безопасности. Это Какой инструмент Wallarm делает
  • Копирование запросов на исследования в области науки данных
  • и т.п.

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

Базовая настройка

Начнем с простой настройки. Скажем, у нас есть бэкэнд, который обрабатывает рабочую нагрузку на производство, и мы ставим доверенность перед ним:

Вот конфигурация Nginx:

upstream backend {
    server backend.local:10000;
}

server {
    server_name proxy.local;
    listen 8000;

    location / {
        proxy_pass http://backend;
    }
}

Есть 2 части — бэкэнд и прокси. Прокси (NGINX) прослушивает на порту 8000 и просто передает запросы на бэкэнд на порту 10000. Ничего особенного, но давайте сделаем быстрый тест на загрузку, чтобы увидеть, как он работает. Я использую Привет инструмент потому что это просто и позволяет создавать постоянную нагрузку вместо бомбардировки как можно более сильно, как это делают многие другие инструменты (WRK, Apache Benchmark, Siege).

$ hey -z 10s -q 1000 -n 100000 -c 1 -t 1 http://proxy.local:8000

Summary:
  Total:    10.0016 secs
  Slowest:  0.0225 secs
  Fastest:  0.0003 secs
  Average:  0.0005 secs
  Requests/sec: 995.8393

  Total data:   6095520 bytes
  Size/request: 612 bytes

Response time histogram:
  0.000 [1] |
  0.003 [9954]  |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
  0.005 [4] |
  0.007 [0] |
  0.009 [0] |
  0.011 [0] |
  0.014 [0] |
  0.016 [0] |
  0.018 [0] |
  0.020 [0] |
  0.022 [1] |

Latency distribution:
  10% in 0.0003 secs
  25% in 0.0004 secs
  50% in 0.0005 secs
  75% in 0.0006 secs
  90% in 0.0007 secs
  95% in 0.0007 secs
  99% in 0.0009 secs

Details (average, fastest, slowest):
  DNS+dialup:   0.0000 secs, 0.0003 secs, 0.0225 secs
  DNS-lookup:   0.0000 secs, 0.0000 secs, 0.0008 secs
  req write:    0.0000 secs, 0.0000 secs, 0.0003 secs
  resp wait:    0.0004 secs, 0.0002 secs, 0.0198 secs
  resp read:    0.0001 secs, 0.0000 secs, 0.0012 secs

Status code distribution:
  [200] 9960 responses

Хорошо, большинство запросов обрабатываются менее чем за миллисекунду, и ошибок нет — это наш базовый уровень.

Основное зеркалирование

Теперь давайте поместим еще один бэкэнд тестирования и отражают его

Основное зеркалирование настроено так:

upstream backend {
    server backend.local:10000;
}

upstream test_backend {
    server test.local:20000;
}

server {
    server_name proxy.local;
    listen 8000;

    location / {
        mirror /mirror;
        proxy_pass http://backend;
    }

    location = /mirror {
        internal;
        proxy_pass http://test_backend$request_uri;
    }

}

Добавляем Зеркало Директива на зеркальные запросы во внутреннее местоположение и определите это внутреннее местоположение. В этом внутреннем месте мы можем делать все, что позволяет нам делать Nginx, но пока мы просто прокси перед прокси.

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

$ hey -z 10s -q 1000 -n 100000 -c 1 -t 1 http://proxy.local:8000

Summary:
  Total:    10.0010 secs
  Slowest:  0.0042 secs
  Fastest:  0.0003 secs
  Average:  0.0005 secs
  Requests/sec: 997.3967

  Total data:   6104700 bytes
  Size/request: 612 bytes

Response time histogram:
  0.000 [1] |
  0.001 [9132]  |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
  0.001 [792]   |■■■
  0.001 [43]    |
  0.002 [3] |
  0.002 [0] |
  0.003 [2] |
  0.003 [0] |
  0.003 [0] |
  0.004 [1] |
  0.004 [1] |

Latency distribution:
  10% in 0.0003 secs
  25% in 0.0004 secs
  50% in 0.0005 secs
  75% in 0.0006 secs
  90% in 0.0007 secs
  95% in 0.0008 secs
  99% in 0.0010 secs

Details (average, fastest, slowest):
  DNS+dialup:   0.0000 secs, 0.0003 secs, 0.0042 secs
  DNS-lookup:   0.0000 secs, 0.0000 secs, 0.0009 secs
  req write:    0.0000 secs, 0.0000 secs, 0.0002 secs
  resp wait:    0.0004 secs, 0.0002 secs, 0.0041 secs
  resp read:    0.0001 secs, 0.0000 secs, 0.0021 secs

Status code distribution:
  [200] 9975 responses

Это почти то же самое — задержка миллисекунды и отсутствие ошибок. И это хорошо, потому что это доказывает, что зеркальное зеркало не влияет на исходные запросы.

Зеркальное зеркало в Buggy Backend

Это все приятно и денди, но что, если зеркальный бэкэнд имеет несколько ошибок, а иногда отвечает с ошибками? Что будет с исходными запросами?

Чтобы проверить это, я сделал Trivial Go Service Это может вносить ошибки случайным образом. Давайте запустим это

$ mirror-backend -errors
2019/01/13 14:43:12 Listening on port 20000, delay is 0, error injecting is true

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

$ hey -z 10s -q 1000 -n 100000 -c 1 -t 1 http://proxy.local:8000

Summary:
  Total:    10.0008 secs
  Slowest:  0.0027 secs
  Fastest:  0.0003 secs
  Average:  0.0005 secs
  Requests/sec: 998.7205

  Total data:   6112656 bytes
  Size/request: 612 bytes

Response time histogram:
  0.000 [1] |
  0.001 [7388]  |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
  0.001 [2232]  |■■■■■■■■■■■■
  0.001 [324]   |■■
  0.001 [27]    |
  0.002 [6] |
  0.002 [2] |
  0.002 [3] |
  0.002 [2] |
  0.002 [0] |
  0.003 [3] |

Latency distribution:
  10% in 0.0003 secs
  25% in 0.0003 secs
  50% in 0.0004 secs
  75% in 0.0006 secs
  90% in 0.0007 secs
  95% in 0.0008 secs
  99% in 0.0009 secs

Details (average, fastest, slowest):
  DNS+dialup:   0.0000 secs, 0.0003 secs, 0.0027 secs
  DNS-lookup:   0.0000 secs, 0.0000 secs, 0.0008 secs
  req write:    0.0000 secs, 0.0000 secs, 0.0001 secs
  resp wait:    0.0004 secs, 0.0002 secs, 0.0026 secs
  resp read:    0.0001 secs, 0.0000 secs, 0.0006 secs

Status code distribution:
  [200] 9988 responses

Ничего не изменилось вообще! И это здорово, потому что ошибки в бэкэнд зеркала не влияют на основной бэкэнд. Nginx Mirror Module игнорирует ответы на зеркальные подчинки, поэтому это поведение приятно и предназначено.

Отражение до медленного бэкэнда

Но что, если наша бэкэнд зеркала не возвращает ошибки, а просто медленно? Как будут работать оригинальные запросы? Давай выясним!

У моего зеркального бэкэнда есть возможность задержать каждый запрос на настроенный объем секунд. Здесь я запускаю его с задержкой на 1 секунду:

$ mirror-backend -delay 1
2019/01/13 14:50:39 Listening on port 20000, delay is 1, error injecting is false

Итак, давайте посмотрим, что шоу нагрузочного теста:

$ hey -z 10s -q 1000 -n 100000 -c 1 -t 1 http://proxy.local:8000

Summary:
  Total:    10.0290 secs
  Slowest:  0.0023 secs
  Fastest:  0.0018 secs
  Average:  0.0021 secs
  Requests/sec: 1.9942

  Total data:   6120 bytes
  Size/request: 612 bytes

Response time histogram:
  0.002 [1] |■■■■■■■■■■
  0.002 [0] |
  0.002 [1] |■■■■■■■■■■
  0.002 [0] |
  0.002 [0] |
  0.002 [0] |
  0.002 [1] |■■■■■■■■■■
  0.002 [1] |■■■■■■■■■■
  0.002 [0] |
  0.002 [4] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
  0.002 [2] |■■■■■■■■■■■■■■■■■■■■

Latency distribution:
  10% in 0.0018 secs
  25% in 0.0021 secs
  50% in 0.0022 secs
  75% in 0.0023 secs
  90% in 0.0023 secs
  0% in 0.0000 secs
  0% in 0.0000 secs

Details (average, fastest, slowest):
  DNS+dialup:   0.0007 secs, 0.0018 secs, 0.0023 secs
  DNS-lookup:   0.0003 secs, 0.0002 secs, 0.0006 secs
  req write:    0.0001 secs, 0.0001 secs, 0.0002 secs
  resp wait:    0.0011 secs, 0.0007 secs, 0.0013 secs
  resp read:    0.0002 secs, 0.0001 secs, 0.0002 secs

Status code distribution:
  [200] 10 responses

Error distribution:
  [10]  Get http://proxy.local:8000: net/http: request canceled (Client.Timeout exceeded while awaiting headers)

Какая? 1,9 RPS? Где мои 1000 RPS? У нас ошибки? Что творится?

Позвольте мне объяснить, как работает зеркальное зеркало в Nginx.

Как работает зеркальное зеркало в Nginx

Когда запрос поступает в NGINX и, если включено зеркальное зеркальное оборудование, NGINX создаст зеркало подвесок и сделает то, что указывает место зеркала — в нашем случае он отправит его на бэкэнд зеркала.

Но дело в том, что SubRequest связан с исходным запросом, так что Насколько я понимаю Если это зеркальное подразделение не завершено, исходные запросы будут застрять.

Вот почему мы получаем ~ 2 RP в предыдущем тесте — Привет Отправлено 10 запросов, получили ответы, отправили следующие 10 запросов, но они остановились, потому что предыдущие зеркальные подборы были отложены, а затем начался тайм -аут и ошибился в последних 10 запросах.

Если мы увеличим время ожидания в эй, скажем, на 10 секунд мы не получим ошибок и 1 RPS:

$ hey -z 10s -q 1000 -n 100000 -c 1 -t 10 http://proxy.local:8000

Summary:
  Total:    10.0197 secs
  Slowest:  1.0018 secs
  Fastest:  0.0020 secs
  Average:  0.9105 secs
  Requests/sec: 1.0978

  Total data:   6732 bytes
  Size/request: 612 bytes

Response time histogram:
  0.002 [1] |■■■■
  0.102 [0] |
  0.202 [0] |
  0.302 [0] |
  0.402 [0] |
  0.502 [0] |
  0.602 [0] |
  0.702 [0] |
  0.802 [0] |
  0.902 [0] |
  1.002 [10]    |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■

Latency distribution:
  10% in 1.0011 secs
  25% in 1.0012 secs
  50% in 1.0016 secs
  75% in 1.0016 secs
  90% in 1.0018 secs
  0% in 0.0000 secs
  0% in 0.0000 secs

Details (average, fastest, slowest):
  DNS+dialup:   0.0001 secs, 0.0020 secs, 1.0018 secs
  DNS-lookup:   0.0000 secs, 0.0000 secs, 0.0005 secs
  req write:    0.0001 secs, 0.0000 secs, 0.0002 secs
  resp wait:    0.9101 secs, 0.0008 secs, 1.0015 secs
  resp read:    0.0002 secs, 0.0001 secs, 0.0003 secs

Status code distribution:
  [200] 11 responses

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

Отражая часть трафика

Если вы не уверены, что Backend Mirror может обрабатывать исходную нагрузку, вы можете отражать только какую -то часть трафика — например, 10%.

Зеркало Директива не настраивается и повторяет все запросы в местоположение зеркала, поэтому не очевидно, как это сделать. Ключевым моментом в достижении этого является внутреннее зеркальное местоположение. Если вы помните, я сказал, что вы можете что -нибудь отражать запросы в его местоположении. Итак, вот как я это сделал:

 1  upstream backend {
 2      server backend.local:10000;
 3  }
 4  
 5  upstream test_backend {
 6      server test.local:20000;
 7  }
 8  
 9  split_clients $remote_addr $mirror_backend {
10      50% test_backend;
11      *   "";
12  }
13  
14  server {
15      server_name proxy.local;
16      listen 8000;
17  
18      access_log /var/log/nginx/proxy.log;
19      error_log /var/log/nginx/proxy.error.log info;
20  
21      location / {
22          mirror /mirror;
23          proxy_pass http://backend;
24      }
25  
26      location = /mirror {
27          internal;
28          if ($mirror_backend = "") {
29              return 400;
30          }
31  
32          proxy_pass http://$mirror_backend$request_uri;
33      }
34  
35  }
36  

Прежде всего, в месте зеркала мы продемонстрируем вверх по течению, который взят из переменной $ mirror_backend (строка 32). Эта переменная установлена в split_client Блок (строки 9-12) на основе удаленного адреса клиента. Что split_client Делает ли он устанавливает значение правой переменной на основе распределения левой переменной. В нашем случае мы рассмотрим запросы удаленного адреса ( $ remote_addr переменная) и на 50% удаленных адресов, которые мы установили $ mirror_backend к test_backend , для других запросов, он настроен на пустую строку. Наконец, частичная часть выполняется в зеркальном месте — если $ mirror_backend Переменная пуста, мы отвергаем это зеркало подвеска, в противном случае мы proxy_pass Это. Помните, что сбой в зеркальных подвесках не влияет на исходные запросы, поэтому безопасно отказаться от запроса со статусом ошибки.

Прелесть этого решения в том, что вы можете разделить трафик для зеркалирования на основе любой переменной или комбинации. Если вы хотите действительно дифференцировать своих пользователей, то удаленный адрес может быть не лучшим ключом для разделения — пользователь может использовать многие IPS или изменить их. В этом случае вам лучше использовать какой-то ключ, такой как клавиша API. Для зеркалирования 50% трафика на основе apikey Параметр запроса мы просто изменяем ключ в split_client :

split_clients $arg_apikey $mirror_backend {
    50% test_backend;
    * "";
}

Когда мы запросите Apikeys от 1 до 20, только половина из них (11) будет отражено. Вот срок:

$ for i in {1..20};do curl -i "proxy.local:8000/?apikey=${i}" ;done

А вот журнал бэкэнда зеркала:

...
2019/01/13 22:34:34 addr=127.0.0.1:47224 host=test_backend uri="/?apikey=1"
2019/01/13 22:34:34 addr=127.0.0.1:47230 host=test_backend uri="/?apikey=2"
2019/01/13 22:34:34 addr=127.0.0.1:47240 host=test_backend uri="/?apikey=4"
2019/01/13 22:34:34 addr=127.0.0.1:47246 host=test_backend uri="/?apikey=5"
2019/01/13 22:34:34 addr=127.0.0.1:47252 host=test_backend uri="/?apikey=6"
2019/01/13 22:34:34 addr=127.0.0.1:47262 host=test_backend uri="/?apikey=8"
2019/01/13 22:34:34 addr=127.0.0.1:47272 host=test_backend uri="/?apikey=10"
2019/01/13 22:34:34 addr=127.0.0.1:47278 host=test_backend uri="/?apikey=11"
2019/01/13 22:34:34 addr=127.0.0.1:47288 host=test_backend uri="/?apikey=13"
2019/01/13 22:34:34 addr=127.0.0.1:47298 host=test_backend uri="/?apikey=15"
2019/01/13 22:34:34 addr=127.0.0.1:47308 host=test_backend uri="/?apikey=17"
...

И самое удивительное, что разделение в split_client последовательно — запросы с apikey = 1 всегда будет зеркально.

Вывод

Так что это был мой опыт с модулем зеркала Nginx. Я показал вам, как просто отразить весь трафик, как отразить часть трафика с помощью split_client модуль. Я также рассмотрел обработку ошибок и невидимую проблему, когда нормальные запросы углублены в случае медленного зеркала.

Надеюсь, вам понравилось! Подписаться на Подача атома . Я также публикую в Twitter @alexdzyoba Анкет

Вот и все, до следующего раза!

Оригинал: «https://dev.to/dzeban/nginx-mirroring-tips-and-tricks-17c2»