Создание прокси-сервиса#
Создание простого прокси-сервиса#
Одной из основных задач для SOWA является защищенное проксирование между участниками взаимодействия. Разберем примеры, как это можно осуществить.
В самом простом случае проксирование представляет собой простое синхронное http взаимодействие, в котором запрос клиента пробрасывается с порта SOWA, на порт backend.
Создадим конфигурационный файл нашего прокси профиля.
Создадим каталог proxies в каталоге examples, и поместим в него конфигурационный файл профиля proxy.yml со следующим содержимым:
profile: simple_proxy
version: 2.0.3
system:
wrk_count: 1
conn_count: 4096
service:
!include: services/simple_proxy_service.yml
В каталоге services создадим файл simple_proxy_service.yml, со следующим содержимым:
service_http_proxy:
- id: 1
url: ^/hello
upstream_groups:
- id: back_1
servers:
- server: 127.0.0.1:10012
upstream_group_id: back_1
allowed_queries:
- method: get
listen:
- port: 10000
Рассмотрим назначение использованных конфигурационных элементов.
service_http_proxy - определяет группу сервисов, реализующих поведение примитива http_proxy;
Далее следует перечислить все сервисы данного примитива (в нашем случае он единственный).
id - уникальный идентификатор сервиса в профиле, обязательный параметр;
url - регулярное выражение, определяющее контекст точки входа прокси-сервиса;
upstream_groups - список элементов типа параметр upstream, определяющих, куда будет проксироваться запрос;
upstream_group_id - обязательный параметр, определяющий идентификатор группы из upstream_groups, на который будут проксироваться запросы в текущей конфигурации;
allowed_queries - набор элементов, определяющий список допустимых HTTP-методов запроса при проксировании (в данный момент обязательный элемент), в нашем случае это будет метод GET;
listen - список точек входа прокси в виде параметр listen.
Далее необходимо создать, сконфигурировать и запустить профиль simple_proxy.
[sowacfg@tkle-ish0038 proxies]$ sowa-config --add-profile simple_proxy
Profile "simple_proxy" was successfully created
[sowacfg@tkle-ish0038 proxies]$ sowa-config --config proxy.yml
Profile "simple_proxy" was successfully configured.
[sowacfg@tkle-ish0038 proxies]$ sowa-config --run simple_proxy
Profile "simple_proxy" was successfully started.
Проверим работоспособность прокси:
[sowacfg@tkle-ish0038 proxies]$ curl -v http://localhost:10000/hello/world
* About to connect() to localhost port 10000 (#0)
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 10000 (#0)
> GET /hello/world HTTP/1.1
> User-Agent: curl/7.29.0
> Host: localhost:10000
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Mon, 05 Apr 2021 09:11:17 GMT
< Content-Type: text/plain
< Content-Length: 11
< Connection: keep-alive
< Server: SOWA
< Accept-Ranges: bytes
<
* Connection #0 to host localhost left intact
Hello world[sowacfg@tkle-ish0038 proxies]$
А теперь проверим, как сработают ограничения на допустимые методы, попытавшись отправить запрос с методом HEAD:
Hello world[sowacfg@tkle-ish0038 proxies]$ curl -X HEAD -v http://localhost:10000/hello/world
* About to connect() to localhost port 10000 (#0)
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 10000 (#0)
> HEAD /hello/world HTTP/1.1
> User-Agent: curl/7.29.0
> Host: localhost:10000
> Accept: */*
>
< HTTP/1.1 500 Internal Server Error
< Date: Mon, 05 Apr 2021 09:14:32 GMT
< Content-Type: text/html
< Content-Length: 169
< Connection: close
< Server: SOWA
<
<html>
<head><title>500 Internal Server Error</title></head>
<body>
<center><h1>500 Internal Server Error</h1></center>
<hr><center>SOWA</center>
</body>
</html>
* Closing connection 0
Создание прокси-сервиса с единой точкой входа#
Зачастую необходимо реализовать схемы взаимодействия, отличные от рассмотренной в предыдущем пункте, а именно схему, при которой в прокси наружу выставлена единая(-ые) точка входа, на которую обращаются клиенты, а за прокси стоит n-ое количество backend, задачу маршрутизации на которые осуществляет собственно прокси-сервис.
Схематично можно представить это на следующих диаграммах:


Для решения обеих задач используется примитив MainProxy.
Рассмотрим, как можно настроить взаимодействие для каждого из этих вариантов.
Одна точка входа, у каждого из сервисов своя точка выхода
Создадим в каталоге examples/proxies/services файл main_proxy.yml, который будет иметь следующее содержимое:
service_main_proxy:
- id: main
listen:
- port: 11000
url: /
optional:
log_level: error
Фактически, это описание нашей единой точки входа на порту 11000 с контекстом "/". Здесь важно определение сервиса как примитива service_main_proxy, при этом сервис данного типа должен присутствовать в профиле в единственном экземпляре.
Теперь добавим два сервиса service_world и service_drlow в файл simple_proxy_service.yml:
- id: service_world
url: ^/world
upstream_group_id: back_hello_world
upstream_groups:
- id: back_hello_world
servers:
- server: 127.0.0.1:10012
allowed_queries:
- method: get
- id: service_dlrow
url: ^/dlrow
upstream_group_id: back_hello_dlrow
upstream_groups:
- id: back_hello_dlrow
servers:
- server: 127.0.0.1:10013
allowed_queries:
- method: post
Следует обратить внимание, что у данных сервисов не определен параметр listen - т.к. слушатель у нас будет в сервисе main_proxy, а также, что у каждого из них свои допустимые методы. Затем добавим main_proxy.yml в общий конфиг профиля.
profile: simple_proxy
version: 2.0.3
system:
wrk_count: 1
conn_count: 4096
service:
!include: services/simple_proxy_service.yml
!include: services/main_proxy.yml
Выполним последовательность операций - остановка профиля, конфигурирование и старт.
Попробуем отправить запросы на два наших сервиса, через сервис main_proxy.
Пример запроса к первому сервису:
[sowacfg@tkle-ish0038 proxies]$ curl -X GET -v http://localhost:11000/world
* About to connect() to localhost port 11000 (#0)
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 11000 (#0)
> GET /world HTTP/1.1
> User-Agent: curl/7.29.0
> Host: localhost:11000
> Accept: */*
>
< HTTP/1.1 404 Not Found
< Date: Mon, 05 Apr 2021 10:08:52 GMT
< Content-Type: text/html
< Content-Length: 145
< Connection: keep-alive
< Server: SOWA
<
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>SOWA</center>
</body>
</html>
* Connection #0 to host localhost left intact
Пример запроса ко второму сервису:
[sowacfg@tkle-ish0038 proxies]$ curl --data "test" -v http://localhost:11000/dlrow
* About to connect() to localhost port 11000 (#0)
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 11000 (#0)
> POST /dlrow HTTP/1.1
> User-Agent: curl/7.29.0
> Host: localhost:11000
> Accept: */*
> Content-Length: 4
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 4 out of 4 bytes
< HTTP/1.1 502 Bad Gateway
< Date: Mon, 05 Apr 2021 10:10:26 GMT
< Content-Type: text/html
< Content-Length: 149
< Connection: keep-alive
< Server: SOWA
<
<html>
<head><title>502 Bad Gateway</title></head>
<body>
<center><h1>502 Bad Gateway</h1></center>
<hr><center>SOWA</center>
</body>
</html>
* Connection #0 to host localhost left intact
И в первом, и во втором случае получаем ошибки, но причины их отличаются.
В первом случае, на backend отсутствует контекст /world, что соответствует действительности, так как единственный сервис, который был объявлен, это сервис /hello/world. При этом сам backend физически доступен.
Во втором случае, в секции upstream_groups объявлен несуществующий сервер, поэтому получили ошибку 502 Bad Gateway.
Внесем изменения в нашу заглушку:
- id: world_plain_service
optional:
response_code: 200
response_body: "World hello"
response_headers:
- name: Content-Type
value: text/plain
url: ^/world
listen:
- port: 10014
- id: dlrow_plain_service
optional:
response_code: 200
response_body: "Dlrow hello"
response_headers:
- name: Content-Type
value: text/plain
url: ^/dlrow
listen:
- port: 10013
Добавим два сервиса с контекстами /world и /dlrow соответственно на портах 10014 и 10013, после чего остановим профиль sample_stub, сконфигурируем и запустим.
Так как сервис world_plain_service теперь объявлен на порту 10014, то вернемся к файлу simple_proxy_service и изменим значение порта backend для сервиса service_world c 10012 на 10014, после чего остановим, сконфигурируем и запустим профиль simple_proxy.
Повторим запросы к сервису main_proxy и посмотрим, что изменилось:
Пример запроса к первому сервису:
[sowacfg@tkle-ish0038 proxies]$ curl -X GET -v http://localhost:11000/world
* About to connect() to localhost port 11000 (#0)
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 11000 (#0)
> GET /world HTTP/1.1
> User-Agent: curl/7.29.0
> Host: localhost:11000
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Mon, 05 Apr 2021 10:27:41 GMT
< Content-Type: text/html
< Content-Length: 145
< Connection: keep-alive
< Server: SOWA
<
* Connection #0 to host localhost left intact
Пример запроса ко второму сервису:
[sowacfg@tkle-ish0038 proxies]$ curl --data "test" -v http://localhost:11000/dlrow
* About to connect() to localhost port 11000 (#0)
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 11000 (#0)
> POST /dlrow HTTP/1.1
> User-Agent: curl/7.29.0
> Host: localhost:11000
> Accept: */*
> Content-Length: 4
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 4 out of 4 bytes
< HTTP/1.1 200 OK
< Date: Mon, 05 Apr 2021 10:23:56 GMT
< Content-Type: text/plain
< Content-Length: 11
< Connection: keep-alive
< Server: SOWA
< Accept-Ranges: bytes
<
* Connection #0 to host localhost left intact
Как видим, оба запроса теперь проходят успешно.
Одна точка входа, сервисы используют общие точки выхода
Создадим еще один профиль для демонстрации второго способа балансировки запросов. В каталоге proxies создадим файл proxy_use_main со следующим содержимым:
profile: proxy_use_main
version: 2.0.3
system:
wrk_count: 1
conn_count: 4096
optional:
log_level: error
service:
service_main_proxy:
- id: main
listen:
- port: 11001
url: /
upstream_groups:
- id: back_1
servers:
- server: 127.0.0.1:10016
- server: 127.0.0.1:10015
service_http_proxy:
- id: 1
name: service_1
url: ^/hello
use_main_upstream_groups: true
upstream_group_id: back_1
allowed_queries:
- method: get
- id: 2
name: service_2
url: ^/bye
upstream_group_id: back_1
use_main_upstream_groups: true
allowed_queries:
- method: get
В этом случае группа backend объявлена в сервисе main_proxy, а ссылка на нее осуществляется в сервисах service_1 и service_2. При этом, для того чтобы эта группа была доступна в сервисах, используется специальная директива use_main_upstream_groups.
Выполним стандартные операции по добавлению профиля, конфигурированию и запуску.
Теперь добавим в нашем профиле для заглушек сервисы sample1 и sample2 для использования в нашем прокси в качестве backend:
- id: sample1
optional:
response_code: 200
response_body: "what do you want?"
response_headers:
- name: Content-Type
value: text/plain
- name: Server-Port
value: $server_port
- name: Request-Uri
value: $request_uri
url: ^/
listen:
- port: 10015
- id: sample2
optional:
response_code: 200
response_body: "what do you want?"
response_headers:
- name: Content-Type
value: text/plain
- name: Server-Port
value: $server_port
- name: Request-Uri
value: $request_uri
url: ^/
listen:
- port: 10016
Следует обратить внимание на то, что добавили в вывод заголовков ответа значение переменных $server_port и $request_uri для того, чтобы на стороне клиента иметь возможность увидеть, на какой backend и на какой контекст backend реально приходят запросы.
Проверим получившиеся запросы.
Два запроса к сервису hello:
[sowacfg@tkle-ish0038 proxies]$ curl -v http://localhost:11001/hello
* About to connect() to localhost port 11001 (#0)
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 11001 (#0)
> GET /hello HTTP/1.1
> User-Agent: curl/7.29.0
> Host: localhost:11001
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Mon, 05 Apr 2021 10:53:44 GMT
< Content-Type: text/plain
< Content-Length: 17
< Connection: keep-alive
< Server-Port: 10016
< Request-Uri: /hello
< Server: SOWA
[sowacfg@tkle-ish0038 proxies]$ curl -v http://localhost:11001/hello
* About to connect() to localhost port 11001 (#0)
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 11001 (#0)
> GET /hello HTTP/1.1
> User-Agent: curl/7.29.0
> Host: localhost:11001
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Mon, 05 Apr 2021 10:56:40 GMT
< Content-Type: text/plain
< Content-Length: 17
< Connection: keep-alive
< Server-Port: 10015
< Request-Uri: /hello
< Server: SOWA
Два запроса к сервису bye:
[sowacfg@tkle-ish0038 proxies]$ curl -v http://localhost:11001/bye
* About to connect() to localhost port 11001 (#0)
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 11001 (#0)
> GET /bye HTTP/1.1
> User-Agent: curl/7.29.0
> Host: localhost:11001
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Mon, 05 Apr 2021 10:58:14 GMT
< Content-Type: text/plain
< Content-Length: 17
< Connection: keep-alive
< Server-Port: 10016
< Request-Uri: /bye
< Server: SOWA
[sowacfg@tkle-ish0038 proxies]$ curl -v http://localhost:11001/bye
* About to connect() to localhost port 11001 (#0)
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 11001 (#0)
> GET /bye HTTP/1.1
> User-Agent: curl/7.29.0
> Host: localhost:11001
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Mon, 05 Apr 2021 10:59:38 GMT
< Content-Type: text/plain
< Content-Length: 17
< Connection: keep-alive
< Server-Port: 10015
< Request-Uri: /bye
< Server: SOWA
Как видно, обращаясь по каждому из url несколько раз, фактически поочередно отправляются запросы на разные серверы из группы бэков.
Балансировка#
Балансировка нагрузки между несколькими серверами — это широко используемый метод для оптимизации использования ресурсов, максимизации пропускной способности, уменьшения задержки и обеспечения отказоустойчивых конфигураций.
Вернемся к нашему конфигурационному файлу proxy_use_main.yml.
profile: proxy_use_main
version: 2.0.3
system:
wrk_count: 1
conn_count: 4096
optional:
log_level: error
service:
service_main_proxy:
- id: main
listen:
- port: 11001
url: /
upstream_groups:
- id: back_1
servers:
- server: 127.0.0.1:10016
- server: 127.0.0.1:10015
service_http_proxy:
- id: 1
name: service_1
url: ^/hello
use_main_upstream_groups: true
upstream_group_id: back_1
allowed_queries:
- method: get
- id: 2
name: service_2
url: ^/bye
upstream_group_id: back_1
use_main_upstream_groups: true
allowed_queries:
- method: get
Параметры балансировки описываются в секции upstream_groups. Поскольку в upstream_groups не указан алгоритм балансировки нагрузки, SOWA (включающая в себя NGINX) использует алгоритм по умолчанию Round Robin (директивы для его включения нет).
Round Robin - это алгоритм, при котором запросы равномерно распределяются по серверам с учетом веса серверов (weight). Это не означает, что запросы будут распределяться между серверами последовательно друг за другом. Равномерность распределения означает, что в случае наличия двух серверов с весами weight: 100 после 200 запросов, увидим в сумме ~50:50 попаданий между ними. Например, 10 запросов уйдут на первый, следующие 13 на второй сервер и т.д.
По умолчанию, вес каждого сервера равен "1". Значение веса — это весовой коэффициент. Чем он больше (относительно других), тем чаще сервер будет вызван.
К примеру, при распределении весов 5 к 2 результат будет следующим:
23 запроса к серверу с портом 10015 и 9 к серверу с портом 10016.
23/9 ~2,5 - как и ожидалось.
Если необходимо использовать другой тип балансировки (не Round Robin), то необходимо использовать группу параметров load_balancer.
В группе load_balancer, существует параметр type - тип балансировки. Рассмотрим следующие типы: hash и sticky.
hash - тип балансировки, при котором соответствие клиента серверу определяется при помощи хэшированного значения "ключа".
Приведем пример использования:
...
url: /
hostname:
- ip1
- ip2
upstream_groups:
- id: back_1
servers:
- server: 127.0.0.1:10016
- server: 127.0.0.1:10015
load_balancing:
type: hash
hash_key: $http_host
hash_consistent: "off"
При использовании значения hash в параметре type, необходимо также указывать параметр hash_key. Если задан параметр hash_consistent ("on"/"off"), то вместо вышеописанного метода будет использоваться метод консистентного хэширования ketama. Значение параметра hash_key хэшируется, и с помощью него устанавливается отображение клиент-сервер. В качестве значения параметра hash_key может использоваться текст, переменные и их комбинации. Будет использоваться переменная $http_ с дополнением host.
$http_имя - произвольное поле заголовка запроса; последняя часть имени переменной соответствует имени поля, приведенному к нижнему регистру, с заменой символов тире на символы подчеркивания. Чтобы продемонстрировать работу балансировки по имени используемого хоста, нужно добавить секцию hostname с параметрами ip1 и ip2.
Отправим запросы к сервисам hello и bye. Укажем Host: ip1
curl -vvv -H "Host: ip1" http://127.0.0.1:11001/hello
curl -vvv -H "Host: ip1" http://127.0.0.1:11001/bye
Результат выполнения запроса:

Видно, что при использовании Host: ip1 произошло залипание на порт 10016.
Теперь отправим запросы к сервисам hello и bye. Укажем Host: ip2
curl -vvv -H "Host: ip2" http://127.0.0.1:11001/hello
curl -vvv -H "Host: ip2" http://127.0.0.1:11001/bye
Результат выполнения запроса:

Видно, что при использовании Host: ip2 произошло "залипание" на порт 10015.
Таким образом, был рассмотрен процесс работы балансировки типа hash.
sticky привязывает запрос (на определенное время или нет) к конкретному upstream'у.
Пример использования:
upstream_groups:
- id: back_1
servers:
- server: 127.0.0.1:10015
- server: 127.0.1.1:10016
load_balancing:
type: sticky
name: SWJSESSIONID
monitor_cookie: UFS-SESSION
name - имя файла cookie, используемого для отслеживания, сохраняющихся upstream srv. monitor_cookie - cookie, устанавливаемая upstream'ом в ответе в заголовке Set-Cookie. Если cookie пришли, выставляется имя сессии.
Внесем эти изменения в конфигурационный файл, переконфигурируем и перезапустим профиль.
Добавим в сервисы sample1 и sample2 (с портами 10015 и 10016 соответственно), в секцию response_headers строки:
response_headers:
...
- name: Set-Cookie
value: UFS-SESSION=9fa7947d-8550-4ecd-9fe6-7d01efec41da
Переконфигурируем и перезапустим профиль заглушек.
Проверим следующие 5 примеров.
Простой запрос:

Отправим запрос на сервис bye.
curl -vvv http://localhost:11001/byeРезультат выполнения запроса:
Был отправлен запрос на SOWA по контексту bye. SOWA проксировала запрос на backend с портом 10015. На backend установлен заголовок запроса Set-Cookie со значением UFS-SESSION (значение совпадает со значением параметра monitor_cookie).Set-Cookie: UFS-SESSION=9fa7947d-8550-4ecd-9fe6-7d01efec41daБлагодаря этому, SOWA добавила сессионную cookie с именем SWJSESSIONID.
Сессионная cookie, устанавливаемая SOW'ой:
Set-Cookie: SWJSESSIONID=5699daa888fef95f98529a0c316350f5; Path=/Запрос с использованием сессионной куки

Отправим запрос на сервис bye с использованием сессионной cookie, чтобы убедиться в "залипании" на конкретный сервер (в данном случае нода с портом 10015).
curl -vvv -H "Cookie: SWJSESSIONID=5699daa888fef95f98529a0c316350f5" http://localhost:11001/byeРезультат выполнения запроса:
Действительно вновь попали на ноду с портом 10015.Теперь в response headers у нас один заголовок Set-Cookie — это cookie backend.
Set-Cookie: UFS-SESSION=9fa7947d-8550-4ecd-9fe6-7d01efec41daSOWA не добавила сессионную cookie с именем SWJSESSIONID, т.к. она использовалась в запросе и "залипание" на ноду произошло.
Сессионная cookie, устанавливаемая SOW'ой: отсутствует.
Запрос с использованием куки, имя которой отличается от сессионной на SOWA

Изменим имя cookie, добавив к нему некоторые символы. Отправим запрос:
curl -vvv -H "Cookie: BADSWJSESSIONID=5699daa888fef95f98529a0c316350f5" http://localhost:11001/byeРезультат выполнения запроса:

В данном случае видно, что хоть и использоваось значение сессионной cookie, но было указано имя, отличающееся от значения, указанного в параметре name. "Залипания" на ноду не произошло.
SOWA проксировала запрос на второй backend (проксирование могло произойти и на первый backend. Здесь сработал механизм распределения запросов по серверам).
При этом заголовок запроса backend Set-Cookie со значением UFS-SESSION совпадает со значением параметра monitor_cookie.
Поэтому видим cookie backend.
Set-Cookie: UFS-SESSION=9fa7947d-8550-4ecd-9fe6-7d01efec41daПлюс SOWA добавила сессионную cookie с именем SWJSESSIONID.
Сессионная cookie, устанавливаемая SOW'ой
SWJSESSIONID=d2e7e87c10c7edf5ae58b0b6a8237d23; Path=/Запрос к серверу, на котором указана кука, отличающаяся от monitor_cookie

Внесем изменения в конфигурационный файл сервисов для заглушки (для сервиса с портом 10015):
response_headers: ... - name: Set-Cookie value: BADUFS-SESSION=9fa7947d-8550-4ecd-9fe6-7d01efec41daПереконфигурируем и перезапустим профиль заглушек.
Отправим запрос на сервис bye.
curl -vvv http://localhost:11001/byeРезультат выполнения запроса:

Видно, что запрос попал на ноду с портом 10015 и SOWA не проставила сессионную cookie, т.к. не совпадает значение параметра monitor_cookie со значением полученным с backend.
BADUFS-SESSION=9fa7947d-8550-4ecd-9fe6-7d01efec41daСессионная cookie, устанавливаемая SOW'ой: отсутствует.
Повторим запрос:
curl -vvv http://localhost:11001/byeРезультат выполнения запроса:

SOWA проксировала запрос на ноду с портом 10016. Здесь изменения не были внесены в заголовок запроса Set-Cookie.
UFS-SESSION=9fa7947d-8550-4ecd-9fe6-7d01efec41daСессионная cookie, устанавливаемая SOW'ой:
SWJSESSIONID=d2e7e87c10c7edf5ae58b0b6a8237d23; Path=/Запрос с сессионной кукой к серверу, на котором куки отличается от monitor_cookie

Отправим запрос на сервис bye с использованием сессионной cookie, чтобы убедиться в "залипании" на конкретный сервер (в нашем случае нода с портом 10015). Но стоить помнить, что значение параметра monitor_cookie не совпадает со значением установленным на backend.
curl -vvv -H "Cookie: SWJSESSIONID=5699daa888fef95f98529a0c316350f5" http://localhost:11001/byeРезультат выполнения запроса:

Видно, что запрос попал на ноду с портом 10015 и произошло "залипание", т.к. имя сессионной cookie корректно.
BADUFS-SESSION=9fa7947d-8550-4ecd-9fe6-7d01efec41daSOWA не добавила сессионную cookie с именем SWJSESSIONID, т.к. она используется в запросе и "залипание" на ноду произошло.
Сессионная cookie, устанавливаемая SOW'ой: отсутствует.
Утилита health check активирует периодические проверки работоспособности серверов в upstream группе. При включенном hc, по умолчанию каждые пять секунд SOWA отправляет запрос каждому серверу в upstream группе. Если возникает какая-либо ошибка связи или тайм-аут, то проверка работоспособности не выполняется.
Сервер помечается как неисправный, и SOWA не отправляет ему клиентские запросы, пока он снова не пройдет проверку работоспособности.
В каталоге examples/proxies создадим конфигурационный файл hc.yml и наполним его содержимым:
profile: hc version: 2.0.3 system: wrk_count: 1 conn_count: 4096 optional: log_level: debug service: service_main_proxy: - id: main listen: - port: 8030 url: / upstream_groups: - id: back_1 servers: - server: 127.0.0.1:18030 health_check: enabled: true service_http_proxy: - id: 1 name: service_1 url: ^\/hello$ use_main_upstream_groups: true upstream_group_id: back_1 allowed_queries: - method: getБыли объявлены два сервиса: первый (main) - тип main, второй (service_1) - тип httpProxy.
В секцию upsteam_groups сервиса main нужно добавить модуль health_check. У модуля health_check, параметр enable является обязательным и, в качестве значения, принимает логические выражения - true/false.
Добавим профиль hc в реестр профилей:
sowa-config --add-profile hcСконфигурируем профиль hc:
sowa-config --config hc.ymlМодуль health_check обладает параметрами по умолчанию, которые будут использоваться, если они не заданы явно:
check_interval: 30000 - интервал времени проверки запроса в ms.
fail_count: 5 - количество неудачных запросов на сервер для маркировки сервера 'down'.
drain: 2 - количество неудачных запросов для маркировки сервера 'drain'. В этом режиме на сервер будут проксироваться только привязанные к нему запросы (см. load_balancer).
rise_count: 2 - количество удачных запросов на сервер для маркировки сервера 'up'.
default_state: down - состояние сервера по умолчанию (up/down).
type: tcp - тип запроса для проверки (tcp, ssl_hello, http, mysql, ajp, fastcgi). Опишем некоторые значения явно и, при необходимости, изменим их значения.
profile: hc version: 2.0.3 system: wrk_count: 1 conn_count: 4096 optional: log_level: debug service: service_main_proxy: - id: main listen: - port: 8030 url: / upstream_groups: - id: back_1 servers: - server: 127.0.0.1:18030 health_check: enabled: true check_interval: 3000 fail_count: 3 rise_count: 1 default_state: up type: http http_send_method: GET http_send_url: /healthcheck check_response_body_pattern: '.*OK.*' http_status_alive: "http_2xx http_3xx" service_http_proxy: - id: 1 name: service_1 url: ^\/hello$ use_main_upstream_groups: true upstream_group_id: back_1 allowed_queries: - method: gethttp_send_method - http метод запроса (POST, GET). http_send_url - url-адрес для проверки работоспособности сервиса. check_response_body_pattern - нечувствительное к регистру регулярное выражение для поиска соответствия в теле ответа. http_status_alive - в значении параметра определяется, какой код возврата считать положительным.
В каталоге stubs создадим файл hc_stub.yml и наполним его следующим содержимым:
profile: hc_stub version: 2.0.3 system: conn_count: 4096 wrk_count: 1 optional: log_level: error service: service_static: - id: hello optional: response_code: 200 response_body: "Hello world" response_headers: - name: Content-Type value: text/plain url: ^/hello$ - id: healthcheck optional: response_code: 200 response_body: "OK" response_headers: - name: Content-Type value: text/plain url: ^/healthcheck service_main_proxy: - id: 1 listen: - port: 18030 hostname: - localhost - 127.0.0.1 url: /Добавим профиль hc_stub в реестр профилей и сконфигурируем его.
Запустим профиль hc и сразу перейдем к его логам. Нам нужен /sowalogs/hc/system/main_error.log. Обратите внимание, что профиль hc_stub не запущен.

Результат:
Интервал запросов на сервис healthcheck равен 3 секунды (check_interval). Отображаются 3 запроса (fail_count) на сервис healthcheck, после которых сервер был помечен как "down"
2020/09/21 10:01:36 [error] 58590#58590: disable check peer: 127.0.0.1:18030
Из п.2 следует, что сервис находился в состоянии "up" (default_state).
Теперь запустим профиль hc_stub. После этого вновь обратимся к логу /sowalogs/hc/system/main_error.log.

После того как сервер был помечен "down", на него продолжали отправляться запросы. Как только профиль hc_stub был запущен, был один запрос (rise_count), после которого сервер вернулся к "up" состоянию. 2020/09/21 10:12:57 [error] 4083#4083: enable check peer: 127.0.0.1:18030
Перейдем к логу заглушки hc_stub: /sowalogs/hc_stub/services/service_static/healthcheck_access.log

Из лога видно, что на сервис healthcheck приходили запросы. Время первого запроса совпадает со временем записи "4083#4083: enable check peer: 127.0.0.1:18030".
Также видно, что это http запрос (type), с методом GET (http_send_method) на сервис /healthcheck (http_send_url).
Пример работы check_response_body_pattern и http_status_alive. Откроем hc_stub.yml и изменим response_body с OK на ERR.
- id: healthcheck optional: response_code: 200 response_body: "ERR"Переконфигурируем и перезапустим профиль.

Отображается сообщение upstream check send data error -1 with peer 127.0.0.1:18030, говорящее о том, что в этот момент backend был недоступен, поэтому необходимо его перезапустить.
Как только профиль заглушки стартовал, то при обращениях к нему видны следующие сообщения:
http response body begin search use pattern
http response body ERR end search text without pattern matching .OK.
upstream check set peer 127.0.0.1:18030 failed because alive message isn't match to pattern
check parse return error because response body is't match to expected
check protocol http error with peer: 127.0.0.1:18030**
Говорящих о несоответствии response_body объявленному в шаблоне (check_response_body_pattern) на SOWA. В этом случае запросы на /hello будут завершаться с код статусом 502.
Перейдем к конфигурационному файлу заглушки hc_stub. Произведем следующую замену:
- id: healthcheck optional: response_code: 400 response_body: "OK"В сообщении указали "ОК", а код возврата установили 400. Переконфигурируем и перезапустим hc_stub.
В main_error.log'е отображается сообщение check parse return error because response status is't match to expected, current_status: 400, expected: 7, говорящее нам о том, что вернувшийся код статус не соответствует установленному в http_status_alive: "http_2xx http_3xx".
Переопределение контекста
До сих пор, при проксировании, контекст входа у нас совпадал с контекстом выхода. Однако, возможно потребуется изменить контекст исходного запроса перед отправкой его на backend.
Создадим профиль proxy_rewrite со следующей конфигурацией:
profile: proxy_rewrite
version: 2.0.3
system:
wrk_count: 1
conn_count: 4096
optional:
log_level: error
service:
service_http_proxy:
- id: rewrite_service
name: service_2
url: ^/bye
upstream_group_id: back_1
upstream_groups:
- id: back_1
servers:
- server: 127.0.0.1:10016
rewrite_uri:
- from: (^/bye)(/.*)
to: /hello$2
allowed_queries:
- method: get
listen:
- port: 11002
Как видно, за изменения контекста отвечает параметр rewrite_uri, представляющий последовательность пар регулярных выражений from/to.
Создадим, сконфигурируем, запустим профиль proxy_rewrite и отправим запрос по url http://127.0.0.1:11002/bye/good.
Пример вызова сервиса bye/good:
[sowacfg@tkle-ish0038 proxies]$ curl -v http://localhost:11002/bye/good
* About to connect() to localhost port 11002 (#0)
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 11002 (#0)
> GET /bye/good HTTP/1.1
> User-Agent: curl/7.29.0
> Host: localhost:11002
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Mon, 05 Apr 2021 13:51:10 GMT
< Content-Type: text/html
< Content-Length: 149
< Connection: keep-alive
< Server-Port: 10016
< Request-Uri: /hello/good
< Server: SOWA
* Connection #0 to host localhost left intact
Как видите по заголовкам ответа от сервера Backend, контекст запроса был изменен с /bye/good на /hello/good, как и ожидалось.