Настройка динамической генерации ответа о текущем состоянии экземпляра Platform V SOWA#

В ряде случаев, сервисам-клиентам, использующим Platform V SOWA, необходимо получение информации о текущем состоянии экземпляра Platform V SOWA. В случае, если состояние экземпляра управляется вручную, это можно сделать с помощью параметра healthcheck. В том же случае, если необходимо это делать на основании некоторой информации о состоянии самого экземпляра или бэкенд-серверов, располагающихся за экземпляром Platform V SOWA, необходимо использовать параметр healthcheck_dynamic.

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

В условиях правил и шаблонах можно использовать следующие конструкции:

  • Константные строковые значения.

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

    • модуля статистики: https://nginx.org/ru/docs/http/ngx_http_stub_status_module.html#variables;

    • текущего времени - $msec, $time_iso8601, $time_local;

    • информации об экземпляре или сервисе, например $server_addr;

    • переменные Platform V SOWA, доступные в случае использования соответствующей функциональности - например, статистика по загрузке CPU и RAM stat_cpu_usage, stat_memory_usage.

  • Переменные, значения которых устанавливаются вручную через загрузку файла с параметрами в момент конфигурирования и через вызов соответствующей командной строки, обращение к ним осуществляется через $ключ_в_файле.

  • Переменные функциональности динамической генерации ответа о текущем состоянии:

    profile_upstream_group_state_fmt - объект в формате JSON или XML или текста (зависит от указанного параметре response.content_type), содержащий информацию о состоянии всех групп балансировки профиля.

    Пример:

      {
        "service_use_bind_ug": {
            "hasLiveUpstream": true,
            "upstreamStates": [
                {
                    "state": "UP",
                    "upstreamName": "internal1"
                },
                {
                    "state": "UP",
                    "upstreamName": "internal2"
                }
            ]
        }
      }
    

    Состояние группы определяется с учетом значения параметра upstream_check_type, в список включаются те группы балансировки, которые удовлетворяют регулярному выражению указанному в параметре upstream_filter.

    profile_upstream_group_state_fmt - объект в формате JSON или XML или текста (зависит от указанного параметре response.content_type), содержащий информацию о состоянии всех групп балансировки профиля. Переменная заполняется только при объявлении параметра endpoints на уровне main_proxy.

    profile_upstream_group_alive - содержит 1, если хотя бы одна группа балансировки профиля находится в активном состоянии, активность состояния определяется также с учетом заданных параметров upstream_check_type и upstream_filter.

    Аналогично, определены переменные bind_upstream_group_state_fmt, bind_upstream_group_alive, которые равны либо service_* либо profile* переменным в зависимости от того, где определен параметр endpoints - в сервисе с типом main_proxy или в сервисе не с типом main_proxy.

    upstream_group_state_имя_группы_балансировки - возвращает состояние группы балансировки, определенной по правилам аналогичным для предыдущих переменных.

    upstream_group_state_orig_имя_группы_балансировки - возвращает состояние группы балансировки, в соответствии со следующими критериями, UP - хотя бы один сервер в состоянии UP, DRAIN - нет серверов в состоянии UP, но есть в состоянии DRAIN, DOWN - нет серверов в состоянии UP или DRAIN.

    Важно

    Использование заглавных букв в имени группы балансировки приводит к некорректному определению статуса групп балансировки. upstream_group_state_orig отдаст статус UNKNOWN (при этом значение переменной null), а upstream_group_state - DOWN.

Особенности применения#

Ниже описаны некоторые особенности настройки динамической генерации ответов на запросы о текущем состоянии экземпляра или сервиса, объявленного в конфигурации:

  • При объявлении параметра endpoints в секции endpoints:

    • В случае наличия сервиса с типом main_proxy доступны при обращении на порты сервиса main_proxy, объявленные в секции listen данного сервиса. При этом переменные service_upstream_upstream_group_state_fmt, service_upstream_group_alive не определены.

    • В случае отсутствия сервиса с типом main_proxy доступны на портах всех сервисов, кроме тех случаев, когда в соответствующем сервисе определен собственный параметр healthcheck_dynamic. Объявление healthcheck_dynamic в рамках сервиса отменяет наследование endpoint с уровня system.

  • Параметры initial_state_param_file и manual_state_var_refresh_interval доступны для определения только в секции system, то есть их действие распространяется на все эндпоинты healthcheck. Это нужно учитывать при ссылке на параметры, указанные в файле параметров. Если необходимо использовать разные значения в разных endpoint, параметрам нужно давать разные имена.

  • Для изменения параметров, задаваемых вручную в файле initial_state_param_file в runtime, необходимо использовать команду --set-healthcheck-params утилиты конфигурирования:

       sowa-config --set-healthcheck-params <имя_профиля> <путь к файлу с параметрами>
    
  • Для получения текущего значения параметров, установленных в runtime, необходимо использовать команду --upload-healthcheck-params:

        sowa-config --upload-healthcheck-params <имя_профиля> <путь к файлу, куда будут сохранены текущие значения параметров>
    
  • При выполнении операции конфигурирования текущее значение параметров, заданных в runtime, будет перезаписано значениями, указанными в файле, заданном в параметре initial_state_param_file, поэтому в момент конфигурирования он должен содержать актуальные значения параметров.

Некоторые примеры использования#

Пример 1

Пусть в сервисе определены две группы балансировки, группа 1 и группа 2, и есть два потребителя, для которых доступность сервиса определяется на основании следующих условий:

  • Для потребителя 1 сервис является доступным, если доступна хотя бы одна группа балансировки.

  • Для потребителя 2 сервис доступен, если доступны или обе группы балансировки одновременно, или группа 2.

  • Группа балансировки считается доступной, если хотя бы один сервер группы находится в состоянии UP.

  • Если условия выполняются, то возвращается ответ со статусом 204 и пустым телом ответа, в ином случае возвращается 503, с ответом {"status": "Service temporary unavailable"}.

Конфигурация определена следующим образом:

   service_http_proxy:
     - id: 1
       listen:
          - 6060
       upstream_groups:
         - id: upstream_group_1
           servers:
             - server: west.myserver.com:30000
               max_fails: 1
         - id: upstream_group_2 
           servers:
             - server: east.myserver.com:30000      
       optional:
         healthcheck_dynamic:
           endpoints:
              - url: /hc-first
                response:
                upstream_check_type: ANY_UPSTREAM_UP
                 rules: rules_1.yml
                 content_type: application/json
              - url: /hc-second
                response:
                upstream_check_type: ANY_UPSTREAM_UP
                 content_type: application/json
                 rules: rules_2.yml    
                      

Файлы с правилами:

rules_1.yml:

   rules: 
     - condition:
         - var: service_upstream_group_alive
           val: 1
           operator: "="
       status: 204
     - default: true
       status: 503
       responsePattern: "{\"status\": \"Service temporary unavailable\"}"          

rules_2.yml:

   rules: 
     - condition:
         - var: service_upstream_group_alive
           val: 1
           operator: "="
       status: 204
     - condition:
         - var: upstream_group_state_upstream_group_2
           val: "UP"
           operator: "="
       status: 204  
     - default: true
       status: 503
       responsePattern: "{\"status\": \"Service temporary unavailable\"}"    

Соответственно, потребитель 1 должен обращаться к endpoint /hc-first, потребитель 2 должен обращаться к endpoint 2.

Пример 2

Пусть у сервиса есть две группы потребителей - привилегированные и непривилегированные. Привилегированным потребителям сервис доступен до тех пор, пока текущая загрузка CPU экземпляра не превысит 80%, непривилегированным потребителям сервис доступен до тех пор, пока нагрузка на CPU не превысит 40%. Обеим группам потребителей сервис не доступен, если администратор выставил вручную параметр manual_set_offline в значение 1.

Если условия выполняются, то возвращается ответ со статусом 204 и пустым телом ответа, в случае, если администратором выставлен флаг manual_set_offline возвращается 503, с ответом {"status": "Service temporary unavailable"}, если превышена нагрузка по ЦПУ, возвращается 503, с ответом {"status": "system overloaded"}.

Конфигурация определена следующим образом:

   system:
     optional:
       #включаем сбор статистики по потреблению системных ресурсов
       get_load: 
        enabled: true
       healthcheck_dynamic:
         initial_state_param_file: profile_params.data
         manual_state_var_refresh_interval: 5s   
   service_http_proxy:
     - id: 1
       listen:
          - 6060
       
       optional:
         healthcheck_dynamic:
           endpoints:
              - url: /hc-priveleged
                response:
                 rules: rules_1.yml
                 content_type: appication/json
              - url: /hc-unpriveleged
                response:
                 content_type: appication/json
                 rules: rules_2.yml    
                      

Файлы с правилами:

rules_1.yml:

   rules: 
     - condition:
         - var: stat_cpu_usage
           val: 80
           operator: "<"
         - var: manual_set_offline
           val: 1
           operator: "!="   
       status: 204      
     - condition:
         - var: stat_cpu_usage
           val: 80
           operator: ">"
         - var: manual_set_offline
           val: 1
           operator: "!="   
       status: 503     
       responsePattern: "{\"status\": \"system overloaded\"}"                
     - condition:
         - var: manual_set_offline
           val: 1
           operator: "="
       responsePattern: "{\"status\": \"Service temporary unavailable\"}"       
       status: 503       
     - default: true
       status: 503
       responsePattern: "{\"status\": \"Service temporary unavailable\"}"       

rules_2.yml:

   rules: 
     - condition:
         - var: stat_cpu_usage
           val: 40
           operator: "<"
         - var: manual_set_offline
           val: 1
           operator: "!="   
       status: 204      
     - condition:
         - var: stat_cpu_usage
           val: 40
           operator: ">"
         - var: manual_set_offline
           val: 1
           operator: "!="   
       status: 503     
       responsePattern: "{\"status\": \"system overloaded\"}"               
     - condition:
         - var: manual_set_offline
           val: 1
           operator: "="
       pattern: "{\"status\": \"Service temporary unavailable\"}"       
       status: 503      
     - default: true
       status: 503
       pattern: "{\"status\": \"Service temporary unavailable\"}"

Файл profile_params.data:

manual_set_offline: 1

Пример 3

Пусть в профиле объявлен сервис service_http_proxy и нужно определить правила определения состояния экземпляра следующим образом:

  • Если администратор выставил вручную параметр manual_set_offline в значение 1, экземпляр считается недоступным, в ответ возвращается {"status": "Service temporary unavailable"}.

  • Если ни одна группа балансировки, определенная в профиле, недоступна, но при этом группа считается доступной, если в ней хотя бы один сервер находится не в состоянии DOWN, возвращается 502 с телом ответа {"status": "Bad gateway"}.

  • Если ни первое, ни второе условие не выполняется, возвращается 200 c перечислением состояния всех групп балансировки для всех сервисов. Потребитель сам должен принять решение, считать ли данный экземпляр доступным.

Конфигурация определена следующим образом:

system:
  optional:
    healthcheck_dynamic:
      initial_state_param_file: profile_params.data
      manual_state_var_refresh_interval: 5s   
service_http_proxy:
  - id: 1
    listen:
       - 6060
    
    optional:
      healthcheck_dynamic:
        endpoints:
           - url: /hc
             upstream_check_type: ANY_UPSTREAM_UP_OR_DRAIN
             response:
              rules: rules_1.yml
              content_type: appication/json

Файл с правилами:

rules_1.yml:

    rules: 
      - condition:
          - var: profile_upstream_group_alive
            val: 1
            operator: "!="   
        status: 502     
        responsePattern: "{\"status\": \"Bad gateway\"}"               
      - condition:
          - var: manual_set_offline
            val: 1
            operator: "="
        responsePattern: "{\"status\": \"Service temporary unavailable\"}"
        priority: 100      
        status: 503       
      - default: true
        status: 200
        responsePattern: "{\"upstream_group_states\": 
        $profile_upstream_group_state_fmt}"     

Файл profile_params.data:

manual_set_offline: 1

Пример 4

В примере ниже, демонстрируется возможность совместного использования и перекрытия точек входа healthcheck.

Здесь доступна точка входа healthcheck для сервиса main_proxy c контекстным путем /healthcheck, она же наследуется в сервисе service_use_main_ug (так как определена в секции system/optional и сервис не имеет своего слушающего порта).

Сервис service_use_bind_ug имеет свой собственный healthcheck, доступный при обращении на порт 28176 по тому же контекстному пути /healthcheck.

  profile: hc_dynamic
  service:
    service_http_proxy:
    - id: service_use_main_ug
      allowed_queries:
        - method: get
      upstream_group_id: extgw_adapter
      #url для HC сервиса service_use_main_ug
      url: ^\/first$
      use_main_upstream_groups: true
    - id: service_use_internal_ug  
      #url для HC  сервиса service_use_internal_ug
      url: ^\/first$
      allowed_queries:
        - method: get    
      upstream_group_id: internal1
      upstream_groups:
      - id: internal1 
        servers:
        - max_fails: 0
          server: myserver:20003
          weight: 1
    - id: service_use_bind_ug 
      #url для HC  сервиса service_use_internal_ug
      listen:
        - port: 28176
      optional:
        healthcheck_dynamic:
          endpoints:
            - url: /healthcheck
              upstream_check_type: ANY_UPSTREAM_UP_OR_DRAIN
              upstream_filter: .*
              response:
                pattern_file: hc_pattern_default.json
                rules: hc_response_rules.data
      url: ^\/second$
      allowed_queries:
        - method: get     
      upstream_bind_variable: $uri
      upstream_groups:
      - id: internal1
        bind_condition: value1
        servers:
        - max_fails: 0
          server: myserver:20004
          weight: 1
      - id: internal2
        bind_condition: value2
        default: true
        servers:
        - max_fails: 0
          server: myserver:20005
          weight: 1      
    service_main_proxy:
    - id: 1
      listen:
      - port: 28175
      modifiers: 
        set_header:
          - Client-Cert $uri
      upstream_groups:
      - id: extgw_ecom_adapter
        keepalive_conn_count: 100
        servers:
        - max_fails: 0
          server: myserver:20000
          weight: 1
      url: /
  system:
    optional:
      healthcheck_dynamic:
         initial_state_param_file: hc_params.data
         manual_state_var_refresh_interval: 1m
         endpoints:
            - url: /healthcheck
              upstream_check_type: ANY_UPSTREAM_UP_OR_DRAIN
              upstream_filter: .*
              response:
                  pattern_file: hc_pattern_default.json
                  rules: hc_response_rules.data
  version: 2.0.3