Руководство прикладного разработчика#
Термины и определения#
Термин/Аббревиатура |
Определение |
|---|---|
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#
Есть два варианта подключения к централизованному SRLS:
через NetworkPolicy;
через Egress.
Артефакт 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.
Настройка Egress Gateway (опционально)#
Трафик между namespace должен быть маршрутизирован через Egress namespace инициатора вызова в сторону Ingress Gateway целевого namespace через Router среды контейнеризации для обеспечения требований безопасности. Поэтому необходимо обеспечить маршрутизацию данного трафика средствами Istio. Трафик должен быть закрыт mTLS для выполнения требований безопасности.
Добавить порт
12001в Deployment вашего Egress Gateway.
В описании контейнера в блоке ports необходимо добавить:
- name: grpc-12001
containerPort: 12001
protocol: TCP
Название порта должно быть именно grpc-12001.
Создать артефакт Service типа headless service (параметр clusterIP = None), указывающий на pod Egress Gateway. Добавить порт
12001в артефакт Service вашего Egress Gateway.
В описании артефакта Service, который с помощью Selector указывает на pod Egress Gateway, необходимо добавить новый порт:
- name: grpc-12001
protocol: TCP
port: 12001
targetPort: 12001
Название порта должно быть именно grpc-12001.
Рекомендуется использовать именно Headless Service для корректной балансировки нагрузки, в противном случае при перезапуске одного из pod Egress Gateway на pod Egress Gateway будет поступать минимальный трафик.
Добавить артефакт Gateway.
Чтобы Egress Gateway начал слушать подключения к новому порту, необходимо добавить Istio артефакт Gateway:
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: egress-rate-limiter
namespace: ${namespace}
spec:
selector:
app: ${app-selector-label}
servers:
- hosts:
- '*'
port:
name: GRPC-12001
number: 12001
protocol: GRPC
Здесь:
${namespace}необходимо заменить на идентификатор вашего namespace;${app-selector-label}необходимо заменить на значение лейбла pod Egress Gateway, помимо лейбла «app» могут быть использованы другие лейблы.
Название и протокол порта должны быть именно GRPC-12001 и GRPC.
Добавить артефакт VirtualService.
Формируем правило маршрутизации с помощью артефакта Istio VirtualService:
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: rls-virtual-service
namespace: ${namespace}
spec:
exportTo:
- .
gateways:
- ${namespace}/egress-rate-limiter
hosts:
- '*'
http:
- match:
- gateways:
- ${namespace}/egress-rate-limiter
port: 12001
rewrite:
authority: ${rate_limit_host_name}
route:
- destination:
host: ${rate_limit_host_name}
port:
number: 443
Здесь:
${namespace}необходимо заменить на идентификатор вашего namespace;${rate_limit_host_name}необходимо заменить на URL централизованного сервиса Rate Limit.
Добавить артефакт ServiceEntry.
Для внешних ресурсов по отношению к Mesh необходимо создать артефакт ServiceEntry:
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: rate-limiter-se
namespace: ${namespace}
spec:
exportTo:
- .
hosts:
- ${rate_limit_host_name}
location: MESH_EXTERNAL
ports:
- name: http2
number: 443
protocol: http2
resolution: DNS
Здесь:
${namespace}необходимо заменить на идентификатор вашего namespace;${rate_limit_host_name}необходимо заменить на URL централизованного сервиса Rate Limit.
Название и протокол порта должны быть именно http2.
Добавить артефакт DestinationRule.
Для формирования mTLS-соединения необходимо прописать сертификаты с помощью артефакта DestinationRule:
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: mtls-rate-limiter
namespace: ${namespace}
spec:
exportTo:
- .
host: ${rate_limit_host_name}
trafficPolicy:
portLevelSettings:
- connectionPool:
http: {}
port:
number: 443
tls:
caCertificates: /etc/rls/egress/ca-root.pem
clientCertificate: /etc/rls/egress/egress.pem
mode: MUTUAL
privateKey: /etc/rls/egress/egress.key
sni: ${rate_limit_host_name}
Здесь:
${namespace}необходимо заменить на идентификатор вашего namespace;${rate_limit_host_name}необходимо заменить на URL централизованного сервиса Rate Limit.
Также необходимо прописать актуальные пути и названия файлов с сертификатами и ключом. Здесь приведены значения для примера. Должны использоваться сертификаты, подписанные УЦ.
Конфигурирование артефактов среды контейнеризации и 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 Path — необходим для формирования запросов к RL Service в режиме работы
Rate Limit Path.
Минимальный набор артефактов включает в себя один Кластер и один артефакт для любого выбранного режима. Для каждого Endpoint, сконфигурированного в артефакте GlobalRateLimit, должен быть создан артефакт EnvoyFilter для соответствующего режима работы компонента SRLS. Допускается объединение всех артефактов EnvoyFilter в один yaml-файл.
Режимы работы описаны в разделе «Режимы работы» настоящего руководства.
Ниже приведены соответствующие артефакты EnvoyFilter для разных версий Envoy: 1.14+ и 1.17+.
Кластер
Ниже приведены параметры для конфигурирования артефакта 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 |
Для версии 1.14+ без закрытия соединения TLS:
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: ${namespace}-rls-cluster
namespace: ${namespace}
labels:
srls: srls
spec:
workloadSelector:
labels:
${labels}
configPatches:
- applyTo: HTTP_FILTER
match:
context: GATEWAY
listener:
filterChain:
filter:
name: "envoy.http_connection_manager"
subFilter:
name: "envoy.router"
patch:
operation: INSERT_BEFORE
value:
name: envoy.rate_limit
config:
domain: ${namespace}
failure_mode_deny: false
rate_limit_service:
grpc_service:
envoy_grpc:
cluster_name: rate_limit_cluster
timeout: 10s
timeout: ${GrpcTimeout}
- applyTo: CLUSTER
match:
cluster:
service: ${RlServer}
patch:
operation: ADD
value:
name: rate_limit_cluster
type: STRICT_DNS
connect_timeout: 10s
lb_policy: ROUND_ROBIN
http2_protocol_options: {}
load_assignment:
cluster_name: rate_limit_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: ${RlServer}
port_value: ${RlServerPort}
Для версии 1.14+ c закрытием соединения TLS при прямом подключении:
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: ${namespace}-rls-cluster
namespace: ${namespace}
labels:
srls: srls
spec:
workloadSelector:
labels:
${labels}
configPatches:
- applyTo: HTTP_FILTER
match:
context: GATEWAY
listener:
filterChain:
filter:
name: "envoy.http_connection_manager"
subFilter:
name: "envoy.router"
patch:
operation: INSERT_BEFORE
value:
name: envoy.rate_limit
config:
domain: ${namespace}
failure_mode_deny: false
rate_limit_service:
grpc_service:
envoy_grpc:
cluster_name: rate_limit_cluster
timeout: 10s
timeout: ${GrpcTimeout}
- applyTo: CLUSTER
match:
cluster:
service: ${RlServer}
patch:
operation: ADD
value:
name: rate_limit_cluster
type: STRICT_DNS
connect_timeout: 10s
lb_policy: ROUND_ROBIN
http2_protocol_options: {}
load_assignment:
cluster_name: rate_limit_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: ${RlServer}
port_value: ${RlServerPort}
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
sni: ${RlServer}
common_tls_context:
tls_certificates:
- certificate_chain:
filename: "${Cert}"
private_key:
filename: "${Key}"
validation_context:
trusted_ca:
filename: ${TrustCA}
Для версии 1.17+ без закрытия соединения TLS:
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: ${namespace}-rls-cluster
namespace: ${namespace}
labels:
srls: srls
spec:
workloadSelector:
labels:
${labels}
configPatches:
- applyTo: HTTP_FILTER
match:
context: GATEWAY
listener:
filterChain:
filter:
name: "envoy.filters.network.http_connection_manager"
subFilter:
name: "envoy.filters.http.router"
patch:
operation: INSERT_BEFORE
value:
name: envoy.filters.http.ratelimit
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit
domain: ${namespace}
failure_mode_deny: false
rate_limit_service:
grpc_service:
envoy_grpc:
cluster_name: rate_limit_cluster
timeout: 10s
transport_api_version: V3
timeout: ${GrpcTimeout}
- applyTo: CLUSTER
match:
cluster:
service: ${RlServer}
patch:
operation: ADD
value:
name: rate_limit_cluster
type: STRICT_DNS
connect_timeout: 10s
lb_policy: ROUND_ROBIN
http2_protocol_options: {}
load_assignment:
cluster_name: rate_limit_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: ${RlServer}
port_value: ${RlServerPort}
Для версии 1.17+ с закрытием соединения TLS при прямом подключении:
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: ${namespace}-rls-cluster
namespace: ${namespace}
labels:
srls: srls
spec:
workloadSelector:
labels:
${labels}
configPatches:
- applyTo: HTTP_FILTER
match:
context: GATEWAY
listener:
filterChain:
filter:
name: "envoy.filters.network.http_connection_manager"
subFilter:
name: "envoy.filters.http.router"
patch:
operation: INSERT_BEFORE
value:
name: envoy.filters.http.ratelimit
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit
domain: ${namespace}
failure_mode_deny: false
rate_limit_service:
grpc_service:
envoy_grpc:
cluster_name: rate_limit_cluster
timeout: 10s
transport_api_version: V3
timeout: ${GrpcTimeout}
- applyTo: CLUSTER
match:
cluster:
service: ${RlServer}
patch:
operation: ADD
value:
name: rate_limit_cluster
type: STRICT_DNS
connect_timeout: 10s
lb_policy: ROUND_ROBIN
http2_protocol_options: {}
load_assignment:
cluster_name: rate_limit_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: ${RlServer}
port_value: ${RlServerPort}
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
sni: ${RlServer}
common_tls_context:
tls_certificates:
- certificate_chain:
filename: "${Cert}"
private_key:
filename: "${Key}"
validation_context:
trusted_ca:
filename: ${TrustCA}
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 для данной точки подключения указано значение поля |
Для версии 1.14+:
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: ${namespace}-${Shortname}
namespace: ${namespace}
labels:
srls: srls
spec:
workloadSelector:
labels:
${labels}
configPatches:
- applyTo: VIRTUAL_HOST
match:
context: GATEWAY
routeConfiguration:
vhost:
name: ${Endpoint}
route:
action: ANY
patch:
operation: MERGE
value:
rate_limits:
// Опциональный блок
- actions:
- generic_key:
descriptor_value: ${Shortname}.over
// Конец опционального блока
- actions:
- generic_key:
descriptor_value: ${Shortname}
- header_value_match:
descriptor_value: header-absent
headers:
- name: ${Header}
present_match: true
invert_match: true
- actions:
- generic_key:
descriptor_value: ${Shortname}
- request_headers:
header_name: ${Header}
descriptor_key: ${Shortname}.${Header}
Для версии 1.17+:
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: ${namespace}-${Shortname}
namespace: ${namespace}
labels:
srls: srls
spec:
workloadSelector:
labels:
${labels}
configPatches:
- applyTo: VIRTUAL_HOST
match:
context: GATEWAY
routeConfiguration:
vhost:
name: ${Endpoint}
route:
action: ANY
patch:
operation: MERGE
value:
rate_limits:
// Опциональный блок
- actions:
- generic_key:
descriptor_key: endpoint
descriptor_value: ${Shortname}.over
// Конец опционального блока
- actions:
- generic_key:
descriptor_key: endpoint
descriptor_value: ${Shortname}
- request_headers:
header_name: ${Header}
descriptor_key: ${Shortname}.${Header}
skip_if_absent: true
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 для данной точки подключения указано значение поля |
Для версии 1.14+:
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: ${namespace}-${Shortname}
namespace: ${namespace}
labels:
srls: srls
spec:
workloadSelector:
labels:
${labels}
configPatches:
- applyTo: VIRTUAL_HOST
match:
context: GATEWAY
routeConfiguration:
vhost:
name: ${Endpoint}
route:
action: ANY
patch:
operation: MERGE
value:
rate_limits:
// Опциональный блок
- actions:
- generic_key:
descriptor_value: ${Shortname}.over
// Конец опционального блока
- actions:
- generic_key:
descriptor_value: ${Shortname}
- request_headers:
header_name: ":path"
descriptor_key: ${Shortname}.prefix
- header_value_match:
descriptor_value: header-absent
headers:
- name: ${Header}
present_match: true
invert_match: true
- actions:
- generic_key:
descriptor_value: ${Shortname}
- request_headers:
header_name: ":path"
descriptor_key: ${Shortname}.prefix
- request_headers:
header_name: ${Header}
descriptor_key: ${Shortname}.${Header}
Для версии 1.17+:
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: ${namespace}-${Shortname}
namespace: ${namespace}
labels:
srls: srls
spec:
workloadSelector:
labels:
${labels}
configPatches:
- applyTo: VIRTUAL_HOST
match:
context: GATEWAY
routeConfiguration:
vhost:
name: ${Endpoint}
route:
action: ANY
patch:
operation: MERGE
value:
rate_limits:
// Опциональный блок
- actions:
- generic_key:
descriptor_key: endpoint
descriptor_value: ${Shortname}.over
// Конец опционального блока
- actions:
- generic_key:
descriptor_key: endpoint
descriptor_value: ${Shortname}
- request_headers:
header_name: ":path"
descriptor_key: ${Shortname}.prefix
- request_headers:
header_name: ${Header}
descriptor_key: ${Shortname}.${Header}
skip_if_absent: true
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 для данной точки подключения указано значение поля |
Для версии 1.14+:
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: ${namespace}-${Shortname}
namespace: ${namespace}
labels:
srls: srls
spec:
workloadSelector:
labels:
${labels}
configPatches:
- applyTo: VIRTUAL_HOST
match:
context: GATEWAY
routeConfiguration:
vhost:
name: ${Endpoint}
route:
action: ANY
patch:
operation: MERGE
value:
rate_limits:
// Опциональный блок
- actions:
- generic_key:
descriptor_value: ${Shortname}.over
// Конец опционального блока
- actions:
- generic_key:
descriptor_value: ${Shortname}
- request_headers:
header_name: ":path"
descriptor_key: ${Shortname}.path
Для версии 1.17+:
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: ${namespace}-${Shortname}
namespace: ${namespace}
labels:
srls: srls
spec:
workloadSelector:
labels:
${labels}
configPatches:
- applyTo: VIRTUAL_HOST
match:
context: GATEWAY
routeConfiguration:
vhost:
name: ${Endpoint}
route:
action: ANY
patch:
operation: MERGE
value:
rate_limits:
// Опциональный блок
- actions:
- generic_key:
descriptor_key: endpoint
descriptor_value: ${Shortname}.over
// Конец опционального блока
- actions:
- generic_key:
descriptor_key: endpoint
descriptor_value: ${Shortname}
- request_headers:
header_name: ":path"
descriptor_key: ${Shortname}.path
skip_if_absent: true
Добавление 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.
При настроенном Deploy Tools выполнить установку в прикладной namespace, действия описаны в подразделе «Автоматизированная установка сервиса с использованием Deploy Tools» документа «Руководство по установке».
Добавить артефакт GlobalRateLimit в прикладной namespace. Пример для тестового сервиса Server-Test:
kind: GlobalRateLimit
apiVersion: ratelimit.service/v1alpha1
metadata:
name: rate-limit-config
namespace: ${namespace}
spec:
envoyVersion: '1.14'
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
Тестирование работы SRLS.
Перед тестированием убедитесь, что в namespace:
все pod поднялись;
фильтры EnvoyFiltеr для сервиса (в примере Server-Test) создались;
выполняется переход по ссылке в pod Ingress Gateway.
Добавьте ресурс 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.
Пример конфигурации:
apiVersion: ratelimit.service/v1alpha1
kind: GlobalRateLimit
metadata:
name: rate-limit-config
namespace: my-namespace
spec:
endpoints:
- endpoint: 'test-server2-endpoint.apps.stands-vdc01.solution.sbt:8080'
name: some service1
shortname: fserver
overall_limit: 70
by_header:
header: synapse-consumerid
unit: second
anon_value: 3
value: 7
invokers:
- header_value: synapse-dev-sandbox
name: client from synapse-dev-sandbox namespace
unit: second
value: 5
- header_value: kordon1
name: client from kordon1 namespace
unit: second
value: 2
- endpoint: 'test-server3-endpoint.apps.stands-vdc01.solution.sbt:8080'
name: some service2
shortname: sserver
overall_limit: 70
by_path:
mask: 'rn/*:*:_'
unit: second
quotas:
soft:
value: 10
step: 1
flat: 15
tenants:
- name: server1.path
resourceName: account-1project-1
unit: second
quotas:
flat: 7
- name: server2.path
resourceName: account-2project-2
unit: second
quotas:
soft:
value: 5
step: 1
flat: 10
- endpoint: 'test-server4-endpoint.apps.stands-vdc01.solution.sbt:8080'
name: srls dev 02
shortname: dev
overall_limit: 100
overall_schedule:
start:
second: '10'
minute: '*'
hour: '*'
day: '*'
month: '*'
weekday: '*'
stop:
second: '30'
minute: '*'
hour: '*'
day: '*'
month: '*'
weekday: '*'
unit: minute
value: 50
by_header:
header: synapse-consumerid
uri_prefixes:
- uri_prefix: "/foo"
unit: minute
value: 7
anon_value: 3
invokers:
- header_value: foo-client
name: client 1
unit: minute
value: 5
- uri_prefix: "/bar"
unit: minute
value: 20
anon_value: 2
invokers:
- header_value: bar-client
name: client 2
unit: minute
value: 2
- uri_prefix: "/foo/bar"
unit: minute
value: 13
anon_value: 4
invokers:
- header_value: foo-bar-client
name: client 3
unit: minute
value: 6
- endpoint: 'test-server1-endpoint.apps.stands-vdc01.solution.sbt:8080'
name: some service
shortname: server
header: synapse-consumerid
unit: second
value: 7
invokers:
- name: client1
header_value: synapse-dev-sandbox
unit: second
value: 5
- name: client2
header_value: kordon1
unit: second
value: 2
rlserver: egress-gateway-headless.my-namespace.svc.cluster.local
rlserverport: 12001
rlnamespace: rlservice-ns
rlservercert:
cert: /etc/rls/ingress/ingress.pem
key: /etc/rls/ingress/ingress.key
trust_ca: /etc/rls/ingress/ca-root.pem
workloadSelector:
labels:
app: ingress-my-namespace
istio: ingress-my-namespace
visibility: cluster
envoyVersion: '1.14'
В примере для 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будут задействовать свои лимиты независимо друг от друга).
При формировании артефакта необходимо указать:
workloadSelector — набор labels для определения нужных pod Ingress (Egress) Gateway;
visibility — отвечает за область видимости данного CRD, возможные значения: cluster, namespace;
envoyVersion — отвечает за наполнение артефакта EnvoyFilter в зависимости от используемой версии Envoy, значение по умолчанию —
'1.14';endpoints — список ваших ресурсов;
rlserver — адрес RLS-сервера (для децентрализованной установки следует указать headless server rls например: «rate-limiter-headless-service», для централизованной установки здесь следует указать сервис Egress Gateway вашего namespace, рекомендованный формат —
<headless_service_name>.<namespace>.svc.cluster.local);rlserverport — порт RLS-сервера, по умолчанию — 12001, если не задан (для децентрализованной установки — 8081, для централизованной установки — 12001);
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.
Пример:
- endpoint: 'test-server-endpoint.apps.domain.org:8080'
name: some service1
shortname: fserver
overall_limit: 70
by_header:
header: synapse-consumerid
unit: minute
anon_value: 3
value: 5
soft:
value: 2
step: 1
invokers:
- header_value: synapse-dev-sandbox
name: client from synapse-dev-sandbox namespace
unit: second
value: 7
soft:
value: 3
step: 1
- header_value: kordon1
name: client from kordon1 namespace
unit: second
value: 2
- endpoint: 'test-endpoint.apps.domain.org:8080'
name: some service2
shortname: fserver2
overall_limit: 50
overall_schedule:
start:
second: '10'
minute: '*'
hour: '*'
day: '*'
month: '*'
weekday: '*'
stop:
second: '30'
minute: '*'
hour: '*'
day: '*'
month: '*'
weekday: '*'
unit: minute
value: 25
by_header:
header: synapse-consumerid
unit: minute
anon_value: 3
value: 5
soft:
value: 2
step: 1
invokers:
- header_value: synapse-test
name: client from synapse-test namespace
unit: second
value: 8
soft:
value: 3
step: 1
schedule:
start:
second: '1'
minute: '5'
hour: '2'
day: '*'
month: '*'
weekday: '1'
stop:
second: '30'
minute: '10'
hour: '6'
day: '*'
month: '*'
weekday: '1'
unit: second
value: 16
soft:
value: 5
step: 2
Параметры блока 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 |
Данные в
schedulerблокиstartиstopдолжны удовлетворять следующим требованиям:
startне должен срабатывать несколько раз до наступления событияstop;stopне должен срабатывать несколько раз до наступления событияstart;startиstopне может быть задан в несуществующую дату, например, в сентябре нет 31 дня, в феврале нет 29 дня и подобные.
В случае установки некорректных параметров в CRD GlobalRateLimit в блоке
schedulerилиoverall_schedule, в статусе CRD будет отражено место с указанием, где задан невалидный интервал времени и даты.
Scheduler в поле 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.
Пример:
exportTo:
- .
gateways:
- my-namespace-02/ingress-gateway
hosts:
- ingress-my-namespace-02.apps.domain.org
http:
- match:
- uri:
prefix: /foo
route:
- destination:
host: fooserv.my-namespace-02.svc.cluster.local
port:
number: 8080
- match:
- uri:
prefix: /bar
route:
- destination:
host: barserv.my-namespace-02.svc.cluster.local
port:
number: 8080
- match:
- uri:
prefix: /foo/bar
route:
- destination:
host: fooserv.my-namespace-02.svc.cluster.local
port:
number: 8080
Для того чтобы для каждого префикса указать собственные значения квоты, в артефакте GlobalRateLimit в разделе by_header необходимо поле header и блок параметров uri_prefixes, в котором необходимо указать префикс в поле uri_prefix и лимиты по аналогии с режимом by-header.
Для работы функционала overall_limit необходимо указать поле unit, по умолчанию, если поле не задано, overall_limit будет рассчитываться в секундах. Счетчик overall_limit общий для всех префиксов. Остальные поля блока by_header будут проигнорированы.
Пример конфигурации endpoint:
- 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: "/foo"
unit: minute
value: 7
anon_value: 3
invokers:
- header_value: foo-client
name: client 1
unit: minute
value: 5
- uri_prefix: "/bar"
unit: minute
value: 20
anon_value: 2
invokers:
- header_value: bar-client
name: client 2
unit: minute
value: 2
- uri_prefix: "/foo/bar"
unit: minute
value: 13
anon_value: 4
invokers:
- header_value: foo-bar-client
name: client 3
unit: minute
value: 6
schedule:
start:
second: '10'
minute: '*'
hour: '*'
day: '*'
month: '*'
weekday: '*'
stop:
second: '45'
minute: '*'
hour: '*'
day: '*'
month: '*'
weekday: '*'
unit: second
value: 10
Таким образом, для вызовов 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, то он будет игнорироваться.
В случаях, когда нет необходимости считать лимиты для всех префиксов, не указанных в артефакте 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 CN#
Данный режим является расширением режимов Rate Limit Header и Prefix. Применяется в случаях, когда требуется идентифицировать потребителя по конкретным атрибутам поля Subject сертификата.
Для добавления в запрос заголовка со значением Subject необходимо вручную загрузить артефакт EnvoyFilter.
Для Istio 1.12 (Envoy 1.20) EnvoyFilter имеет следующий вид:
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: subj-filter
namespace: ${namespace}
spec:
configPatches:
- applyTo: HTTP_FILTER
match:
context: GATEWAY
listener:
filterChain:
filter:
name: envoy.http_connection_manager
portNumber: ${port}
patch:
operation: INSERT_BEFORE
value:
name: envoy.filters.http.lua
typed_config:
'@type': type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
inline_code: |
function envoy_on_request(request_handle)
local peer_subject = request_handle:streamInfo():downstreamSslConnection():subjectPeerCertificate()
if peer_subject == nil then
request_handle:respond(
{[":status"] = "403",
["info"] = "no subjectPeerCertificate in request"},
"nope")
end
request_handle:logDebug(peer_subject)
request_handle:headers():replace("${header}", peer_subject)
end
workloadSelector:
labels:
${labels}
Для более ранних версий Envoy необходимо использовать EnvoyFilter, который имеет следующий вид:
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: subj-filter
namespace: ${namespace}
spec:
configPatches:
- applyTo: HTTP_FILTER
match:
context: GATEWAY
listener:
filterChain:
filter:
name: envoy.http_connection_manager
portNumber: ${port}
patch:
operation: INSERT_BEFORE
value:
name: envoy.filters.http.lua
typed_config:
'@type': type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
inline_code: |
function envoy_on_request(request_handle)
local peer_subject = request_handle:headers():get("x-forwarded-client-cert")
if peer_subject == nil then
request_handle:respond(
{[":status"] = "403",
["info"] = "no x-forwarded-client-cert header"},
"nope")
end
local i,k = string.find(peer_subject, 'Subject=\"')
if i == nil then
request_handle:respond(
{[":status"] = "403",
["info"] = "no subject in cert"},
"nope")
end
local j = string.find(peer_subject, "\"", k+1)
if j == nil then
request_handle:respond(
{[":status"] = "403",
["info"] = "cant parse subject"},
"nope")
end
local subj = string.sub(peer_subject, k+1, j-1)
request_handle:logDebug(subj)
request_handle:headers():replace("${header}", subj)
end
workloadSelector:
labels:
${labels}
Этот фильтр менее эффективен, так как в нём значение Subject вычленяется из заголовка "x-forwarded-client-cert", но позволяет работать даже с Envoy 1.14.
В соответствии с добавленным фильтром:
запросы, в которых не будет данных о клиентском сертификате, будут отклонены с ошибкой 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:
- endpoint: 'test-server3-endpoint:8080'
name: some service3
overall_limit: 70
shortname: servert
by_path:
mask: 'rn/*:*:_'
unit: second
quotas:
flat: 15
soft:
step: 1
value: 10
tenants:
- name: server1.path
quotas:
flat: 7
resourceName: account-1project-1
unit: second
schedule:
start:
second: '0'
minute: '0'
hour: '2'
day: '1'
month: '1'
weekday: '*'
stop:
second: '0'
minute: '0'
hour: '12'
day: '5'
month: '1'
weekday: '*'
unit: minute
value: 20
- name: server2.path
quotas:
flat: 10
soft:
step: 1
value: 5
resourceName: account-2project-2
unit: second
Параметры блока 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 (опционально).
Алгоритм следующий:
Пользователь создает в своем namespace артефакт GlobalRateLimit.
RL Operator считывает поле «rlnamespace», сравнивает значение с идентификатором namespace, в котором он установлен.
если значение не совпадает, RL Operator не берет в работу.
Если поле в GlobalRateLimit не задано, RL Operator осуществляет проверку на наличие прав доступа к артефакту EnvoyFilter в namespace пользователя, если права есть — берет GlobalRateLimit в работу, если доступа нет, не берет в работу.
Часто встречающиеся проблемы и пути их устранения#
Код ошибки |
Пример ошибки |
Решение |
|---|---|---|
'grpc-status', '12' |
2022-02-22T13:51:47.544976Z debug envoy http [external/envoy/source/common/http/conn_manager_impl.cc:1729] [C8647][S166541960535733715] encoding headers via codec (end_stream=true): ':status', '200' 'content-type', 'application/grpc' 'grpc-status', '12' 'date', 'Tue, 22 Feb 2022 13:51:47 GMT' 'server', 'istio-envoy' |
Для анализа необходимо проследить цепочку от Ingress Gateway в прикладном namespace до Rate Limit в централизованном namespace. Для анализа необходимо включить расширенное логирование на Egress Gateway в прикладном namespace и Ingress Gateway в namespace централизованного Rate Limit. Это можно сделать с помощью |
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 { IP_ADDRESS }:53: no such host |
Необходимо удостовериться, что 3 pod StatefullSet redis-sentivel запущены корректно |
Некорректная балансировка запросов от Ingress Gateway до Egress Gateway трафика в сторону RL Service |
В случае перезапуска pod Egress gateway или увеличения числа pods, трафик на вновь созданный pod почти не поступает |
Убедиться, что в артефакте GlobalRateLimit указан Headless Service Egress Gateway, а не обычный. Headless Service — это Service, у которого параметр clusterIP выставлен в None |
Не срабатывает RateLimit |
- |
Проверить, что: |
Не запускается 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.sbt:8080] tenant.Name [server1.path] tenant.ResourceName [account-3] duplicate error: GlobalRateLimit manifest not valid |
Значение поля resourceName должно быть уникальным при конфигурировании endpoint. Скорректируйте значение поля resourceName |
Не запускается pod |
Error creating: pods "rloperator-f9bc4fcdc-" is forbidden: unable to validate against any security context constraint: [provider "anyuid": Forbidden: not usable by user or serviceaccount, provider restricted: .spec.securityContext.fsGroup: Invalid value: []int64{2000}: 2000 is not an allowed group, spec.containers[1].securityContext.runAsUser: Invalid value: 1001560000: must be in the ranges: [1000900000, 1000909999], provider "mef-scc": Forbidden: not usable by user or serviceaccount, provider "nonroot": Forbidden: not usable by user or serviceaccount, provider "hostmount-anyuid": Forbidden: not usable by user or serviceaccount, provider "aqua-scc": Forbidden: not usable by user or serviceaccount, provider "machine-api-termination-handler": Forbidden: not usable by user or serviceaccount, provider "hostnetwork": Forbidden: not usable by user or |
Ошибка говорит, что при развертывании в кластере OpenShift было выставлено значение |
Не запускается pod rloperator |
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 |