Introduction : Pourquoi le Rate Limiting est Crucial pour vos API IA
Dans l'écosystème des API d'intelligence artificielle, la gestion du trafic constitue un enjeu stratégique majeur. Les fournisseurs comme OpenAI, Anthropic ou Google appliquent des limites strictes par plan tarifaire, et chaque requête excédentaire peut générer des erreurs coûteuses ou des interruptions de service. Face à ce défi, les développeurs recherchents des solutions robustes pour protéger leurs infrastructures tout en optimisant les coûts.
Découvrez comment HolySheep AI révolutionne l'accès aux API IA avec une infrastructure optimisée et des tarifs compétitifs.
Tableau Comparatif : HolySheep vs API Officielles vs Services Relais
| Critère | HolySheep AI | API Officielles (OpenAI/Anthropic) | Services Relais Classiques |
|---|---|---|---|
| Latence moyenne | <50ms 🏆 | 150-300ms | 80-200ms |
| GPT-4.1 ($/1M tokens) | $8.00 | $60.00 | $15-25 |
| Claude Sonnet 4.5 ($/1M tokens) | $15.00 | $45.00 | $25-40 |
| DeepSeek V3.2 ($/1M tokens) | $0.42 🏆 | N/A | $0.80-1.50 |
| Mode de paiement | WeChat, Alipay, Carte 💳 | Carte internationale uniquement | Variable |
| Économie vs officiel | 85%+ | Référence | 40-60% |
| Crédits gratuits | ✅ Inclus | Limité | Rare |
| Rate Limiting intégré | ✅ Configurable | ⚠️ Fixe par plan | Variable |
Comprendre le Rate Limiting pour les API IA
Concepts Fondamentaux
Le rate limiting est un mécanisme qui contrôle le nombre de requêtes qu'un client peut effectuer dans un laps de temps donné. Pour les API IA, cela revêt une importance particulière car les modèles de langage sont gourmands en ressources et les coûts peuvent exploser rapidement.
Les stratégies principales incluent :
- Token Bucket : Autorise un burst de requêtes puis régule le flux
- Leaky Bucket : Traite les requêtes à un rythme constant
- Fixed Window : Compteur par intervalle de temps fixe
- Sliding Window : Comptage continu avec fenêtre glissante
Architecture Nginx Lua pour le Contrôle de Trafic
Installation de OpenResty
Pour implémenter un rate limiting sophistiqué avec Lua, nous utilisons OpenResty, une plateforme qui étend Nginx avec des capacités Lua avancées.
# Installation sur Ubuntu/Debian
sudo apt-get update
sudo apt-get install -y build-essential libpcre3 libpcre3-dev \
libssl-dev unzip zlib1g-dev
Télécharger OpenResty
wget https://openresty.org/download/openresty-1.21.4.2.tar.gz
tar -xzf openresty-1.21.4.2.tar.gz
cd openresty-1.21.4.2
Configurer et compiler
./configure --with-luajit \
--with-http_ssl_module \
--with-http_v2_module \
--with-stream_ssl_module
make -j$(nproc)
sudo make install
Ajouter au PATH
export PATH=/usr/local/openresty/nginx/sbin:$PATH
Configuration du Rate Limiter avec Lua
-- /etc/openresty/rate_limiter.lua
-- Module de rate limiting token bucket pour API IA
local redis = require "resty.redis"
local cjson = require "cjson"
local _M = {}
-- Configuration par défaut
local DEFAULT_CONFIG = {
max_tokens = 100, -- Bucket size (requêtes max simultanées)
refill_rate = 10, -- Tokens ajoutés par seconde
window = 60, -- Fenêtre en secondes
block_duration = 300, -- Durée de blocage (seconds)
}
-- Créer un nouveau rate limiter
function _M.new(config)
local self = setmetatable({}, { __index = _M })
self.config = vim.tbl_extend("force", DEFAULT_CONFIG, config or {})
self.redis = redis:new()
self.redis:set_timeout(1000) -- 1 second timeout
return self
end
-- Connexion à Redis
function _M:connect()
local ok, err = self.redis:connect("127.0.0.1", 6379)
if not ok then
return nil, "Impossible de se connecter à Redis: " .. err
end
return true
end
-- Obtenir la clé Redis pour un client
local function get_key(client_id, prefix)
return string.format("ratelimit:%s:%s", prefix, client_id)
end
-- Vérifier et mettre à jour le bucket
function _M:check(client_id, tokens_needed)
tokens_needed = tokens_needed or 1
local now = ngx.now()
local key = get_key(client_id, "bucket")
local block_key = get_key(client_id, "blocked")
-- Vérifier si bloqué
local blocked, err = self.redis:get(block_key)
if blocked == "1" then
return false, "Trop de requêtes. Réessayez dans quelques minutes."
end
-- Obtenir l'état actuel du bucket
local bucket, err = self.redis:hgetall(key)
local tokens = self.config.max_tokens
local last_refill = now
if bucket and #bucket > 0 then
-- Parser les valeurs Redis hash
for i = 1, #bucket, 2 do
if bucket[i] == "tokens" then
tokens = tonumber(bucket[i+1])
elseif bucket[i] == "last_refill" then
last_refill = tonumber(bucket[i+1])
end
end
-- Calculer le refill depuis la dernière requête
local elapsed = now - last_refill
local refill_amount = elapsed * self.config.refill_rate
tokens = math.min(self.config.max_tokens, tokens + refill_amount)
end
-- Vérifier si assez de tokens
if tokens >= tokens_needed then
tokens = tokens - tokens_needed
-- Mettre à jour le bucket atomiquement
self.redis:multi()
self.redis:hset(key, "tokens", tostring(tokens))
self.redis:hset(key, "last_refill", tostring(now))
self.redis:expire(key, self.config.window * 2)
local results, err = self.redis:exec()
if err then
ngx.log(ngx.ERR, "Redis error: ", err)
return true -- Allow on error to avoid blocking legitimate requests
end
return true, tokens
else
-- Incrémenter compteur de tentatives échouées
local fail_key = get_key(client_id, "failures")
local failures = self.redis:incr(fail_key)
self.redis:expire(fail_key, self.config.window)
-- Bloquer si trop d'échecs
if failures >= 5 then
self.redis:setex(block_key, self.config.block_duration, "1")
self.redis:del(fail_key)
ngx.log(ngx.WARN, "Client ", client_id, " temporairement bloqué")
end
return false, string.format(
"Rate limit dépassé. Tokens disponibles: %.2f/%d",
tokens, self.config.max_tokens
)
end
end
-- Fermer la connexion Redis
function _M:close()
self.redis:close()
end
return _M
Configuration Nginx avec Intégration API IA
# /etc/openresty/nginx.conf
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
use epoll;
}
http {
include /etc/nginx/mime.types;
default_type application/json;
# Zone de mémoire partagée pour rate limiting
lua_shared_dict ratelimit 10m;
# Init OpenResty
init_by_lua_block {
require "resty.core"
package.path = "/etc/openresty/?.lua;/usr/local/openresty/?.lua;;"
}
# Logging personnalisé
log_format ai_access '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'rt=$request_time uct="$upstream_connect_time" '
'uht="$upstream_header_time" upstream_addr=$upstream_addr';
access_log /var/log/nginx/access.log ai_access;
server {
listen 8080;
server_name _;
# Headers de sécurité
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
location /ai/proxy {
internal;
# Configuration HolySheep API
set $api_base "https://api.holysheep.ai/v1";
set $api_key "YOUR_HOLYSHEEP_API_KEY";
# Headers pour le proxy
proxy_method POST;
proxy_pass_request_body on;
proxy_pass $api_base$upstream_path;
# Headers upstream
proxy_set_header Host "api.holysheep.ai";
proxy_set_header Authorization "Bearer $api_key";
proxy_set_header Content-Type "application/json";
proxy_set_header "OpenAI-Organization" "";
# Timeouts
proxy_connect_timeout 10s;
proxy_send_timeout 60s;
proxy_read_timeout 120s;
# Buffering
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
}
location /v1/chat/completions {
access_by_lua_block {
local ratelimit = require "rate_limiter"
local limiter = ratelimit:new({
max_tokens = 50,
refill_rate = 5,
window = 60,
block_duration = 600
})
local ok, err = limiter:connect()
if not ok then
ngx.log(ngx.ERR, "Redis connection failed: ", err)
-- Allow request if Redis is down
else
-- Utiliser IP + API key comme identifiant client
local client_id = ngx.var.remote_addr
local api_key = ngx.var.http_authorization
if api_key then
client_id = client_id .. ":" .. api_key
end
local allowed, msg = limiter:check(client_id, 1)
if not allowed then
ngx.status = 429
ngx.say(cjson.encode({
error = {
message = msg,
type = "rate_limit_exceeded",
code = 429
}
}))
return ngx.exit(429)
end
limiter:close()
end
}
# Transformer la requête pour HolySheep
rewrite_by_lua_block {
local cjson = require "cjson"
local args = ngx.req.get_body_data()
if args then
local body = cjson.decode(args)
-- Store original body for proxy
ngx.ctx.original_body = args
-- Optionally modify model name
if body.model then
ngx.var.upstream_path = "/chat/completions"
end
end
}
proxy_pass http://127.0.0.1:8080/ai/proxy;
}
location /v1/models {
proxy_pass https://api.holysheep.ai/v1/models;
proxy_set_header Host api.holysheep.ai;
proxy_set_header Authorization "Bearer YOUR_HOLYSHEEP_API_KEY";
}
# Endpoint de santé
location /health {
content_by_lua_block {
ngx.say(cjson.encode({
status = "healthy",
timestamp = ngx.now(),
redis = "connected"
}))
}
}
# Statistiques rate limiting
location /stats {
access_by_lua_block {
-- Allow only from localhost
if ngx.var.remote_addr ~= "127.0.0.1" then
ngx.exit(ngx.HTTP_FORBIDDEN)
end
}
content_by_lua_block {
local ratelimit = require "rate_limiter"
local redis = require "resty.redis"
local cjson = require "cjson"
local r = redis:new()
r:set_timeout(1000)
local ok, err = r:connect("127.0.0.1", 6379)
if not ok then
ngx.say(cjson.encode({error = "Redis connection failed"}))
return
end
-- Get rate limit stats
local keys = r:keys("ratelimit:*")
local stats = {
active_clients = 0,
blocked_clients = 0,
total_keys = #keys
}
for _, key in ipairs(keys) do
if string.find(key, "blocked") then
stats.blocked_clients = stats.blocked_clients + 1
else
stats.active_clients = stats.active_clients + 1
end
end
r:close()
ngx.say(cjson.encode(stats))
}
}
}
# Stream pour WebSockets (si nécessaire)
stream {
lua_ssl_verify_depth 2;
server {
listen 8443 ssl;
ssl_certificate /etc/ssl/certs/server.crt;
ssl_certificate_key /etc/ssl/private/server.key;
ssl_protocols TLSv1.2 TLSv1.3;
content_by_lua_block {
ngx.say("AI Gateway Stream Endpoint")
}
}
}
}
Exemple d'Intégration Complète côté Client
#!/usr/bin/env python3
"""
Client Python pour l'API Gateway HolySheep avec Rate Limiting local
Compatible avec OpenAI SDK
"""
import os
import time
import logging
from typing import Optional, Dict, Any
from dataclasses import dataclass
from threading import Lock
from collections import deque
try:
import openai
OPENAI_SDK_AVAILABLE = True
except ImportError:
OPENAI_SDK_AVAILABLE = False
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
@dataclass
class RateLimitConfig:
"""Configuration du rate limiter"""
max_requests_per_minute: int = 60
max_tokens_per_minute: int = 100000
max_retries: int = 3
retry_delay: float = 2.0
backoff_factor: float = 1.5
class TokenBucket:
"""Implémentation Token Bucket pour rate limiting local"""
def __init__(self, rate: float, capacity: float):
self.rate = rate
self.capacity = capacity
self.tokens = capacity
self.last_update = time.time()
self.lock = Lock()
def consume(self, tokens: float = 1.0) -> bool:
with self.lock:
now = time.time()
elapsed = now - self.last_update
self.tokens = min(self.capacity, self.tokens + elapsed * self.rate)
self.last_update = now
if self.tokens >= tokens:
self.tokens -= tokens
return True
return False
def wait_time(self, tokens: float = 1.0) -> float:
with self.lock:
if self.tokens >= tokens:
return 0.0
return (tokens - self.tokens) / self.rate
class HolySheepAIClient:
"""
Client optimisé pour HolySheep AI avec rate limiting intelligent
"""
BASE_URL = "https://api.holysheep.ai/v1"
def __init__(
self,
api_key: str,
rate_limit_config: Optional[RateLimitConfig] = None,
max_tokens_config: Optional[RateLimitConfig] = None,
):
self.api_key = api_key
self.rate_limiter = TokenBucket(
rate=rate_limit_config.max_requests_per_minute / 60.0,
capacity=rate_limit_config.max_requests_per_minute or 60
)
self.token_limiter = TokenBucket(
rate=max_tokens_config.max_tokens_per_minute / 60.0,
capacity=max_tokens_config.max_tokens_per_minute or 100000
)
self.rate_config = rate_limit_config or RateLimitConfig()
self.request_history = deque(maxlen=1000)
if OPENAI_SDK_AVAILABLE:
self.client = openai.OpenAI(
api_key=self.api_key,
base_url=self.BASE_URL,
timeout=120.0,
max_retries=0 # Géré manuellement
)
else:
self.client = None
logger.warning("OpenAI SDK non installé. Utilisation mode manuel.")
def _estimate_tokens(self, messages: list) -> int:
"""Estimation grossière des tokens"""
total = 0
for msg in messages:
total += len(str(msg)) // 4 # Approximation
return max(total, 10)
def _wait_for_capacity(self, estimated_tokens: int):
"""Attendre que la capacité soit disponible"""
wait_time = max(
self.rate_limiter.wait_time(1),
self.token_limiter.wait_time(estimated_tokens)
)
if wait_time > 0:
logger.info(f"Rate limit atteint, attente de {wait_time:.2f}s")
time.sleep(wait_time)
def chat_completions(
self,
model: str,
messages: list,
temperature: float = 0.7,
max_tokens: Optional[int] = None,
**kwargs
) -> Dict[str, Any]:
"""
Créer une complétion de chat avec gestion intelligente du rate limiting
"""
estimated_tokens = self._estimate_tokens(messages) + (max_tokens or 500)
retry_count = 0
while retry_count < self.rate_config.max_retries:
# Vérifier la capacité
self._wait_for_capacity(estimated_tokens)
try:
if OPENAI_SDK_AVAILABLE and self.client:
response = self.client.chat.completions.create(
model=model,
messages=messages,
temperature=temperature,
max_tokens=max_tokens,
**kwargs
)
else:
# Mode manuel avec requests
import requests
response = requests.post(
f"{self.BASE_URL}/chat/completions",
headers={
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json"
},
json={
"model": model,
"messages": messages,
"temperature": temperature,
"max_tokens": max_tokens,
**kwargs
},
timeout=120
)
response.raise_for_status()
response = response.json()
# Enregistrer la requête réussie
self.request_history.append({
"timestamp": time.time(),
"model": model,
"success": True
})
return response
except Exception as e:
error_str = str(e)
retry_count += 1
if "rate_limit" in error_str.lower() or "429" in error_str:
wait_time = self.rate_config.retry_delay * (
self.rate_config.backoff_factor ** retry_count
)
logger.warning(f"Rate limit API, tentative {retry_count}, "
f"attente {wait_time:.1f}s")
time.sleep(wait_time)
elif "context_length" in error_str.lower():
raise ValueError(f"Context length exceeded: {e}")
else:
if retry_count >= self.rate_config.max_retries:
raise
time.sleep(self.rate_config.retry_delay)
raise RuntimeError(f"Échec après {self.rate_config.max_retries} tentatives")
def get_stats(self) -> Dict[str, Any]:
"""Obtenir les statistiques d'utilisation"""
return {
"rate_limiter_tokens": self.rate_limiter.tokens,
"token_limiter_tokens": self.token_limiter.tokens,
"requests_last_hour": len([
r for r in self.request_history
if time.time() - r["timestamp"] < 3600
]),
"success_rate": sum(
1 for r in self.request_history if r.get("success")
) / max(len(self.request_history), 1) * 100
}
Exemple d'utilisation
if __name__ == "__main__":
client = HolySheepAIClient(
api_key="YOUR_HOLYSHEEP_API_KEY",
rate_limit_config=RateLimitConfig(
max_requests_per_minute=60,
max_tokens_per_minute=100000
)
)
# Test avec GPT-4.1
response = client.chat_completions(
model="gpt-4.1",
messages=[
{"role": "system", "content": "Tu es un assistant utile."},
{"role": "user", "content": "Explique le rate limiting en 2 phrases."}
],
max_tokens=100
)
print(f"Réponse: {response.choices[0].message.content}")
print(f"Stats: {client.get_stats()}")
Monitoring et Analytics
Pour superviser efficacement votre gateway, nous recommandons l'utilisation de Prometheus et Grafana avec les métriques suivantes :
# /etc/openresty/prometheus.lua
local _M = {}
local metrics = {
requests_total = ngx.shared.ratelimit,
request_duration = {},
tokens_used = {},
rate_limit_hits = {},
}
function _M.record_request(client_id, duration, tokens)
local c = ngx.shared.ratelimit
c:incr("requests:total", 1, 0)
c:incr("requests:" .. client_id, 1, 0)
if duration then
local key = "duration:" .. client_id
c:lpush(key, duration)
c:ltrim(key, 0, 999) -- Garder les 1000 dernières
end
end
function _M.record_rate_limit(client_id)
local c = ngx.shared.ratelimit
c:incr("ratelimit:hits", 1, 0)
end
function _M.get_metrics()
local c = ngx.shared.ratelimit
local status = c:get_status()
return {
requests_total = status["requests:total"] or 0,
rate_limit_hits = status["ratelimit:hits"] or 0,
active_clients = c:get("active_clients") or 0,
}
end
return _M
Erreurs Courantes et Solutions
Erreur 1 : "Connection refused" vers Redis
Symptôme : Toutes les requêtes échouent avec l'erreur "Connection refused" même après le démarrage de Redis.
Causes possibles :
- Redis écoute sur une interface incorrecte
- Le pare-feu bloque le port 6379
- Problème de configuration du bind address
Solution :
# Vérifier la configuration Redis
cat /etc/redis/redis.conf | grep bind
Généralement, remplacer :
bind 127.0.0.1
par :
bind 127.0.0.1 ::1
Redémarrer Redis
sudo systemctl restart redis
Tester la connexion
redis-cli ping
Doit retourner PONG
Si le pare-feu bloque, ouvrir le port :
sudo ufw allow 6379/tcp
Erreur 2 : "429 Too Many Requests" malgré le rate limiter
Symptôme : Les clients reçoivent des erreurs 429 de la part de l'API HolySheep même avec un rate limiter local.
Causes possibles :
- Plusieurs instances du rate limiter non synchronisées
- Compteur mal initialisé dans Redis
- Race condition lors de la mise à jour atomique
Solution :
-- Script Lua corrigé pour éviter les race conditions
-- Utiliser une transaction Lua atomique
local redis = require "resty.redis"
local red = redis:new()
red:set_timeout(1000)
local function atomic_check_and_consume(key, max_tokens, needed)
-- Script Lua atomique exécuté côté Redis
local script = [[
local key = KEYS[1]
local max_tokens = tonumber(ARGV[1])
local needed = tonumber(ARGV[2])
local now = tonumber(ARGV[3])
local refill_rate = tonumber(ARGV[4])
local window = tonumber(ARGV[5])
-- Récupérer l'état actuel
local data = redis.call('HMGET', key, 'tokens', 'last_refill')
local tokens = tonumber(data[1]) or max_tokens
local last_refill = tonumber(data[2]) or now
-- Refill tokens
local elapsed = now - last_refill
tokens = math.min(max_tokens, tokens + (elapsed * refill_rate))
-- Vérifier et consommer
if tokens >= needed then
tokens = tokens - needed
redis.call('HMSET', key, 'tokens', tokens, 'last_refill', now)
redis.call('EXPIRE', key, window * 2)
return {1, tokens} -- Autorisé
else
redis.call('HMSET', key, 'tokens', tokens, 'last_refill', now)
redis.call('EXPIRE', key, window * 2)
return {0, tokens} -- Refusé
end
]]
return red:eval(script, 1, key,
tostring(max_tokens),
tostring(needed),
tostring(ngx.now()),
tostring(refill_rate),
tostring(window)
)
end
-- Utilisation
local key = "ratelimit:bucket:" .. client_id
local result = atomic_check_and_consume(key, 100, 1)
if result[1] == 0 then
ngx.log(ngx.WARN, "Rate limit atteint pour ", client_id)
end
Erreur 3 : Latence excessive (>200ms) sur les requêtes
Symptôme : Le gateway ajoute plus de 200ms de latence à chaque requête, ruinant les performances.
Causes possibles :
- Timeout Redis trop long
- Connexion Redis non réutilisée (connection pool)
- Logs trop verbeux en phase de production
- Synchronous I/O dans Lua
Solution :
# Configuration Nginx optimisée pour la performance
http {
# Pool de connexions Redis
lua_socket_pool_size 100;
lua_socket_connect_timeout 100ms; # Reduit de 1000ms
lua_socket_send_timeout 100ms;
lua_socket_read_timeout 100ms;
# Buffering désactivé pour réduire la latence
proxy_buffering off;
# Cache de code Lua
lua_code_cache on; # Désactiver uniquement pour dev
# Logging optimisé
access_log off; # Désactiver en prod si nécessaire
# ou utiliser un buffer de log
access_log /var/log/nginx/access.log buffer=16k flush=2s;
server {
# Location optimisée
location /v1/chat/completions {
# Vérification de rate limit avec cache mémoire
set $limit_key "rl:$remote_addr";
# Accès mémoire partagée (très rapide)
set $current_count ngx.shared.ratelimit:get($limit_key);
# Log conditionnel
if ($request_time > 0.1) {
add_header X-Slow-Request "true";
}
# ... reste de la config
}
}
}
Erreur 4 : "Invalid API Key" après migration vers HolySheep
Symptôme : Les requêtes échouent avec une erreur d'authentification après avoir migré vers HolySheep.
Cause : L'URL de base n'a pas été mise à jour ou l'API key n'est pas valide.
Solution :
# Vérification de la configuration HolySheep
1. S'assurer que l'URL est correcte (TOUJOURS api.holysheep.ai)
INCORRECT : https://api.openai.com/v1
CORRECT : https://api.holysheep.ai/v1
2. Vérifier votre clé API sur le dashboard HolySheep
https://www.holysheep.ai/dashboard
3. Tester avec curl
curl -X POST https://api.holysheep.ai/v1/chat/completions \
-H "Authorization: Bearer YOUR_HOLYSHEEP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-4.1",
"messages": [{"role": "user", "content": "test"}],
"max_tokens": 10
}'
4. Si l'erreur persiste, générez une nouvelle clé
Dashboard > API Keys > Generate New Key
5. Mettre à jour la configuration
/etc/openresty/nginx.conf
set $api_key "VOTRE_NOUVELLE_CLE_HOLYSHEEP";
set $api_base "https://api.holysheep.ai/v1";
Pour qui / Pour qui ce n'est pas fait
✅ Ce tutoriel est fait pour vous si :
- Vous gérez une application avec plusieurs utilisateurs共用 une infrastructure API IA
- Vous avez besoin de contrôler précisément les coûts liés aux tokens
- Votre architecture nécessite une latence <100ms pour les appels API
- Vous souhaitez centraliser la gestion des clés API pour votre équipe
- Vous utilisez plusieurs fournisseurs (OpenAI, Anthropic, Google) et voulez un point d'entrée unique
- Vous avez besoin de conformité et de traçabilité pour les audits
❌ Ce tutoriel n'est pas fait pour vous si :
- Vous êtes un développeur solo avec un usage très limité (<100 requêtes/jour)
- Vous n'avez pas accès à un serveur dédié ou VPS pour installer OpenResty
- Vous préférez utiliser des solutions SaaS gérées sans maintenance
- Votre application n'a pas besoin de contrôle de trafic (prototypes, tests)
- Vous n'avez pas les compétences DevOps pour maintenir une infrastructure Nginx