07 апреля, 2015

Организация a/b тестирования веб-проектов средставами nginx

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

Определимся с условиями нашей задачи:
  1.  На новую версию сервиса мы должны направить ≈20% всех пользователей
  2.  Есть ip-адреса клиентов, которые должны всенепременно попадать на старую версию
  3.  То же самое, но – всенепременно на новую.
Далее я приведу конфигурационный файл целиком, а после опишу принцип его действия.

geo $mytest_participant {
  default              1;
  10.1.1.1             0;
  192.168.45.2         2;
}

map $mytest_participant $resulting_upstream_name {
  0     "old_version_upstream";
  1     "distributed_upstream";
  2     "new_version_upstream";
}

upstream distributed_upstream {
  ip_hash;
  server 1.1.1.1 weight=8; # Old version server, 80% of clients
  server 2.2.2.2; # New version server
}

upstream old_version_upstream {
  server 1.1.1.1;
}

upstream new_version_upstream {
  server 2.2.2.2;
}

server {
  listen 80;

  server_name example.com;

  location / {
    proxy_pass http://$resulting_upstream_name;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For  $proxy_add_x_forwarded_for;
  }

}

Итак, первый блок (geo) описывает как раз распределение пользователей по апстримам.
ip-адреса (можно использовать CIDR-нотацию, разумеется), для которых выставлено значение 0, будут направляться на старую версию принудительно, 1 - распределяться по вышеуказанному алгоритму (80% – на старую, 20% – на новую версию), ну и 2 - принудительно на новую.

Именно в зависимости от этого значения и присваивается имя апстрима в следующем блоке (map).

Далее все просто: описываются апстримы для старой и новых версий, а также наш распределенный апстрим. Для последнего используется алгоритм балансировки ip_hash, благодаря которому одни и те же ip-адреса будут попадать строго на тот апстрим, на который попали впервые. Если нам нужно изменить процентное соотношение распределения по старой и новым версиям, то правим соответствующим образом параметр weight для старого апстрима.

Вот, собственно, и всё.

Нужно, однако, помнить, что ip_hash использует только первые три октета ip-адреса клиента, т.е. на ту или иную версию попадет сразу весь /24 сегмент. Однако обычно это не является чем-то критичным, тем более, что в блоке geo можно подкорректировать ситуацию для отдельных ip-адресов, при наличии необходимости.