Aller au contenu principal

Ressources

OpenCost sur Kubernetes : passer du namespace au coût de chaque client SaaS

OpenCost facture les requests, pas la charge. Répartir le coût partagé d'un cluster par client SaaS, réconcilier le total et choisir une clé explicable.

L'équipe CTN Solutions · publié le · 12 min de lecture

Faits vérifiés sur les sources primaires le

Un cluster Kubernetes sort une facture par nœud, par namespace ou par équipe. Votre client, lui, demande pourquoi il paie davantage qu'un autre pour le même service. Dans un SaaS multi-tenant, les clients partagent une API et des jobs d'import qui tournent sur les mêmes pods : la facture n'a pas de colonne « client ». La réponse dépend alors d'une règle de répartition, et une règle qui n'est pas écrite change le résultat sans que personne le voie.

Cet article montre comment OpenCost chiffre un workload, comment relier ce workload à l'usage de chaque client, et comment vérifier que la répartition revient exactement au total. Il montre aussi ce qui change lorsque la règle change.

Ce qu'on répartit : workloads, idle et overhead

OpenCost distingue trois familles de coût dans un cluster. Le coût d'un workload est une dépense directement attribuable à un ensemble de pods [1]. Le coût idle est la part du coût des ressources qui n'est allouée à aucun workload [1]. L'overhead mesure ce qu'il faut pour faire fonctionner l'ensemble des actifs du cluster [1]. Pour un workload, la spécification retient max(request, usage) lorsque les ressources ont un coût d'allocation [1] : un pod qui réserve 1,5 vCPU paie 1,5 vCPU, même s'il en consomme beaucoup moins.

Un environnement dédié par client est un autre cas, traité dans SaaS single-tenant : un environnement par client. Ici, les clients partagent les mêmes workloads. La notion de coût partagé et sa place dans une démarche FinOps sont posées dans FinOps, c'est quoi ? Définition 2026, méthode et par où commencer : cet article ne la redéfinit pas et se concentre sur la répartition par client.

Lecture des chiffres

CTN Solutions ne publie aucun pourcentage d'économie type : les écarts dépendent des usages réels et des contrats en cours.

Vérifié le

Le tarif public d'une m6i.large à Paris est de 0,112 USD de l'heure pour 2 vCPU et 8 GiB (Price List du 7 novembre 2026 [7]). La méthode retient 60 % pour le CPU et 40 % pour la RAM, un choix de modèle et non un fait AWS : 0,0336 USD par vCPU-heure et 0,0056 USD par GiB-heure. Appliqués aux 12 vCPU et 31,3 GiB du nœud d'un cluster Kubernetes avec OpenCost 1.121.3, ces prix donnent 0,578 USD pour une heure. Les montants sont en dollars américains.

Exemple : un éditeur SaaS B2B dont trois clients partagent une API et un worker d'import. Le client-a appelle l'API en continu, à environ 19 requêtes par seconde. Le client-b envoie peu de requêtes, mais des imports lourds de trois secondes de CPU chacun. Le client-c est petit et peu actif.

Sur cette heure, la répartition du coût du cluster est la suivante.

Répartition du coût du cluster sur une heure

Le graphique donne, pour chaque catégorie de coût OpenCost, sa part du total de 0,57844 USD sur une heure.

Schéma défilable horizontalement ; sa version textuelle complète suit.

Lire le schéma sous forme textuelle
  1. Idle : 55,65 %, soit 0,32191 USD.
  2. Workloads partagés (API et import-worker) : 23,72 %, soit 0,1372 USD.
  3. Réserve de capacité : 11,62 %, soit 0,0672 USD.
  4. Plateforme et outillage : 6,56 %, soit 0,03792 USD.
  5. Overhead (edge-proxy) : 1,57 %, soit 0,0091 USD.
  6. Générateurs de charge : 0,88 %, soit 0,00511 USD.
Barres : part de chaque catégorie dans le coût total de 0,57844 USD.

La réserve est un pod qui demande 1,5 vCPU et 3 GiB sans trafic : elle coûte 0,0672 USD et n'appartient à aucun client. Les deux workloads partagés coûtent 0,0588 USD pour l'API et 0,0784 USD pour l'import-worker. Le calcul est exact : l'API demande 1,5 vCPU et 1,5 GiB, soit 1,5 × 0,0336 + 1,5 × 0,0056 = 0,0588 USD, alors qu'elle n'utilise en moyenne que 0,19 vCPU. Le coût suit les requests, pas la charge. Tant que l'usage reste sous les requests, davantage de trafic ne change pas la facture de ce workload. L'idle, qui n'est attribué à aucun workload, est la plus grosse part : c'est là que la répartition se joue.

Relier un workload mutualisé à l'usage de chaque client

Un workload mutualisé ne porte pas de client. OpenCost sait qu'un pod a coûté 0,0588 USD, pas que cette heure de CPU venait de client-a. Il faut donc un export applicatif qui compte, pour chaque service et chaque heure, les requêtes et le temps de traitement de chaque client, identifié par un en-tête de requête. Le temps de traitement est le temps CPU du thread, agrégé par client. Les deux mesures ne disent pas la même chose : les requêtes comptent les appels, le temps de traitement compte le travail effectué.

Ajouter un identifiant client comme label de séries Prometheus demande de contrôler le volume de séries et la confidentialité de ces données. Un export agrégé par client, par service et par heure suffit à la répartition.

De la facture au coût par client

Le schéma montre comment le coût d'un workload partagé passe par une clé pour arriver à chaque client, et comment le reste du total est conservé.

Schéma défilable horizontalement ; sa version textuelle complète suit.

Lire le schéma sous forme textuelle
  1. Coût du workload partagé : API 0,0588 USD et import-worker 0,0784 USD.
  2. Usage par client : requêtes et temps de traitement, par service et par heure.
  3. Clé de répartition : elle transforme l'usage en parts.
  4. Coût par client : chaque part du coût partagé.
  5. Idle, overhead et réserve : gardés en résiduel, donc visibles.
  6. Plateforme et générateurs de charge : exclusions documentées, hors périmètre client.
  7. Total de référence : clients, plus résiduel, plus exclusions.
Modèle de la méthode.

Le tableau suivant lit l'usage mesuré dans les deux clés : la part des requêtes et la part du temps CPU, par service.

Tableau : Client ; API, part des requêtes ; API, part du temps CPU ; Import, part des requêtes ; Import, part du temps CPU
ClientAPI : part des requêtesAPI : part du temps CPUImport : part des requêtesImport : part du temps CPU
client-a97,0 %88,4 %40,1 %4,5 %
client-b2,5 %11,1 %55,7 %94,3 %
client-c0,5 %0,5 %4,2 %1,2 %

Au nombre de requêtes, client-a porte 97 % de l'API. Au temps de traitement de l'import, client-b porte 94,3 % du CPU alors qu'il envoie 160 requêtes sur 287. Une même heure ne se répartit donc pas de la même façon selon la mesure retenue.

Deux clés sur les mêmes données : le total ne bouge pas, la part de chaque client oui

La clé « requêtes » répartit le coût proportionnellement au nombre de requêtes. La clé « temps de traitement » le répartit selon le temps CPU consommé. Les deux répartissent exactement le même montant : 0,2022 USD sur l'heure. Ce montant regroupe le coût des workloads partagés, soit 0,1372 USD, et le service externe. Le service externe est un forfait illustratif de 0,065 USD par heure, ajouté pour l'exemple : OpenCost ne le lit pas.

Avec la clé requêtes, client-a porte 0,1514 USD, client-b 0,0469 USD et client-c 0,0039 USD. Avec la clé temps de traitement, client-a tombe à 0,0883 USD, client-b monte à 0,1121 USD et client-c descend à 0,0017 USD. Le passage d'une clé à l'autre fait baisser le coût de client-a de 41,6 %, monter celui de client-b de 139,2 % et baisser celui de client-c de 56,2 %, sans que le total change.

Coût horaire de chaque client selon la clé

Le graphique compare, pour chaque client, le coût horaire obtenu avec la clé requêtes et avec la clé temps de traitement.

Schéma défilable horizontalement ; sa version textuelle complète suit.

Lire le schéma sous forme textuelle
  1. client-a : 0,1514 USD avec la clé requêtes, 0,0883 USD avec la clé temps de traitement.
  2. client-b : 0,0469 USD avec la clé requêtes, 0,1121 USD avec la clé temps de traitement.
  3. client-c : 0,0039 USD avec la clé requêtes, 0,0017 USD avec la clé temps de traitement.
Barres : coût horaire en USD. Première barre de chaque client : clé requêtes ; seconde : clé temps de traitement.

Une seconde fenêtre d'une heure, décalée de quinze minutes, donne une hausse de 145,6 % pour client-b : l'écart ne tient pas à une seule fenêtre.

Sur cette mesure, où l'idle pèse 55,65 % du coût, répartir l'idle au prorata du coût, avec l'option shareIdle de l'API d'allocation [2], fait passer le montant imputé aux clients de 0,1372 à 0,30144 USD hors service externe, alors que le changement de clé laisse ce total fixe et ne déplace que la répartition entre clients (client-b +106,5 % avec l'idle réparti).

Réconcilier le total sans compter deux fois

Une répartition n'est fiable que si elle revient au total. Le contrôle se fait par cascade : le coût attribué aux clients, plus le non attribué, plus l'idle, l'overhead, la réserve et les exclusions, doit égaler le coût de référence. Ici, cela donne 0,2022 + 0,32191 + 0,0091 + 0,0672 + 0,00511 + 0,03792 = 0,64344 USD. C'est le coût OpenCost de 0,57844 USD, augmenté du service externe de 0,065 USD. L'écart est nul pour les deux clés.

Cascade de réconciliation sur une heure

Le graphique cumule chaque poste du coût de référence et montre que la somme se referme sur 0,64344 USD.

Schéma défilable horizontalement ; sa version textuelle complète suit.

Lire le schéma sous forme textuelle
  1. Clients : 0,2022 USD.
  2. Non attribué : 0 USD, le total ne bouge pas.
  3. Idle : total cumulé 0,52411 USD.
  4. Overhead : total cumulé 0,53321 USD.
  5. Réserve : total cumulé 0,60041 USD.
  6. Générateurs de charge : total cumulé 0,60552 USD.
  7. Plateforme et outillage : total cumulé 0,64344 USD.
  8. Référence : coût OpenCost de 0,57844 USD plus service externe de 0,065 USD, soit 0,64344 USD.
Barres : total cumulé en USD à chaque étape.

Ajouter au total OpenCost le coût du nœud tel que la facture le donne compte deux fois la même dépense. Le split cost allocation de l'AWS CUR répartit le coût amorti d'une instance EC2 entre les conteneurs qui tournent dessus, à partir de leur part de CPU et de mémoire [5]. Dans la mesure, le coût du nœud lu dans OpenCost est 0,578428 USD : un coût de type CUR ajouté au total donnerait 1,157 USD pour une dépense de 0,578 USD.

La page AWS s'arrête au pod, au namespace, au nœud et au workload. Elle ne décrit ni la notion de client ni la répartition de l'idle. Pour rapprocher les deux vues, les tags aws:eks:namespace et aws:eks:workload-name servent de points de jonction ; aws:eks:workload-type n'est renseigné que s'il existe exactement un workload qui gère le pod [5]. Le détail du coût amorti et des crédits dans une facture est traité dans Coût facturé, coût amorti, crédits : réconcilier des montants AWS qui ne tombent jamais juste.

Lire le coût par client et décider sans surinterpréter la précision

Un coût par client se présente avec trois éléments : la règle appliquée, la part non attribuée et le niveau de confiance. Dans l'exemple, client-b coûte 0,0469 USD l'heure avec la clé requêtes et 0,1121 USD avec la clé temps. Avant de modifier un prix, la question utile est donc : quel workload et quelle part de travail produisent ce coût ? Mesurer l'import, ou isoler le workload qui domine, répond à cette question. Changer un prix sur la seule base d'une clé ne la règle pas.

La règle doit rester stable dans le temps. Quand elle change, gardez l'historique et indiquez le changement de dénominateur, sinon une baisse apparente du coût d'un client vient seulement de la règle. Le cadre FinOps demande de documenter les sources de données, les corrélations et les calculs des métriques unitaires, ainsi que les coûts inclus dans chaque métrique [6]. Une fiche par client doit donc citer la clé, le périmètre et les exclusions.

Pour relier ce coût à un budget mensuel et à ses écarts, voir Budget cloud d'un SaaS en croissance : prévoir trois scénarios.

L'offre FinOps rapproche la facturation, les usages, l'étiquetage et les contrats, puis remet une grille de lecture des coûts et des recommandations chiffrées sur vos données. Elle exclut la mise en œuvre des recommandations et la gouvernance continue des coûts. Voir l'offre FinOps et son livrable.

Sources officielles et limites de lecture

Faits datés

Les pages officielles citées ont été lues le .

Trois limites changent la décision. Les montants portent sur un nœud et une heure, et l'usage y reste très en dessous des requests : le coût ne bouge donc pas avec la charge, et une règle de répartition ne dit rien d'un cluster où l'usage dépasse les requests ou où l'autoscaling change le nombre de nœuds. OpenCost ne documente pas la devise des prix personnalisés : les montants sont exprimés en USD. Enfin, une clé de répartition est un choix à documenter, pas une vérité du coût : la mesure en compare deux, elle ne dit pas laquelle est juste.

La page de la spécification n'indique pas de numéro de version. La version 1.121.3 a été publiée le 16 septembre 2026, et le chart Helm opencost-2.5.33 le 14 octobre 2026 [3][4].

Sources

  1. OpenCost, spécification des coûts : https://opencost.io/docs/specification/
  2. OpenCost, API d'allocation (paramètres window, aggregate, shareIdle, includeIdle) : https://opencost.io/docs/integrations/api/
  3. OpenCost, version 1.121.3 publiée le 16 septembre 2026 : https://github.com/opencost/opencost/releases/tag/v1.121.3
  4. Chart Helm OpenCost, opencost-2.5.33 publié le 14 octobre 2026 : https://github.com/opencost/opencost-helm-chart/releases/tag/opencost-2.5.33
  5. AWS, split cost allocation data (Cost and Usage Report) : https://docs.aws.amazon.com/cur/latest/userguide/split-cost-allocation-data.html
  6. FinOps Foundation, capacité Unit Economics : https://www.finops.org/framework/capabilities/unit-economics/
  7. AWS Price List, offre AmazonEC2, région eu-west-3 (Paris), version 20261107002852 (publication du 7 novembre 2026), SKU ZSV6H9225MC9C8F4 : https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/AmazonEC2/current/eu-west-3/index.json

Parlons de votre contexte.

Cet article expose une pratique générale ; votre situation a ses propres contraintes. Décrivez-la, nous répondons avec un périmètre.

Ouvrir le formulaire