Создание прокси-сервиса#

Создание простого прокси-сервиса#

Одной из основных задач для 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, задачу маршрутизации на которые осуществляет собственно прокси-сервис.

Схематично можно представить это на следующих диаграммах:

d1

d2

Для решения обеих задач используется примитив 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 результат будет следующим: d3 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

Результат выполнения запроса: d4

Видно, что при использовании 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

Результат выполнения запроса: d5

Видно, что при использовании 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 примеров.

  1. Простой запрос:

    zapros1

    Отправьте запрос на сервис bye.

     curl -vvv http://localhost:11001/bye
    

    Результат выполнения запроса: d6 Был отправлен запрос на 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=/
    
  2. Запрос с использованием сессионной cookie zapros2

    Отправим запрос на сервис bye с использованием сессионной cookie, чтобы убедиться в «залипании» на конкретный сервер (в данном случае node с портом 10015).

    curl -vvv -H "Cookie: SWJSESSIONID=5699daa888fef95f98529a0c316350f5" http://localhost:11001/bye
    

    Результат выполнения запроса: d7 Действительно вновь попали на node с портом 10015.

    Теперь в response headers один заголовок Set-Cookie — это cookie backend.

    Set-Cookie: UFS-SESSION=9fa7947d-8550-4ecd-9fe6-7d01efec41da
    

    SOWA не добавила сессионную cookie с именем SWJSESSIONID, т.к. она использовалась в запросе и «залипание» на node произошло.

    Сессионная cookie, устанавливаемая SOWA: отсутствует.

  3. Запрос с использованием cookie, имя которой отличается от сессионной на SOWA.

    zapros3

    Измените имя cookie, добавив к нему некоторые символы. Отправив запрос:

    curl -vvv -H "Cookie: BADSWJSESSIONID=5699daa888fef95f98529a0c316350f5" http://localhost:11001/bye
    

    Результат выполнения запроса: d8

    В данном случае видно, что хоть и использовалось значение сессионной 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=/
    
  4. Запрос к серверу, на котором указана cookie, отличающаяся от monitor_cookie.

    zapros4

    Внесите изменения в конфигурационный файл сервисов для заглушки (для сервиса с портом 10015):

     response_headers:
    ...
      - name: Set-Cookie
        value: BADUFS-SESSION=9fa7947d-8550-4ecd-9fe6-7d01efec41da 
    

    Переконфигурируйте и перезапустите профиль заглушек.

    Отправьте запрос на сервис bye.

    curl -vvv http://localhost:11001/bye
    

    Результат выполнения запроса:

    d9

    Видно, что запрос попал на node с портом 10015 и SOWA не проставила сессионную cookie, т.к. не совпадает значение параметра monitor_cookie со значением полученным с backend.

    BADUFS-SESSION=9fa7947d-8550-4ecd-9fe6-7d01efec41da
    

    Сессионная cookie, устанавливаемая SOWA: отсутствует.

    Повторите запрос:

    curl -vvv http://localhost:11001/bye
    

    Результат выполнения запроса: d10

    SOWA проксировала запрос на node с портом 10016. Здесь изменения не были внесены в заголовок запроса Set-Cookie.

    UFS-SESSION=9fa7947d-8550-4ecd-9fe6-7d01efec41da
    

    Сессионная cookie, устанавливаемая SOWA:

    SWJSESSIONID=d2e7e87c10c7edf5ae58b0b6a8237d23; Path=/
    
  5. Запрос с сессионной cookie к серверу, на котором cookie отличается от monitor_cookie.

    zapros5

    Отправим запрос на сервис bye с использованием сессионной cookie, чтобы убедиться в «залипании» на конкретный сервер (в данном случае node с портом 10015). Но стоить помнить, что значение параметра monitor_cookie не совпадает со значением установленным на backend.

    curl -vvv -H "Cookie: SWJSESSIONID=5699daa888fef95f98529a0c316350f5" http://localhost:11001/bye
    

    Результат выполнения запроса: d11

    Видно, что запрос попал на node с портом 10015 и произошло «залипание», т.к. имя сессионной cookie корректно.

    BADUFS-SESSION=9fa7947d-8550-4ecd-9fe6-7d01efec41da
    

    SOWA не добавила сессионную 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 не запущен. d12

Результат: - Интервал запросов на сервис 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. d13

После того как сервер был помечен «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 d14

Из лога видно, что на сервис 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"

Переконфигурируйте и перезапустите профиль. d15

Отображается сообщение 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. d16 В 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, как и ожидалось.