Создание прокси-сервиса#
Создание простого прокси-сервиса#
Одной из основных задач для 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
Выполните последовательность операций — остановка профиля, конфигурирование и старт.
[sowacfg@tkle-ish0038 proxies]$ sowa-config --stop simple_proxy
Profile "simple_proxy" was successfully stopped.
[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.
Попробуйте отправить запросы на два сервиса, через сервис 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.
Внесем изменения в заглушку в файле stubs/services/plain_service.yml.
- id: world_plain_service
optional:
response_code: 200
response_body: "World hello"
response_headers:
- name: Content-Type
value: text/plain
url: ^/world
allowed_queries:
- method: get
- method: post
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
allowed_queries:
- method: get
- method: post
listen:
- port: 10013
Добавьте два сервиса с контекстами /world и /dlrow соответственно на портах 10014 и 10013, после чего остановите профиль sample_stub, сконфигурируйте и запустите.
Так как сервис world_plain_service теперь объявлен на порту 10014, то вернитесь к файлу simple_proxy_service.yml и измените значение порта 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
<
World hello
* 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
<
Dlrow hello
* 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: ^/
allowed_queries:
- method: get
- method: post
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: ^/
allowed_queries:
- method: get
- method: post
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 несколько раз, фактически поочередно отправляются запросы на разные серверы из группы backend.
Балансировка#
Балансировка нагрузки между несколькими серверами — это широко используемый метод для оптимизации использования ресурсов, максимизации пропускной способности, уменьшения задержки и обеспечения отказоустойчивых конфигураций.
Вернитесь к конфигурационному файлу 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, устанавливаемая SOWA:
Set-Cookie: SWJSESSIONID=5699daa888fef95f98529a0c316350f5; Path=/Запрос с использованием сессионной cookie

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

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

В данном случае видно, что хоть и использовалось значение сессионной cookie, но было указано имя, отличающееся от значения, указанного в параметре name. «Залипания» на node не произошло.
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, устанавливаемая SOWA
SWJSESSIONID=d2e7e87c10c7edf5ae58b0b6a8237d23; Path=/Запрос к серверу, на котором указана cookie, отличающаяся от monitor_cookie.

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

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

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

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

Видно, что запрос попал на node с портом 10015 и произошло «залипание», т.к. имя сессионной cookie корректно.
BADUFS-SESSION=9fa7947d-8550-4ecd-9fe6-7d01efec41daSOWA не добавила сессионную cookie с именем SWJSESSIONID, т.к. она используется в запросе и «залипание» на node произошло.
Сессионная cookie, устанавливаемая SOWA: отсутствует.
Проверка работоспособности сервера#
Утилита 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: get
http_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$
allowed_queries:
- method: get
- id: healthcheck
optional:
response_code: 200
response_body: "OK"
response_headers:
- name: Content-Type
value: text/plain
url: ^/healthcheck
allowed_queries:
- method: get
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, как и ожидалось.