clusters:
- name: service_1
type: STRICT_DNS
lb_policy: LEAST_REQUEST
# Политики:
# ROUND_ROBIN — по очереди
# LEAST_REQUEST — наименьшее число запросов
# RING_HASH — consistent hashing
# RANDOM — случайный
# MAGLEV — Maglev hashing
# CLUSTER_PROVIDED — определяет провайдер
# Настройка RING_HASH:
# lb_subset_config:
# subset_selectors:
# - keys: ["version", "stage"]
# - keys: ["version"]
# Weighted round robin:
# weighted_lb:
# hosts:
# - endpoint: { address: { socket_address: { address: app1, port_value: 3000 } } }
# weight: 3
# - endpoint: { address: { socket_address: { address: app2, port_value: 3000 } } }
# weight: 1
# Locality weighted LB:
# locality_lb_config:
# locality_weighted_lb: true
# locality_specific_lb_config:
# zone: us-east-1a
# Consistent hashing
# ring_hash_lb_config:
# minimum_ring_size: 1024
# hash_function: XX_HASH # или MURMUR_HASH_2</code></pre></td></tr>
80Locality weighted LB.clusters:
- name: service_1
type: STRICT_DNS
lb_policy: LEAST_REQUEST
locality_weighted_lb: true
load_assignment:
cluster_name: service_1
endpoints:
- locality:
region: us-east-1
zone: us-east-1a
sub_zone: az1
lb_endpoints:
- endpoint:
address:
socket_address: { address: app1, port_value: 3000 }
health_status: HEALTHY
metadata:
filter_metadata:
envoy.lb:
version: v1
priority: 0
weight: 70
- locality:
region: us-east-1
zone: us-east-1b
lb_endpoints:
- endpoint:
address:
socket_address: { address: app2, port_value: 3000 }
priority: 0
weight: 30
- locality:
region: us-west-2
zone: us-west-2a
lb_endpoints:
- endpoint:
address:
socket_address: { address: app3, port_value: 3000 }
priority: 1 # fallback, если us-east недоступен
weight: 100
81Плагины Kong.# Установка плагина:
docker run -d --name kong \
-e "KONG_DATABASE=postgres" \
-e "KONG_PG_HOST=kong-db" \
-e "KONG_PLUGINS= bundled,my-custom-plugin" \
-p 8000:8000 kong
Включение плагина на сервис:
curl -X POST http://localhost:8001/services/my-service/plugins
-d "name=rate-limiting"
-d "config.minute=100"
-d "config.policy=local"
Популярные плагины:
rate-limiting — лимит запросов
proxy-cache — кэширование
jwt — JWT аутентификация
oauth2 — OAuth 2.0
acl — доступ по ролям
cors — CORS
ip-restriction — ограничение по IP
bot-detection — защита от ботов
request-transformer — трансформация запроса
response-transformer — трансформация ответа
file-log — логирование в файл
datadog — метрики в DataDog
prometheus — метрики для Prometheus
aws-lambda — интеграция с Lambda
azure-functions — интеграция с Azure Functions
82Разработка плагина.# Структура плагина:
# ./my-plugin/
# handler.lua
# schema.lua
handler.lua:
local MyHandler = {
VERSION = "1.0.0",
PRIORITY = 1000
}
function MyHandler:access(conf)
kong.log.debug("My plugin access phase")
local token = kong.request.get_header("x-api-key")
if not token then
return kong.response.exit(401, { error = "API key required" })
end
if token ~= conf.expected_key then
return kong.response.exit(403, { error = "Invalid API key" })
end
kong.ctx.shared.authenticated = true
end
function MyHandler:response(conf)
kong.response.set_header("X-My-Plugin", "processed")
end
return MyHandler
schema.lua:
return {
name = "my-plugin",
fields = {
{ config = {
type = "record",
fields = {
{ expected_key = { type = "string", required = true } }
}
} }
}
}
Установка:
docker exec -it kong bash
luarocks make my-plugin-1.0.0-1.rockspec
83Цепочки middleware.docker-compose.yml:
version: "3"
services:
traefik:
image: traefik:v3.0
command:
- "--providers.docker=true"
- "--entrypoints.web.address=:80"
ports:
- "80:80"
app:
image: myapp
labels:
- "traefik.enable=true"
- "traefik.http.routers.app.rule=Host(app.example.com)"
- "traefik.http.routers.app.middlewares=ratelimit,auth,headers"
- "traefik.http.middlewares.ratelimit.ratelimit.average=100"
- "traefik.http.middlewares.ratelimit.ratelimit.burst=50"
- "traefik.http.middlewares.auth.basicauth.users=admin:$$apr1$$...$$..."
- "traefik.http.middlewares.headers.headers.customrequestheaders.X-Proxy=traefik"
- "traefik.http.middlewares.headers.headers.customresponseheaders.X-Powered-By="
- "traefik.http.middlewares.redirect.redirectscheme.scheme=https"
- "traefik.http.middlewares.compress.compress=true"
- "traefik.http.middlewares.circuitbreaker.circuitbreaker.expression=NetworkErrorRatio() > 0.5"
84Ocelot API Gateway.// ocelot.json — конфигурация Ocelot
{
"Routes": [
{
"DownstreamPathTemplate": "/api/users/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{ "Host": "users-service", "Port": 3000 }
],
"UpstreamPathTemplate": "/users/{everything}",
"UpstreamHttpMethod": [ "GET", "POST", "PUT", "DELETE" ],
"LoadBalancerOptions": {
"Type": "LeastConnection"
},
"RateLimitOptions": {
"ClientWhitelist": [],
"EnableRateLimiting": true,
"Period": "1m",
"PeriodTimespan": 60,
"Limit": 100
},
"AuthenticationOptions": {
"AuthenticationProviderKey": "Bearer",
"AllowedScopes": [ "api.read" ]
}
},
{
"DownstreamPathTemplate": "/api/orders/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{ "Host": "orders-service", "Port": 3001 }
],
"UpstreamPathTemplate": "/orders/{everything}",
"UpstreamHttpMethod": [ "GET" ]
}
],
"GlobalConfiguration": {
"BaseUrl": "https://api.example.com"
}
}
// Program.cs
// builder.Services.AddOcelot();
// app.UseOcelot().Wait();
85API Gateway + Lambda.# Серверная часть (Lambda + Express):
// lambda.js
const serverless = require("serverless-http");
const express = require("express");
const app = express();
app.get("/users", async (req, res) => {
const users = await User.find();
res.json(users);
});
app.get("/users/:id", async (req, res) => {
const user = await User.findById(req.params.id);
if (!user) return res.status(404).json({ error: "Not found" });
res.json(user);
});
module.exports.handler = serverless(app);
serverless.yml
service: my-api
provider:
name: aws
runtime: nodejs20.x
region: us-east-1
iamRoleStatements:
- Effect: Allow
Action:
- dynamodb:Query
- dynamodb:Scan
Resource: "arn:aws:dynamodb:::table/Users"
functions:
app:
handler: lambda.handler
events:
- httpApi:
path: /{proxy+}
method: ANY
Deploy: sls deploy
86Zuul Gateway.# Spring Cloud Gateway конфигурация:
# application.yml
spring:
cloud:
gateway:
routes:
- id: users-service
uri: lb://users-service
predicates:
- Path=/api/users/**
filters:
- StripPrefix=1
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
- name: CircuitBreaker
args:
name: usersCircuitBreaker
fallbackUri: forward:/fallback/users
- id: orders-service
uri: lb://orders-service
predicates:
- Path=/api/orders/**
- Method=GET,POST
filters:
- StripPrefix=1
- AddRequestHeader=X-Gateway, spring-cloud
- AddResponseHeader=X-Powered-By, Spring Cloud Gateway
default-filters:
- name: Retry
args:
retries: 3
statuses: BAD_GATEWAY
methods: GET
Netflix Zuul (legacy):
@EnableZuulProxy
zuul.routes.users.path=/api/users/**
87Кастомный gateway.# OpenResty — Nginx + Lua для кастомного gateway
worker_processes auto;
events {
worker_connections 1024;
}
http {
Маршрутизация через Lua
init_by_lua_block {
routes = {
{ prefix = "/users", upstream = "users_backend" },
{ prefix = "/orders", upstream = "orders_backend" },
{ prefix = "/payments", upstream = "payments_backend" }
}
}
upstream users_backend { server users:3000; }
upstream orders_backend { server orders:3001; }
upstream payments_backend { server payments:3002; }
server {
listen 80;
# Lua роутинг
location / {
access_by_lua_block {
local token = ngx.var.http_authorization
if not token then
ngx.exit(401)
end
-- JWT верификация
local jwt = require("resty.jwt")
local decoded = jwt:verify("secret", token)
if not decoded.verified then
ngx.exit(403)
end
ngx.ctx.user_id = decoded.payload.sub
}
set $upstream "";
rewrite_by_lua_block {
for _, route in ipairs(routes) do
if ngx.var.uri:find(route.prefix, 1, true) == 1 then
ngx.var.upstream = route.upstream
break
end
end
}
proxy_pass http://$upstream;
}
}
}
88Federation gateway.# Apollo Federation — объединение GraphQL сервисов
gateway.js
const { ApolloGateway } = require("@apollo/gateway");
const { ApolloServer } = require("apollo-server-express");
const gateway = new ApolloGateway({
serviceList: [
{ name: "users", url: "http://users-service:4001/graphql" },
{ name: "posts", url: "http://posts-service:4002/graphql" },
{ name: "comments", url: "http://comments-service:4003/graphql" }
],
buildService({ name, url }) {
const { RemoteGraphQLDataSource } = require("@apollo/gateway");
return new RemoteGraphQLDataSource({
url,
willSendRequest({ request, context }) {
request.http.headers.set("authorization", context.authHeader);
}
});
}
});
const server = new ApolloServer({
gateway,
subscriptions: false,
context: ({ req }) => ({ authHeader: req.headers.authorization })
});
await server.start();
server.applyMiddleware({ app, path: "/graphql" });
Nginx прокси для federation:
location /graphql {
}
Subschema (users):
type User @key(fields: "id") {
id: ID!
name: String!
posts: [Post]
}
extend type Query {
users: [User]
}
89Kong service mesh.# Kong Service Mesh — service mesh на основе Kong
# Работает как sidecar proxy (как Istio, но на Lua)
Установка:
kubectl create namespace kong-mesh
helm install kong-mesh kong/kong-mesh --namespace kong-mesh
Deployment с sidecar:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
annotations:
kuma.io/sidecar-injection: "enabled"
spec:
containers:
- name: myapp
image: myapp:latest
ports:
- containerPort: 3000
TrafficRoute:
apiVersion: kuma.io/v1alpha1
kind: TrafficRoute
metadata:
name: myapp-route
spec:
sources:
- match:
kuma.io/service: "*"
destinations:
- match:
kuma.io/service: myapp_svc_3000
conf:
loadBalancer:
roundRobin: {}
split:
- weight: 90
destination:
kuma.io/service: myapp_svc_3000
version: "1.0"
- weight: 10
destination:
kuma.io/service: myapp_svc_3000
version: "2.0"
90Логирование прокси.# Nginx access log с JSON:
http {
log_format json escape=json
'{"timestamp":"$time_iso8601",'
'"remote_addr":"$remote_addr",'
'"request":"$request",'
'"status":$status,'
'"body_bytes":$body_bytes_sent,'
'"request_time":$request_time,'
'"upstream_addr":"$upstream_addr",'
'"upstream_status":"$upstream_status",'
'"upstream_response_time":"$upstream_response_time",'
'"http_referer":"$http_referer",'
'"http_user_agent":"$http_user_agent",'
'"http_x_forwarded_for":"$http_x_forwarded_for"}';
access_log /var/log/nginx/access.log json;
}
HAProxy JSON log:
global
log 127.0.0.1:514 len 4096 local0 format rfc5424
defaults
log global
option httplog
log-format {"timestamp":"%t","src":"%ci","method":"%ST","uri":"%HU","status":"%ST","backend":"%b","server":"%s"}
Envoy access log (см. entry 74)
Log aggregation:
filebeat, logstash, fluentd
docker logs --tail 100 nginx
journalctl -u nginx -f
91Tracing в прокси.# Envoy + OpenTelemetry:
static_resources:
listeners:
- name: listener_0
address:
socket_address: { address: 0.0.0.0, port_value: 10000 }
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
generate_request_id: true
tracing:
client_sampling: 100
random_sampling: 10
overall_sampling: 5
max_path_tag_length: 256
custom_tags:
- tag: version
literal:
value: "1.0.0"
- tag: environment
environment:
name: ENVIRONMENT
default_value: production
http_filters:
- name: envoy.filters.http.router
tracing:
http:
name: envoy.tracers.opentelemetry
typed_config:
"@type": type.googleapis.com/envoy.config.trace.v3.OpenTelemetryConfig
grpc_service:
envoy_grpc:
cluster_name: opentelemetry_collector
timeout: 0.5s
service_name: envoy-proxy
clusters:
- name: opentelemetry_collector
type: STRICT_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: opentelemetry_collector
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address: { address: otel-collector, port_value: 4317 }
92mTLS глубоко.# Nginx mTLS:
server {
listen 443 ssl;
server_name api.example.com;
Server certificate
ssl_certificate /etc/ssl/certs/server.crt;
ssl_certificate_key /etc/ssl/private/server.key;
Client certificate verification
ssl_client_certificate /etc/ssl/certs/ca.crt;
ssl_verify_client on;
ssl_verify_depth 2;
Optional: извлечь сертификат клиента
ssl_trusted_certificate /etc/ssl/certs/ca.crt;
location / {
if ($ssl_client_verify != SUCCESS) {
return 403;
}
# Передать DN клиента в backend
proxy_set_header X-Client-DN $ssl_client_s_dn;
proxy_set_header X-Client-CN $ssl_client_s_dn_cn;
proxy_set_header X-Client-Verify $ssl_client_verify;
proxy_pass http://backend;
}
}
HAProxy mTLS:
frontend api
bind *:443 ssl crt /etc/ssl/certs/server.pem
ca-file /etc/ssl/certs/ca.crt
verify required
http-request set-header X-SSL $ssl_fc
default_backend app
Istio mTLS (PeerAuthentication):
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
spec:
mtls:
mode: STRICT
93ModSecurity WAF.# Nginx + ModSecurity 3.0
# Установка:
# apt install libmodsecurity3 nginx-mod-modsecurity
server {
listen 80;
modsecurity on;
modsecurity_rules_file /etc/nginx/modsecurity.conf;
location / {
proxy_pass http://backend;
}
}
/etc/nginx/modsecurity.conf
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
OWASP CRS (Core Rule Set)
Include /etc/nginx/owasp-crs/crs-setup.conf
Include /etc/nginx/owasp-crs/rules/*.conf
Custom rules
SecRule ARGS "@contains <script>" "id:10001,phase:2,deny,status:403,msg:'XSS detected'"
SecRule REQUEST_URI "@contains /etc/passwd" "id:10002,phase:1,deny,status:403"
SecRule REQUEST_HEADERS:User-Agent "@pmFromFile blocked_agents.txt" "id:10003,phase:1,deny"
Rate limiting
SecRule IP:rate "@gt 100" "id:10004,phase:1,deny,status:429,msg:'Rate limit exceeded'"
Audit log
SecAuditEngine RelevantOnly
SecAuditLog /var/log/modsec_audit.log
Корреляция
SecRule REQUEST_HEADERS:Content-Type "!@streq application/json" \
"chain,id:10005,phase:1,deny"
SecRule REQUEST_METHOD "POST"
94DDoS защита.# Nginx DDoS защита:
1. Rate limiting
limit_req_zone $binary_remote_addr zone=ddos:50m rate=30r/s;
limit_conn_zone $binary_remote_addr zone=conn:10m;
server {
listen 80;
Per IP rate
limit_req zone=ddos burst=50 nodelay;
Per IP connections
limit_conn conn 10;
Slowloris protection
client_body_timeout 5s;
client_header_timeout 5s;
Buffers
client_body_buffer_size 8k;
client_header_buffer_size 1k;
large_client_header_buffers 2 1k;
Block empty user agents
if ($http_user_agent = "") {
return 403;
}
Challenge
location / {
# Использовать JS challenge или CAPTCHA
proxy_pass http://backend;
}
}
HAProxy DDoS:
frontend web
bind *:80
Connection limiting
stick-table type ip size 1m expire 30s store conn_cur,conn_rate(10s)
tcp-request connection track-sc1 src
tcp-request connection reject if { sc_conn_cur(0) gt 50 }
tcp-request connection reject if { sc_conn_rate(0) gt 500 }
HTTP rate limiting
stick-table type ip size 1m expire 30s store http_req_rate(10s)
http-request track-sc2 src
http-request deny deny_status 429 if { sc_http_req_rate(1) gt 100 }
default_backend app
95GSLB.# GSLB — распределение трафика между дата-центрами
DNS-based GSLB (AWS Route53):
latency-based, geolocation-based, weighted
Nginx + DNS:
upstream global_backend {
zone global 256k;
server us-east.example.com:3000 resolve;
server eu-west.example.com:3000 resolve;
server ap-southeast.example.com:3000 resolve;
}
HAProxy GSLB через DNS:
resolvers dns_resolver
nameserver dns1 8.8.8.8:53
nameserver dns2 1.1.1.1:53
hold valid 10s
hold obsolete 60s
backend global_app
balance roundrobin
server us-east us-east.example.com:3000 resolvers dns_resolver resolve weight=100
server eu-west eu-west.example.com:3000 resolvers dns_resolver resolve weight=80 check
server ap-southeast ap-southeast.example.com:3000 resolvers dns_resolver resolve weight=60 check
Anycast GSLB:
Все дата-центры объявляют один IP через BGP
Маршрутизация до ближайшего по протоколу BGP
GSLB решения:
- AWS Route53 (DNS)
- Azure Traffic Manager (DNS)
- Google Cloud Load Balancing (Anycast)
- Cloudflare (Anycast)
- NS1 (DNS)
96DNS балансировка.# DNS Round Robin — простейшая балансировка
# Несколько A/AAAA записей для одного имени
# example.com IN A 10.0.0.1
# example.com IN A 10.0.0.2
# example.com IN A 10.0.0.3
Nginx resolver:
http {
resolver 8.8.8.8 1.1.1.1 valid=30s ipv6=off;
upstream backend {
zone backend 64k;
server app.example.com:3000 resolve;
}
server {
listen 80;
location / {
proxy_pass http://backend;
}
}
}
HAProxy DNS resolution:
resolvers mydns
nameserver dns1 8.8.8.8:53
nameserver dns2 1.1.1.1:53
hold valid 10s
hold nx 30s
hold timeout 30s
hold refused 30s
hold obsolete 60s
accepted_payload_size 8192
resolve_retries 3
timeout retry 1s
backend app
server app1 app1.example.com:3000 resolvers mydns resolve weight=10
server app2 app2.example.com:3000 resolvers mydns resolve weight=20 check inter 30s
Consul DNS:
resolver consul local=on
server app consul.service.consul:3000 resolve
97GeoDNS настройка.# PowerDNS + GeoIP
# Разные IP для разных регионов
Nginx GeoDNS конфиг:
geo $backend {
default eu-backend;
10.0.0.0/8 us-backend;
ranges;
}
http {
upstream us-backend {
zone us 64k;
server us-api.example.com:3000 resolve;
}
upstream eu-backend {
zone eu 64k;
server eu-api.example.com:3000 resolve;
}
server {
listen 80;
location / {
proxy_pass http://$backend;
}
}
}
AWS Route53 GeoDNS:
- Geolocation routing policy
- Latency routing policy
yaml:
MyRecord:
Type: A
SetIdentifier: "US"
GeoLocation:
ContinentCode: "NA"
TTL: 60
ResourceRecords:
- Value: "203.0.113.10"
---
MyRecord:
Type: A
SetIdentifier: "EU"
GeoLocation:
ContinentCode: "EU"
TTL: 60
ResourceRecords:
- Value: "198.51.100.20"
98Anycast маршрутизация.# Anycast — один IP, много серверов, BGP маршрутизация
BGP конфигурация (FRR/Bird):
router bgp 65001
network 203.0.113.0/24
neighbor 10.0.0.1 remote-as 65000
Nginx на Anycast ноде:
http {
Локальная привязка к anycast IP
server {
listen 203.0.113.1:80;
server_name example.com;
location / {
proxy_pass http://backend;
}
}
}
Health check для BGP:
Если Nginx упал, BGP должен withdraw объявление
Используем ExaBGP или gobgp:
Anycast vs DNS:
Anycast: автоматическая маршрутизация, быстрый failover
DNS: медленное обновление (TTL), простота
Пример: Cloudflare использует Anycast
Один IP 1.1.1.1 работает во всём мире
Проверка anycast:
traceroute 1.1.1.1
whois 1.1.1.1 | grep -i origin
mtr 1.1.1.1
99Бенчмаркинг прокси.# wrk — HTTP benchmark tool
wrk -t12 -c400 -d30s http://localhost:80/api/users
wrk -t4 -c100 -d60s --latency http://localhost:80
hey (альтернатива wrk)
hey -n 100000 -c 200 http://localhost:80/api/users
hey -n 50000 -c 100 -m POST -H "Content-Type: application/json" -d '{"key":"val"}' http://localhost:80/api
h2load (HTTP/2)
h2load -n100000 -c100 -m10 http://localhost:80
Тестирование прокси:
wrk напрямую к бэкенду:
wrk -t4 -c100 -d30s http://backend:3000
wrk через прокси:
wrk -t4 -c100 -d30s http://proxy:80
Сравнение:
Прокси добавляет latency:
Nginx: ~1-5ms
HAProxy: ~0.5-2ms
Envoy: ~2-10ms
Kong: ~5-20ms
Инструменты:
wrk, hey, h2load, autocannon (Node), k6, vegeta
vegeta attack -targets=targets.txt -rate=1000 -duration=30s | vegeta report -type=text
100Синтетический мониторинг.# Synthetic monitoring — искусственные проверки из разных точек
1. curl-based health check
#!/bin/bash
/etc/monitoring/check_proxy.sh
for endpoint in /api/users /api/health /static/index.html; do
status=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 http://proxy/$endpoint)
if [ "$status" != "200" ] && [ "$status" != "301" ]; then
echo "FAIL: $endpoint returned $status"
exit 2
fi
echo "OK: $endpoint -> $status"
done
2. Multi-step transaction test
curl -c /tmp/cookies.txt -b /tmp/cookies.txt http://proxy/login -d "user=test&pass=test"
curl -c /tmp/cookies.txt -b /tmp/cookies.txt http://proxy/dashboard
3. Using checklyhq.com or similar:
- HTTP checks every 5 minutes
- Multi-step browser checks
- API checks with assertions
- Alerting via Slack/PagerDuty
4. Prometheus blackbox exporter:
modules:
http_2xx:
prober: http
http:
valid_http_versions: ["HTTP/1.1", "HTTP/2"]
valid_status_codes: [200, 301, 302]
fail_if_ssl: false
preferred_ip_protocol: "ip4"
5. Grafana Synthetics:
k6 based synthetic monitoring
import http from "k6/http";
import { check } from "k6";
export default function () {
check(res, { "status is 200": (r) => r.status === 200 });
}
Лицензия
MIT
72Ext Authz.actions:remote_address: {}limit:
requests_per_unit: 100
unit: SECOND