Руководство прикладного разработчика#

Термины и определения#

Термин/Аббревиатура

Определение

Endpoint

Сетевая точка, обеспечивающая взаимодействие с поставщиком

gRPC

Современная высокопроизводительная платформа, которая используется для развития устаревшего протокола удаленного вызова процедур (RPC)

Ingress (Egress) Gateway

Ресурс Istio, который описывает правила маршрутизации и балансировки входящих запросов. Альтернатива K8s Ingress (Egress) Gateway, настраивается через ресурсы Gateway, Virtual Service и при необходимости DestinationRule

K8s

Kubernetes, платформа с открытым исходным кодом для управления кластерами приложений и сервисами на основе контейнеров

OS

OpenShift, открытая и расширяемая платформа приложений-контейнеров

RL Operator

Компонент SRLS, обеспечивающий конфигурирование Ingress (Egress) Gateway в соответствии с CRD в runtime

RL Service

Компонент SRLS, сервис расчета лимитов, принимает gRPC-запросы от Ingress (Egress) Gateway, содержащие информацию о домене (в качестве домена используется название прикладного namespace), о вызываемом сервисе и данные из служебного заголовка, например, synapse-consumerid

SRLS (SRL)

Программный компонент Synapse Rate Limiter (Synapse Rate Limit), сервис ограничения (квотирования) входящих запросов необходим для ограничения прикладной нагрузки (payload) со стороны потребителя на защищаемые сервисы, исполняемые в рамках Synapse Service Mesh

Deploy Tools

Программный компонент, предназначенный для автоматизации развертывания и автоматической установки технологических сервисов и бизнес-приложений на тестовых и промышленных стендах (CDJE), программного продукта Platform V DevOps Tools (DOT)

Pod

Набор контейнеров внутри узла кластера Kubernetes или Red Hat OpenShift (опционально)

ТУЗ

Технологическая учетная запись

УЦ

Удостоверяющий центр

Системные требования#

Подробно системные требования описаны в документе «Руководство по установке» в разделе «Системные требования».

Подключение и конфигурирование#

В зависимости от выбранного варианта развертывания компонента SRLS (централизованный, децентрализованный) воспользуйтесь одной из инструкций:

  • конфигурирование артефактов среды контейнеризации и Istio для централизованного варианта развертывания;

  • конфигурирование артефактов среды контейнеризации и Istio для децентрализованного варианта развертывания.

Конфигурирование артефактов среды контейнеризации и Istio для централизованного варианта развертывания#

Пререквизиты#

Права доступа для Оператора Rate Limit выполнены по принципу минимальных прав для обеспечения требований безопасности. Для доступа к артефактам EnvoyFilter прикладного namespace требуется создать конфигурации ClusterRole и RoleBinding в yaml-формате.

ClusterRole:

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: srls-role
rules:
  - verbs:
      - get
      - list
      - watch
      - create
      - update
      - patch
      - delete
    apiGroups:
      - networking.istio.io
    resources:
      - envoyfilters

RoleBinding, здесь необходимо указать ServiceAccount в namespace централизованного Rate Limit.

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: srls-role-binding
  namespace: ${namespace}
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: srls-role
subjects:
- kind: ServiceAccount
  name: rate-limiter-service
  namespace: ${rls_namespace}

Здесь RLS-namespace — идентификатор namespace централизованного Rate Limit.

Для конфигурирования Rate Limit используется артефакт GlobalRateLimit. Для возможности его создания средствами DevOps для учетной записи Jenkins нужно добавить RoleBinding для следующей роли:

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: pipeline-rate-limiter-service-role
rules:
  - verbs:
      - create
      - update
      - patch
      - delete
      - get
      - list
    apiGroups:
      - ratelimit.service
    resources:
      - globalratelimits

Конфигурирование для подключения с использованием NetworkPolicy#

Артефакт NetworkPolicy, приведенный в документе «Руководство по установке» в разделе «Сетевая видимость RL Service в рамках кластера», разрешает подключение из других namespace к Pod RL Service по порту 8081. В этом случае трафик не покидает сеть кластера Kubernetes (OpenShift опционально). Для обеспечения сетевой видимости необходимо наличие label srls: ratelimit у Pod Ingress Gateway и/или Egress Gateway, на которых выполняется квотирование. Его необходимо добавить в Deployment по пути .spec.template.metadata.labels, например:

kind: Deployment
apiVersion: apps/v1
metadata:
  name: ingress-gateway
  namespace: my-namespace
  labels:
    app: ingress-my-namespace
    istio: ingress-my-namespace
    proj: synapse-rls
    agent: srls
spec:
  replicas: 2
  selector:
    matchLabels:
      app: ingress-my-namespace
      istio: ingress-my-namespace
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: ingress-my-namespace
        chart: gateways
        istio: ingress-my-namespace
        release: istio
        srls: ratelimit
      annotations:
        sidecar.istio.io/inject: 'false'
    spec:
      restartPolicy: Always

В артефакте GlobalRateLimit в поле rlserver следует указать значение rate-limiter-headless-service.${rls-namespace}.svc.cluster.local, где rls-namespace — namespace централизованного SRLS, в поле rlserverport следует указать 8081.

Конфигурирование артефактов среды контейнеризации и Istio для децентрализованного варианта развертывания#

Децентрализованный вариант развертывания позволяет выбрать, как будут созданы необходимые для работы артефакты EnvoyFilter: автоматизированно или вручную.

Для автоматизированного варианта требуется предоставление соответствующих прав компоненту RL Operator.
Подробное описание приведено в документе «Руководство по установке» в разделе «Ролевая модель децентрализованного варианта развертывания».
В зависимости от выбранной ролевой модели необходимо выставить корректное значение стендозависимого параметра disable_envoy_filter_automation.
Описание параметра приведено в документе «Руководство по установке» в разделе «Параметры установки».

Для корректного отключения механизма квотирования входящих запросов при выключенном автоматизированном управлении артефактами EnvoyFilter (значение стендозависимого параметра disable_envoy_filter_automation выставлено в true) необходимо удалить как артефакт GlobalRateLimit, так и артефакты EnvoyFilter, которые были созданы по инструкции из раздела «Создание артефактов EnvoyFilter вручную».

Ниже приведена инструкция по созданию артефактов EnvoyFilter вручную.

Создание артефактов EnvoyFilter вручную#

Виды артефактов EnvoyFilter:

  • Кластер — необходим для настройки параметров подключения к компоненту RL Service.

  • Rate Limit Header — необходим для формирования запросов к RL Service в режиме работы Rate Limit Header.

  • Rate Limit Prefix — необходим для формирования запросов к RL Service в режиме работы Rate Limit Prefix.

  • Rate Limit Method — необходим для формирования запросов к RL Service в режиме работы Rate Limit Method.

  • Rate Limit Path — необходим для формирования запросов к RL Service в режиме работы Rate Limit Path.

Минимальный набор артефактов включает в себя один Кластер и один артефакт для любого выбранного режима. Для каждого Endpoint, сконфигурированного в артефакте GlobalRateLimit, должен быть создан артефакт EnvoyFilter для соответствующего режима работы компонента SRLS. Допускается объединение всех артефактов EnvoyFilter в один yaml-файл.

Режимы работы описаны в разделе «Режимы работы» настоящего руководства.

Ниже приведены ссылки на соответствующие артефакты EnvoyFilter.

Артефакты EnvoyFilter приведены для версий 1.25 (версии ниже не поддерживаются).

  1. Кластер

Ниже приведены параметры для конфигурирования артефакта EnvoyFilter для вида Кластер:

Название параметра

Описание

Пример

namespace

Идентификатор пространства имен

my-namespace

labels

Список label для определения необходимого Ingress (Egress) Gateway

app: ingress-my-namespace

GrpcTimeout

Максимальное время ожидания ответа на запрос к компоненту RL Service

1s

RlServer

Адрес для подключения к компоненту RL Service

rate-limiter-headless-service.my-namespace.svc.cluster.local

RlServerPort

Порт для подключения к компоненту RL Service

8081

Cert

Путь к клиентскому сертификату

/etc/rls/ingress/ingress.pem

Key

Путь к приватному ключу сертификата

/etc/rls/ingress/ingress.key

TrustCA

Путь к доверительному сертификату

/etc/rls/ingress/ca-root.pem

EnvoyFilter без закрытия соединения TLS.

EnvoyFilter с закрытием соединения TLS при прямом подключении.

  1. Rate Limit Header

Ниже приведены параметры для конфигурирования артефакта EnvoyFilter для вида Rate Limit Header:

Название параметра

Описание

Пример

namespace

Идентификатор пространства имен

my-namespace

labels

Список label для определения необходимого Ingress (Egress) Gateway

app: ingress-my-namespace

Endpoint

Точка подключения входящего трафика

my-server-my-namespace.domain.ru:8080

Shortname

Уникальное короткое название точки подключения

myserver

Header

Название заголовка для определения потребителя

synapse-consumerid

Опциональный блок

Если в конфигурации GlobalRateLimit для данной точки подключения указано значение поля overall_limit отличное от -1, необходимо добавить данный опциональный блок

EnvoyFilter - by header.

  1. Rate Limit Prefix

Ниже приведены параметры для конфигурирования артефакта EnvoyFilter для вида Rate Limit Prefix:

Название параметра

Описание

Пример

namespace

Идентификатор пространства имен

my-namespace

labels

Список label для определения необходимого Ingress (Egress) Gateway

app: ingress-my-namespace

Endpoint

Точка подключения входящего трафика

my-server-my-namespace.domain.ru:8080

Shortname

Уникальное короткое название точки подключения

myserver

Header

Название заголовка для определения потребителя

synapse-consumerid

Опциональный блок

Если в конфигурации GlobalRateLimit для данной точки подключения указано значение поля overall_limit отличное от -1, необходимо добавить данный опциональный блок

EnvoyFilter - by prefix.

  1. Rate Limit Method

Ниже приведены параметры для конфигурирования артефакта EnvoyFilter для вида Rate Limit Method:

Название параметра

Описание

Пример

namespace

Идентификатор пространства имен

my-namespace

labels

Список label для определения необходимого Ingress (Egress) Gateway

app: ingress-my-namespace

Endpoint

Точка подключения входящего трафика

my-server-my-namespace.domain.ru:8080

Shortname

Уникальное короткое название точки подключения

myserver

Header

Название заголовка для определения потребителя

synapse-consumerid

Опциональный блок

Если в конфигурации GlobalRateLimit для данной точки подключения указано значение поля overall_limit отличное от -1, необходимо добавить данный опциональный блок

EnvoyFilter - by method.

  1. Rate Limit Path

Ниже приведены параметры для конфигурирования артефакта EnvoyFilter для вида Rate Limit Prefix:

Название параметра

Описание

Пример

namespace

Идентификатор пространства имен

my-namespace

labels

Список label для определения необходимого Ingress (Egress) Gateway

app: ingress-my-namespace

Endpoint

Точка подключения входящего трафика

my-server-my-namespace.domain.ru:8080

Shortname

Уникальное короткое название точки подключения

myserver

Опциональный блок

Если в конфигурации GlobalRateLimit для данной точки подключения указано значение поля overall_limit отличное от -1, необходимо добавить данный опциональный блок

EnvoyFilter - by path.

Добавление HTTP-заголовка в запрос на стороне потребителя без доработки кода (опционально)#

Данный заголовок должен формировать клиент (потребитель сервиса). Если клиент развернут в Red Hat OpenShift (опционально) и использует функционал Red Hat Service Mesh, то заголовок можно проставить на Egress Gateway namespace клиента с помощью артефакта VirtualService и блока headers, например:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: rldemo-server-vs
  namespace: synapse-dev-sandbox
spec:
  exportTo:
    - .
  gateways:
    - rldemo-istio-egressgateway
  hosts:
    - rldemoserver.synapse-dev-sandbox3.apps.syn-sb.local.host.ru
  http:
    - headers:
        request:
          set:
            synapse-consumerid: synapse-dev-sandbox
      match:
        - gateways:
            - rldemo-istio-egressgateway
          port: 8081
      route:
        - destination:
            host: rldemoserver.synapse-dev-sandbox3.apps.syn-sb.local.host.ru
            port:
              number: 80

Миграция на текущую версию#

Миграция на текущую версию происходит путем обновления программного компонента Synapse Rate Limit, которое описано в разделе «Обновление» документа «Руководство по установке».

Быстрый старт#

Ниже приведены шаги для децентрализованного варианта развертывания компонента SRLS.

  1. При настроенном Deploy Tools выполнить установку в прикладном namespace. Действия по установке описаны в подразделе «Автоматизированная установка сервиса с использованием Deploy Tools» документа «Руководство по установке».

  2. Добавить артефакт GlobalRateLimit в прикладной namespace. Пример для тестового сервиса Server-Test:

kind: GlobalRateLimit
apiVersion: ratelimit.service/v1alpha1
metadata:
  name: rate-limit-config
  namespace: ${namespace}
spec:
  envoyVersion: '1.25'
  workloadSelector:
    labels:
      istio: ingressgateway
  rlserver: rate-limiter-headless-service.${namespace}.svc.cluster.local
  rlserverport: 8081
  endpoints:
  - endpoint: test-server-${namespace}.${cluster_domain_name}:8080  # вызываемый сервис
    name: "some service"                                            # описание сервиса
    shortname: "server"                                             # краткое название сервиса (3-10 латинских букв в нижнем регистре)
    by_header:
      header: synapse-consumerid                                    # на текущий момент единственно возможный вариант
      unit: second                                                  # возможные значения: second, minute, hour, day
      value: 7                                                      # применяется для всех клиентов, не указанных в invokers
      visibility: namespace                                         # на текущий момент единственно возможный вариант
      invokers:
        - name: "header_client1"
          header_value: "client1"
          unit: minute                                              # возможные значения: second, minute, hour, day
          value: 5
        - name: "header_client2"
          header_value: "client2"
          unit: second                                              # возможные значения: second, minute, hour, day
          value: 2
  1. Тестирование работы SRLS.

  • Перед тестированием убедитесь, что в namespace:

    1. readiness/liveness-пробы Pods успешные и Pods по пробам периодически не перезапускаются.

    2. Создались EnvoyFiltеr (${namespace}-rls-cluster, ${namespace}-server) для тестового сервиса.

    3. Обращение к Pod тестового сервиса проходит через Ingress Gateway (в логах Ingress Gateway присутствуют запросы к test-server-${namespace}.${cluster_domain_name}:8080).

  • Добавьте ресурс kind: GlobalRateLimit name: rate-limit-config из пункта выше, заменив параметры ${namespace} и ${cluster_domain_name}.

  • C помощью утилиты Curl выполните запрос к тестовому сервису в консоли рабочей станции:

curl -v -H 'synapse-consumerid: client1'  test-server-${rls_namespace}.${cluster_domain_name} 2>&1 | grep HTTP

Здесь client1 — метка клиента (invokers) для ресурса test-server-${rls_namespace}.${cluster_domain_name}:8080, заданного в ресурсе GlobalRateLimit.

  • Выполните N запросов от трех различных пользователей: client1, client2, client3. Убедитесь, что ограничения по каждому пользователю соответствуют указанным в настройках в ресурсе GlobalRateLimit.

При корректно выполненной настройке должен прийти ответ вида:

> GET / HTTP/1.1
< HTTP/1.1 200 OK

Согласно выставленным ограничениям для client1 (5 запросов в минуту), выполним 7 запросов к ресурсу:

for run in {1..7}; do  curl -v -H 'synapse-consumerid: client1'  test-server-${rls_namespace}.${cluster_domain_name} 2>&1 | grep HTTP; done

Ответ:

> GET / HTTP/1.1
< HTTP/1.1 200 OK
> GET / HTTP/1.1
< HTTP/1.1 200 OK
> GET / HTTP/1.1
< HTTP/1.1 200 OK
> GET / HTTP/1.1
< HTTP/1.1 200 OK
> GET / HTTP/1.1
< HTTP/1.1 200 OK
> GET / HTTP/1.1
< HTTP/1.1 429 Too Many Requests
> GET / HTTP/1.1
< HTTP/1.1 429 Too Many Requests

Выполнив повторно запрос до истечения заданного в настройках интервала (5 запросов в минуту для пользователя client1), можно убедиться, что запросы будут возвращать 429 ошибку. Это ожидаемое поведение.

Использование программного компонента#

Обращения к сервису Rate Limit формирует либо Ingress Gateway в прикладном namespace, доступ к ресурсам которого (namespace) необходимо ограничить, либо Egress Gateway в namespace, из которого делается вызов. Конфигурирование лимитов осуществляется через формирование артефакта GlobalRateLimit. Централизованный сервис Rate Limit разворачивается свой в каждом физическом кластере Kubernetes или Red Hat OpenShift (опционально).

Для возможности загрузки артефакта GlobalRateLimit необходимо создать роль K8s и привязать к ней ТУЗ Jenkins в namespace.

Роль:

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: pipeline-rate-limiter-service-role
rules:
  - verbs:
      - create
      - update
      - patch
      - delete
      - get
      - list
    apiGroups:
      - ratelimit.service
    resources:
      - globalratelimits

Формирование и применение артефакта GlobalRateLimit#

Конфигурирование лимитов осуществляется через артефакт GlobalRateLimit. Для применения лимитов необходимо создать артефакт GlobalRateLimit в namespace, где запущены прикладные сервисы, которые указаны в секции endpoints.

Пример конфигурации: GlobalRateLimit.

В примере для endpoint указаны три способа задания лимитов:

  • первый вариант (test-server1) — для обратной совместимости с версией RLS 2.0, нет возможности использовать произвольный заголовок для идентификации потребителя, нет возможности указать раздельно лимит для безымянных потребителей (отсутствует указанный заголовок в запросе) и неуказанных в конфигурации;

  • второй вариант (test-server2) — применение лимитов с идентификацией потребителя в заголовке, можно указать произвольный заголовок (валидный для протокола HTTP) и раздельно указать лимиты для безымянных потребителей (отсутствует указанный заголовок в запросе) и неуказанных в конфигурации, для этого введено новое поле anon_value;

  • третий вариант (test-server3) — применение лимитов в разрезе ResourceName, новый функционал, появившийся в релизе 2.1, для идентификации потребителя (тенанта) из URL-запроса;

  • четвертый вариант (test-server4) — применение лимитов с идентификацией потребителя в заголовке, отличие от второго варианта — лимиты конфигурируются для каждого указанного префикса URL в рамках одного endpoint независимо (с релиза 2.4 префиксы /foo и /foo/bar являются независимыми с точки зрения Rate Limiter — запросы /foo/test1?param1=value1 и /foo/bar/test2 будут задействовать свои лимиты независимо друг от друга);

  • пятый вариант (test-server5) — применение лимитов с идентификацией потребителя в заголовке, отличие от четвертого варианта — для одного из префиксов лимиты конфигурируются для указанных HTTP-методов в рамках одного префикса URL независимо.

При формировании артефакта необходимо указать:

  • workloadSelector — набор labels для определения нужных Pod Ingress (Egress) Gateway;

  • visibility — отвечает за область видимости данного CRD, возможные значения: cluster, namespace;

  • envoyVersion — отвечает за наполнение артефакта EnvoyFilter в зависимости от используемой версии Envoy, значение по умолчанию — '1.25';

  • endpoints — список ваших ресурсов;

  • rlserver — адрес RLS-сервера (для децентрализованной установки следует указать headless server rls например: «rate-limiter-headless-service», для централизованной установки здесь следует указать headless server с namespace rls, рекомендованный формат — <headless_service_name>.<namespace>.svc.cluster.local);

  • rlserverport — порт RLS-сервера, по умолчанию — 8081;

  • rlnamespace — namespace, в котором развернут централизованный оператор, данное поле необходимо указать для возможности развертывания нескольких экземпляров централизованного SRLS в рамках одного кластера Kubernetes или Red Hat OpenShift (опционально), если данное поле не задано, оператор проверит наличие прав на артефакт EnvoyFilter в прикладном namespace;

  • rlservercert — опциональный блок, позволяет задавать сертификаты для доступа к RLS-серверу при прямом подключении.

rlservercert состоит из следующих полей:

  • cert – путь к клиентскому (extendedKeyUsage = clientAuth) сертификату в файловой системе Ingress (Egress) Gateway;

  • key – путь к приватному ключу сертификата в файловой системе Ingress (Egress) Gateway;

  • trust_ca — путь к доверительному сертификату в файловой системе Ingress (Egress) Gateway.

Важно!

Потребитель сам должен обеспечить наличие данных сертификатов на Ingress (Egress) Gateway, подмонтировав их в необходимую директорию.

Для каждого endpoint:

  • обязательно наличие его короткого имени shortname, используется в работе, для каждого endpoint значение shortname должно быть уникальным;

  • endpoint — целевой адрес в формате host:port, здесь host — DNS адрес, на который поступает входящий трафик (определяется в артефакте Route, используется в артефактах Gateway, VirtualService для манипуляции трафиком с помощью Service Mesh), port — порт, который слушает Ingress (Egress) Gateway (задается в артефакте Gateway), например, 8080 для HTTP трафика или 8443 для HTTPS трафика;

  • overall_limit — максимальное ограничение квоты для endpoint; 0 — блокировка всех запросов, отрицательное значение — нет ограничения; если значение больше 0, дополнительно создается отдельный счетчик всех запросов на endpoint; в случае превышения запрос будет отклонен, даже если потребитель еще не исчерпал свою квоту; если используется режим работы Rate Limit Prefix, overall_limit считается общий для всех префиксов URL-запроса, указанных в конфигурации;

  • by_header — список настроек для функционала идентификации потребителя по HTTP-заголовку (подробное описание приведено в разделах «Rate Limit Header» и «Rate Limit Prefix» настоящего руководства);

  • by_path — список настроек для функционала идентификации потребителя по resourceName (подробное описание приведено в разделе «Rate Limit Path» настоящего руководства);

  • может быть задан лишь один из типов лимитов «by_header» или «by_path»;

  • overall_schedule – опциональный блок, позволяет изменять overall_limit по расписанию;

  • schedule – опциональный блок, позволяет изменять лимиты по расписанию.

Лимиты могут быть заданы в следующих единицах измерения:

  • second;

  • minute;

  • hour;

  • day.

Режимы работы#

Rate Limit Header#

Данный режим работы используется при задании лимитов с идентификацией потребителя по значению HTTP-заголовка. Если HTTP-заголовок отсутствует в запросе, идентифицировать пользователя невозможно, для таких запросов используется общая квота анонимных вызовов, определенная полем anon_value. Для конфигурирования используйте специальный блок полей by_header.

Пример: some service1.

Параметры блока by_header:

  • header — произвольный HTTP-заголовок (валидный для протокола HTTP);

  • unit — опциональное поле, по умолчанию second, единица измерения лимитов, используется при расчете overall_limit, для запросов со значениями заголовка header, не указанными в блоке invokers, и для неидентифицированных потребителей;

  • anon_value — опциональное поле, позволяет задать квоту для неидентифицированных (анонимных) потребителей, то есть в запросе отсутствует выбранный HTTP-заголовок; если поле не задано, используется значение поля value;

  • value — опциональное поле, по умолчанию 1, позволяет задать квоту для запросов со значениями заголовка header, неуказанными в блоке invokers, и для неидентифицированных потребителей;

  • soft — опциональный блок, если задан, создается метрика, показывающая приближение к лимиту. soft.value — значение квоты, после которой с шагом кратным soft.step будет инкрементирована метрика мониторинга превышения уровня soft квоты для конкретного потребителя вплоть до превышения основного лимита;

  • invokers — опциональный блок, позволяет задать квоту для определенных потребителей независимо;

  • overall_schedule – опциональный блок, позволяет изменять overall_limit по расписанию.

overall_schedule и schedule состоят из следующих полей:

  • start – блок, позволяет задать время и дату начала изменения параметров, указанных в блоке;

  • stop – блок, позволяет задать время и дату окончания изменения параметров, указанных в блоке;

  • unit — поле, по умолчанию second, единица измерения лимитов;

  • value — поле, по умолчанию 1, позволяет задать квоту;

  • soft — опциональный блок, если задан, создается метрика, показывающая приближение к лимиту. soft.value — значение квоты, после которой с шагом кратным soft.step будет инкрементирована метрика мониторинга превышения уровня soft квоты для конкретного потребителя вплоть до превышения основного лимита.

start и stop состоят из следующих полей:

  • second – блок, секунды, возможные значения – (0-59);

  • minute – блок, минуты, возможные значения – (0-59);

  • hour – блок, часы, возможные значения – (0-23);

  • day – блок, дни, возможные значения – (1-31);

  • month – блок, месяц, возможные значения – (1-12, где 1-январь);

  • weekday – блок, дни недели, возможные значения – (0-6, где 0-воскресенье).

Значение '*' для полей start и stop является допустимым и позволяет задать любую минуту, час, день, месяц, день недели.

Например:

overall_schedule в примере выше означает, что overall_limit 50 запросов в секунду будет изменен в период с 10 секунды до 30 секунды каждой минуты каждого часа каждого дня каждого месяца в течении всей недели на overall_limit 25 запросов в минуту.

Second

Minute

Hour

Day

Month

Weekday

Примечание

start

'1'

'*'

'*'

'*'

'*'

'*'

Начало каждую 1 секунду

stop

'10'

'*'

'*'

'*'

'*'

'*'

Окончание каждую 10 секунду

start

'1'

'5'

'3'

'*'

'*'

'*'

Начало каждый день в 3:05:01

stop

'10'

'20'

'6'

'*'

'*'

'*'

Окончание каждый день в 6:20:10

start

'1'

'5'

'3'

'5'

'*'

'*'

Начало каждый день в 3:05:01

stop

'10'

'20'

'6'

'7'

'*'

'*'

Окончание каждый день в 6:20:10

start

'1'

'5'

'3'

'5'

'*'

'*'

Начало 5 числа каждого месяца в 3:05:01

stop

'10'

'20'

'6'

'7'

'*'

'*'

Окончание 7 числа каждого месяца в 6:20:10

start

'1'

'5'

'3'

'5'

'11'

'*'

Начало 5 ноября в 3:05:01

stop

'10'

'20'

'6'

'7'

'11'

'*'

Окончание 7 ноября в 6:20:10

start

'1'

'5'

'3'

'*'

'*'

'1'

Начало каждый понедельник в 3:05:01

stop

'10'

'20'

'6'

'*'

'*'

'1'

Окончание каждый понедельник в 6:20:10

  • Данные в schedule блоки start и stop должны удовлетворять следующим требованиям:

  1. start не должен срабатывать несколько раз до наступления события stop;

  2. stop не должен срабатывать несколько раз до наступления события start;

  3. start и stop не может быть задан в несуществующую дату, например, в сентябре нет 31 дня, в феврале нет 29 дня и подобные.

  • В случае установки некорректных параметров в CRD GlobalRateLimit в блоке schedule или overall_schedule, в статусе CRD будет отражено место с указанием, где задан невалидный интервал времени и даты.

schedule в поле invokers: header_value: synapse-test означает, что лимит 8 запросов в секунду с soft.value=3 и soft.step=1 будет изменен каждый понедельник в период с 2 часов 5 минут 1 секунды до 6 часов 10 минут 30 секунд на 16 запросов в секунду с soft.value=5 и soft.step=2.

Для каждого invoker можно задать:

  • header_value — значение заголовка, указанного в поле header;

  • name — опциональное поле, позволяет указать метаданные для header_value;

  • unit — опциональное поле, по умолчанию second, единица измерения лимитов для header_value;

  • value — опциональное поле, по умолчанию 1, позволяет задать квоту для запросов со значением заголовка header, равным header_value;

  • soft — опциональный блок, если задан, создается метрика, показывающая приближение к лимиту. soft.value — значение квоты, после которой с шагом кратным soft.step будет инкрементирована метрика мониторинга превышения уровня soft квоты для header_value вплоть до превышения основного лимита;

  • schedule – опциональный блок, позволяет изменять лимиты по расписанию.

Rate Limit Prefix#

Данный режим является расширением режима Rate Limit Header. Применяется в случаях, когда средствами Service Mesh входящий трафик на endpoint на Ingress Gateway распределяется по сервисам этого namespace на основе префикса URL-запроса.

Для настройки распределения трафика в Service Mesh используется артефакт VirtualService.

Пример артефакта VirtualService.

Для того чтобы для каждого префикса указать собственные значения квоты, в артефакте GlobalRateLimit в разделе by_header необходимо поле header и блок параметров uri_prefixes, в котором необходимо указать префикс в поле uri_prefix и лимиты по аналогии с режимом by-header.
Для работы функционала overall_limit необходимо указать поле unit, по умолчанию, если поле не задано, overall_limit будет рассчитываться в секундах. Счетчик overall_limit общий для всех префиксов. Остальные поля блока by_header будут проигнорированы.

Пример конфигурации endpoint.

Таким образом, для вызовов http://ingress-my-namespace-02.apps.domain.org/foo и http://ingress-my-namespace-02.apps.domain.org/bar/pruduct?param=1 будут применяться различные значения лимитов.

С релиза 2.4 префиксы /foo и /foo/bar являются независимыми с точки зрения Rate Limiter — для запросов http://ingress-my-namespace-02.apps.domain.org/foo/test1?param1=value1 и http://ingress-my-namespace-02.apps.domain.org/foo/bar/test2 также будут применяться различные значения лимитов.

Поле schedule в примере выше означает, что для префикса /foo/bar для client 3 будет изменен лимит 6 запросов в минуту в период с 10 секунды по 45 секунду каждой минуты каждого часа каждого дня каждого месяца на 10 запросов в секунду.

Если делается вызов с префиксом, для которого не указан лимит в артефакте GlobalRateLimit, то запросы на него будут идти без лимита. При этом в логах RL Service будут сообщения об ошибках no prefix found, valid prefixes: [/bar /foo/bar]", и метрика ошибок ratelimiterservice_should_rate_limit_unknown_prefix_error будет увеличиваться.

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

    - endpoint: 'ingress-my-namespace-02.apps.domain.org:8080'
      name: srls dev 02
      shortname: dev
      overall_limit: 100
      by_header:
        header: synapse-consumerid
        unit: minute
        uri_prefixes:
          - uri_prefix: "/healthcheck"
            value: -1
          - uri_prefix: "/bar"
            unit: minute
            value: 20
            anon_value: 2
            invokers:
              - header_value: bar-client
                name: client 2
                unit: minute
                value: 2

В данном примере запросы с префиксом /healthcheck будут ограничены только лимитом на весь endpoint (значением overall_limit). При этом если будут указаны блоки invokers и/или http_methods, то они будут игнорироваться.

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

    - endpoint: 'ingress-my-namespace-02.apps.domain.org:8080'
      name: srls dev 02
      shortname: dev
      overall_limit: 100
      by_header:
        header: synapse-consumerid
        unit: minute
        uri_prefixes:
          - uri_prefix: "/"
            value: -1
          - uri_prefix: "/bar"
            unit: minute
            value: 20
            anon_value: 2
            invokers:
              - header_value: bar-client
                name: client 2
                unit: minute
                value: 2

В данном примере лимит будет считаться только для запросов с префиксом /bar. Запросы со всеми остальными префиксами будут ограничены только лимитом на весь endpoint (значением overall_limit).

Префикс / так же может быть использован для подсчета общего лимита для всех остальных префиксов, если в поле value задано конкретное значение.

    - endpoint: 'ingress-my-namespace-02.apps.domain.org:8080'
      name: srls dev 02
      shortname: dev
      overall_limit: 100
      by_header:
        header: synapse-consumerid
        unit: minute
        uri_prefixes:
          - uri_prefix: "/"
            value: 10
            unit: minute
          - uri_prefix: "/bar"
            unit: minute
            value: 20

В данном примере лимит для вызовов с префиксом /bar будет 20 запросов в минуту. Запросы со всеми остальными префиксами будут ограничены лимитом 10 запросов в минуту.

Rate Limit Method#

Данный режим является расширением режима Rate Limit Prefix. Позволяет задать ограничения в разрезе HTTP-методов для отдельных префиксов URL запроса.

Для указания отдельных значений квот для различных методов в артефакте GlobalRateLimit в блоке соответствующего префикса необходимо добавить блок параметров http_methods. Блок является списком объектов, каждый из которых имеет поле непосредственно метода http_method и другие поля лимитов по аналогии с режимом Rate Limit Prefix (value, unit, anon_value, soft, schedule, invokers).

Пример конфигурации endpoint:

    - endpoint: 'ingress-my-namespace-02.apps.domain.org:8080'
      name: methodsservice
      shortname: srvmethod
      by_header:
        header: synapse-consumerid
        uri_prefixes:
          - uri_prefix: "/foo"
            unit: minute
            value: 7
            http_methods:
              - http_method: GET
                unit: minute
                value: 4
              - http_method: POST
                unit: minute
                value: 10
                invokers:
                  - header_value: foo-POST-client
                    name: client 1
                    unit: minute
                    value: 5
          - uri_prefix: "/bar"
            unit: minute
            value: 20
            invokers:
              - header_value: bar-client
                name: client 2
                unit: minute
                value: 2

При отправке запросов на адрес http://ingress-my-namespace-02.apps.domain.org/foo/test методами GET и POST будут применяться различные значения лимитов.

При отправке запроса методом, для которого не указан лимит в артефакте GlobalRateLimit (например, PUT для /foo или любой запрос для /bar), будут применяться значения лимитов из самого префикса с учетом invokers из префикса.

Для снятия ограничений для отдельного метода можно указать для него в поле value значение -1. В таком случае блок invokers (при его наличии) для соответствующего метода будет проигнорирован.

    - endpoint: 'ingress-my-namespace-02.apps.domain.org:8080'
      name: methodsservice
      shortname: srvmethod
      by_header:
        header: synapse-consumerid
        uri_prefixes:
          - uri_prefix: "/foo"
            unit: minute
            value: 7
            http_methods:
              - http_method: GET
                unit: minute
                value: 4
              - http_method: POST
                value: -1

В данном примере POST-запросы с префиксом /foo будут ограничены только лимитом на весь endpoint (значением overall_limit при его наличии).

Rate Limit CN#

Данный режим является расширением режимов Rate Limit Header и Prefix. Применяется в случаях, когда требуется идентифицировать потребителя по конкретным атрибутам поля Subject сертификата.

Для добавления в запрос заголовка со значением Subject необходимо вручную загрузить артефакт EnvoyFilter.

EnvoyFilter для Istio 1.17 (Envoy 1.25).

В соответствии с добавленным фильтром:

  • запросы, в которых не будет данных о клиентском сертификате, будут отклонены с ошибкой 403;

  • если в запросе уже будет заголовок ${header}, то он будет перезаписан.

Ниже приведены параметры для конфигурирования данного артефакта EnvoyFilter:

Название параметра

Описание

Пример

namespace

Идентификатор пространства имен

my-namespace

labels

Список label для определения необходимого Ingress (Egress) Gateway

app: ingress-my-namespace

port

Порт, на который отправляются запросы

8443

header

Название заголовка, в который будет записываться поле Subject

synapse-consumerid

При конфигурировании лимитов можно использовать как все поле Subject целиком, так и конкретный атрибут (или набор атрибутов) из него. Для указания конкретных атрибутов необходимо добавить в артефакт GlobalRateLimit раздел modify_header.

Блок modify_header влияет на весь endpoint, указывается внутри by_header и содержит в себе два параметра:

  • type — тип преобразования заголовка (для режима Rate Limit CN необходимо указывать значение cert — преобразования в терминах сертификата);

  • rule — правило преобразования заголовка (для типа cert через запятую указываются необходимые атрибуты сертификата из поля Subject).

Правило преобразования rule поддерживает следующие атрибуты поля Subject сертификата:

  • CN: CommonName

  • OU: OrganizationalUnit

  • O: Organization

  • L: Locality

  • S или ST: StateOrProvinceName

  • C: CountryName

Пример поля Subject: CN=tribe-sy-dev-srls-dev-04, OU=R&D, O=sbertech, L=Moscow 10, S=Moscow, C=RU.

Примеры указания правила преобразования заголовка: rule: cn, rule: CN, o, rule: Ou,O

Пример конфигурации endpoint c modify_header:

- endpoint: 'ingress-my-namespace-02.apps.domain.org:8443'
  name: srls dev 02
  shortname: dev
  by_header:
    modify_header:
      type: cert
      rule: cn
    anon_value: 1
    header: synapse-consumerid
    invokers:
      - header_value: tribe-sy-dev-srls-dev-04
        name: client 1
        unit: minute
        value: 13
    unit: minute
    value: 7

Предположим, что в сертификате задан Subject="CN=tribe-sy-dev-srls-dev-04, OU=R&D, O=sbertech, C=RU". В таком случае, после применения EnvoyFilter, в пользовательский заголовок, например в synapse-consumerid, будет добавлено значение CN=tribe-sy-dev-srls-dev-04, OU=R&D, O=sbertech, C=RU. Рассмотрим для этого примера варианты указания блока modify_header и соответствующие ему header_value для задания лимитов:

  • блок modify_header не задан — преобразование заголовка не выполняется, для задания лимита используется все значение поля Subject:

 by_header:
   header: synapse-consumerid
   invokers:
    - header_value: CN=tribe-sy-dev-srls-dev-04, OU=R&D, O=sbertech, C=RU
      name: client 1
      unit: minute
      value: 13
   unit: minute
   value: 7
  • в modify_header не задано правило преобразования rule (или задано пустое значение) — преобразование заголовка не выполняется, для задания лимита используется все значение поля Subject:

 by_header:
   modify_header:
     type: cert
   header: synapse-consumerid
   invokers:
    - header_value: CN=tribe-sy-dev-srls-dev-04, OU=R&D, O=sbertech, C=RU
      name: client 1
      unit: minute
      value: 13
   unit: minute
   value: 7
  • в modify_header в правиле указан один атрибут, который присутствует в Subject — преобразование заголовка выполнится, для задания лимита используется значение указанного атрибута:

 by_header:
   modify_header:
     type: cert
     rule: cn
   header: synapse-consumerid
   invokers:
    - header_value: tribe-sy-dev-srls-dev-04
      name: client 1
      unit: minute
      value: 13
   unit: minute
   value: 7
  • в modify_header в правиле указано несколько атрибутов, которые присутствуют в Subject — преобразование заголовка выполнится, для задания лимита используется конкатенация значений указанных атрибутов:

 by_header:
   modify_header:
     type: cert
     rule: cn,o
   header: synapse-consumerid
   invokers:
    - header_value: tribe-sy-dev-srls-dev-04sbertech
      name: client 1
      unit: minute
      value: 13
   unit: minute
   value: 7
  • в modify_header в правиле указано несколько атрибутов, но не все они присутствуют в Subject — преобразование заголовка выполнится, для задания лимита используется конкатенация значений атрибутов, имеющихся в Subject:

 by_header:
   modify_header:
     type: cert
     rule: cn,l,st,o
   header: synapse-consumerid
   invokers:
    - header_value: tribe-sy-dev-srls-dev-04sbertech
      name: client 1
      unit: minute
      value: 13
   unit: minute
   value: 7
  • в modify_header в правиле указано несколько атрибутов, но они не присутствуют в Subject — преобразование заголовка выполнится с ошибкой и вернет исходное значение. Для задания лимита в таком случае, можно использовать все значение поля Subject. Но при этом метрика ошибок модификации ratelimiterservice_should_rate_limit_modify_header_error будет постоянно увеличиваться, и в логах rate-limiter-service c уровнем debug будет выводиться ошибка типа: "Error modify header: the specified cert artifact was not found in the header: CN=tribe-sy-dev-srls-dev-04, OU=R&D, O=sbertech, C=RU"

 by_header:
   modify_header:
     type: cert
     rule: l,st
   header: synapse-consumerid
   invokers:
    - header_value: CN=tribe-sy-dev-srls-dev-04, OU=R&D, O=sbertech, C=RU
      name: client 1
      unit: minute
      value: 13
   unit: minute
   value: 7
  • EnvoyFilter не добавлен, пользователь при запросе добавляет заголовок, например synapse-consumerid: Kafka, но в артефакте GlobalRateLimit задан блок modify_header — преобразование заголовка выполнится с ошибкой и вернет исходное значение. Для задания лимита в таком случае, можно использовать значение "Kafka". Но при этом метрика ошибок модификации ratelimiterservice_should_rate_limit_modify_header_error будет постоянно увеличиваться, и в логах rate-limiter-service c уровнем debug будет выводиться ошибка типа: "Error modify header: the specified cert artifact was not found in the header: CN=tribe-sy-dev-srls-dev-04, OU=R&D, O=sbertech, C=RU"

 by_header:
   modify_header:
     type: cert
     rule: cn
   header: synapse-consumerid
   invokers:
    - header_value: Kafka
      name: client 1
      unit: minute
      value: 13
   unit: minute
   value: 7

Rate Limit Path#

Данный режим позволяет идентифицировать потребителя (тенанта) по ResourceName из URL запроса.

Формат ResourceName состоит из трех частей: account-id, project-id, resource-id. В конфигурации Rate Limit можно задать значимую часть идентификатора потребителя с помощью маски для расчета лимита.

Варианты маски:

  • mask = "rn/::*" получим итоговое значение: account-idproject-idresource-id;

  • mask = "rn/::_" получим итоговое значение: account-idproject-id;

  • mask = "rn/*::" получим итоговое значение: account-id.

Формат URL запроса:

"http(s)://{HOST}:{PORT}/[{*Код продукта*}]/[{*версия*}]/rn/account-id:project-id:resource-id/{Контекст продукта}"

Пример конфигурации для режима Rate Limit Path.

Параметры блока by_path:

  • mask — опциональное поле, по умолчанию rn/*:*:*, маска для определения значимой части идентификатора потребителя;

  • unit — опциональное поле, по умолчанию second, единица измерения лимитов, используется при расчете overall_limit и потребителей, неуказанных в блоке tenants;

  • quotas — конфигурация квоты для потребителей, неуказанных в блоке tenants;

  • tenants — опциональный блок, позволяет задать квоту для определенных потребителей независимо.

Параметры блока quotas:

  • flat — опциональное поле, по умолчанию 1, позволяет задать квоту для запросов;

  • soft — опциональный блок, если задан, создается метрика, показывающая приближение к лимиту. soft.value — значение квоты, после которой с шагом кратным soft.step будет инкрементирована метрика мониторинга превышения уровня soft квоты для конкретного потребителя вплоть до превышения основного лимита.

Для каждого tenant можно задать:

  • name — опциональное поле, позволяет указать метаданные для resourceName;

  • quotas — конфигурация квоты для потребителя, указанного в resourceName;

  • resourceName — идентификатор потребителя, значение необходимо указать в формате account-id:project-id:resource-id без разделителя, например accProjRes, если используется маска, необходимо указать только значимую часть, например accProj;

  • unit — опциональное поле, по умолчанию second, единица измерения лимитов;

  • schedule – опциональный блок, позволяет изменять лимиты по расписанию.

Поле schedule в примере выше означает, что для tenant: name: server1.path будет изменен лимит 7 запросов в секунду в период с 2 часов 1 января по 12 часов 12 января на 20 запросов в минуту.

Применение нескольких артефактов GlobalRateLimit в рамках одного namespace#

В рамках одного namespace можно создать несколько артефактов GlobalRateLimit для независимого конфигурирования лимитов для сервисов. При этом можно создавать артефакты с разным значением поля workloadSelector, например, для работы как с Ingress Gateway, так и с Egress Gateway.

После создания артефакта происходит «слияние» артефактов с одним и тем же workloadSelector, на этапе «слияния» выполняется валидация конфигурации лимитов.

Осуществляется проверка на уникальность значений полей endpoint и shortname, уникальность значения поля resourceName для одного endpoint и валидация значений полей overall_schedule и schedule.

В случае ошибки валидации необходимо скорректировать значения невалидного артефакта GlobalRateLimit или удалить артефакт.

Ограничение списка namespace для централизованного SRLS#

Ограничение списка namespace для централизованного SRLS необходимо, чтобы была возможность развернуть несколько централизованных SRLS в рамках одного кластера Kubernetes или Red Hat OpenShift (опционально).

Алгоритм следующий:

  1. Пользователь создает в своем namespace артефакт GlobalRateLimit.

  2. RL Operator считывает поле «rlnamespace» из созданного артефакта GlobalRateLimit, сравнивает значение с идентификатором namespace, в котором RL Operator установлен.

    1. Если значение не совпадает, RL Operator не берет в работу артефакт GlobalRateLimit.

    2. Если значение совпадает, RL Operator формирует конфигурацию для RL Service.

    3. Если поле «rlnamespace» не задано, RL Operator осуществляет проверку на наличие прав доступа к артефакту EnvoyFilter в namespace пользователя:

      • если права есть, формирует конфигурацию для RL Service;

      • если доступа нет, не берет в работу.

Часто встречающиеся проблемы и пути их устранения#

Код ошибки

Пример ошибки

Решение

Pod RL Service завершается с ошибкой

time="2022-08-04T14:30:21Z" level=warning msg="connecting to redis on mymaster,redis-sentinel-0.redis-sentinel:26379,redis-sentinel-1.redis-sentinel:26379,redis-sentinel-2.redis-sentinel:26379 with pool size 10" panic: dial tcp: lookup redis-sentinel-2.redis-sentinel on 29.66.0.10:53: no such host

Необходимо удостовериться, что 3 Pod StatefulSet redis-sentinel запущены корректно

Некорректная балансировка запросов от Ingress Gateway до RL Service

В случае перезапуска Pod RL Service или увеличения числа Pods, трафик на вновь созданный Pod почти не поступает

Убедиться, что в артефакте GlobalRateLimit указан Headless Service RL Service, а не обычный. Headless Service — это Service, у которого параметр clusterIP выставлен в None

Не срабатывает RateLimit

-

Проверить, что:
1) все Pod RL Operator, RL Service, Redis, Sentinel запущены, в логах приложений нет ошибок;
2) загружен артефакт GlobalRateLimit (GRL) с необходимыми настройками;
3) в артефакте GRL выставлено корректное значение поля visibility в соответствии с типом установки компонента SRLS, для централизованного — cluster, для децентрализованного — namespace;
4) значение поля workloadSelector, указанное в GRL, соответствует меткам Ingress-gateway;
5) указанное значение поля envoyVersion соответствует версии Envoy, используемой в Ingress-gateway;
6) указанные значения настроек rlserver и rlserverport верны — в качестве host должен быть указан Headless Service RL Service, порт — 8081);
7) в случае централизованного варианта установки в поле rlnamespace должен быть указан целевой namespace централизованного компонента SRLS;
8) созданы все необходимые роли и RoleBinding в соответствии с инструкцией по установке;
9) в полях endpoint указан host, на который поступает входящий трафик (из артефактов Route, Gateway, VirtualService), и порт, который слушает Ingress (задается в артефакте Gateway)

Не запускается Pod

Failed to pull image "registry.mydomain.ru/ci90000055_srls/operator@sha256:9f45c61299009bf91a63514680230151e02bc2bde35f38c14b381edacd291992": rpc error: code = Unknown desc = Error reading manifest sha256:9f45c61299009bf91a63514680230151e02bc2bde35f38c14b381edacd291992 in registry.mydomain.ru/domain/srls/operator: unauthorized: authentication required

Предоставьте права на доступ к образу для пользователя, прописанного в ImagePullSecret rls-image-pull-secret

Не применяется конфигурация GlobalRateLimit

Ошибка в блоке status: endpoint [test-server1-endpoint-tribe-sy-srls-dev-03.apps.stands-vdc01.solution.test:8080] tenant.Name [server1.path] tenant.ResourceName [account-3] duplicate error: GlobalRateLimit manifest not valid

Значение поля resourceName должно быть уникальным при конфигурировании endpoint. Скорректируйте значение поля resourceName

Не запускается Pod RL Operator

W0816 16:15:07.596714 1 reflector.go:424] sigs.k8s.io/controller-runtime/pkg/cache/internal/informers_map.go:262: failed to list *v1alpha1.GlobalRateLimit: globalratelimits.ratelimit.service is forbidden: User "system:serviceaccount:some-namespace:rate-limiter-service" cannot list resource "globalratelimits" in API group "ratelimit.service" at the cluster scope

Убедитесь, что созданы артефакты ClusterRole и ClusterRoleBinding, предоставляющие права на артефакт GlobalRateLimit для ServiceAccount rate-limiter-service в вашем namespace

Warning в статусе GlobalRateLimit

Сообщения с подобным текстом могут появляться в status артефакта GlobalRateLimit и логах RL Operator:
1) endpoint [ingress-my-namespace-02.apps.domain.org:8080] byHeader [synapse-consumerid] invokers are shadowed by uri_prefixes

В сообщении говорится о некритичной ошибке конфигурации:
1) Для endpoint в секции by_header указаны одновременно invokers и uri_prefixes; uri_prefixes имеет более высокий приоритет, поэтому блок invokers игнорируется — необходимо удалить одну из секций