Abonnez-vous pour recevoir des notifications sur les nouveaux articles :

Pingora, notre framework Rust de développement de services réseau programmables, est en open source

2024-02-28

Lecture: 5 min.
Cet article est également disponible en English, en 繁體中文, en Deutsch, en 日本語, en 한국어, en Português, en Español et en 简体中文.

Nous nous réjouissons d'annoncer aujourd'hui la publication en open source de Pingora, le framework Rust que nous utilisons pour développer les services qui soutiennent une grande partie du trafic de Cloudflare. Pingora est distribué sous licence Apache 2.0.

Open sourcing Pingora: our Rust framework for building programmable network services

Comme nous l'avons mentionné dans notre précédent article de blog, Pingora est un framework Rust multithread et asynchrone qui nous aide à développer des services de proxy HTTP. Depuis ce dernier article, Pingora a traité près d'un million de milliards de requêtes Internet sur l'ensemble de notre réseau mondial.

Nous publions Pingora en open source afin de contribuer à bâtir un Internet meilleur et plus sécurisé, au-delà de notre propre infrastructure. Nous souhaitons proposer les outils, les idées et l'inspiration nécessaires à nos clients, nos utilisateurs et aux autres intervenants afin de leur permettre de développer leur propre infrastructure Internet à l'aide d'un cadre de travail sécurisé du point de vue de la mémoire. En effet, le fait de disposer d'un tel cadre s'avère particulièrement crucial, compte tenu de la prise de conscience croissante concernant l'importance de la sécurité de la mémoire dans l'ensemble du secteur et au sein du gouvernement des États-Unis. Conformément à cet objectif commun, nous collaborons avec l'Internet Security Research Group (ISRG), dans le cadre de son projet Prossimo, afin de faire progresser l'adoption de Pingora au sein de l'infrastructure la plus essentielle d'Internet.

Dans notre précédent article de blog, nous avons expliqué pourquoi et comment nous avons développé Pingora. Dans celui-ci, nous vous expliquerons pourquoi et comment vous pouvez utiliser Pingora.

Pingora fournit non seulement une base d'éléments fondamentaux aux proxys, mais également aux clients et aux serveurs. En plus de ces composants, nous proposons également plusieurs bibliothèques d'utilitaires permettant de mettre en œuvre une logique commune, comme un compteur d'événements, la gestion des erreurs et la mise en cache.

Que contient la solution ?

Pingora propose des bibliothèques et des API pour développer des services reposant sur les protocoles HTTP/1 et HTTP/2, TLS ou tout simplement TCP/UDP. En tant que proxy, la solution prend en charge la mise en proxy par HTTP/1 et HTTP/2 de bout en bout, gRPC et websocket. (La prise en charge de l'HTTP/3 figure sur notre feuille de route.) Elle s'accompagne également de stratégies personnalisables en matière d'équilibrage de charge et de basculement. Concernant la conformité et la sécurité, la solution prend en charge les bibliothèques courantes OpenSSL et BoringSSL, qui proposent elles-mêmes la conformité FIPS et la cryptographie post-quantique.

En plus de ces fonctionnalités, Pingora propose des filtres et des fonctions de rappel permettant à ses utilisateurs de personnaliser entièrement la manière dont le service doit traiter, transformer et transmettre les requêtes. Ces API sont particulièrement connues des utilisateurs d'OpenResty et de NGINX, car beaucoup d'entre eux les font correspondre intuitivement aux fonctions de rappel « *_by_lua » d'OpenResty.

Sur le plan opérationnel, Pingora propose des redémarrages en douceur sans interruption de service, afin de permettre l'actualisation sans abandonner la moindre requête entrante. En outre, les outils Syslog, Prometheus, Sentry, OpenTelemeter et d'autres outils d'observabilité indispensables s'intègrent facilement à Pingora.

Qui peut bénéficier de Pingora ?

Vous devriez envisager d'utiliser Pingora si :

La sécurité est votre priorité absolue : Pingora est une alternative plus sûre en termes de mémoire pour les services écrits en C/C++. Tandis que certains pourront débattre de la sécurité de la mémoire entre les différents langages de programmation, d'après notre expérience pratique, nous estimons être beaucoup moins enclins à commettre des erreurs de codage susceptibles de conduire à des problèmes de sécurité de la mémoire. En outre, comme nous passons moins de temps à lutter contre ces problèmes, nous nous montrons plus productifs lors de la mise en œuvre de nouvelles fonctionnalités.

Votre service est sensible aux performances : Pingora est rapide et efficace. Comme nous l'avons détaillé dans notre précédent article de blog, nous avons économisé énormément de ressources processeur et de mémoire grâce à l'architecture multithread de Pingora. Les économies en termes de temps et de ressources peuvent également s'avérer convaincantes pour les charges de travail sensibles aux coûts et/ou à la vitesse du système.

Votre service nécessite une personnalisation poussée : les API fournies par le cadre de proxy Pingora sont hautement programmables. Pour les utilisateurs qui souhaitent développer une passerelle ou un équilibreur de charge personnalisé et avancé, Pingora offre des moyens de mise en œuvre à la fois puissants et simples à utiliser. Vous en trouverez quelques exemples dans la section suivante.

Développons un équilibreur de charge

Explorons l'API programmable de Pingora en développant un simple équilibreur de charge. Ce dernier sélectionnera à tour de rôle le résolveur https://1.1.1.1/ et https://1.0.0.1/ pour être placé en amont.

Commençons par créer un proxy HTTP vide.

Tout objet mettant en œuvre le trait ProxyHttp (selon un processus similaire au concept d'interface en C++ ou Java) est un proxy HTTP. La seule méthode nécessaire ici est upstream_peer(), appelée pour chaque requête. Cette fonction doit renvoyer un HttpPeer contenant l'adresse IP d'origine à laquelle se connecter et la marche à suivre pour le faire.

pub struct LB();

#[async_trait]
impl ProxyHttp for LB {
    async fn upstream_peer(...) -> Result<Box<HttpPeer>> {
        todo!()
    }
}

Implémentons maintenant la sélection à tour de rôle. Le cadre Pingora fournit déjà le LoadBalancer avec des algorithmes de sélection courants (comme la sélection à tour de rôle et le hachage), alors utilisons-le. Si le scénario d'utilisation nécessite une logique de sélection de serveur plus sophistiquée ou personnalisée, les utilisateurs pourront simplement la mettre en œuvre eux-mêmes dans cette fonction.

Puisque nous nous connectons à un serveur HTTPS, le SNI doit également être défini. Si nécessaire, vous pouvez également définir les certificats, le délai d'expiration et les autres options de connexion ici, dans l'objet HttpPeer.

pub struct LB(Arc<LoadBalancer<RoundRobin>>);

#[async_trait]
impl ProxyHttp for LB {
    async fn upstream_peer(...) -> Result<Box<HttpPeer>> {
        let upstream = self.0
            .select(b"", 256) // hash doesn't matter for round robin
            .unwrap();

        // Set SNI to one.one.one.one
        let peer = Box::new(HttpPeer::new(upstream, true, "one.one.one.one".to_string()));
        Ok(peer)
    }
}

Il ne reste plus qu'à mettre le service en œuvre. Dans cet exemple, nous codons en dur les adresses IP du serveur d'origine. En conditions de travail réelles, les adresses IPS du serveur d'origine peuvent également être identifiées de manière dynamique lorsque le paramètre upstream_peer() est appelé ou fonctionne en arrière-plan. Une fois le service créé, il nous suffit de demander au service d'équilibrage de charge d'écouter l'adresse 127.0.0.1:6188. Au final, nous avons créé un serveur Pingora, qui sera le processus exécutant le service d'équilibrage de charge.

Essayons notre équilibreur de charge :

fn main() {
    let mut upstreams = LoadBalancer::try_from_iter(["1.1.1.1:443", "1.0.0.1:443"]).unwrap();

    let mut lb = pingora_proxy::http_proxy_service(&my_server.configuration, LB(upstreams));
    lb.add_tcp("127.0.0.1:6188");

    let mut my_server = Server::new(None).unwrap();
    my_server.add_service(lb);
    my_server.run_forever();
}

Nous pouvons voir que le proxy fonctionne, mais que le serveur d'origine nous rejette en nous renvoyant un code 403. Cette erreur est due au fait que notre service se contente de mettre en proxy l'en-tête Host, 127.0.0.1:6188, défini par curl. Or, ce comportement perturbe le serveur d'origine. Comment faire pour que le proxy corrige ce comportement ? Nous pouvons le faire très simplement en ajoutant un autre filtre appelé upstream_request_filter. Ce filtre s'exécute sur chaque requête après la connexion du serveur d'origine et avant l'envoi d'une requête HTTP. Nous pouvons ajouter, supprimer ou modifier les en-têtes de requête http au sein de ce filtre.

curl 127.0.0.1:6188 -svo /dev/null
> GET / HTTP/1.1
> Host: 127.0.0.1:6188
> User-Agent: curl/7.88.1
> Accept: */*
> 
< HTTP/1.1 403 Forbidden

Essayons à nouveau :

async fn upstream_request_filter(…, upstream_request: &mut RequestHeader, …) -> Result<()> {
    upstream_request.insert_header("Host", "one.one.one.one")
}

Cette fois, ça fonctionne ! Vous retrouverez l'exemple dans son intégralité ici.

curl 127.0.0.1:6188 -svo /dev/null
< HTTP/1.1 200 OK

Vous trouverez ci-dessous un schéma très simple de la manière dont cette requête transite à travers les fonctions de rappel et de filtrage que nous avons utilisées dans cet exemple. Le framework du proxy Pingora propose actuellement davantage de filtres et de fonctions de rappel à différentes étapes d'une requête afin de permettre aux utilisateurs de modifier, de rejeter, d'acheminer et/ou de journaliser la requête (et la réponse).

En arrière-plan, le framework du proxy Pingora s'occupe de la mise en pool des connexions, des négociations TLS, de la lecture, de l'écriture et de l'analyse des requêtes, ainsi que de toutes les autres tâches de proxy courantes, afin que les utilisateurs puissent se concentrer sur la logique qui compte pour eux.

Open source, présent et futur

Pingora est une bibliothèque et un ensemble d'outils, pas un fichier binaire exécutable. En d'autres termes, Pingora est le moteur qui entraîne la voiture et non la voiture elle-même. Bien que Pingora soit prêt à l'utilisation en production dans le secteur, nous comprenons que de nombreux utilisateurs souhaitent disposer d'un service web intégré et prêt à l'emploi, doté d'options de configuration à codage faible ou sans codage. Le développement de cette application sur Pingora sera l'objectif de notre collaboration avec l'ISRG et permettra d'étendre la portée de Pingora. Restez à l'écoute pour découvrir nos futures annonces concernant ce projet.

Autres points à garder à l'esprit :

  • La stabilité des API n'est pas garantie à l'heure actuelle. Si nous nous efforçons de minimiser la fréquence à laquelle nous apportons des modifications affectant le fonctionnement, nous nous réservons néanmoins toujours le droit d'ajouter, de supprimer ou de modifier certains éléments, comme les filtres de requête et de réponse, au fur et à mesure que la bibliothèque évolue, en particulier au cours de cette période antérieure à la version 1.0.

  • La prise en charge des systèmes d'exploitation non basés sur Unix ne figure pas actuellement sur notre feuille de route. Nous ne prévoyons pas de prendre en charge ces systèmes dans l'immédiat, mais ce point pourrait évoluer à l'avenir.

Comment contribuer ?

N'hésitez pas à nous faire part de vos rapports de bugs, de vos problèmes de documentation ou de vos demandes de fonctionnalités à l'aide de notre outil de suivi des problèmes disponible dans GitHub. Avant d'ouvrir une requête d'extraction, nous vous suggérons fortement de consulter notre guide de contribution.

Conclusion

Dans cet article de blog, nous avons annoncé le lancement en open source de notre framework Pingora. Nous avons démontré que les entités et l'infrastructure Internet pouvaient bénéficier de la sécurité, des performances et des possibilités de personnalisation de Pingora. Nous vous avons également montré à quel point Pingora s'avère facile à utiliser et à personnaliser.

Que vous soyez en train de développer des services web de production ou d'expérimenter les technologies réseau, nous espérons que vous apprécierez Pingora. Le chemin a été long, mais notre objectif a toujours été de partager ce projet avec la communauté open source. Nous tenons à remercier la communauté Rust, car le service Pingora est développé à partir d'un grand nombre de formidables crates Rust open source. La migration vers un Internet sécurisé du point de vue de la mémoire peut paraître un voyage impossible, mais nous espérons que vous nous rejoindrez dans celui-ci.

Nous protégeons des réseaux d'entreprise entiers, aidons nos clients à développer efficacement des applications à l'échelle d'Internet, accélérons tous les sites web ou applications Internet, repoussons les attaques DDoS, tenons les pirates informatiques à distance et pouvons vous accompagner dans votre parcours d'adoption de l'architecture Zero Trust.

Accédez à 1.1.1.1 depuis n'importe quel appareil pour commencer à utiliser notre application gratuite, qui rend votre navigation Internet plus rapide et plus sûre.

Pour en apprendre davantage sur notre mission, à savoir contribuer à bâtir un Internet meilleur, cliquez ici. Si vous cherchez de nouvelles perspectives professionnelles, consultez nos postes vacants.
Developer PlatformDéveloppeursRustOpen SourcePerformance

Suivre sur X

Cloudflare|@cloudflare

Publications associées

31 octobre 2024 à 13:00

Moving Baselime from AWS to Cloudflare: simpler architecture, improved performance, over 80% lower cloud costs

Post-acquisition, we migrated Baselime from AWS to the Cloudflare Developer Platform and in the process, we improved query times, simplified data ingestion, and now handle far more events, all while cutting costs. Here’s how we built a modern, high-performing observability platform on Cloudflare’s network. ...