Intégration des rails
bext se trouve devant une application Rubis sur Rails en tant que couche périphérique. Rails conserve l'intégralité de l'expérience du framework - contrôleurs, vues, ActiveRecord, générateurs, tests - et bext ajoute les problèmes de production que chaque équipe Rails doit créer ou intégrer : terminaison TLS, HTTP/2 et HTTP/3, un pare-feu d'application Web, un cache périphérique, Prometheus + OpenTelemetry et une absorption DDoS.
Il ne s'agit pas d'un adaptateur au sens Astro / SvelteKit. Rails est un framework côté serveur complet qui fournit sa propre couche HTTP via Puma ; bext s'intègre par proxy inverse à Puma plutôt qu'en encapsulant le pipeline de rendu de Rails. Le démarreur ci-dessous est un échafaudage – un exemple fonctionnel de la forme, pas une application Rails exécutable. Vous apportez Rails ; bext le fait face.
Quand l’utiliser. Si votre équipe possède déjà un Rails application et vous souhaitez mettre bext devant pour le bord préoccupations (TLS, cache, WAF, métriques), c'est l'intégration pour toi. Si vous souhaitez créer une nouvelle application SSR non-Rails sur bext, utilisez PRISM ou l'un des framework JS adaptateurs à la place.
Architecture#
┌─────────┐ ┌───────────────────────────┐ ┌─────────────┐
│ │ HTTPS │ bext │ HTTP │ Puma │
│ client │ ───────► │ TLS • WAF • cache • obs │ ───────► │ :3000 │
│ │ :443 │ :8080 │ 127.0.0.1│ + Rails │
└─────────┘ └───────────────────────────┘ └─────────────┘
Parcours d’une requête :
- Le client ouvre une connexion TLS à bext sur
:443(ou:8080en développement). - bext termine TLS, exécute le WAF, vérifie le cache, enregistre
métriques, puis transmet la demande à Puma via HTTP/1.1 sur
127.0.0.1:3000via le pool en amont. - Rails traite la demande. La réponse revient à travers bext, qui applique la compression et la journalisation, puis au client via HTTP/2 ou HTTP/3.
Puma ne voit jamais l'Internet public. Rails n'expédie jamais de code TLS. bext n’analyse jamais ActionDispatch.
bext.config.toml#
La configuration du projet de démarrage, réduite à l’essentiel :
[server]
port = 8080
# Upstream: Puma running Rails
[upstreams.rails]
strategy = "round_robin"
keepalive = 32
[[upstreams.rails.servers]]
url = "http://127.0.0.1:3000"
[upstreams.rails.health]
enabled = true
path = "/up" # Rails 7.1+ ships this endpoint
interval_ms = 10000
[upstreams.rails.retry]
count = 1
retry_on = ["connect_error", "502", "503"]
[[route_rules]]
pattern = "/*"
proxy = "rails"
[waf]
enabled = true
Les parties importantes :
[upstreams.rails]déclare le pool. Pour un seul Puma processus, une entrée[[upstreams.rails.servers]]suffit. Pour plusieurs instances Puma (déploiements sans temps d'arrêt, bleu/vert), ajoutez plus d'entrées et des round robins bext entre elles.[upstreams.rails.health]exécute une sonde en arrière-plan sur Point de terminaison/upintégré de Rails. Un Puma mort est expulsé de la piscine en quelques secondes.[upstreams.rails.retry]réessaye un ensemble limité d'idempotents échecs (erreurs de connexion, 502/503) une fois, avec un délai de 100 ms. Valeurs par défaut sûres : remplacez chaque projet si nécessaire.[[route_rules]]envoie chaque chemin vers lerailsen amont. Ajoutez des règles plus spécifiques au-dessus de celle-ci pour créer des chemins d'actifs statiques (par exemple,/assets/*servi directement à partir de bext ) ou des itinéraires natifs bext qui contournent complètement Rails.
Exécuter Puma sous systemd#
bext s'attend à ce que Puma soit opérationnel avant de commencer le transfert. En production, exécutez Puma en tant qu'unité systemd pour que le système d'exploitation le redémarre en cas de crash :
# /etc/systemd/system/rails-app.service
[Unit]
Description=Puma for Rails app
After=network.target
[Service]
Type=simple
User=deploy
Group=deploy
WorkingDirectory=/srv/app/current
Environment=RAILS_ENV=production
ExecStart=/usr/bin/env bundle exec puma -C config/puma.rb -p 3000
Restart=on-failure
RestartSec=2
StandardOutput=append:/var/log/rails-app.log
StandardError=append:/var/log/rails-app.log
[Install]
WantedBy=multi-user.target
Alors:
sudo systemctl enable --now rails-app
sudo systemctl enable --now bext
L'ordre de redémarrage n'a pas d'importance dans la pratique : la sonde de santé en amont de bext trouvera Puma dès qu'elle apparaîtra.
Ce que bext ajoute#
| Préoccupation | Qui s'en occupe |
|---|---|
| TLS / ACME | bext |
| HTTP/2, HTTP/3 (QUIC) | bext |
| WAF (bots, limite de débit) | bext |
| Cache périphérique (ISR/SWR) | bext |
| Compression (brotli) | bext |
| Métriques Prometheus | bext |
| Traces OpenTelemetry | bext |
| Service d'actifs statiques | Soit (voir ci-dessous) |
| Logique métier | Rails |
| ActiveRecord/DB | Rails |
| Travaux en arrière-plan | Rails (Sidekiq, Solid Queue, etc.) |
| Pipeline d’actifs | Rails / Arbre de transmission / Pignons |
Actifs statiques#
Par défaut, le démarreur transmet tout à Puma, donc celui de Rails
Le répertoire public/ est servi par Rails. Pour de meilleures performances de pointe, servez public/ directement depuis bext :
[[route_rules]]
pattern = "/assets/*"
render = "static"
# bext will look for files under `public/assets/` relative to app_dir.
[[route_rules]]
pattern = "/packs/*" # or /packs-test/* if using webpacker
render = "static"
[[route_rules]]
pattern = "/*"
proxy = "rails"
Avec ces règles, bext sert des ensembles d'actifs hachés avec des en-têtes de cache immuables ; Puma ne voit que le trafic dynamique.
Invalidation du cache#
Le cache périphérique de bext est désactivé par défaut pour les routes proxy - les réponses Rails définissent déjà les en-têtes Cache-Control appropriés et bext les respecte. Pour mettre en cache une réponse Rails en périphérie, définissez un
Cache-Control: public, max-age=... dans le contrôleur et ajoutez une entrée [[route_rules]] correspondante avec cache = true.
Pour invalider le cache depuis Rails, appelez l'API de purge basée sur les balises de bext :
# app/services/bext_cache.rb
require "net/http"
require "json"
class BextCache
def self.invalidate(tag)
uri = URI("http://127.0.0.1:8080/__bext/cache/purge")
Net::HTTP.post(uri, { keys: [tag] }.to_json, "Content-Type" => "application/json")
end
end
Désormais, BextCache.invalidate("products") efface chaque entrée de cache marquée products sur le périmètre bext.
Détection#
Le scanner importation infrarouge de bext reconnaît un projet Rails à partir de :
- un
Gemfilequi déclaregem "rails", et - un fichier
config.ru
Lorsque les deux sont présents, bext infra import suggère le modèle d'intégration Rails et propose d'échafauder à partir de @bext/starter-rails.
L'entrée de catalogue réside dans crates/bext-import/catalog/frameworks.yaml :
- match:
any_of_files:
- "Gemfile"
- "config.ru"
gemfile:
any_of_gems:
- "rails"
extract:
framework: rails
starter: "@bext/starter-rails"
confidence: high
Le démarreur#
bext new my-rails-app --template @bext/starter-rails
cd my-rails-app
bundle install
bin/rails server -p 3000 # one terminal
bext serve # another
Visitez http://localhost:8080.
Le démarreur est , un échafaudage, pas une application Rails fonctionnelle. Il est livré avec la forme minimale dont bext a besoin : un Gemfile, un config.ru, un
config/routes.rb, un petit contrôleur home#index et une vue ERB, ainsi que la plomberie bext.config.toml câblée. Utilisez rails new pour générer un véritable projet Rails et supprimez le démarreur
bext.config.toml dedans - c'est le flux de travail canonique.
Si votre équipe migre depuis une installation nginx + Puma existante, consultez À partir de nginx : le mode de compatibilité nginx de bext peut lire directement votre nginx.conf existant, ce qui permet une première migration sans réécriture.
Comparaison avec nginx + Puma#
Si vous utilisez actuellement nginx devant Puma, la migration est mécanique :
nginx.conf bext.config.toml
───────── ────────────────
upstream rails { server … } ───► [upstreams.rails.servers] url = …
proxy_pass http://rails; ───► [[route_rules]] proxy = "rails"
listen 443 ssl; ───► [tls] (auto ACME)
ssl_certificate …; ───► [tls.acme] domains = […]
limit_req_zone …; ───► [waf.rate_limit]
Le Mode de compatibilité nginx de bext peut même lire votre nginx.conf existant et l’exécuter directement : aucune migration nécessaire pour cette première étape.
Limites#
- Les extensions bext ne peuvent pas encore modifier les requêtes. Les extensions bext
peuvent s’exécuter lors des requêtes et des réponses, mais le chemin de proxy inverse n’expose pas encore toutes les possibilités offertes aux routes SSR. L’intégration des extensions aux routes passant par le proxy arrivera avec la
fonctionnalité
Routerdans E11. - Pas de multiplexage WebSocket. bext transmet actuellement WebSockets au
serveur en amont, mais ne partage pas encore de pool de connexions pour ces échanges. Les applications Action Cable à forte diffusion devraient suivre les prochains travaux sur
bext-realtime. - Pas d’environnement d’exécution Ruby intégré. Contrairement à l’intégration PHP qui embarque libphp via une SAPI personnalisée, bext n’intègre pas de machine virtuelle Ruby. Rails s’exécute dans son propre processus Puma. Ce choix convient à Rails : son équipe fournit Puma, bundler et l’ensemble des outils ; placer bext devant eux via HTTP conserve toutes les facilités de développement offertes en amont.
En rapport#
@bext/starter-rails— l'échafaudage- Documentation du proxy inverse — la primitive du pool en amont
- TLS automatique — Certificats basés sur ACME
- WAF et sécurité — protections de bord
- À partir de la migration nginx — traduction de blocs en amont
Links#
- Rubis sur Rails — documentation officielle de Rails