This is the full developer documentation for BlackSwift Kontainers # Grille de facturation > La grille tarifaire Kontainers : compute au bundle, stockage au Go, LoadBalancer. Facturation à l'heure, sans engagement, sans frais réseau. Kontainers est facturé **à l’heure**, sans palier ni engagement, sans frais réseau. Tous les tarifs sont en euros HT (mois de référence : 720 h). ## Compute | | | | ---------- | ------------------------------------------------------------------------------------------------------------- | | **Unité** | Bundle 0,1 vCPU + 0,2 Go de RAM | | **Prix** | **0,004 € / heure** (≈ 2,88 € / mois par bundle) | | **Calcul** | Sur le **maximum** entre la CPU réservée (par tranche de 0,1 vCPU) et la RAM réservée (par tranche de 0,2 Go) | | **Base** | Les **requests** de vos conteneurs, pour les pods à l’état **Running** uniquement | ### Exemples | Conteneur (requests) | Bundles | Coût mensuel | | --------------------------------------------- | ---------------------------------- | ------------ | | 100 m CPU / 200 Mi RAM *(défauts plateforme)* | 1 | ≈ 2,88 € | | 250 m CPU / 256 Mi RAM | 3 *(max(2,5 ; 1,25) arrondi sup.)* | ≈ 8,64 € | | 1 vCPU / 2 Go RAM | 10 | ≈ 28,80 € | ### Quatre règles à connaître 1. **Seuls les pods `Running` sont facturés.** Un pod `Pending`, `Completed` ou un Deployment scalé à zéro ne coûte rien en compute. 2. **La granularité est l’heure.** Pour chaque heure, la base facturée est la somme des requests **maximales observées pendant l’heure** : un pod qui a tourné 5 minutes dans l’heure est facturé pour l’heure ; des jobs successifs qui ne se chevauchent pas ne s’additionnent pas. 3. **Les requests injectées par défaut sont facturées.** Un pod déclaré sans `resources` réserve automatiquement 0,1 vCPU / 200 Mi ([mutations](/reference/mutations-et-politiques/)) — soit 1 bundle. Déclarer des requests réalistes, c’est piloter sa facture. 4. **La limite mémoire étant égale à la request**, la RAM que vous payez est exactement celle dont dispose votre application. ## Stockage persistant | | | | ---------- | ---------------------------------------------------------------------------------------- | | **Prix** | **0,001 € / Go / heure** (≈ 0,72 € / Go / mois) | | **Base** | Capacité **provisionnée** des PVC (pas l’espace consommé) | | **Inclus** | Réplication, chiffrement, [sauvegarde quotidienne](/operer/sauvegardes-et-restauration/) | Les deux [classes de stockage](/reference/quotas-et-limites/#classes-de-stockage) sont au même tarif. ## LoadBalancer | | | | ---------- | --------------------------------------------------------------------------- | | **Prix** | **0,02 € / heure** par LoadBalancer (≈ 14,40 € / mois) | | **Inclus** | IP publique dédiée | | **Quota** | 1 par namespace (augmentable via [ticket](/depanner/contacter-le-support/)) | Pour du trafic HTTP(S), l’[Ingress mutualisé](/deployer/exposer-en-https/) est **inclus sans supplément** — le LoadBalancer n’est utile que pour les protocoles non-HTTP ou une IP dédiée. ## Inclus sans supplément * Trafic réseau entrant et sortant ; * [Monitoring Grafana](/operer/acceder-au-monitoring/), y compris vos métriques custom et dashboards personnalisés (fair use) ; * [Sauvegardes quotidiennes](/operer/sauvegardes-et-restauration/) ; * Certificats TLS Let’s Encrypt ; * Namespaces supplémentaires (création via ticket) ; * Support. ## Suivre sa consommation Voir [Consommation et facturation](/operer/consommation-et-facturation/) pour consulter votre consommation en temps réel et estimer votre facture. # Gérer son profil SSO > Gérer votre compte BlackSwift : informations personnelles, liaison GitHub/Google, double authentification et mot de passe. Votre compte SSO BlackSwift se gère depuis la console Keycloak : 🔗 **** Tout ce que vous y modifiez est automatiquement répercuté sur Rancher, le monitoring et les autres services BlackSwift. ## Modifier vos informations personnelles | Information | Modifiable | | ------------------- | ------------------------------------------------------------ | | Prénom, nom | ✅ | | E-mail de connexion | ❌ ([support](/depanner/contacter-le-support/) si nécessaire) | Onglet **« Informations personnelles »** de la console → modifiez → **Enregistrer**. ## Lier votre compte à GitHub ou Google Pour vous connecter en un clic avec un compte existant : 🔗 **[Comptes liés](https://sso.blackswift.cloud/realms/blackswift/account/account-security/linked-accounts)** 1. Cliquez sur **« Lier »** à côté de GitHub ou Google ; 2. Authentifiez-vous chez le fournisseur et autorisez la liaison ; 3. C’est fait : les prochaines connexions pourront passer par ce compte. Votre mot de passe BlackSwift continue de fonctionner en parallèle, et la liaison se retire à tout moment depuis la même page. ## Activer la double authentification (2FA) Fortement recommandée : votre compte donne accès à vos environnements de production. 🔗 **[Paramètres de connexion](https://sso.blackswift.cloud/realms/blackswift/account/account-security/signing-in)** 1. Dans **« Authenticator Application »**, cliquez sur **« Configurer »** ; 2. Scannez le QR code avec votre application (Google Authenticator, 1Password, Microsoft Authenticator…) ; 3. Saisissez le code généré pour valider, puis testez en vous reconnectant. ## Mot de passe oublié ou à changer 🔗 **[Réinitialiser le mot de passe](https://sso.blackswift.cloud/realms/blackswift/login-actions/reset-credentials)** Saisissez votre e-mail : vous recevrez un lien de réinitialisation. Le lien **« Mot de passe oublié ? »** de la page de connexion mène au même endroit. E-mail non reçu ? Vérifiez vos spams, patientez quelques minutes, vérifiez l’orthographe de l’adresse — puis [contactez le support](/depanner/contacter-le-support/) pour un renvoi manuel. ## Bonnes pratiques * Activez la **2FA** ; * Utilisez un mot de passe **unique** (gestionnaire de mots de passe) ; * Vérifiez périodiquement vos **sessions actives** dans la console Keycloak (« Device activity ») et déconnectez ce que vous ne reconnaissez pas. # Labels et annotations > Index des labels et annotations à effet plateforme sur Kontainers : exposition publique, certificats TLS, classe d'ingress. Certains labels et annotations déclenchent un comportement de la plateforme. Cette page les recense tous — une ligne par entrée, le détail dans la page liée. ## Labels | Label | Se pose sur | Effet | Détail | | -------------------- | ------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | | `exposition: public` | les **pods** (template du Deployment) | Autorise le trafic Internet **direct** vers ces pods (NodePort, LoadBalancer). Jamais nécessaire pour l’Ingress. | [Isolation réseau](/comprendre/isolation-reseau/#le-label-exposition-public) | ## Annotations | Annotation | Se pose sur | Effet | Détail | | ------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | | `cert-manager.io/issuer: letsencrypt` | Ingress | Émission et renouvellement automatiques du certificat TLS. Le secret désigné dans `spec.tls[].secretName` est créé par la plateforme. | [Exposer votre application](/deployer/exposer-en-https/) | ## Champs de spec à effet plateforme | Champ | Valeur | Effet | | ----------------------------- | ----------------------------------------- | -------------------------------------------------------------------------------------------- | | `spec.ingressClassName` | `nginx` | Seule classe d’Ingress disponible — c’est la valeur par défaut, la préciser est optionnel | | `spec.storageClassName` (PVC) | `ceph-block-rwo` ou `ceph-filesystem-rwx` | [Les deux classes de stockage](/deployer/stockage-persistant/#choisir-sa-classe-de-stockage) | ## Annotations ingress-nginx usuelles L’Ingress de la plateforme est propulsé par [ingress-nginx](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/) : ses annotations standard fonctionnent, notamment : > Le projet ingress-nginx est arrêté depuis mars 2026 ; la plateforme utilise les images maintenues par Chainguard (« zéro CVE ») — vos annotations continuent de fonctionner à l’identique ([détails](/deployer/exposer-en-https/)). | Annotation | Usage courant | | ------------------------------------------------ | --------------------------------------------------------------------------------------------------- | | `nginx.ingress.kubernetes.io/proxy-body-size` | Augmenter la taille max d’upload (`"50m"`…) | | `nginx.ingress.kubernetes.io/ssl-redirect` | Forcer (ou non) la redirection HTTPS | | `nginx.ingress.kubernetes.io/proxy-read-timeout` | Allonger le timeout pour les requêtes longues | | `nginx.ingress.kubernetes.io/affinity: "cookie"` | Sticky sessions : chaque visiteur reste sur le même pod ([exemple WordPress](/exemples/wordpress/)) | Si un comportement de la plateforme vous semble déclenché par un label non documenté ici, c’est un oubli — [dites-le-nous](/depanner/contacter-le-support/). # Limitations connues > Ce qui ne fonctionne pas comme sur un cluster Kubernetes classique : la liste honnête, avec le contournement pour chaque limite. Kontainers est un service de namespaces managés sur un cluster mutualisé — pas un cluster dédié. Certaines capacités d’un cluster « vanilla » sont donc absentes ou encadrées, volontairement. Cette page les liste **toutes**, avec le contournement quand il existe. | Limitation | Pourquoi | Contournement | | -------------------------------------------------------------- | ----------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | | Pas de DaemonSets | Objet cluster-wide par nature (un pod par nœud) | Deployment avec plusieurs replicas + [PDB](/reference/permissions-et-rbac/) | | Pas de ReplicationControllers (quota 0) | Objet déprécié | Deployments | | NetworkPolicies en lecture seule | L’isolation inter-tenants est gérée par la plateforme | Lisez les règles avec `kubectl get netpol` ; besoin spécifique → [support](/depanner/contacter-le-support/) | | LoadBalancer limité à 1 par namespace | Option payante (0,02 €/h), IP publique dédiée | Quota supplémentaire via ticket ; pour du HTTP(S), l’[Ingress mutualisé](/deployer/exposer-en-https/) est inclus | | Pas d’accès aux nœuds, PV, CRDs, objets cluster | Isolation multi-tenant | — | | `kubectl get namespaces` refusé | Pas de visibilité inter-tenants | Vos namespaces sont dans votre kubeconfig | | Pods privilégiés, hostPath, hostNetwork rejetés | Pod Security `baseline` | Repenser le besoin ; cas légitime → support | | Tolerations control-plane rejetées | Protection du control plane | Retirer la toleration | | Limite mémoire = request (pas d’overcommit) | Stabilité du nœud, prévisibilité de la facturation | Dimensionner la request sur le pic réel — voir [Mutations](/reference/mutations-et-politiques/) | | Maximum 8 Gi de RAM par conteneur | Plafond LimitRange | Augmentable via ticket avec justification | | Stockage éphémère limité à 2 Gi par conteneur | Protection des nœuds | Monter un [volume persistant](/deployer/stockage-persistant/) pour les données volumineuses | | Pas de restauration de sauvegarde en self-service | La restauration est une opération encadrée | [Ticket support](/depanner/contacter-le-support/), généralement traité en heures ouvrées | | Rétention des sauvegardes fixe (10 jours) | Politique plateforme | Dumps applicatifs complémentaires (pg\_dump en CronJob) pour un RPO/rétention sur mesure | | Pas de création de namespace en self-service | Validation manuelle (quotas, nommage) | [Ticket support](/depanner/contacter-le-support/), gratuit | | `pods/proxy`, `pods/attach` indisponibles (compte utilisateur) | Surface d’attaque réduite | `kubectl port-forward` et `kubectl exec` couvrent l’essentiel ; le SA CI dispose d’`attach`/`debug` | ## En résumé Si votre application : * tourne dans des conteneurs **non privilégiés**, * s’expose en **HTTP(S) via Ingress** (ou TCP via NodePort/LoadBalancer), * stocke ses données dans des **PVC** ou une base **PostgreSQL managée**, …alors aucune de ces limitations ne vous concernera au quotidien. Dans le doute, [contactez le support](/depanner/contacter-le-support/) avant de migrer : mieux vaut une réponse en amont qu’une surprise en production. # Mutations automatiques et politiques > Tout ce que la plateforme Kontainers modifie ou impose automatiquement sur vos pods : imagePullPolicy, requests, limites mémoire, sécurité. Kontainers applique automatiquement certaines modifications (*mutations*) et certains contrôles (*politiques*) sur vos workloads au moment de leur création. Si vous avez l’habitude d’un cluster Kubernetes « vanilla », **cette page est celle à lire en premier** : elle recense tout ce qui se passe sans que vous l’ayez demandé. ## Vue d’ensemble | Comportement | Effet | | --------------------------------------------- | -------------------------------------------------------------------- | | `imagePullPolicy` forcé à `Always` | L’image est re-vérifiée auprès du registre à chaque démarrage de pod | | Requests injectées si absentes | `cpu: 100m`, `memory: 200Mi`, `ephemeral-storage: 1Gi` | | `limits.memory` alignée sur `requests.memory` | Toujours, même si vous déclarez une valeur différente | | Limite de stockage éphémère | 2 Gi par conteneur si non déclarée | | Aucune limite CPU | Jamais injectée, jamais imposée | | Pod Security `baseline` | Pods privilégiés, hostPath, hostNetwork… rejetés | | Tolerations control-plane | Pods rejetés à l’admission | ## Avant / après : un exemple réel Voici ce que devient un Deployment déclaré **sans aucune section `resources`** : ```yaml # Ce que vous soumettez containers: - name: web image: nginx:alpine ``` ```yaml # Ce qui tourne réellement (observé sur le cluster) containers: - name: web image: nginx:alpine imagePullPolicy: Always resources: requests: cpu: 100m memory: 200Mi ephemeral-storage: 1Gi limits: memory: 200Mi ephemeral-storage: 2Gi ``` Et si vous déclarez une limite mémoire **différente** de la request, elle est ramenée à la valeur de la request : ```yaml # Vous déclarez : requests.memory: 300Mi / limits.memory: 500Mi # La plateforme applique : requests.memory: 300Mi / limits.memory: 300Mi ``` ## Conséquences opérationnelles ### `imagePullPolicy: Always` * Un tag mutable (`latest`, `main`…) est re-téléchargé à chaque redémarrage : vos pods suivent le registre, ce qui peut être voulu… ou pas. * **Si votre registre est indisponible ou votre token de pull expiré, vos pods ne peuvent plus redémarrer** — même si l’image est déjà présente sur le nœud. Pensez-y avant une opération de maintenance sur votre registre, et surveillez l’expiration de vos [pull secrets](/depanner/pod-en-erreur/). Pourquoi cette règle ? Les nœuds du cluster gardent en cache les images de **tous** les tenants. Sans re-vérification au démarrage, un pod pourrait démarrer sur l’image privée d’un autre client simplement parce qu’elle est déjà présente sur le nœud — en contournant l’authentification du registre. `Always` garantit que chaque démarrage revalide le droit de tirer l’image : c’est le prix de la confidentialité des images privées sur une plateforme mutualisée. ### `limits.memory = requests.memory` * Il n’y a **pas d’overcommit mémoire** : ce que vous réservez est ce que vous pouvez consommer. Dès que votre conteneur dépasse sa request, il est **OOMKilled**. * Le réflexe vanilla « j’augmente la limit » ne fonctionne pas ici : il faut **augmenter la request** (et c’est elle qui est [facturée](/reference/facturation/)). * Dimensionnez la request mémoire sur votre pic réel de consommation, observable dans le [monitoring](/operer/acceder-au-monitoring/). Pourquoi cette règle ? La mémoire est une ressource **non compressible** : contrairement au CPU (qu’on peut throttler), un dépassement mémoire ne peut se régler que par un kill. Autoriser `limit > request` reviendrait à faire de l’overcommit — où un nœud saturé OOM-kill des pods **d’autres tenants** qui, eux, respectaient leur réservation. Avec `request = limit`, chaque tenant est garanti d’avoir exactement ce qu’il réserve (et paie), et ne peut jamais déborder sur les voisins — c’est la condition d’une isolation prévisible en multi-tenant. Et cela rend l’assiette de facturation honnête : la réservation facturée correspond à la consommation maximale réellement possible. ### Requests injectées * Même un pod déclaré « sans ressources » réserve 0,1 vCPU et 200 Mi — et cette réservation est [facturée](/reference/facturation/). Déclarer vos `resources` explicitement, c’est piloter votre facture. Pourquoi cette règle ? Sans request, le scheduler place les pods à l’aveugle : un nœud peut accepter plus de charge qu’il n’en supporte, et ce sont les workloads voisins qui en paient les conséquences. Injecter des requests par défaut garantit que **tout** pod du cluster a une réservation cohérente — le placement reste fiable pour tout le monde, et chaque consommation a une assiette de facturation. ### CPU sans limite * Vos conteneurs peuvent burster au-delà de leur request CPU sans throttling. * En cas de contention sur un nœud, le partage se fait au prorata des requests : une request CPU réaliste vous garantit votre part. Pourquoi pas de limite CPU ? Le CPU est une ressource **compressible** : en cas de contention, le noyau throttle et partage au prorata des requests — personne ne peut affamer ses voisins, même sans limite. Une limite CPU n’apporterait donc rien à l’isolation ; elle ne ferait qu’ajouter de la latence artificielle sur vos pics de charge. C’est le symétrique exact de la règle mémoire : on limite strictement ce qui est non compressible, on laisse respirer ce qui l’est. ## Politiques de rejet Certaines créations sont refusées à l’admission. Les messages d’erreur ci-dessous sont ceux que vous verrez réellement. ### Pods privilégiés (Pod Security `baseline`) `privileged: true`, `hostPath`, `hostNetwork`, `hostPID`, `hostIPC` sont rejetés. Si un chart Helm public échoue au déploiement, c’est une cause fréquente. Pourquoi cette règle ? Toutes ces capacités donnent accès au **nœud** — qui est partagé entre les tenants. Un pod privilégié ou un montage `hostPath` permettrait de lire les données des voisins, voire de compromettre le nœud entier. Le standard `baseline` bloque précisément les vecteurs d’évasion connus tout en laissant passer les applications ordinaires : c’est le socle de l’isolation entre clients. ### Tolerations control-plane ```text admission webhook "validate.kyverno.svc-fail" denied the request: restrict-controlplane-scheduling: 'validation error: Pods may not use tolerations which schedule on control plane nodes.' ``` Retirez la toleration `node-role.kubernetes.io/control-plane` de votre manifest. Pourquoi cette règle ? Les nœuds control-plane hébergent l’API et etcd — le cerveau du cluster, partagé par tous les tenants. Un workload client qui s’y planifierait entrerait en concurrence de ressources avec eux : c’est la disponibilité de la plateforme entière qui serait en jeu. Astuce Un pod rejeté à l’admission n’apparaît jamais dans `kubectl get pods` : c’est le ReplicaSet qui porte l’erreur. Voir [Aucun pod ne se crée](/depanner/checklist-de-diagnostic/). # Permissions et RBAC > La liste exacte de ce que votre compte Kontainers peut et ne peut pas faire dans vos namespaces : ressources, verbes, ServiceAccount CI. Cette page décrit les permissions **réellement déployées** pour un compte client Kontainers, dans ses namespaces. C’est l’équivalent lisible de `kubectl auth can-i --list`. Deux identités coexistent : * **votre compte utilisateur** (kubeconfig téléchargé depuis Rancher) — usage interactif ; * **le ServiceAccount `bs-namespace-member`**, pré-provisionné dans chaque namespace — pensé pour la CI/CD. ## Workloads | Ressource | Utilisateur | SA `bs-namespace-member` | | ----------------------------------------------------------- | ------------ | ------------------------ | | Pods, Deployments, StatefulSets, Jobs, CronJobs | CRUD complet | CRUD complet | | ReplicaSets | CRUD complet | lecture seule | | `deployments/scale`, `statefulsets/scale` | ✅ | ✅ | | `pods/log`, `pods/exec`, `pods/portforward` | ✅ | ✅ | | `pods/attach`, `pods/ephemeralcontainers` (`kubectl debug`) | ❌ | ✅ | ## Réseau et exposition | Ressource | Utilisateur | SA | | ---------------- | ------------- | ------------- | | Services | CRUD complet | CRUD complet | | `services/proxy` | ❌ | ✅ | | Ingresses | CRUD complet | CRUD complet | | NetworkPolicies | lecture seule | lecture seule | ## Configuration et stockage | Ressource | Utilisateur | SA | | ---------------------- | ------------ | ------------- | | ConfigMaps, Secrets | CRUD complet | CRUD complet | | PersistentVolumeClaims | CRUD complet | CRUD complet | | ServiceAccounts | CRUD complet | lecture seule | ## Certificats TLS (cert-manager) | Ressource | Droits | | ---------------------------------------------- | ------------- | | Certificates, Issuers | CRUD complet | | CertificateRequests, Orders, Challenges (ACME) | lecture seule | Vous pouvez donc suivre l’émission d’un certificat Let’s Encrypt de bout en bout : ```bash kubectl get certificates,certificaterequests,orders,challenges -n ``` ## PostgreSQL managé (CNPG) | Ressource | Droits | | ----------------------------------------------------------- | ------------- | | Clusters, Databases, Poolers, Backups, ScheduledBackups | CRUD complet | | ImageCatalogs, Publications, Subscriptions, FailoverQuorums | lecture seule | Un guide dédié PostgreSQL managé est à venir. ## Scaling et disponibilité | Ressource | Droits | | ------------------------ | ------------ | | HorizontalPodAutoscalers | CRUD complet | | PodDisruptionBudgets | CRUD complet | ## Observabilité et diagnostic | Ressource | Droits | | ----------------------------------------- | ------------- | | ServiceMonitors, PodMonitors (Prometheus) | CRUD complet | | Events | lecture seule | | ResourceQuotas, LimitRanges | lecture seule | | Endpoints, EndpointSlices | lecture seule | | ControllerRevisions | lecture seule | | `metrics.k8s.io` (`kubectl top pods`) | lecture seule | ## Accès CI/CD | Ressource | Droits | | ------------------------------ | ------------ | | BlackswiftPermanentKubeconfigs | CRUD complet | Permet de générer des kubeconfigs **non expirants** pour vos pipelines. Voir *CI/CD avec le ServiceAccount* *(à venir)*. ## Lectures cluster-wide autorisées * `storageclasses` (les 2 classes disponibles) ; * `apiservices` ; * `get` sur vos propres namespaces (pas de listing global). ## Ce que vous ne pouvez pas faire * Lister les namespaces du cluster (`kubectl get ns` est refusé) — utilisez les namespaces nommés dans votre kubeconfig ; * Accéder aux nœuds, PersistentVolumes, CRDs, ClusterRoles ; * Créer ou modifier des NetworkPolicies ; * `pods/proxy`, `deletecollection` ; * Toute action hors de vos namespaces. Note Ces restrictions sont le socle de l’isolation multi-tenant de la plateforme — voir [Le modèle Kontainers](/comprendre/le-modele-kontainers/). Si un droit vous manque pour un cas d’usage légitime, parlez-en au [support](/depanner/contacter-le-support/). ## Vérifier vous-même ```bash # Qui suis-je ? kubectl auth whoami # Que puis-je faire dans mon namespace ? kubectl auth can-i --list -n # Test ciblé kubectl auth can-i create clusters.postgresql.cnpg.io -n ``` # Quotas et limites de la plateforme > Tous les quotas, limites et plafonds appliqués à votre namespace Kontainers : ressources compute, objets Kubernetes, stockage et sécurité. Cette page est la référence exhaustive des plafonds appliqués à chaque namespace Kontainers. Toutes les valeurs ci-dessous sont celles réellement en vigueur. ## Ressources compute (par conteneur) Chaque conteneur est soumis aux bornes suivantes : | Ressource | Défaut (request) | Défaut (limit) | Minimum | Maximum | | ----------------- | ---------------- | ------------------ | ------- | ---------- | | CPU | 100 m | *aucune limite* | 10 m | *illimité* | | Mémoire | 200 Mi | 200 Mi (= request) | 64 Mi | **8 Gi** | | Stockage éphémère | 1 Gi | 2 Gi | 100 Mi | **2 Gi** | Les valeurs « défaut » sont injectées automatiquement si vous ne déclarez pas de `resources` — et la limite mémoire est **toujours** alignée sur la request, même si vous déclarez une valeur différente. Le détail de ces comportements est décrit dans [Mutations automatiques et politiques](/reference/mutations-et-politiques/). Note Il n’y a volontairement **pas de limite CPU** : vos conteneurs peuvent absorber des pics de charge sans throttling. La request CPU garantit votre part de processeur en cas de contention. ## Quotas d’objets (par namespace) | Objet | Quota | | ---------------------- | --------------------------------------------------- | | Pods | **20** | | Services | 50 | | Services NodePort | 10 | | Services LoadBalancer | **1** (option payante, en cours de généralisation¹) | | ConfigMaps | 50 | | Secrets | 50 | | PersistentVolumeClaims | **10** | | StatefulSets | 20 | | Jobs | 20 | | CronJobs | 10 | | ReplicationControllers | 0 *(utilisez des Deployments)* | ¹ Si votre namespace affiche encore `services.loadbalancers: 0`, demandez l’activation via un [ticket support](/depanner/contacter-le-support/). ## Quotas de ressources agrégées (par namespace) | Ressource | Quota | | -------------------------------------- | ----------- | | CPU réservée (somme des requests) | **10 vCPU** | | Mémoire réservée (somme des requests) | **20 Gi** | | Mémoire en limite (somme des limits) | 20 Gi | | Stockage éphémère (requests et limits) | 50 Gi | | Stockage persistant total | **200 Gi** | | — dont classe `ceph-block-rwo` | 100 Gi | | — dont classe `ceph-filesystem-rwx` | 100 Gi | Astuce Le quota de CPU/mémoire réservée est le **plafond effectif de votre autoscaling** : un HPA ne pourra jamais faire dépasser à vos replicas la somme de 10 vCPU / 20 Gi de requests. ## Classes de stockage | Classe | Mode d’accès | Technologie | Agrandissement | Par défaut | | --------------------- | ------------- | --------------------- | ----------------------- | ---------- | | `ceph-block-rwo` | ReadWriteOnce | Bloc (Ceph RBD, ext4) | ✅ (jamais de réduction) | ✅ | | `ceph-filesystem-rwx` | ReadWriteMany | Fichier (CephFS) | ✅ (jamais de réduction) | | Usage détaillé : [Stockage persistant](/deployer/stockage-persistant/). ## Politiques de sécurité * **Pod Security Standard `baseline`** en mode *enforce* : les pods `privileged`, les montages `hostPath`, `hostNetwork`, `hostPID`/`hostIPC` sont rejetés à la création. * **Tolerations control-plane rejetées** : un pod qui tente de se rendre schedulable sur les nœuds control-plane est refusé à l’admission. * **NetworkPolicies en lecture seule** : vous pouvez consulter les règles appliquées (`kubectl get networkpolicies`) mais pas en créer. Le modèle est décrit dans [Isolation réseau](/comprendre/isolation-reseau/). ## Consulter vos quotas en temps réel Votre consommation par rapport aux quotas est visible à tout moment : ```bash kubectl describe resourcequota blackswift-standard -n ``` Elle est aussi affichée sur vos [dashboards de monitoring](/operer/acceder-au-monitoring/). ## Demander une augmentation Tous les quotas sont augmentables. Ouvrez un [ticket support](/depanner/contacter-le-support/) en précisant : 1. le namespace concerné ; 2. le quota et la valeur souhaitée ; 3. le besoin (charge prévue, benchmark…). # Assistants IA et agents > Kontainers est pensé pour être piloté par vos assistants IA : documentation lisible par les LLM, RBAC qui permet le diagnostic autonome, accès dédiés et révocables pour vos agents. Que vous travailliez avec Claude, Cursor, Copilot ou un agent qui pilote `kubectl`, votre assistant IA est un utilisateur de la plateforme comme un autre. Nous l’outillons comme tel — voici tout ce qui est en place pour qu’il soit efficace chez nous, et pourquoi ça change votre quotidien. ## Donnez cette documentation à votre IA Un assistant généraliste connaît Kubernetes, pas Kontainers : il vous proposera spontanément des manifests *presque* justes (une limite mémoire différente de la request, un Service LoadBalancer par réflexe…). La parade tient en une URL — cette documentation est publiée au format [llms.txt](https://llmstxt.org/), le standard de documentation lisible par les LLM : | URL | Contenu | Usage | | ----------------------------------------------------------------- | -------------------------------------------------- | ----------------------------------------------------- | | [`/llms.txt`](https://docs.blackswift.cloud/llms.txt) | L’index + les spécificités plateforme essentielles | Point d’entrée, découverte automatique par les outils | | [`/llms-full.txt`](https://docs.blackswift.cloud/llms-full.txt) | Les 40+ pages en markdown brut, Référence en tête | À charger dans le contexte de votre assistant | | [`/llms-small.txt`](https://docs.blackswift.cloud/llms-small.txt) | Version compacte | Petites fenêtres de contexte | En pratique : ```text Lis https://docs.blackswift.cloud/llms-full.txt puis génère le manifest de déploiement de mon application sur mon namespace Kontainers. ``` Un assistant qui a lu ces pages produit spontanément des manifests conformes : `requests = limits` en mémoire, Ingress plutôt que LoadBalancer, classes de stockage correctes, CNPG pour PostgreSQL. ## Un RBAC qui permet le diagnostic autonome Le talon d’Achille des agents IA sur un cluster managé : les lectures interdites. Un agent qui enchaîne les `Forbidden` sur `events` ou `resourcequotas` abandonne — ou pire, invente. Sur Kontainers, [le rôle client](/reference/permissions-et-rbac/) a été élargi précisément pour que la boucle de diagnostic se fasse **sans intervention humaine** : | Symptôme | Ce que votre agent peut lire | | ---------------------- | ------------------------------------------------------------------------------------- | | Pod qui ne se crée pas | `events`, conditions du ReplicaSet, `resourcequotas` (avec le message de rejet exact) | | OOMKilled | `describe pod`, `kubectl top pods`, `limitranges` | | Certificat bloqué | Toute la chaîne ACME : `certificates`, `certificaterequests`, `orders`, `challenges` | | Connexion refusée | Les `networkpolicies` réellement appliquées, `endpointslices` | Et les [messages d’erreur de la plateforme sont documentés verbatim](/depanner/pod-en-erreur/) : quand votre agent rencontre un rejet Kyverno ou PodSecurity, le texte exact de l’erreur est cherchable dans cette doc — donc dans son `llms-full.txt`. ## Un accès dédié, borné et révocable pour vos agents Ne prêtez pas votre kubeconfig personnel à un agent. Créez-lui un [**kubeconfig permanent**](/operer/ci-cd-avec-le-serviceaccount/) : * périmètre limité à **un namespace** — l’agent ne voit rien d’autre ; * n’expire pas en pleine session de travail ; * révocable en une commande (`kubectl delete blackswiftpermanentkubeconfig`) ; * traçable : la colonne `LAST USED` vous dit s’il sert encore. Et si votre agent s’emballe, [les garde-fous de la plateforme](/reference/mutations-et-politiques/) jouent pour lui comme pour vous : quotas plafonnés, requests injectées, pods privilégiés rejetés, namespaces des autres clients inaccessibles [par design](/comprendre/isolation-reseau/). Un agent ne peut ni faire déborder votre facture au-delà des quotas, ni toucher au reste du cluster. ## Des exemples que votre IA peut recopier les yeux fermés Chaque exemple de cette documentation ([WordPress](/exemples/wordpress/), [stack complète](/exemples/stack-web-complete/), [n8n](/exemples/n8n/)…) a été **réellement déployé et testé sur la plateforme** avant publication. Un assistant qui s’en inspire part d’une base qui fonctionne — pas d’un extrait de blog de 2021. ## Le mot de prudence Un assistant outillé reste un assistant : relisez ce qu’il applique sur un namespace de production, et préférez lui donner un namespace de dev pour itérer ([les environnements sont faits pour ça](/operer/organiser-vos-environnements/)). La [sauvegarde quotidienne](/operer/sauvegardes-et-restauration/) est votre filet — pour vous comme pour lui. # Isolation réseau et sécurité > Comment le réseau est isolé entre clients sur Kontainers : qui peut joindre qui, le rôle du label exposition: public, et ce que cela implique pour vos environnements. Vos namespaces vivent dans un cluster mutualisé : l’isolation réseau entre clients est assurée par la plateforme, via des NetworkPolicies que vous pouvez **consulter** mais pas modifier : ```bash kubectl get networkpolicies -n ``` ```text NAME POD-SELECTOR AGE blackswift-public exposition=public ... blackswift-standard ... ``` ## Qui peut joindre qui ? | Source du trafic | Vers vos pods | Autorisé ? | | ----------------------------------------------------- | ------------- | ---------------------------------------------- | | Un pod du **même namespace** | | ✅ | | Un pod d’un **autre namespace de votre organisation** | | ✅ | | Les services de la plateforme (ingress, monitoring…) | | ✅ | | Un pod d’une **autre organisation** | | ❌ bloqué by design | | Internet, via l’**Ingress** HTTP(S) | | ✅ (transite par le reverse-proxy plateforme) | | Internet, **en direct** (NodePort, LoadBalancer) | | ❌ sauf label `exposition: public` sur les pods | En **sortie** (egress), vos pods sont libres : Internet et les services de votre organisation sont joignables sans configuration. ## Le label `exposition: public` La politique `blackswift-standard` protège vos pods de tout trafic entrant extérieur à votre organisation. Deux chemins pour recevoir du trafic public : 1. **L’Ingress HTTP(S)** — aucun label requis : le trafic entre par le reverse-proxy de la plateforme, qui est autorisé à joindre vos pods. C’est le chemin recommandé pour tout ce qui est web ([guide](/deployer/exposer-en-https/)). 2. **L’exposition directe** (Service NodePort ou LoadBalancer) — le trafic arrive **directement sur vos pods** : vous devez déclarer explicitement cette intention en posant le label `exposition: public` **sur les pods** (pas sur le Service) : ```yaml template: metadata: labels: app: mon-app exposition: public ``` ## Le piège à connaître : dev peut joindre prod L’ouverture intra-organisation vaut pour **tous vos namespaces** : un pod de `c-acme-rtximz-dev` peut joindre la base de données de `c-acme-rtximz-prod`, et vous ne pouvez pas poser de NetworkPolicy pour l’empêcher. Ce comportement est utile (un namespace `tooling` peut superviser dev et prod), mais impose de la rigueur : des secrets distincts par environnement, et jamais d’URL de prod dans une configuration de dev. ## Le reste de la sécurité * **Pod Security Standard `baseline`** : pods privilégiés, `hostPath`, `hostNetwork`… rejetés à la création ([détails](/reference/mutations-et-politiques/#politiques-de-rejet)) ; * **TLS automatique** : certificats Let’s Encrypt émis et renouvelés par la plateforme ([guide](/deployer/exposer-en-https/)) ; * **Stockage chiffré et répliqué** ([détails](/deployer/stockage-persistant/)). ## En cas de problème réseau Connexion refusée, service injoignable, certificat bloqué : suivez la [checklist de diagnostic](/depanner/checklist-de-diagnostic/#connexion-refus%C3%A9e-vers-un-autre-service). # Kubernetes en 10 notions > Le vocabulaire Kubernetes minimum pour utiliser Kontainers : Pod, Deployment, Service, Ingress, ConfigMap, PVC… expliqués en quelques phrases chacun. Pas besoin de maîtriser Kubernetes pour utiliser Kontainers — mais dix notions reviennent partout dans cette documentation. Trois phrases sur chacune, avec le lien vers la page Kontainers qui la met en pratique. ## 1. Pod L’unité d’exécution : un ou plusieurs conteneurs qui partagent réseau et stockage. Un pod est **éphémère** — il peut être recréé à tout moment, sur un autre nœud. On ne crée presque jamais un pod directement. → [Déployer un conteneur](/deployer/deployer-un-conteneur/) · [doc officielle](https://kubernetes.io/fr/docs/concepts/workloads/pods/) ## 2. Deployment Le chef d’orchestre de vos pods : il en maintient le nombre voulu (*replicas*), remplace ceux qui meurent, et orchestre les mises à jour progressives. C’est l’objet que vous manipulerez le plus. → [Déployer un conteneur](/deployer/deployer-un-conteneur/) · [doc officielle](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) ## 3. Service Un nom stable devant des pods éphémères : les autres applications joignent `mon-service` sans se soucier de quels pods tournent où. C’est le « load balancer interne » du cluster. → [Exposer votre application](/deployer/exposer-en-https/) · [doc officielle](https://kubernetes.io/fr/docs/concepts/services-networking/service/) ## 4. Ingress Le reverse-proxy d’entrée : il route le trafic HTTP(S) venant d’Internet vers vos Services, selon le nom de domaine. Sur Kontainers, il apporte aussi le certificat TLS automatique. → [Exposer votre application](/deployer/exposer-en-https/) · [doc officielle](https://kubernetes.io/docs/concepts/services-networking/ingress/) ## 5. Namespace Votre espace isolé dans le cluster : vos objets, vos quotas, vos permissions. Chez Kontainers, un namespace = un environnement (`c-acme-rtximz-prod`…). → [Le modèle Kontainers](/comprendre/le-modele-kontainers/) ## 6. ConfigMap et Secret La configuration hors de l’image : variables d’environnement et fichiers montés. Le Secret est la variante pour les données sensibles. → [Configurer votre application](/deployer/configmaps-et-secrets/) ## 7. PersistentVolumeClaim (PVC) Votre demande de disque persistant : les données y survivent aux redémarrages des pods. Deux modes : un seul écrivain (RWO) ou partagé (RWX). → [Stockage persistant](/deployer/stockage-persistant/) ## 8. Requests et limits La réservation de ressources d’un conteneur : la *request* est garantie (et [facturée](/reference/facturation/)), la *limit* est le plafond. Particularité Kontainers : la limit mémoire est toujours égale à la request. → [Mutations automatiques](/reference/mutations-et-politiques/) ## 9. HorizontalPodAutoscaler (HPA) L’autoscaling : il ajuste le nombre de replicas d’un Deployment selon la charge (CPU, mémoire). Vos pics de trafic sont absorbés sans intervention. → [Scaling et haute disponibilité](/operer/scaling-et-disponibilite/) ## 10. kubectl L’outil en ligne de commande qui parle au cluster : `kubectl apply` déploie un fichier YAML, `kubectl get pods` liste vos pods, `kubectl logs` affiche les logs. Tout ce que fait la console Rancher, kubectl le fait aussi. → [Kubeconfig et outillage local](/demarrer/recuperer-votre-kubeconfig/) *** Envie de pratiquer tout de suite ? [Déployez votre première application](/demarrer/premiere-application/) — moins de 15 minutes, tout est expliqué. # Le modèle Kontainers > Comprendre ce qu'est Kontainers : des namespaces managés dans un cluster mutualisé, ce que gère BlackSwift, ce que vous gérez, et pourquoi certaines choses sont volontairement interdites. Kontainers est un service de **namespaces Kubernetes managés** : vous recevez un ou plusieurs namespaces dans un cluster mutualisé haute disponibilité, opéré par BlackSwift — pas un cluster dédié. Ce choix structure tout le reste : ce que vous pouvez faire, ce que nous faisons pour vous, et ce qui est volontairement fermé. ## Vos namespaces Chaque namespace suit le format `c--` (par exemple `c-acme-rtximz-prod`). Vous y accédez de deux façons interchangeables : * la console web [Rancher](https://rancher.fr.blackswift.cloud) ; * `kubectl` (ou Lens, k9s…) avec votre [kubeconfig](/demarrer/recuperer-votre-kubeconfig/). Vous pouvez avoir plusieurs namespaces (dev, prod, tooling…) — leur création est gratuite, sur simple [ticket](/depanner/contacter-le-support/). ## Qui gère quoi ? | Géré par BlackSwift | Géré par vous | | -------------------------------------------------------------------- | ---------------------------------------- | | Les nœuds, le control plane, les mises à jour Kubernetes | Vos Deployments, Services, Ingress | | Le réseau, l’isolation entre clients, les certificats TLS | La configuration de vos applications | | Le stockage (Ceph répliqué, chiffré) | Vos PVC et la gestion de vos données | | Les [sauvegardes quotidiennes](/operer/sauvegardes-et-restauration/) | Le dimensionnement de vos ressources | | La [stack de monitoring](/operer/acceder-au-monitoring/) | Vos métriques custom et dashboards | | L’opérateur PostgreSQL (CNPG) | Vos clusters PostgreSQL et leurs backups | ## Les garde-fous Parce que le cluster est partagé, chaque namespace est encadré par : * des [**quotas**](/reference/quotas-et-limites/) (pods, CPU, mémoire, stockage…) ; * des [**mutations automatiques**](/reference/mutations-et-politiques/) qui normalisent vos workloads (requests par défaut, limite mémoire alignée…) ; * le **Pod Security Standard `baseline`** (pas de pods privilégiés, pas de `hostPath`…) ; * une [**isolation réseau**](/comprendre/isolation-reseau/) gérée par la plateforme : vos namespaces se voient entre eux, personne d’autre ne les voit. Un utilisateur Kubernetes expérimenté remarquera vite ce qui manque : pas d’accès aux nœuds, pas de CRDs, pas de NetworkPolicies custom, pas de DaemonSets. C’est le prix de l’isolation et de la stabilité pour tous — la liste complète, avec les contournements, est dans [Limitations connues](/reference/limitations-connues/). ## Ce que ce modèle vous apporte * **Zéro opération cluster** : pas de mise à jour de control plane, pas de gestion de nœuds, pas d’etcd à sauvegarder. * **Un coût proportionnel à l’usage** : vous payez les ressources que vos pods réservent, [à l’heure](/reference/facturation/), pas un cluster entier. * **Des permissions larges dans votre périmètre** : dans vos namespaces, vous disposez d’un [RBAC étendu](/reference/permissions-et-rbac/) — exec, logs, port-forward, PostgreSQL managé, métriques custom, certificats. ## Pour aller plus loin * [Quotas et limites](/reference/quotas-et-limites/) — les chiffres exacts ; * [Mutations automatiques](/reference/mutations-et-politiques/) — ce qui est modifié sans vous le dire ; * [Permissions et RBAC](/reference/permissions-et-rbac/) — ce que votre compte peut faire, verbe par verbe. # Créer un compte > Procédure d'inscription à BlackSwift Kontainers et règles de nommage des organisations et namespaces. Pour ouvrir votre espace sur la plateforme **BlackSwift Kontainers** : 1. Rendez-vous sur notre formulaire d’inscription : [https://blackswift.fr/inscription](https://blackswift.fr/inscription?code=I-DO-RTFM). 2. Remplissez les champs obligatoires ; 3. Validez. Notre équipe vérifie votre demande (contrôle anti-spam / cohérence des informations). 4. Vous recevez un courriel de confirmation une fois le compte activé (généralement < 1 jour ouvré). Lecteurs de la doc, ce code est pour vous Le champ « Code promo ou parrainage » du formulaire accepte le code **`I-DO-RTFM`** — il est déjà pré-rempli si vous passez par le lien ci-dessus. > **Bon à savoir :** seul·e·s les contacts déclarés reçoivent les e-mails de création de compte et d’accès au Rancher. *** ## Champs importants du formulaire | Champ | Règles | Exemple saisi | Exemple réel après validation | | ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | ----------------------------- | | **Identifiant d’organisation** | 2 – 8 caractères **alphanumériques** (a-z, 0-9). Nous y ajoutons automatiquement un **suffixe aléatoire de 6 lettres** pour renforcer la sécurité et éviter les collisions. | `acme` | `acme-rtximz` | | **Nom du namespace à créer** | Nom de votre **premier namespace** : commence par une lettre, peut contenir `a-z`, `0-9`, `-`. | `prod` | `c-acme-rtximz-prod` | ### Modèle de nommage dans Kubernetes ```text c--- ``` *Le préfixe `c-` signifie « client » et garantit que tous les namespaces clients sont distingués des ressources internes.* Exemple complet : ```text # Saisie : Identifiant = acme Namespace = prod # Créé par BlackSwift : Organisation = acme-rtximz # suffixe aléatoire de 6 lettres Namespace = c-acme-rtximz-prod ``` > Vous n’avez donc pas besoin de vérifier si le slug est déjà pris ; plusieurs organisations peuvent choisir le même identifiant « acme », elles seront différenciées par le suffixe. *** **Étape suivante** : une fois votre e-mail d’activation reçu, place à la [première connexion](/demarrer/premiere-connexion/). # Votre première application > Déployez et testez votre première application sur Kontainers en moins de 15 minutes, sans domaine ni configuration préalable. Dans ce tutoriel, vous allez déployer un serveur web, vérifier qu’il tourne, et vous y connecter — le tout en moins de 15 minutes, sans aucun prérequis autre que [votre kubeconfig](/demarrer/recuperer-votre-kubeconfig/). **Prérequis** : `kubectl` installé et votre kubeconfig activé ([voir comment](/demarrer/recuperer-votre-kubeconfig/)). Voici le manifest complet que nous allons déployer : premiere-app.yaml ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: premiere-app spec: selector: matchLabels: app: premiere-app template: metadata: labels: app: premiere-app spec: containers: - name: web image: nginx:alpine ``` 1. **Créez le fichier** Copiez le manifest ci-dessus dans un fichier nommé `premiere-app.yaml`. 💡 Débutant Kubernetes ? Un **Deployment** décrit une application à faire tourner : quelle image de conteneur, combien d’exemplaires. Kubernetes crée alors un **Pod** — l’unité d’exécution qui héberge votre conteneur — et le recrée automatiquement s’il s’arrête. 2. **Déployez-le** ```bash kubectl apply -f premiere-app.yaml ``` ```text deployment.apps/premiere-app created ``` 3. **Vérifiez que le pod démarre** ```bash kubectl get pods ``` ```text NAME READY STATUS RESTARTS AGE premiere-app-7d9c5b6f4-x2krl 1/1 Running 0 10s ``` Attendez que `STATUS` affiche `Running` (quelques secondes, le temps de télécharger l’image). 4. **Connectez-vous à votre application** Ouvrez un tunnel entre votre machine et le pod : ```bash kubectl port-forward deploy/premiere-app 8080:80 ``` Puis, dans un autre terminal (ou votre navigateur) : ```bash curl http://localhost:8080 ``` La page d’accueil nginx s’affiche : **votre application tourne sur Kontainers.** 🎉 5. **Nettoyez (optionnel)** ```bash kubectl delete -f premiere-app.yaml ``` Note Vous n’avez déclaré ni CPU ni mémoire : la plateforme a réservé automatiquement 0,1 vCPU et 200 Mi pour votre conteneur. C’est ce qui est [facturé](/reference/facturation/) tant que le pod tourne — détails dans [Mutations automatiques](/reference/mutations-et-politiques/). ## Et maintenant ? Votre application n’est pour l’instant visible que par vous, via le tunnel. Les étapes suivantes du parcours : * [**Exposer votre application**](/deployer/exposer-en-https/) — la rendre accessible sur Internet avec votre domaine et un certificat automatique ; * [**Ajouter du stockage persistant**](/deployer/stockage-persistant/) — pour que vos données survivent aux redémarrages ; * [**Configurer votre application**](/deployer/configmaps-et-secrets/) — variables d’environnement et secrets. # Première connexion > Activez votre compte BlackSwift, découvrez le SSO et connectez-vous à la console Rancher pour voir vos namespaces. Votre compte vient d’être créé ? Ce guide vous emmène de l’e-mail d’activation jusqu’à la console Rancher, où vous verrez vos namespaces pour la première fois. ## Un seul compte pour tout : le SSO L’authentification unique (SSO) BlackSwift vous permet d’accéder avec un seul compte à tous les services : * **Rancher** : la console web de gestion de vos conteneurs ; * **Monitoring** : vos dashboards Grafana. | Concept | Description | | ----------------------- | --------------------------------------------------- | | **Compte personnel** | Votre identité unique sur la plateforme | | **Organisation** | L’entreprise ou le projet auquel vous appartenez | | **Multi-organisations** | Un compte peut appartenir à plusieurs organisations | ## Activer votre compte 1. **Recevez l’e-mail d’activation** Vous recevrez un e-mail de BlackSwift contenant un lien d’activation et vos informations d’organisation. 2. **Définissez votre mot de passe** Cliquez sur le lien d’activation et choisissez un mot de passe fort. 3. **C’est activé !** Votre compte donne désormais accès à tous les services de votre organisation. Note E-mail non reçu ? Vérifiez vos spams, puis [contactez le support](/depanner/contacter-le-support/) pour un renvoi. ## Se connecter à Rancher Rancher est votre console web : elle offre une vue d’ensemble de vos applications et permet de gérer vos déploiements, services et volumes sans ligne de commande. ![Interface de Rancher](/_astro/bs-demo-rancher.DkJKzrKR_ZclfGU.webp) 1. Rendez-vous sur 2. Cliquez sur le bouton **« Single Sign-On » (SSO)** ![Page de login de Rancher](/_astro/rancher-login-sso.IOADZRKC_IYRtC.webp) 3. Authentifiez-vous avec votre compte BlackSwift Si vous êtes déjà connecté (par exemple après l’activation), vous serez authentifié automatiquement. 4. Vous voilà dans Rancher : sélectionnez le cluster proposé pour voir vos namespaces (leur nom suit le format `c--`). Astuce Vous pouvez lier votre compte à **GitHub ou Google** pour simplifier vos prochaines connexions, et activer la **double authentification** — voir [Gérer son profil SSO](/reference/gerer-son-profil-sso/). ## Inviter un équipier L’ajout d’un membre à votre organisation passe par le [support](/depanner/contacter-le-support/) : indiquez l’e-mail de la personne et l’organisation concernée, elle recevra son propre e-mail d’activation. ## Étape suivante [Récupérer votre kubeconfig ](/demarrer/recuperer-votre-kubeconfig/)Pour utiliser kubectl, Lens ou k9s en plus de la console web. # Kubeconfig et outillage local > Récupérer son fichier kubeconfig depuis Rancher, installer kubectl et se préparer à travailler en ligne de commande. ## Pourquoi récupérer un kubeconfig ? Le fichier `kubeconfig` contient toutes les informations nécessaires pour s’authentifier et interagir avec votre cluster. Il est uniquement nécessaire si vous souhaitez accéder à votre cluster Kubernetes depuis des outils externes comme [`kubectl`](https://kubernetes.io/fr/docs/tasks/tools/), [Lens](https://k8slens.dev/) ou [k9s](https://k9scli.io/). Cette étape est optionnelle Si vous n’utilisez que l’interface [Rancher](https://rancher.fr.blackswift.cloud), vous n’avez pas besoin de télécharger le kubeconfig car l’authentification est déjà gérée. ## Récupérer son kubeconfig depuis Rancher 1. Connectez-vous à l’interface Rancher : 2. Sélectionnez le cluster auquel vous souhaitez accéder dans le menu de gauche. 3. Cliquez sur le bouton **“Download Kubeconfig”** en haut à droite de la page. ![Localisation du bouton download kubeconfig dans Rancher](/_astro/rancher-download-kubeconfig.BMLHm3MW_Z2eP4ko.webp) 4. Un fichier `kubeconfig.yaml` sera téléchargé sur votre ordinateur. Sécurité Votre fichier kubeconfig contient des informations sensibles (tokens d’accès). Ne le partagez jamais et stockez-le dans un endroit sûr. ## Installer kubectl `kubectl` est l’outil en ligne de commande officiel de Kubernetes. Installation : * **macOS** : `brew install kubectl` * **Linux** : suivez la [documentation officielle](https://kubernetes.io/fr/docs/tasks/tools/install-kubectl-linux/) (paquet `kubectl` disponible dans la plupart des distributions) * **Windows** : `winget install Kubernetes.kubectl` ## Utiliser votre kubeconfig Indiquez à kubectl où trouver votre fichier, au choix : ```bash # Pour la session de terminal en cours export KUBECONFIG=~/Téléchargements/kubeconfig.yaml kubectl get pods # Ou ponctuellement, commande par commande kubectl --kubeconfig ~/Téléchargements/kubeconfig.yaml get pods # Ou de façon permanente (emplacement par défaut de kubectl) mv ~/Téléchargements/kubeconfig.yaml ~/.kube/config ``` Vérifiez que tout fonctionne : ```bash kubectl auth whoami kubectl get pods ``` Le namespace par défaut Votre kubeconfig définit un **namespace par défaut** : toute commande lancée sans `-n ` s’exécute dans celui-ci. Si vous avez plusieurs environnements (dev, prod…), vérifiez toujours où vous êtes avant d’agir : ```bash kubectl config view --minify | grep namespace ``` Pour la liste exacte de ce que votre compte peut faire avec kubectl, consultez [Permissions et RBAC](/reference/permissions-et-rbac/). ## Outils graphiques (optionnel) Le même kubeconfig fonctionne avec [Lens](https://k8slens.dev/) et [k9s](https://k9scli.io/) : indiquez simplement le chemin du fichier dans leur configuration. *** **Étape suivante** : [déployez votre première application](/demarrer/premiere-application/) en moins de 15 minutes. # Aucun pod ne se crée > Le Deployment existe mais aucun pod n'apparaît, ou reste en Pending : quotas, politiques de sécurité et autres causes de rejet, avec les messages d'erreur réels. Vous avez appliqué votre manifest, `kubectl get deploy` montre bien le Deployment… mais `READY` reste à `0/1` et aucun pod n’apparaît (ou il reste bloqué en `Pending`). Sur Kontainers, quatre causes couvrent la quasi-totalité des cas. ## Où lire l’erreur Un pod rejeté à l’admission **n’apparaît jamais** dans `kubectl get pods` : c’est le ReplicaSet (créé par le Deployment) qui porte le message : ```bash kubectl describe deploy -n | tail -5 kubectl get events -n --sort-by=.lastTimestamp | tail -10 ``` ## Cause 1 : quota atteint Le message contient `exceeded quota: blackswift-standard` : ```text Error creating: pods "web-xxx" is forbidden: exceeded quota: blackswift-standard, requested: requests.memory=4Gi, used: requests.memory=18Gi, limited: requests.memory=20Gi ``` Le message dit tout : ce qui est demandé, ce qui est déjà utilisé, le plafond. Vérifiez votre consommation globale : ```bash kubectl describe resourcequota blackswift-standard -n ``` Solutions : réduire les requests du nouveau workload, scaler à zéro ce qui ne sert pas, ou [demander une augmentation](/depanner/contacter-le-support/). Les plafonds par défaut sont listés dans [Quotas et limites](/reference/quotas-et-limites/). ## Cause 2 : rejet Pod Security Le message mentionne `violates PodSecurity "baseline:latest"` : ```text Error creating: pods "web-xxx" is forbidden: violates PodSecurity "baseline:latest": privileged (container "web" must not set securityContext.privileged=true) ``` Votre pod demande une capacité interdite : `privileged`, montage `hostPath`, `hostNetwork`, `hostPID`… C’est fréquent avec des charts Helm prévus pour un cluster dédié. Retirez l’option en cause — si elle est indispensable à votre cas d’usage, [parlez-en au support](/depanner/contacter-le-support/). ## Cause 3 : toleration control-plane ```text admission webhook "validate.kyverno.svc-fail" denied the request: restrict-controlplane-scheduling: 'validation error: Pods may not use tolerations which schedule on control plane nodes.' ``` Retirez la toleration `node-role.kubernetes.io/control-plane` (ou `master`) du manifest — souvent héritée d’un chart ou d’un copier-coller. ## Cause 4 : pod `Pending` Le pod existe mais ne démarre pas. `kubectl describe pod` vous dira pourquoi ; les deux cas courants : * **`pod has unbound immediate PersistentVolumeClaims`** : le PVC référencé n’existe pas encore ou vient d’être créé — transitoire si le PVC se `Bound` ([vérifier](/deployer/stockage-persistant/)) ; * **volume RWO déjà monté ailleurs** : deux pods se disputent le même volume ReadWriteOnce — utilisez `strategy: Recreate` sur le Deployment concerné. ## Cas particulier : DaemonSets et ReplicationControllers Ces deux types d’objets ne sont pas disponibles sur Kontainers ([pourquoi](/reference/limitations-connues/)). Un chart qui en dépend doit être adapté — le besoin se couvre généralement avec un Deployment. *** Pas votre cas ? Retour à la [checklist de diagnostic](/depanner/checklist-de-diagnostic/), ou [contactez le support](/depanner/contacter-le-support/) avec la sortie des commandes ci-dessus. # Checklist de diagnostic > Quelque chose ne fonctionne pas ? Trouvez la bonne piste en 4 commandes, symptôme par symptôme. Quelque chose ne va pas et vous ne savez pas par où commencer ? Suivez cette page dans l’ordre. ## Les 4 commandes réflexes ```bash # 1. Vue d'ensemble : quels pods, dans quel état ? kubectl get pods -n # 2. Le détail d'un pod : événements, causes de redémarrage kubectl describe pod -n # 3. Les logs de l'application kubectl logs -n --previous # 4. Les événements récents du namespace kubectl get events -n --sort-by=.lastTimestamp ``` L’option `--previous` de `kubectl logs` affiche les logs de l’exécution **précédente** — indispensable quand le conteneur redémarre en boucle. ## Trouver son symptôme | Symptôme | Cause la plus probable sur Kontainers | Page | | -------------------------------------------------- | -------------------------------------------------------------------------- | ------------------------------------------------------------------ | | `CrashLoopBackOff` | L’application plante au démarrage | [Mon pod est en erreur](/depanner/pod-en-erreur/) | | `OOMKilled` | Mémoire consommée > request (la limite lui est égale) | [Mon pod est en erreur](/depanner/pod-en-erreur/#oomkilled) | | `ImagePullBackOff` | Registre inaccessible ou token expiré (aggravé par le pull `Always` forcé) | [Mon pod est en erreur](/depanner/pod-en-erreur/#imagepullbackoff) | | Le Deployment existe mais **aucun pod** n’apparaît | Quota atteint ou politique de sécurité | ci-dessous | | Pod `Pending` | Quota CPU/mémoire du namespace atteint | ci-dessous | | Site injoignable / certificat absent | DNS, Ingress ou émission ACME | ci-dessous | | Connexion refusée entre deux services | Isolation réseau (namespaces d’organisations différentes) | ci-dessous | ## Aucun pod ne se crée Le Deployment est là, `kubectl get pods` ne montre rien (ou `Pending`). La cause est presque toujours visible dans le ReplicaSet ou les événements : ```bash kubectl describe deploy -n | tail -5 kubectl get events -n --sort-by=.lastTimestamp | tail -10 ``` Causes fréquentes, par ordre de probabilité : 1. **Quota atteint** — le message contient `exceeded quota: blackswift-standard`. Vérifiez votre consommation : ```bash kubectl describe resourcequota blackswift-standard -n ``` Réduisez les requests, supprimez des workloads inutiles, ou [demandez une augmentation](/depanner/contacter-le-support/). 2. **Rejet Pod Security** — le message mentionne `violates PodSecurity "baseline"` : votre pod demande `privileged`, `hostPath`, `hostNetwork`… Ce n’est pas permis ([pourquoi](/reference/limitations-connues/)). 3. **Toleration control-plane** — message `Pods may not use tolerations which schedule on control plane nodes` : retirez la toleration du manifest. ## Site injoignable ou certificat absent Dans l’ordre : ```bash # 1. Le DNS pointe-t-il au bon endroit ? dig +short www.example.com # Attendu : ingress.k-bdkbsh.blackswift.hosting. puis des IP # 2. L'Ingress est-il correctement câblé ? kubectl describe ingress -n # Vérifiez host, service cible et port — un 503 = service/port incorrects # 3. Le certificat est-il émis ? kubectl get certificates -n # READY=False ? Creusez : kubectl get certificaterequests,orders,challenges -n ``` Un challenge ACME bloqué vient presque toujours d’un DNS qui ne pointe pas encore vers la plateforme — voir [Configurer votre DNS](/deployer/configurer-votre-dns/). ## Connexion refusée vers un autre service * Les namespaces d’une **même organisation** communiquent librement entre eux. * Un service dans le namespace d’une **autre organisation** est injoignable **par design** ([isolation réseau](/comprendre/isolation-reseau/)). * Un NodePort/LoadBalancer injoignable depuis Internet ? Le label `exposition: public` doit être **sur les pods**, pas sur le Service ([détails](/deployer/exposer-en-https/#3-exposer-un-protocole-non-http)). ## Toujours bloqué ? [Contactez le support](/depanner/contacter-le-support/) avec le namespace, les manifests concernés et la sortie des commandes ci-dessus : vous gagnerez un aller-retour. # Support et état des services > Tous les canaux pour joindre BlackSwift, la page de statut des services, et comment rédiger un ticket efficace. ## Les canaux | Canal | Usage | Adresse | | ----------------------- | --------------------------------------------------- | -------------------------------------------------------------------- | | **Ticket** | Toute demande : incident, question, opération | [blackswift.link/ticket](https://blackswift.link/ticket) | | **E-mail** | Équivalent du ticket | | | **Slack communautaire** | Questions rapides, entraide, annonces | [blackswift-hosting.slack.com](https://blackswift-hosting.slack.com) | | **Page de statut** | État des services en temps réel, incidents en cours | [status.blackswift.cloud](https://status.blackswift.cloud) | Avant d’ouvrir un ticket pour un dysfonctionnement général, un coup d’œil à la **page de statut** vous dira si un incident est déjà identifié. ## Les opérations qui passent par le support Certaines actions ne sont volontairement pas en self-service. Les demander via ticket est le fonctionnement normal — comptez généralement moins d’un jour ouvré : * **Créer un namespace supplémentaire** (gratuit) — indiquez votre organisation, le nom souhaité et l’usage ; * **Augmenter un quota** (pods, CPU, mémoire, stockage, LoadBalancer…) — indiquez le namespace, le quota visé et le besoin ; * **Activer un LoadBalancer** si votre namespace affiche encore `services.loadbalancers: 0` ; * **Restaurer une sauvegarde** — voir [Sauvegardes et restauration](/operer/sauvegardes-et-restauration/) ; * **Inviter un membre** dans votre organisation — indiquez son e-mail ; * **Dépasser une borne** de la plateforme (plus de 8 Gi de RAM par conteneur…) — avec justification technique. ## Rédiger un ticket efficace Un ticket complet évite un aller-retour et se résout deux fois plus vite. Incluez : 1. **Le namespace concerné** (ex. `c-acme-rtximz-prod`) ; 2. **Le symptôme et depuis quand** (« le pod X redémarre en boucle depuis 14h ») ; 3. **Ce que vous avez déjà vérifié** — idéalement la sortie de : ```bash kubectl describe pod -n kubectl logs -n --previous kubectl get events -n --sort-by=.lastTimestamp | tail -10 ``` 4. **Le manifest** de la ressource concernée si vous venez de la modifier. Astuce La [checklist de diagnostic](/depanner/checklist-de-diagnostic/) résout la majorité des cas courants en quelques minutes — et si elle ne résout pas le vôtre, sa sortie fera un excellent contenu de ticket. # Mon pod est en erreur > Diagnostiquer et corriger les trois états d'échec les plus courants : CrashLoopBackOff, ImagePullBackOff et OOMKilled — avec les causes spécifiques à Kontainers. Votre pod apparaît dans `kubectl get pods`, mais son statut n’est pas `Running` (ou il redémarre en boucle). Cette page couvre les trois cas les plus fréquents, avec les causes propres à Kontainers qu’un habitué d’un autre cluster ne devinerait pas. Commencez toujours par : ```bash kubectl describe pod -n kubectl logs -n --previous ``` ## CrashLoopBackOff **Le conteneur démarre puis s’arrête, en boucle.** Kubernetes espace les redémarrages (`back-off`), d’où des périodes d’attente croissantes. C’est presque toujours votre application qui se termine : configuration manquante, dépendance injoignable, erreur au boot. Les logs de l’exécution précédente (`--previous`) contiennent la vraie erreur. Vérifiez notamment : * une variable d’environnement ou un secret manquant ([ConfigMaps et Secrets](/deployer/configmaps-et-secrets/)) ; * une dépendance pas encore prête (base de données…) — ajoutez une `readinessProbe` et laissez le pod redémarrer, ou un `initContainer` d’attente ; * une dépendance dans un namespace d’une **autre organisation** : bloquée par design ([isolation réseau](/comprendre/isolation-reseau/)). ## ImagePullBackOff **L’image ne peut pas être téléchargée.** ```bash kubectl describe pod -n | grep -A3 "Failed" ``` Causes classiques : nom d’image mal orthographié, tag inexistant, registre privé sans pull secret. Spécificité Kontainers `imagePullPolicy` est [forcé à `Always`](/reference/mutations-et-politiques/) : l’image est re-vérifiée auprès du registre **à chaque démarrage**, même si elle est déjà sur le nœud. Conséquence : **un token de registre expiré ou un registre en panne empêche vos pods de redémarrer** — y compris ceux qui tournaient très bien jusque-là. Si un pod qui n’a pas changé tombe en `ImagePullBackOff`, vérifiez d’abord votre registre et la validité de votre pull secret. ## OOMKilled **Le conteneur a dépassé sa mémoire allouée et a été tué.** Repérable dans `describe pod` : ```text Last State: Terminated Reason: OOMKilled ``` Spécificité Kontainers — lisez avant d’augmenter la limite Sur Kontainers, [la limite mémoire est toujours égale à la request](/reference/mutations-et-politiques/#limitsmemory--requestsmemory). Le réflexe habituel « j’augmente `limits.memory` » **ne fonctionne pas** : la limite serait ramenée à la request. Pour donner plus de mémoire à votre conteneur, **augmentez `requests.memory`** : ```yaml resources: requests: memory: 512Mi # ← c'est cette valeur qui fait foi (et qui est facturée) ``` Dimensionnez la request sur le pic réel de consommation, visible dans votre [monitoring](/operer/acceder-au-monitoring/) ou via : ```bash kubectl top pods -n ``` ## Ce n’est pas l’un de ces trois cas ? * Aucun pod ne se crée du tout → [Checklist de diagnostic](/depanner/checklist-de-diagnostic/#aucun-pod-ne-se-cr%C3%A9e) ; * Le pod tourne mais l’application est injoignable → [Checklist : site injoignable](/depanner/checklist-de-diagnostic/#site-injoignable-ou-certificat-absent) ; * Toujours bloqué → [contactez le support](/depanner/contacter-le-support/) avec la sortie de `kubectl describe pod` et les logs. # Problème de connexion ou de réseau > Mon application ne joint pas sa dépendance, mon site est injoignable, mon certificat ne s'émet pas : diagnostic guidé par le modèle réseau Kontainers. Deux familles de symptômes, deux démarches. Dans les deux cas, gardez en tête le [modèle réseau de la plateforme](/comprendre/isolation-reseau/) : il explique 80 % des surprises. ## « Mon application ne joint pas sa dépendance » ### La matrice qui-joint-qui | Cible | Joignable ? | | ----------------------------------------------------------------------------------------------- | ------------------------------------------------------- | | Service du même namespace (`http://mon-svc`) | ✅ | | Service d’un autre namespace de **votre organisation** (`http://mon-svc.c-acme-rtximz-tooling`) | ✅ | | Service d’une **autre organisation** | ❌ bloqué by design — passez par son exposition publique | | Internet (API externes, registres…) | ✅ egress libre | ### Diagnostic pas à pas ```bash # 1. La cible existe-t-elle et a-t-elle des endpoints ? kubectl get svc,endpointslices -n # Un Service sans endpoints = selector qui ne matche aucun pod prêt # 2. Test de connectivité depuis un pod kubectl exec -it deploy/mon-app -n -- sh wget -qO- http://mon-svc.:80/ --timeout=5 ``` Causes fréquentes, dans l’ordre : 1. **Service sans endpoints** : le `selector` du Service ne correspond pas aux labels des pods, ou les pods ne passent pas leur `readinessProbe` ; 2. **Mauvais port** : le Service écoute sur `port`, votre app sur `targetPort` — vérifiez les deux ; 3. **Cible dans une autre organisation** : c’est l’isolation qui joue, pas une panne ([pourquoi](/comprendre/isolation-reseau/)) ; 4. **L’egress n’est jamais le coupable** : les connexions sortantes (Internet, APIs externes) sont libres — si une API externe ne répond pas, le problème est chez elle ou dans votre configuration (DNS, proxy applicatif…). ## « Mon site est injoignable depuis Internet » ### Via Ingress (HTTP/HTTPS) ```bash # 1. Le DNS pointe-t-il vers la plateforme ? dig +short www.example.com # Attendu : ingress.k-bdkbsh.blackswift.hosting. puis des IP # 2. L'Ingress est-il bien câblé ? kubectl describe ingress -n ``` | Symptôme | Cause probable | | ------------------------------ | ------------------------------------------------------------------------------------------- | | Timeout / mauvais site affiché | DNS pas propagé ou CNAME absent — [Configurer votre DNS](/deployer/configurer-votre-dns/) | | `404` de nginx | Aucune règle Ingress ne matche ce `host` — vérifiez l’orthographe du domaine dans l’Ingress | | `503` | Le Service cible n’existe pas, mauvais port, ou aucun pod prêt derrière | | Avertissement certificat | Certificat pas encore émis — voir ci-dessous | ### Certificat TLS bloqué L’émission Let’s Encrypt se suit de bout en bout : ```bash kubectl get certificates -n # READY doit passer à True kubectl get certificaterequests,orders,challenges -n kubectl describe challenge -n # le détail du blocage ``` Un challenge qui boucle = Let’s Encrypt n’arrive pas à joindre `http://votre-domaine/.well-known/acme-challenge/…`. Presque toujours : le DNS ne pointe pas (encore) vers la plateforme. Corrigez le [DNS](/deployer/configurer-votre-dns/), l’émission repart seule. ### Via NodePort ou LoadBalancer (TCP direct) Le trafic arrive **directement sur vos pods** : le label `exposition: public` est requis **sur les pods** — pas sur le Service. Vérifiez : ```bash kubectl get pods -n -L exposition # La colonne EXPOSITION doit afficher "public" ``` Le label se pose dans le template du Deployment ([exemple](/comprendre/isolation-reseau/#le-label-exposition-public)) — un label ajouté à la main sur un pod disparaît à son prochain remplacement. *** Toujours bloqué ? [Contactez le support](/depanner/contacter-le-support/) avec la sortie des commandes de cette page. # Configurer votre application > Injecter de la configuration et des secrets dans vos conteneurs avec les ConfigMaps et Secrets : variables d'environnement, fichiers montés, bonnes pratiques. Une même image de conteneur doit pouvoir tourner en dev et en prod : la configuration ne se met pas dans l’image, elle s’injecte au déploiement. Kubernetes fournit deux objets pour ça : * **ConfigMap** : configuration non sensible (URLs, options, fichiers de conf) ; * **Secret** : données sensibles (mots de passe, tokens, clés d’API). Exemple complet, prêt à copier : ```yaml apiVersion: v1 kind: ConfigMap metadata: name: mon-app-config data: APP_ENV: "production" nginx.conf: | server { listen 8080; } --- apiVersion: v1 kind: Secret metadata: name: mon-app-secrets stringData: DB_PASSWORD: "changez-moi" ``` ## Créer une ConfigMap et un Secret * Via Rancher 1. Dans votre namespace, ouvrez **Storage › ConfigMaps** (ou **Storage › Secrets**). 2. Cliquez sur **Create**, nommez l’objet, puis saisissez vos paires clé/valeur. 3. Validez avec **Create**. * Via kubectl ```bash # ConfigMap depuis des littéraux kubectl create configmap mon-app-config \ --from-literal=APP_ENV=production -n # ConfigMap depuis un fichier kubectl create configmap mon-app-config \ --from-file=nginx.conf -n # Secret kubectl create secret generic mon-app-secrets \ --from-literal=DB_PASSWORD='changez-moi' -n ``` ## Les injecter dans un conteneur ### En variables d’environnement ```yaml containers: - name: web image: mon-app:1.0 env: # Une clé précise d'un Secret - name: DB_PASSWORD valueFrom: secretKeyRef: name: mon-app-secrets key: DB_PASSWORD # Toutes les clés d'une ConfigMap d'un coup envFrom: - configMapRef: name: mon-app-config ``` ### En fichiers montés Idéal pour les fichiers de configuration complets : ```yaml containers: - name: web image: nginx:alpine volumeMounts: - name: config mountPath: /etc/nginx/conf.d volumes: - name: config configMap: name: mon-app-config items: - key: nginx.conf path: default.conf ``` Attention **Une modification de ConfigMap ou de Secret n’est pas vue par les pods qui tournent.** Les variables d’environnement sont figées au démarrage du conteneur ; les fichiers montés se mettent à jour avec un délai, mais la plupart des applications ne relisent pas leur configuration. Après un changement : ```bash kubectl rollout restart deployment/mon-app -n ``` ## Bonnes pratiques * **Jamais de secrets dans l’image ni dans le YAML committé.** Utilisez `stringData` localement, ou créez les Secrets via `kubectl create secret` / Rancher, hors de votre dépôt Git. * **Un objet par application** plutôt qu’une ConfigMap géante partagée : les rollouts restent ciblés. * **Quotas** : 50 ConfigMaps et 50 Secrets par namespace ([détails](/reference/quotas-et-limites/)). 💡 Débutant Kubernetes ? Documentation officielle : [ConfigMaps](https://kubernetes.io/docs/concepts/configuration/configmap/) et [Secrets](https://kubernetes.io/docs/concepts/configuration/secret/). ## Étapes suivantes * [Exposer votre application](/deployer/exposer-en-https/) ; * [Migrer depuis un docker-compose](/migrer/depuis-un-docker-compose/) — la conversion `environment:`/`env_file:` → ConfigMap/Secret y est détaillée. # Configurer votre DNS > Pointer votre domaine vers Kontainers : CNAME vers l'ingress, cas du domaine racine (apex), guides OVHcloud, Gandi, IONOS, Cloudflare et Route 53. Pour publier une application sur votre domaine, celui-ci doit pointer vers la plateforme. La règle est simple : ```text www.example.com → CNAME → ingress.k-bdkbsh.blackswift.hosting ``` Vous créez cet enregistrement chez **votre** fournisseur DNS (là où votre domaine est géré). Cette page couvre les fournisseurs les plus courants et le cas particulier du domaine racine. Ne pointez pas sur les adresses IP Ne créez pas d’enregistrement **A** vers les adresses IP qui se cachent derrière `ingress.k-bdkbsh.blackswift.hosting` si vous pouvez l’éviter : nous pouvons être amenés à les faire évoluer. Le CNAME (ou équivalent ALIAS) suit ces changements automatiquement, pas l’enregistrement A. ## Sous-domaine (www, app, api…) : un simple CNAME | Fournisseur | Marche à suivre | | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **OVHcloud** | Zone DNS de votre domaine → **Ajouter une entrée** → type **CNAME** → sous-domaine `www`, cible `ingress.k-bdkbsh.blackswift.hosting.` (avec le point final) | | **Gandi** | Domaine → **Enregistrements DNS** → **Ajouter** → type **CNAME**, nom `www`, valeur `ingress.k-bdkbsh.blackswift.hosting.` | | **IONOS (1&1)** | Domaines & SSL → votre domaine → **DNS** → **Ajouter un enregistrement** → **CNAME** | | **Cloudflare** | DNS → **Add record** → type **CNAME**, name `www`, target `ingress.k-bdkbsh.blackswift.hosting` — mode **DNS only** (nuage gris) recommandé au départ | | **Route 53 (AWS)** | Hosted zone → **Create record** → type **CNAME** | ## Domaine racine (apex) : `example.com` sans sous-domaine Le standard DNS n’autorise pas de CNAME sur un domaine racine. Le support d’une alternative (ALIAS / CNAME flattening) varie fortement selon le fournisseur — état vérifié : | Fournisseur | Apex vers un hostname externe ? | | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | | **Cloudflare** | ✅ CNAME accepté sur la racine, aplati automatiquement (tous les plans) | | **Gandi** | ✅ enregistrement **ALIAS** (LiveDNS) — n’oubliez pas le **point final** dans la cible | | **Route 53** | ❌ les ALIAS ne ciblent **que des ressources AWS** (ELB, CloudFront…) ou un enregistrement du même type de la zone — pas de hostname externe | | **OVHcloud** | ❌ pas d’ALIAS — utilisez la **redirection visible** `example.com → www.example.com`, ou les enregistrements A ci-dessous | | **IONOS** | ❌ pas d’ALIAS dans la zone DNS standard | Chez Cloudflare ou Gandi : créez le CNAME/ALIAS de `example.com` vers `ingress.k-bdkbsh.blackswift.hosting.` et c’est terminé. Gandi et DNSSEC L’ALIAS Gandi nécessite les serveurs de noms LiveDNS et désactive de fait la signature DNSSEC sur la racine du domaine — à savoir si vous avez activé DNSSEC. ### Votre fournisseur ne le propose pas (Route 53, OVHcloud, IONOS…) ⚠️ En dernier recours, créez des enregistrements **A** avec les adresses IP actuelles de l’ingress. Récupérez-les : ```bash dig +short ingress.k-bdkbsh.blackswift.hosting ``` À surveiller impérativement Ces adresses IP **peuvent changer**. Si vous pointez en direct, vous devez pouvoir réagir : surveillez nos annonces (Slack communautaire, page de statut — voir [Contacter le support](/depanner/contacter-le-support/)) et prévoyez un TTL court (300 s) sur ces enregistrements. Alternative sans risque : servez votre site sur `www.example.com` (CNAME) et configurez chez votre fournisseur une **redirection web** de `example.com` vers `www.example.com` — la plupart (OVHcloud, IONOS, Gandi) proposent cette fonction. ## Vérifier la propagation ```bash # Le CNAME répond-il ? dig +short www.example.com # Attendu : ingress.k-bdkbsh.blackswift.hosting. puis des adresses IP # Tester avant même la propagation DNS (remplacez IP par une IP de l'ingress) curl -H "Host: www.example.com" https:/// -k ``` La propagation prend de quelques secondes à quelques heures selon le TTL de votre zone. Pendant une migration, abaissez le TTL à 300 s la veille de la bascule. ## Étape suivante Votre DNS pointe vers la plateforme ? Créez l’Ingress avec certificat automatique : [Exposer votre application](/deployer/exposer-en-https/). # Déployer avec Helm > Installer des charts Helm publics sur Kontainers : les values à adapter aux contraintes plateforme (pas de LoadBalancer par défaut, limites mémoire, classes de stockage). [Helm](https://helm.sh) est le gestionnaire de paquets de Kubernetes : un **chart** décrit une application complète (Deployments, Services, PVC…) que vous installez et paramétrez en une commande. La plupart des applications open source en proposent un. ## Installer un chart ```bash # Installer Helm : https://helm.sh/docs/intro/install/ (brew install helm) # Ajouter le dépôt du chart, exemple avec Bitnami helm repo add bitnami https://charts.bitnami.com/bitnami helm repo update # Installer dans votre namespace, avec vos values helm upgrade --install mon-blog bitnami/wordpress \ -n \ -f values.yaml ``` `helm upgrade --install` crée la release si elle n’existe pas et la met à jour sinon — la seule commande à retenir. Les autres verbes utiles : ```bash helm list -n # les releases installées helm rollback mon-blog -n # revenir à la version précédente helm uninstall mon-blog -n ``` ## Les values à surveiller sur Kontainers Les charts publics sont écrits pour un cluster générique : quatre réglages demandent presque toujours votre attention. | Value (nom usuel) | À mettre | Pourquoi | | ------------------------------- | -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `service.type` | `ClusterIP` (+ un Ingress) | Beaucoup de charts proposent `LoadBalancer` par défaut — sur Kontainers c’est une [option payante limitée à 1](/reference/quotas-et-limites/) ; l’Ingress mutualisé est inclus | | `resources.requests` / `limits` | Requests réalistes, `limits.memory` **= requests.memory** | [La plateforme aligne la limite mémoire sur la request](/reference/mutations-et-politiques/) — un chart qui les différencie verra sa limit modifiée | | `persistence.storageClass` | `ceph-block-rwo` ou `ceph-filesystem-rwx` | [Les deux classes disponibles](/deployer/stockage-persistant/) — la valeur par défaut du chart n’existe pas ici | | `ingress.*` | `enabled: true`, `ingressClassName: nginx`, annotation `cert-manager.io/issuer: letsencrypt` | Pour l’[exposition HTTPS](/deployer/exposer-en-https/) directement via le chart | Exemple de `values.yaml` pour un chart typique : ```yaml service: type: ClusterIP resources: requests: cpu: 250m memory: 512Mi limits: memory: 512Mi persistence: enabled: true storageClass: ceph-block-rwo size: 10Gi ingress: enabled: true ingressClassName: nginx hostname: app.example.com annotations: cert-manager.io/issuer: letsencrypt tls: true ``` ## Si l’installation échoue Deux causes plateforme reviennent souvent avec les charts publics : * **Le chart déploie un composant privilégié** (agent nœud, DaemonSet…) → rejet [Pod Security ou quota](/depanner/aucun-pod-ne-se-cree/). Cherchez une option pour désactiver le composant en cause ; * **Le chart attend une StorageClass inexistante** → PVC en `Pending`. Fixez `storageClass` comme ci-dessus. Le diagnostic est le même que pour tout déploiement : [checklist](/depanner/checklist-de-diagnostic/), en ajoutant : ```bash helm status mon-blog -n helm get values mon-blog -n # les values réellement appliquées ``` Astuce Pour un premier déploiement WordPress, notre [exemple en manifests bruts](/exemples/wordpress/) — testé sur la plateforme — est plus simple à appréhender qu’un chart ; le chart devient intéressant quand vous voulez industrialiser. # Déployer un conteneur > Déployer une image de conteneur sur Kontainers via Rancher ou en YAML : Deployments, StatefulSets et les spécificités plateforme à connaître. Le manifest complet, prêt à copier : ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: nginx-demo spec: selector: matchLabels: app: nginx-demo template: metadata: labels: app: nginx-demo spec: containers: - name: web image: nginx:alpine resources: requests: cpu: 100m memory: 200Mi ``` 💡 Débutant Kubernetes ? Un **Pod** est l’unité d’exécution minimale sur Kubernetes ; il regroupe un ou plusieurs conteneurs. Dans la pratique, on crée rarement un Pod « nu » : on préfère un **Deployment**, qui crée les Pods, maintient le nombre désiré de répliques et gère les mises à jour progressives (*roll-outs*) et retours arrière (*roll-backs*). Pour approfondir : [Pods](https://kubernetes.io/docs/concepts/workloads/pods/) et [Deployments](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/). ## Déployer `nginx:alpine` * Via l'interface graphique de Rancher 1. Ouvrez le menu **Workloads › Deployments** dans votre namespace. ![Créer un Deployment dans Rancher](/_astro/deployer-un-conteneur-rancher-workloads.B-V6NhtJ_1X83pI.webp) 2. Cliquez sur **Create** puis remplissez : * **Name** : `nginx-demo` * **Container name** : `web` * **Container Image** : `nginx:alpine` ![Créer un Deployment dans Rancher](/_astro/deployer-un-conteneur-rancher-create.BR32Y_-G_Rv57S.webp) 3. Validez avec **Create**. Votre workload apparaît en vert quand le Pod est prêt. ![Créer un Deployment dans Rancher](/_astro/deployer-un-conteneur-rancher-resultat.BQSC2W7h_NhihF.webp) * Via un manifest YAML 1. Créez un fichier `nginx-deployment.yaml` avec le manifest en tête de page. 2. Appliquez-le dans votre namespace : ```bash kubectl apply -f nginx-deployment.yaml -n ``` 3. Vérifiez : ```bash kubectl get pods -n ``` ## Les spécificités Kontainers à connaître * **`imagePullPolicy` est forcé à `Always`** : l’image est re-vérifiée auprès du registre à chaque démarrage de pod. Conséquence importante : si votre registre est indisponible (ou un token expiré), vos pods ne peuvent plus **re**démarrer. * **Des requests par défaut sont injectées** si vous n’en déclarez pas (0,1 vCPU / 200 Mi) — et elles sont [facturées](/reference/facturation/). Déclarez toujours vos `resources`, comme dans l’exemple en tête de page. * **La limite mémoire est toujours égale à la request** : au-delà, le conteneur est OOMKilled. Dimensionnez la request sur votre pic réel. * **Quota : 20 pods par namespace** (augmentable via [ticket](/depanner/contacter-le-support/)). Le détail de ces comportements : [Mutations automatiques et politiques](/reference/mutations-et-politiques/). ## Applications avec état : StatefulSets Pour les applications qui exigent une identité stable par réplique (bases de données, files de messages…), Kubernetes propose les [StatefulSets](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/) — disponibles sur Kontainers (quota : 20 par namespace). Chaque réplique y reçoit un nom stable (`db-0`, `db-1`…) et son propre [volume persistant](/deployer/stockage-persistant/). Pour PostgreSQL, préférez l’opérateur managé — voir [Permissions et RBAC](/reference/permissions-et-rbac/#postgresql-manag%C3%A9-cnpg) *(guide dédié à venir)*. ## Étapes suivantes * [Configurer votre application](/deployer/configmaps-et-secrets/) — variables d’environnement et secrets ; * [Exposer votre application](/deployer/exposer-en-https/) ; * [Mon pod est en erreur](/depanner/pod-en-erreur/) — si le déploiement se passe mal. # Exposer votre application > Rendre votre application joignable, dans le cluster ou sur Internet : Service ClusterIP, Ingress HTTPS avec certificat automatique, NodePort et LoadBalancer. Cette page vous guide pour rendre vos applications accessibles — à l’intérieur du cluster ou depuis Internet, en HTTPS avec un certificat automatique. Kubernetes en 2 phrases * **Service** = petit **load balancer interne** : il donne une IP + un nom DNS stable pour joindre vos Pods. * **Ingress** = **reverse-proxy frontal** (HTTPS) : il reçoit les requêtes venant d’Internet et les renvoie vers un Service. ## 1. Rendre l’application joignable dans le cluster (Service) * Via Rancher 1. Ouvrez **Service Discovery › Services** dans votre namespace. 2. Cliquez sur **Create** et choisissez **ClusterIP**. 3. Remplissez : * **Name** : `web` * **Selectors** : `app=nginx-demo` (les labels de vos pods) * **Port** : 80 (port d’écoute de votre conteneur) 4. Validez avec **Create**. * Via kubectl ```bash kubectl expose deploy/nginx-demo --name web --port 80 -n ``` Ou en YAML : ```yaml apiVersion: v1 kind: Service metadata: name: web spec: type: ClusterIP selector: app: nginx-demo ports: - port: 80 targetPort: 80 ``` Votre application répond maintenant, depuis n’importe quel pod de vos namespaces, sur : ```text http://web..svc.cluster.local ``` ## 2. Publier en HTTPS sur votre domaine (Ingress) ### Étape 1 : pointez votre domaine Créez un enregistrement **CNAME** chez votre fournisseur DNS : ```text www.example.com → ingress.k-bdkbsh.blackswift.hosting ``` Le guide détaillé par fournisseur (OVHcloud, Gandi, Cloudflare…), y compris le cas du domaine racine (`example.com` sans `www`), est ici : [Configurer votre DNS](/deployer/configurer-votre-dns/). ### Étape 2 : créez l’Ingress avec certificat automatique * Via kubectl ```bash kubectl create ingress web-public -n \ --rule "www.example.com/*=web:80,tls=web-tls" \ --annotation cert-manager.io/issuer=letsencrypt ``` * Via un manifest YAML ```yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: web-public annotations: cert-manager.io/issuer: letsencrypt spec: rules: - host: www.example.com http: paths: - path: / pathType: Prefix backend: service: name: web port: number: 80 tls: - hosts: ["www.example.com"] secretName: web-tls ``` * Certificat personnalisé Si vous avez déjà un certificat (wildcard d’entreprise…), créez le secret TLS vous-même et omettez l’annotation cert-manager : ```bash kubectl -n create secret tls web-tls \ --cert=fullchain.pem --key=privkey.pem ``` Puis référencez `web-tls` dans la section `tls` de l’Ingress. L’annotation `cert-manager.io/issuer: letsencrypt` déclenche l’émission automatique d’un certificat Let’s Encrypt (généralement en 1 à 2 minutes, une fois le DNS propagé). Suivez l’émission : ```bash kubectl get certificates,challenges -n ``` > ✅ **Test** : ouvrez `https://www.example.com` — votre application s’affiche, avec un certificat valide. Note Aucun label particulier n’est requis sur vos pods pour l’exposition via Ingress : le trafic transite par le reverse-proxy de la plateforme. Le label `exposition: public` ne concerne que le NodePort et le LoadBalancer ci-dessous. Au sujet du retrait du projet ingress-nginx Le projet open source ingress-nginx a été arrêté en mars 2026 — vous l’avez peut-être lu. C’est anticipé et géré : la plateforme s’appuie sur les images maintenues par **Chainguard** (programme EmeritOSS), spécialiste des images « zéro CVE », qui corrige les vulnérabilités et met à jour les dépendances. Vos Ingress et leurs annotations continuent de fonctionner à l’identique, sans aucune action de votre part. ## 3. Exposer un protocole non-HTTP ### Option A — NodePort (inclus, quota 10) Pour du TCP simple (MQTT, SSH, jeu…) : ```bash kubectl -n expose deploy/ \ --type=NodePort --port= --name= ``` ⚠️ **Deux conditions** pour que le service soit joignable depuis Internet : 1. Ajoutez le label **`exposition: public` sur vos pods** (pas sur le Service) — sans lui, la [politique réseau](/comprendre/isolation-reseau/) bloque le trafic externe ; 2. Relevez le port attribué (`kubectl get svc -n `) et connectez-vous sur `nodeport.k-bdkbsh.blackswift.hosting:`. À savoir : **un NodePort ne peut pas être restreint par adresse IP source** — une fois exposé, le port répond à tout Internet. Si vous devez limiter l’accès à certaines IP (VPN d’entreprise, partenaires…), utilisez un LoadBalancer et son `loadBalancerSourceRanges` (ci-dessous), ou gérez la restriction dans l’application. ### Option B — LoadBalancer (option payante, IP dédiée) Pour une IP publique dédiée avec le port de votre choix : ```yaml apiVersion: v1 kind: Service metadata: name: mon-service-public spec: type: LoadBalancer externalTrafficPolicy: Cluster # recommandé — voir ci-dessous loadBalancerSourceRanges: # optionnel : IP sources autorisées - 203.0.113.0/24 - 198.51.100.42/32 selector: app: mon-app # pods qui doivent porter le label exposition: public ports: - port: 5432 ``` * Tarif : [0,02 €/h par LoadBalancer](/reference/facturation/#loadbalancer) ; * Quota : 1 par namespace — si votre namespace affiche encore `services.loadbalancers: 0`, demandez l’activation via [ticket](/depanner/contacter-le-support/) ; * Le label `exposition: public` est requis sur les pods ciblés ; * **`loadBalancerSourceRanges`** restreint l’accès aux plages d’IP listées (CIDR) — c’est l’avantage du LoadBalancer sur le NodePort, qui ne le permet pas. Omettez le champ pour un service ouvert à tous ; * **Gardez `externalTrafficPolicy: Cluster`** (notre recommandation) : les nœuds du cluster sont répartis sur plusieurs régions, et en mode `Local` le LoadBalancer ne peut servir que les pods hébergés dans **sa** région — si vos pods sont planifiés ailleurs, le service ne répond plus. En mode `Cluster`, le trafic est routé vers vos pods où qu’ils soient ; la contrepartie est que l’adresse IP source vue par l’application est celle du réseau interne, pas celle du client (la restriction `loadBalancerSourceRanges` reste appliquée en amont, elle). ## Récapitulatif | Besoin | Objet K8s | Accès | | --------------------- | ----------------------------------------- | -------------------------------------------------------- | | Interne au cluster | Service ClusterIP | `..svc.cluster.local` | | HTTP(S) public | Ingress + Service | votre domaine ([CNAME](/deployer/configurer-votre-dns/)) | | TCP public mutualisé | NodePort + label `exposition: public` | `nodeport.k-bdkbsh.blackswift.hosting:` | | TCP public, IP dédiée | LoadBalancer + label `exposition: public` | IP dédiée (0,02 €/h) | Un problème d’exposition ? Voir [Problème de connexion ou de réseau](/depanner/checklist-de-diagnostic/). # Jobs et CronJobs > Exécuter des tâches ponctuelles et planifiées sur Kontainers : migrations de schéma, maintenance, sauvegardes applicatives — pour un coût quasi nul. Tout ne mérite pas un Deployment qui tourne en permanence. Pour une tâche qui s’exécute puis se termine — migration de base, import, purge, dump — Kubernetes propose le **Job** (exécution unique) et le **CronJob** (planifiée). Et comme [seuls les pods Running sont facturés](/reference/facturation/), à l’heure entamée, une tâche quotidienne ne coûte qu’une heure par jour — environ 25 fois moins qu’un pod permanent. ## Job : une exécution unique Cas d’usage type : une migration de schéma avant une mise en production. ```yaml apiVersion: batch/v1 kind: Job metadata: name: migrate-v2 spec: backoffLimit: 3 # nb de tentatives en cas d'échec ttlSecondsAfterFinished: 3600 # auto-nettoyage 1 h après la fin (important !) template: spec: restartPolicy: Never containers: - name: migrate image: registry.exemple.com/acme/mon-app:2.0.0 command: ["./migrate.sh"] env: - name: DATABASE_URL valueFrom: secretKeyRef: {name: ma-base-app, key: uri} resources: requests: {cpu: 100m, memory: 256Mi} ``` ```bash kubectl apply -f migrate.yaml -n kubectl wait --for=condition=complete job/migrate-v2 -n --timeout=300s kubectl logs job/migrate-v2 -n ``` Toujours mettre `ttlSecondsAfterFinished` Un Job terminé laisse son pod `Completed` — qui ne coûte rien, mais **compte dans le quota de 20 pods** du namespace. Sans TTL, les jobs s’accumulent jusqu’à bloquer vos déploiements ([symptôme](/depanner/aucun-pod-ne-se-cree/)). Le TTL supprime le Job et son pod automatiquement. ## CronJob : l’exécution planifiée Cas d’usage type : un dump de base quotidien ([exemple complet](/operer/sauvegardes-et-restauration/#compl%C3%A9ter-avec-des-sauvegardes-applicatives)), une purge de fichiers, un rapport hebdomadaire. ```yaml apiVersion: batch/v1 kind: CronJob metadata: name: purge-quotidienne spec: schedule: "30 4 * * *" # tous les jours à 4h30 (heure UTC) concurrencyPolicy: Forbid # jamais deux exécutions en parallèle successfulJobsHistoryLimit: 3 # ne garder que les 3 derniers pods réussis failedJobsHistoryLimit: 3 jobTemplate: spec: backoffLimit: 2 template: spec: restartPolicy: Never containers: - name: purge image: registry.exemple.com/acme/mon-app:2.0.0 command: ["./purge-old-files.sh"] resources: requests: {cpu: 100m, memory: 128Mi} ``` ```bash kubectl get cronjobs -n # Déclencher manuellement sans attendre l'horaire (pour tester) kubectl create job purge-test --from=cronjob/purge-quotidienne -n ``` Les `HistoryLimit` jouent le même rôle que le TTL des Jobs : ils bornent le nombre de pods terminés conservés — gardez-les bas. Note Le champ `schedule` est en **UTC** : `30 4 * * *` = 5h30 ou 6h30 heure de Paris selon la saison. À ne pas confondre avec le format à 6 champs (secondes en tête) des [ScheduledBackups CNPG](/deployer/postgresql-manage/#sauvegardes-self-service). ## Ce qui s’applique aussi aux Jobs Un pod de Job est un pod comme un autre sur Kontainers : * [Requests injectées](/reference/mutations-et-politiques/) si absentes — déclarez-les ; * Limite mémoire = request : un job de build ou d’import gourmand doit demander sa vraie consommation, sinon [OOMKilled](/depanner/pod-en-erreur/#oomkilled) ; * Quotas : **20 Jobs et 10 CronJobs** par namespace ([référence](/reference/quotas-et-limites/)), et les pods actifs comptent dans les 20 pods ; * Le coût : la facturation est [à l’heure entamée](/reference/facturation/) — un CronJob quotidien de quelques minutes à 1 bundle ≈ 1 h facturée par jour, soit **≈ 0,12 € par mois**. Comparez au même script dans un Deployment qui dort : 2,88 €/mois. Et des exécutions rapprochées dans la même heure ne s’additionnent pas tant qu’elles ne se chevauchent pas (la base horaire est le maximum observé). # PostgreSQL managé (CNPG) > Créer un cluster PostgreSQL haute disponibilité sur Kontainers avec l'opérateur CloudNativePG : connexion, réplicas, sauvegardes vers votre stockage S3. Kontainers intègre l’opérateur [CloudNativePG](https://cloudnative-pg.io) (CNPG) : vous décrivez votre base PostgreSQL en YAML, l’opérateur s’occupe du reste — réplication, bascule automatique, secrets de connexion, sauvegardes. Plus fiable et plus simple qu’un conteneur `postgres` à gérer soi-même. **Le manifest testé sur la plateforme**, prêt à copier : ```yaml apiVersion: postgresql.cnpg.io/v1 kind: Cluster metadata: name: ma-base spec: instances: 2 # 1 primaire + 1 réplica avec bascule auto storage: size: 10Gi storageClass: ceph-block-rwo resources: requests: cpu: 250m memory: 512Mi limits: memory: 512Mi bootstrap: initdb: database: app owner: app ``` ```bash kubectl apply -f ma-base.yaml -n kubectl get clusters.postgresql.cnpg.io -n # NAME INSTANCES READY STATUS PRIMARY # ma-base 2 2 Cluster in healthy state ma-base-1 ``` Le cluster est prêt en une à deux minutes. ## Se connecter depuis votre application L’opérateur génère tout ce qu’il faut : * **trois Services** : `ma-base-rw` (écriture, pointe le primaire), `ma-base-ro` (lecture, les réplicas), `ma-base-r` (lecture, toutes instances) ; * **un Secret** `ma-base-app` contenant identifiants et chaînes de connexion prêtes à l’emploi. Dans votre Deployment : ```yaml env: - name: DATABASE_URL valueFrom: secretKeyRef: name: ma-base-app key: uri # postgresql://app:•••@ma-base-rw:5432/app ``` Pour explorer la base à la main : ```bash kubectl run psql --rm -it --restart=Never -n \ --image=postgres:17-alpine \ --env="PGPASSWORD=$(kubectl get secret ma-base-app -n -o jsonpath='{.data.password}' | base64 -d)" \ -- psql -h ma-base-rw -U app -d app ``` ## Sauvegardes vers votre stockage S3 Votre base est déjà couverte par la [sauvegarde plateforme quotidienne](/operer/sauvegardes-et-restauration/) (volumes inclus, rétention 10 jours, restauration via ticket). Pour aller plus loin — sauvegarde continue avec **restauration à un instant précis (PITR)**, rétention sur mesure, restauration en autonomie — CNPG sait archiver vers **votre propre stockage objet S3** : un bucket OVHcloud Object Storage ou Scaleway, par exemple. Créez le secret d’accès au bucket, puis ajoutez la section `backup` au Cluster : ```yaml apiVersion: v1 kind: Secret metadata: name: backup-s3-credentials stringData: ACCESS_KEY_ID: "votre-access-key" SECRET_ACCESS_KEY: "votre-secret-key" --- # À ajouter dans le spec du Cluster : backup: barmanObjectStore: destinationPath: s3://mon-bucket/ma-base endpointURL: https://s3.gra.io.cloud.ovh.net # OVH — ou Scaleway : # https://s3.fr-par.scw.cloud s3Credentials: accessKeyId: name: backup-s3-credentials key: ACCESS_KEY_ID secretAccessKey: name: backup-s3-credentials key: SECRET_ACCESS_KEY retentionPolicy: "30d" ``` L’archivage continu des WAL démarre aussitôt. Sauvegarde à la demande : ```yaml apiVersion: postgresql.cnpg.io/v1 kind: Backup metadata: name: avant-migration-v2 spec: cluster: name: ma-base ``` Et en planifié (ici, tous les jours à 3h00) : ```yaml apiVersion: postgresql.cnpg.io/v1 kind: ScheduledBackup metadata: name: ma-base-quotidien spec: schedule: "0 0 3 * * *" # format cron à 6 champs (secondes en premier) backupOwnerReference: self cluster: name: ma-base ``` ```bash kubectl get backups -n ``` Note La restauration depuis le bucket se fait en créant un nouveau Cluster avec `bootstrap.recovery` (y compris à un instant précis, grâce aux WAL archivés) — voir la [documentation CNPG](https://cloudnative-pg.io/documentation/current/backup/) et, en cas de doute, [le support vous accompagne](/depanner/contacter-le-support/). ## Ce qu’il faut savoir * **Dimensionnement** : chaque instance consomme ses requests ([facturées](/reference/facturation/)) et son volume — `instances: 2` = 2 × CPU/RAM et 2 × stockage. Pour du dev, `instances: 1` est très bien ; * **La bascule automatique** (avec `instances: 2+`) remplace le primaire défaillant en quelques secondes — votre application doit juste savoir se reconnecter ; * **Extensions** : ajoutez-les au bootstrap, par exemple `postInitApplicationSQL: ["CREATE EXTENSION IF NOT EXISTS vector;"]` pour pgvector ; * **Version PostgreSQL** : celle par défaut de l’opérateur, ou fixez-la avec `imageName: ghcr.io/cloudnative-pg/postgresql:` ; * **Mises à jour mineures** : gérées par l’opérateur au fil des rolling updates ; * Le stockage compte dans vos [quotas PVC](/reference/quotas-et-limites/) (les volumes apparaissent dans `kubectl get pvc`). ## Superviser ```bash kubectl get clusters.postgresql.cnpg.io -n # état global kubectl describe cluster ma-base -n # événements, bascules kubectl logs ma-base-1 -n # logs PostgreSQL ``` L’opérateur expose aussi des métriques Prometheus par instance, visibles dans votre [monitoring](/operer/acceder-au-monitoring/). # Construire, pousser et déployer vos images > Le cycle complet d'une image privée : build Docker, push vers un registre (Froggit, GitLab, GHCR), pull secret et déploiement sur Kontainers. Les guides précédents déploient des images publiques (`nginx:alpine`…). Pour votre propre application, il faut construire une image, la stocker dans un **registre**, et permettre à Kontainers de l’y récupérer. Le cycle complet : ```text docker build → docker push → pull secret → Deployment (local/CI) (registre) (namespace) (Kontainers) ``` ## 1. Choisir un registre N’importe quel registre d’images fonctionne. Suggestions : | Registre | Pour qui | | --------------------------------------- | ------------------------------------------------------------------- | | **[Froggit](https://froggit.fr)** | GitLab managé **français** — cohérent avec un hébergement souverain | | **gitlab.com** | Si votre code y est déjà (registre intégré) | | **GitHub Container Registry** (ghcr.io) | Si votre code est sur GitHub | | **Docker Hub** | Images publiques, comptes personnels | Les exemples ci-dessous utilisent un registre GitLab (Froggit ou gitlab.com) — adaptez simplement les URLs pour un autre fournisseur. ## 2. Construire et pousser l’image Depuis la racine de votre projet (là où vit le `Dockerfile`) : ```bash # Construire (--platform garantit une image compatible avec le cluster, # indispensable depuis un Mac Apple Silicon) docker build --platform linux/amd64 -t registry.exemple.com/acme/mon-app:1.0.0 . # Se connecter au registre puis pousser docker login registry.exemple.com docker push registry.exemple.com/acme/mon-app:1.0.0 ``` Taguez immuablement Préférez un tag de version (`1.0.0`, un SHA de commit) à `latest`. Sur Kontainers, [`imagePullPolicy` est forcé à `Always`](/reference/mutations-et-politiques/) : avec `latest`, chaque redémarrage de pod peut ramasser une image différente. En CI, cette étape est généralement faite par votre pipeline (GitLab CI propose des variables prêtes à l’emploi : `$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA`). ## 3. Créer le token de lecture Kontainers a besoin d’identifiants **en lecture seule** sur votre registre. Ne réutilisez jamais votre compte personnel. Sur GitLab (Froggit ou gitlab.com) : projet → **Settings › Access tokens** → **Add new token** : * **Nom** : `kontainers-pull` * **Rôle** : Reporter * **Scope** : `read_registry` uniquement * **Expiration** : selon votre politique — et notez-la quelque part (voir l’avertissement plus bas) Copiez le token affiché : il ne sera plus jamais montré. ## 4. Créer le pull secret dans votre namespace ```bash kubectl -n create secret docker-registry registry-pull \ --docker-server=registry.exemple.com \ --docker-username=kontainers-pull \ --docker-password='' ``` ## 5. Référencer le secret dans vos Deployments ```yaml spec: template: spec: imagePullSecrets: - name: registry-pull containers: - name: app image: registry.exemple.com/acme/mon-app:1.0.0 resources: requests: {cpu: 100m, memory: 256Mi} ``` Déployez, et vérifiez que l’image se télécharge : ```bash kubectl get pods -n # ImagePullBackOff ? → kubectl describe pod pour le message exact ``` Un token expiré casse les redémarrages, pas seulement les déploiements Avec [`imagePullPolicy: Always`](/reference/mutations-et-politiques/), le registre est contacté à **chaque** démarrage de pod. Si votre token expire, ce ne sont pas seulement vos prochains déploiements qui échouent : **un simple redémarrage d’un pod qui tournait très bien tombera en `ImagePullBackOff`**. Mettez une date d’expiration longue ou un rappel calendrier, et surveillez [cette erreur](/depanner/pod-en-erreur/#imagepullbackoff). ## Registres alternatifs : les mêmes commandes Seul le `--docker-server` et le format des identifiants changent : | Registre | `--docker-server` | Username / password | | ---------------------------- | ----------------------------- | --------------------------------------- | | GitLab (Froggit, gitlab.com) | `registry.` | nom du token / token | | GHCR | `ghcr.io` | user GitHub / PAT scope `read:packages` | | Docker Hub | `https://index.docker.io/v1/` | user / access token | # Stockage persistant > Créer et monter des volumes persistants sur Kontainers : choisir entre RWO et RWX, dimensionner, agrandir à chaud. Par défaut, les données écrites dans un conteneur sont **éphémères** : elles disparaissent quand le pod redémarre. Pour conserver vos données, demandez un volume persistant via un **PersistentVolumeClaim (PVC)**. Exemple complet, prêt à copier : ```yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: mes-donnees spec: accessModes: - ReadWriteOnce storageClassName: ceph-block-rwo resources: requests: storage: 10Gi ``` ## Choisir sa classe de stockage **La règle en une ligne : un seul pod écrit → `ceph-block-rwo` ; plusieurs pods lisent/écrivent en même temps → `ceph-filesystem-rwx`.** | | `ceph-block-rwo` (défaut) | `ceph-filesystem-rwx` | | ------------ | -------------------------------------------- | ---------------------------------------------------------- | | Mode d’accès | ReadWriteOnce — un seul pod écrivain | ReadWriteMany — accès partagé | | Typique pour | Bases de données (MySQL, PostgreSQL, Redis…) | Fichiers partagés entre replicas (uploads WordPress, CMS…) | | Performance | Optimale (stockage bloc) | Normale (système de fichiers distribué) | Les deux classes sont répliquées, chiffrées, [sauvegardées chaque nuit](/operer/sauvegardes-et-restauration/) et au même [tarif](/reference/facturation/#stockage-persistant). Quotas : 10 PVC et 100 Gi par classe ([détails](/reference/quotas-et-limites/#classes-de-stockage)). ## Créer un volume * Via Rancher 1. Dans votre namespace : **Storage › PersistentVolumeClaims** → **Create**. 2. Nommez le PVC, choisissez la **Storage Class** et le mode d’accès (**Single-Node Read/Write** pour RWO, **Many-Node Read/Write** pour RWX), puis la taille. 3. Validez avec **Create** — le volume est provisionné en quelques secondes. * Via kubectl Appliquez le manifest en tête de page : ```bash kubectl apply -f pvc.yaml -n kubectl get pvc -n ``` Le `STATUS` passe à `Bound` en quelques secondes. ## Monter le volume dans un pod Exemple avec MySQL (un écrivain unique → RWO) : ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: mysql spec: replicas: 1 selector: matchLabels: app: mysql template: metadata: labels: app: mysql spec: containers: - name: mysql image: mysql:8.4 env: - name: MYSQL_ROOT_PASSWORD valueFrom: secretKeyRef: name: mysql-secret key: password resources: requests: cpu: 250m memory: 512Mi volumeMounts: - name: data mountPath: /var/lib/mysql volumes: - name: data persistentVolumeClaim: claimName: mes-donnees ``` Attention Avec un volume **RWO**, gardez `replicas: 1` et utilisez la stratégie `Recreate` si vos rollouts échouent : deux pods ne peuvent pas monter le même volume RWO simultanément sur des nœuds différents. ## Agrandir un volume Un PVC peut être **agrandi à chaud, jamais réduit**. * Via Rancher **Storage › PersistentVolumeClaims** → menu **⋯** du PVC → augmentez la taille → enregistrez. * Via kubectl ```bash kubectl edit pvc mes-donnees -n # spec.resources.requests.storage: 20Gi ``` L’espace supplémentaire apparaît dans le pod en quelques instants (certaines applications nécessitent un redémarrage pour le voir). ## Bon à savoir * **Vos volumes sont sauvegardés automatiquement chaque nuit** — périmètre et procédure de restauration dans [Sauvegardes et restauration](/operer/sauvegardes-et-restauration/) ; * Le stockage est facturé sur la **capacité provisionnée** : commencez petit, agrandissez au besoin ; * Un PVC en statut `Pending` prolongé ? Vérifiez le [quota de stockage](/reference/quotas-et-limites/) : `kubectl describe pvc mes-donnees -n `. ## Étapes suivantes * [WordPress complet](/exemples/wordpress/) — un exemple réel combinant RWO et RWX ; * [Sauvegardes et restauration](/operer/sauvegardes-et-restauration/). # CI/CD : runner GitLab dans votre namespace > Déployer un runner GitLab dans votre namespace Kontainers : le gestionnaire s'authentifie via le ServiceAccount pré-provisionné, les jobs restent sans privilège. Héberger votre runner GitLab **dans votre namespace** a deux vertus : vos jobs tournent au plus près de vos applications, et le **gestionnaire du runner** s’authentifie via le ServiceAccount `bs-namespace-member` pré-provisionné — aucun credential à créer pour faire tourner l’infrastructure de CI elle-même. ## Le principe : des privilèges au bon endroit Un runner GitLab sur Kubernetes, ce sont deux étages : | Étage | Rôle | ServiceAccount | | ----------------------------------- | ------------------------------------- | -------------------------------------------- | | Le **gestionnaire** (pod permanent) | Crée et supervise un pod par job CI | `bs-namespace-member` — il a besoin de l’API | | Les **pods de jobs** (éphémères) | Exécutent vos scripts (build, tests…) | `default` — **aucun droit API** | C’est le moindre privilège : un job de build compromis (dépendance malveillante, script tiers…) ne peut rien faire sur le cluster. Vos jobs de **déploiement**, qui ont légitimement besoin de l’API, s’authentifient explicitement — voir plus bas. ## Déployer le runner Avec le [chart Helm officiel](https://docs.gitlab.com/runner/install/kubernetes/) ([Helm sur Kontainers](/deployer/deployer-avec-helm/)) : values-runner.yaml ```yaml gitlabUrl: https://gitlab.com/ # ou votre instance (Froggit…) rbac: create: false # on n'a pas les droits de créer des rôles… serviceAccount: create: false name: bs-namespace-member # …et pas besoin : celui-ci est fourni # (gestionnaire uniquement — les pods de # jobs restent sur le SA default) concurrent: 2 runners: config: | [[runners]] [runners.kubernetes] namespace = "{{ .Release.Namespace }}" cpu_request = "100m" memory_request = "256Mi" memory_limit = "256Mi" ephemeral_storage_request = "1Gi" ephemeral_storage_limit = "2Gi" resources: requests: {cpu: 100m, memory: 128Mi} limits: {memory: 128Mi} ``` ```bash # Le token vient de GitLab : Settings › CI/CD › Runners › New project runner kubectl create secret generic gitlab-runner-secret -n \ --from-literal=runner-token='glrt-…' --from-literal=runner-registration-token='' helm repo add gitlab https://charts.gitlab.io helm upgrade --install runner gitlab/gitlab-runner \ -n -f values-runner.yaml \ --set runners.secret=gitlab-runner-secret ``` Le runner apparaît dans GitLab, et chaque job CI devient un pod dans votre namespace — soumis aux mêmes [quotas](/reference/quotas-et-limites/) que le reste (comptez-les dans votre budget de 20 pods). ## Un `.gitlab-ci.yml` qui déploie Les pods de jobs n’ayant aucun droit API, le job de déploiement s’authentifie avec un **[kubeconfig permanent](/operer/ci-cd-avec-le-serviceaccount/)** stocké en variable CI (type *File*, masquée et protégée) : ```yaml deploy: stage: deploy image: bitnami/kubectl:latest script: - export KUBECONFIG=$KUBE_CONFIG_FILE - kubectl set image deploy/boutique app=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA - kubectl rollout status deploy/boutique --timeout=120s environment: production rules: - if: $CI_COMMIT_TAG ``` Seul ce job reçoit la variable : les jobs de build et de test du même pipeline restent sans accès au cluster. ## Les contraintes à connaître * **Pas de Docker-in-Docker privilégié** : la [politique de sécurité](/reference/mutations-et-politiques/#politiques-de-rejet) interdit les pods privilégiés. Pour construire des images dans la CI, utilisez [kaniko](https://github.com/GoogleContainerTools/kaniko), qui build sans privilèges : ```yaml build: stage: build image: name: gcr.io/kaniko-project/executor:debug entrypoint: [""] script: - /kaniko/executor --context $CI_PROJECT_DIR --destination $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA ``` * **Stockage éphémère limité à 2 Gi par conteneur** : les gros builds (caches npm/gradle…) peuvent le dépasser — montez un [PVC de cache](/deployer/stockage-persistant/) via `[runners.kubernetes.volumes.pvc]` si besoin ; * **Runner d’équipe multi-environnements** : hébergez-le dans un namespace `tooling` — [le réseau intra-organisation](/comprendre/isolation-reseau/) permet à ses jobs de joindre dev et prod. Côté déploiement, créez un [kubeconfig permanent](/operer/ci-cd-avec-le-serviceaccount/) **par namespace cible** : une variable CI par environnement, des périmètres étanches. # n8n : automatisation et agents IA > Héberger votre instance n8n sur Kontainers : workflows, webhooks et agents IA en HTTPS, pour moins de 6 € par mois — manifests testés. [n8n](https://n8n.io) est l’outil d’automatisation le plus populaire du moment : workflows visuels, 400+ intégrations, agents IA. L’auto-héberger sur Kontainers vous donne une instance **souveraine, sauvegardée et en HTTPS** — pour moins de 6 € par mois. **Les manifests de cette page ont été déployés et testés sur la plateforme.** ## Le déploiement Une instance mono-replica avec sa base SQLite sur volume persistant — le mode le plus simple, largement suffisant pour un usage individuel ou une petite équipe : ```yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: n8n-data spec: accessModes: [ReadWriteOnce] storageClassName: ceph-block-rwo resources: requests: storage: 2Gi --- apiVersion: apps/v1 kind: Deployment metadata: name: n8n spec: replicas: 1 # SQLite = un seul écrivain strategy: type: Recreate # volume RWO : jamais 2 pods simultanés selector: matchLabels: {app: n8n} template: metadata: labels: {app: n8n} spec: securityContext: fsGroup: 1000 # n8n tourne en user 1000 : accès au volume containers: - name: n8n image: n8nio/n8n:latest ports: [{name: http, containerPort: 5678}] env: - name: N8N_HOST value: n8n.example.com # ← votre domaine - name: WEBHOOK_URL value: https://n8n.example.com/ # ← URL publique des webhooks - name: GENERIC_TIMEZONE value: Europe/Paris resources: requests: {cpu: 100m, memory: 512Mi} volumeMounts: - name: data mountPath: /home/node/.n8n readinessProbe: httpGet: {path: /healthz, port: http} initialDelaySeconds: 10 periodSeconds: 5 volumes: - name: data persistentVolumeClaim: {claimName: n8n-data} --- apiVersion: v1 kind: Service metadata: name: n8n spec: selector: {app: n8n} ports: [{name: http, port: 5678}] --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: n8n annotations: cert-manager.io/issuer: letsencrypt nginx.ingress.kubernetes.io/proxy-body-size: "16m" # uploads de fichiers spec: rules: - host: n8n.example.com http: paths: - path: / pathType: Prefix backend: service: {name: n8n, port: {number: 5678}} tls: - hosts: [n8n.example.com] secretName: n8n-tls ``` ## Mise en route ```bash kubectl apply -f n8n.yaml -n kubectl get pods -n -w # prêt en ~1 minute ``` Pointez votre [DNS](/deployer/configurer-votre-dns/) et ouvrez `https://n8n.example.com` : n8n vous demande de créer le compte administrateur au premier lancement, et vous voilà dans l’éditeur de workflows. Les **webhooks entrants** (déclencheurs de workflows depuis l’extérieur) fonctionnent immédiatement : ils passent par l’Ingress HTTPS comme le reste — c’est le rôle de la variable `WEBHOOK_URL`. Attention Créez votre compte admin immédiatement après le déploiement : l’instance est publique dès que le DNS pointe. Pour restreindre l’accès davantage, n8n propose le SSO et la 2FA dans ses réglages. ## Le coût | Poste | Détail | € / mois | | -------- | -------------------------- | ------------ | | Compute | 100 m / 512 Mi → 3 bundles | 8,64 € max\* | | Stockage | 2 Gi | 1,44 € | *\*En pratique moins : n8n consomme peu au repos, et vous pouvez [scaler à zéro](/operer/consommation-et-facturation/) l’instance quand elle ne sert pas — les workflows planifiés ratés pendant l’arrêt ne s’exécutent simplement pas.* Le volume est [sauvegardé chaque nuit](/operer/sauvegardes-et-restauration/) — vos workflows et credentials n8n sont couverts sans rien configurer. ## Pour aller plus loin * **Plusieurs utilisateurs intensifs ?** Passez la base sur [PostgreSQL managé](/deployer/postgresql-manage/) (variables `DB_TYPE=postgresdb`, `DB_POSTGRESDB_*` alimentées par le secret CNPG) — n8n supporte alors le mode file d’attente et les replicas multiples ; * **Agents IA** : les nœuds IA de n8n appellent les APIs externes (OpenAI, Anthropic, Mistral…) sans configuration réseau particulière — [l’egress est libre](/comprendre/isolation-reseau/) ; * Suivez la consommation réelle dans le [monitoring](/operer/acceder-au-monitoring/) pour ajuster les requests. # Stack web complète > Une application de production assemblée avec les bonnes pratiques Kontainers : app répliquée, PostgreSQL HA, cache Redis, HTTPS, métriques, autoscaling et sauvegardes — testée sur la plateforme, budget chiffré. Cet exemple assemble tout ce que la plateforme offre, sur une application web réaliste : **2 replicas applicatifs** derrière un Ingress HTTPS, **PostgreSQL haute disponibilité** (2 instances avec bascule automatique), **cache Redis**, **métriques custom** scrapées et **autoscaling**. **L’ensemble a été déployé et validé sur la plateforme** (avec une image de démonstration à la place de l’application) : pods prêts, base saine, métriques collectées, le tout dans les quotas par défaut. ## L’architecture ```text Internet ──HTTPS──▶ Ingress ──▶ boutique (×2, HPA 2→6) │ │ ▼ ▼ boutique-db (CNPG ×2) boutique-cache (Redis) ``` Chaque choix est annoté — c’est le « pourquoi » qui fait les bonnes pratiques. ## 1. La base PostgreSQL (haute disponibilité) ```yaml apiVersion: postgresql.cnpg.io/v1 kind: Cluster metadata: name: boutique-db spec: instances: 2 # bascule automatique en cas de panne storage: {size: 10Gi, storageClass: ceph-block-rwo} resources: requests: {cpu: 250m, memory: 512Mi} limits: {memory: 512Mi} # = request : c'est la règle plateforme bootstrap: initdb: {database: boutique, owner: boutique} ``` **Pourquoi** : l’opérateur gère la réplication et la bascule ([détails](/deployer/postgresql-manage/)) ; les volumes sont couverts par la [sauvegarde plateforme quotidienne](/operer/sauvegardes-et-restauration/) — et pour un point de reprise plus fin (PITR), CNPG sait [archiver vers votre propre bucket S3](/deployer/postgresql-manage/#sauvegardes-vers-votre-stockage-s3). ## 2. Le cache Redis ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: boutique-cache spec: replicas: 1 selector: matchLabels: {app: boutique-cache} template: metadata: labels: {app: boutique-cache} spec: containers: - name: redis image: redis:7-alpine args: ["--maxmemory", "200mb", "--maxmemory-policy", "allkeys-lru"] ports: [{name: redis, containerPort: 6379}] resources: requests: {cpu: 100m, memory: 256Mi} --- apiVersion: v1 kind: Service metadata: name: boutique-cache spec: selector: {app: boutique-cache} ports: [{name: redis, port: 6379}] ``` **Pourquoi** : un cache est reconstructible — pas de volume, pas de replicas. `maxmemory` est calé sous la request pour que Redis évince ses clés (\[LRU]) au lieu de se faire [OOMKill](/depanner/pod-en-erreur/#oomkilled) par la limite mémoire (= request sur Kontainers). ## 3. L’application ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: boutique spec: replicas: 2 # survit à la perte d'un pod ou d'un nœud selector: matchLabels: {app: boutique} template: metadata: labels: {app: boutique} # PAS de label exposition: public — tout passe par l'Ingress spec: containers: - name: app image: registry.exemple.com/acme/boutique:2.1 # tag immuable ports: [{name: http, containerPort: 8080}] env: - name: DATABASE_URL valueFrom: secretKeyRef: {name: boutique-db-app, key: uri} # ← généré par CNPG, jamais écrit à la main - name: REDIS_URL value: redis://boutique-cache:6379 resources: requests: {cpu: 200m, memory: 256Mi} readinessProbe: httpGet: {path: /health, port: http} initialDelaySeconds: 3 --- apiVersion: v1 kind: Service metadata: name: boutique labels: {app: boutique} # sélectionné par le ServiceMonitor spec: selector: {app: boutique} ports: [{name: http, port: 8080}] ``` **Pourquoi** : le secret de connexion vient de CNPG (zéro mot de passe dans le YAML) ; la `readinessProbe` conditionne le trafic ET les rollouts ; les requests sont calées sur la consommation réelle observée au [monitoring](/operer/acceder-au-monitoring/). ## 4. Exposition HTTPS ```yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: boutique annotations: cert-manager.io/issuer: letsencrypt spec: rules: - host: boutique.example.com http: paths: - path: / pathType: Prefix backend: service: {name: boutique, port: {number: 8080}} tls: - hosts: [boutique.example.com] secretName: boutique-tls ``` **Pourquoi** : [Ingress mutualisé inclus](/deployer/exposer-en-https/), certificat automatique, et aucun label réseau requis ([le trafic passe par le proxy plateforme](/comprendre/isolation-reseau/)). ## 5. Observabilité et scaling ```yaml apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: name: boutique spec: selector: matchLabels: {app: boutique} endpoints: - port: http path: /metrics interval: 30s --- apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: boutique spec: scaleTargetRef: {apiVersion: apps/v1, kind: Deployment, name: boutique} minReplicas: 2 maxReplicas: 6 metrics: - type: Resource resource: name: cpu target: {type: Utilization, averageUtilization: 80} --- apiVersion: policy/v1 kind: PodDisruptionBudget metadata: name: boutique spec: minAvailable: 1 selector: matchLabels: {app: boutique} ``` **Pourquoi** : les [métriques custom](/operer/metriques-applicatives/) alimentent vos dashboards (inclus, fair use) ; l’[HPA](/operer/scaling-et-disponibilite/) absorbe les pics dans la limite du budget ; le PDB protège la disponibilité pendant les maintenances du cluster. ## Le budget, chiffré Requests totales au repos (validées sur la plateforme) : **1,3 vCPU / 2,25 Gi** — 13 % du [quota namespace](/reference/quotas-et-limites/). | Composant | Requests | Bundles | € / mois | | ------------------ | --------------------- | ------- | -------------------- | | App × 2 | 200 m / 256 Mi chacun | 2 × 2 | 11,52 € | | PostgreSQL × 2 | 250 m / 512 Mi chacun | 3 × 2 | 17,28 € | | Redis | 100 m / 256 Mi | 2 | 5,76 € | | Stockage | 2 × 10 Gi | — | 14,40 € | | **Total au repos** | | | **≈ 49 € HT / mois** | Si l’HPA monte à 6 replicas en pic : +11,52 €/mois *au prorata des heures de pic uniquement* — c’est le principe de la [facturation à l’heure](/reference/facturation/). Note Additionnez large : même à 6 replicas, la stack reste sous 2,2 vCPU / 3,25 Gi de requests — un seul namespace Kontainers héberge confortablement plusieurs stacks de cette taille. # WordPress complet > Déployer un WordPress de production sur Kontainers : MySQL avec volume persistant, fichiers partagés en RWX, HTTPS automatique — manifests testés et coût estimé. Cet exemple déploie un WordPress complet, prêt pour la production : une base MySQL sur volume persistant, des fichiers WordPress partagés entre 2 replicas, et l’exposition HTTPS avec certificat automatique. **Les manifests de cette page ont été déployés et testés tels quels sur la plateforme.** Il ne vous reste qu’à changer le domaine et les mots de passe. Note L’architecture illustre les deux [classes de stockage](/deployer/stockage-persistant/) : MySQL écrit seul → volume **RWO** ; les 2 replicas WordPress partagent les uploads → volume **RWX**. ## 1. Les secrets ```yaml apiVersion: v1 kind: Secret metadata: name: wordpress-secrets stringData: MYSQL_ROOT_PASSWORD: "changez-moi-root" MYSQL_PASSWORD: "changez-moi-wp" ``` ## 2. MySQL ```yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: mysql-data spec: accessModes: [ReadWriteOnce] storageClassName: ceph-block-rwo resources: requests: storage: 5Gi --- apiVersion: apps/v1 kind: Deployment metadata: name: mysql spec: replicas: 1 strategy: type: Recreate # volume RWO : jamais 2 pods MySQL simultanés selector: matchLabels: {app: mysql} template: metadata: labels: {app: mysql} spec: containers: - name: mysql image: mysql:8.4 env: - name: MYSQL_ROOT_PASSWORD valueFrom: secretKeyRef: {name: wordpress-secrets, key: MYSQL_ROOT_PASSWORD} - name: MYSQL_PASSWORD valueFrom: secretKeyRef: {name: wordpress-secrets, key: MYSQL_PASSWORD} - name: MYSQL_DATABASE value: wordpress - name: MYSQL_USER value: wordpress resources: requests: {cpu: 250m, memory: 512Mi} volumeMounts: - name: data mountPath: /var/lib/mysql volumes: - name: data persistentVolumeClaim: {claimName: mysql-data} --- apiVersion: v1 kind: Service metadata: name: mysql spec: selector: {app: mysql} ports: [{port: 3306}] ``` ## 3. WordPress ```yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: wordpress-files spec: accessModes: [ReadWriteMany] # partagé entre les replicas storageClassName: ceph-filesystem-rwx resources: requests: storage: 5Gi --- apiVersion: apps/v1 kind: Deployment metadata: name: wordpress spec: replicas: 2 selector: matchLabels: {app: wordpress} template: metadata: labels: {app: wordpress} spec: containers: - name: wordpress image: wordpress:6-apache env: - name: WORDPRESS_DB_HOST value: mysql - name: WORDPRESS_DB_NAME value: wordpress - name: WORDPRESS_DB_USER value: wordpress - name: WORDPRESS_DB_PASSWORD valueFrom: secretKeyRef: {name: wordpress-secrets, key: MYSQL_PASSWORD} resources: requests: {cpu: 200m, memory: 512Mi} volumeMounts: - name: files mountPath: /var/www/html readinessProbe: httpGet: {path: /wp-login.php, port: 80} initialDelaySeconds: 15 periodSeconds: 10 volumes: - name: files persistentVolumeClaim: {claimName: wordpress-files} --- apiVersion: v1 kind: Service metadata: name: wordpress spec: selector: {app: wordpress} ports: [{port: 80}] ``` ## 4. L’exposition HTTPS Prérequis : votre domaine pointe vers la plateforme ([Configurer votre DNS](/deployer/configurer-votre-dns/)). ```yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: wordpress annotations: cert-manager.io/issuer: letsencrypt # Sticky sessions : chaque visiteur reste sur le même pod (cf. encadré) nginx.ingress.kubernetes.io/affinity: "cookie" nginx.ingress.kubernetes.io/session-cookie-name: "wp-affinity" spec: rules: - host: blog.example.com http: paths: - path: / pathType: Prefix backend: service: {name: wordpress, port: {number: 80}} tls: - hosts: [blog.example.com] secretName: wordpress-tls ``` ## Déployer ```bash kubectl apply -f wordpress-stack.yaml -n # Suivre le démarrage (~1 minute) kubectl get pods -n -w ``` Une fois les pods `Running` et `READY 1/1`, ouvrez `https://blog.example.com` : l’assistant d’installation WordPress vous accueille. Deux replicas et les sessions PHP ? WordPress lui-même n’utilise **pas** les sessions PHP : l’authentification repose sur des cookies signés, validés contre la base — et les clés de signature vivent dans `wp-config.php`, généré **sur le volume partagé**, donc identiques sur les deux pods (vérifié sur la plateforme). Le cœur de WordPress est ainsi indifférent au pod qui répond. Certains **plugins**, en revanche, ouvrent des sessions PHP — stockées localement dans chaque pod. C’est le rôle des deux annotations `affinity` de l’Ingress ci-dessus : chaque visiteur reste collé à « son » pod (sticky sessions), et ces plugins fonctionnent sans rien remarquer. Alternative plus robuste pour un site à fort trafic : stocker les sessions en base ou dans un Redis via un plugin dédié. ## Coût mensuel estimé Avec la [grille tarifaire](/reference/facturation/) (facturé à l’heure, ici ramené à un mois de 720 h) : | Composant | Requests | Bundles | Coût / mois | | ------------- | --------------------- | ------- | -------------------- | | MySQL | 250 m / 512 Mi | 3 | 8,64 € | | WordPress × 2 | 200 m / 512 Mi chacun | 3 × 2 | 17,28 € | | Stockage | 5 Gi RWO + 5 Gi RWX | — | 7,20 € | | **Total** | | | **≈ 33 € HT / mois** | Sauvegardes quotidiennes, certificat TLS, trafic et monitoring [inclus](/reference/facturation/#inclus-sans-suppl%C3%A9ment). ## Pour aller plus loin * Réduire le coût d’un environnement de test : `replicas: 1` et [scale à zéro la nuit](/operer/consommation-et-facturation/) (\~20 €/mois) ; * Les fichiers et la base sont [sauvegardés chaque nuit](/operer/sauvegardes-et-restauration/) — ajoutez un dump MySQL en CronJob pour un point de reprise plus fin ; * Suivez la consommation réelle dans le [monitoring](/operer/acceder-au-monitoring/) et ajustez les requests. # Documentation Kontainers > Documentation officielle de BlackSwift Kontainers — vos namespaces Kubernetes managés, hébergés en France. Bienvenue sur la documentation de **Kontainers** : vos namespaces Kubernetes managés, hébergés en France, facturés à l’heure et sans engagement. Vous vous occupez de votre application, nous nous occupons du cluster. [Déployer ma première application](/demarrer/creer-un-compte/) [Voir la grille tarifaire](/reference/facturation/) ## Par où commencer ? Je débute sur Kubernetes De la création de compte à votre première application en ligne, un parcours guidé pas à pas — aucune connaissance Kubernetes requise. [Commencer le parcours →](/demarrer/creer-un-compte/) Je migre une application existante Depuis un docker-compose ou un serveur : tables de correspondance et migration guidée vers des manifests déployables. [Migrer depuis un docker-compose →](/migrer/depuis-un-docker-compose/) J'opère en production Monitoring, consommation, sauvegardes : tout pour exploiter sereinement vos applications au quotidien. [Accéder au monitoring →](/operer/acceder-au-monitoring/) Je connais déjà Kubernetes Ce qui change par rapport à un cluster classique : quotas exacts, mutations automatiques, permissions RBAC, limitations connues. [Le modèle Kontainers →](/comprendre/le-modele-kontainers/) ## Ce que Kontainers vous fournit * un ou plusieurs **namespaces dédiés** dans un cluster Kubernetes haute disponibilité, managé et hébergé en France ; * des outils prêts à l’emploi : console [Rancher](https://rancher.fr.blackswift.cloud), [monitoring Grafana](/operer/acceder-au-monitoring/), stockage persistant (RWO et RWX), [PostgreSQL managé](/deployer/postgresql-manage/), [sauvegardes quotidiennes](/operer/sauvegardes-et-restauration/) ; * une [**facturation à l’heure**](/reference/facturation/) : seuls vos pods en cours d’exécution sont facturés. Envie d'essayer avec votre propre namespace ? Compte et premier namespace créés en un jour ouvré — avec un avantage de bienvenue réservé aux lecteurs de la doc : * ✓ Sans engagement * ✓ Facturation à l'heure * ✓ Code I‑DO‑RTFM pré-rempli [Créer mon compte →](https://blackswift.fr/inscription?code=I-DO-RTFM) Un problème ? Consultez le [dépannage](/depanner/checklist-de-diagnostic/) ou [contactez le support](/depanner/contacter-le-support/). # Migrer depuis un docker-compose > Convertir un docker-compose.yml en manifests Kubernetes déployables sur Kontainers : table de correspondance complète et migration guidée d'une stack web + base de données. Vous avez une application qui tourne avec `docker compose up` et vous voulez la faire tourner sur Kontainers ? Bonne nouvelle : les concepts se correspondent presque un pour un. Cette page donne la table de conversion, puis migre une stack réaliste de bout en bout. ## Table de correspondance | docker-compose | Kubernetes | Notes | | ------------------------------------ | ----------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | | `services.` | **Deployment** + **Service** | Le Service reprend le nom : les autres conteneurs le joignent par ce nom, comme dans compose | | `image:` | `spec.containers[].image` | Registre privé ? Il faudra un pull secret | | `ports:` (exposition web) | **Ingress** | [HTTPS automatique](/deployer/exposer-en-https/) — jamais de `ports:` publiés directement | | `ports:` (TCP brut) | Service **NodePort** ou **LoadBalancer** | Avec le label [`exposition: public`](/deployer/exposer-en-https/#3-exposer-un-protocole-non-http) sur les pods | | `environment:` | `env:` ou **ConfigMap** | [Guide](/deployer/configmaps-et-secrets/) | | `env_file:` | **ConfigMap** (+ **Secret** pour le sensible) | `envFrom:` reproduit le comportement | | `volumes:` (named volume) | **PersistentVolumeClaim** | [Guide](/deployer/stockage-persistant/) | | `volumes:` (bind mount `./conf:...`) | **ConfigMap** montée en volume | Pas de bind mount sur un cluster | | `depends_on:` | `readinessProbe` (+ `initContainers` au besoin) | Kubernetes redémarre jusqu’à ce que ça marche — les probes rendent ça propre | | `restart: always` | *(comportement par défaut d’un Deployment)* | Rien à faire | | `deploy.resources` | `resources.requests` | **À poser d’emblée** : sinon [0,1 vCPU / 200 Mi sont injectés et facturés](/reference/mutations-et-politiques/) | | `networks:` | *(rien)* | Tous les pods d’un namespace se joignent par nom de Service | ### Ce qui ne se transpose pas * **`network_mode: host`**, `privileged: true`, montages de `/var/run/docker.sock` : rejetés par les [politiques de sécurité](/reference/limitations-connues/) — repensez le besoin ou parlez-en au [support](/depanner/contacter-le-support/) ; * **`build:`** : Kubernetes ne construit pas d’images. Construisez et poussez vers un registre en amont (CI ou poste local), puis référencez l’image *(guide registre privé à venir)*. ## Migration guidée : web + base de données + volume Le `docker-compose.yml` de départ, très classique : ```yaml services: web: image: ghcr.io/acme/mon-app:1.4 ports: - "8080:8080" environment: DATABASE_URL: postgres://app:secret@db:5432/app db: image: postgres:17-alpine environment: POSTGRES_USER: app POSTGRES_PASSWORD: secret POSTGRES_DB: app volumes: - dbdata:/var/lib/postgresql/data volumes: dbdata: ``` Sa traduction complète pour Kontainers — un seul fichier, prêt à adapter : ```yaml # --- Le volume de la base (remplace "volumes: dbdata") --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: dbdata spec: accessModes: [ReadWriteOnce] storageClassName: ceph-block-rwo resources: requests: storage: 10Gi --- # --- Les secrets (remplacent les mots de passe en clair) --- apiVersion: v1 kind: Secret metadata: name: db-credentials stringData: POSTGRES_USER: app POSTGRES_PASSWORD: changez-moi POSTGRES_DB: app DATABASE_URL: postgres://app:changez-moi@db:5432/app --- # --- Le service "db" --- apiVersion: apps/v1 kind: Deployment metadata: name: db spec: replicas: 1 strategy: type: Recreate # volume RWO : jamais 2 pods db en même temps selector: matchLabels: {app: db} template: metadata: labels: {app: db} spec: containers: - name: postgres image: postgres:17-alpine envFrom: - secretRef: {name: db-credentials} resources: requests: {cpu: 250m, memory: 512Mi} volumeMounts: - name: data mountPath: /var/lib/postgresql/data volumes: - name: data persistentVolumeClaim: {claimName: dbdata} --- apiVersion: v1 kind: Service metadata: name: db # ← le nom que "web" utilise, comme dans compose spec: selector: {app: db} ports: [{port: 5432}] --- # --- Le service "web" --- apiVersion: apps/v1 kind: Deployment metadata: name: web spec: replicas: 2 selector: matchLabels: {app: web} template: metadata: labels: {app: web} spec: containers: - name: web image: ghcr.io/acme/mon-app:1.4 env: - name: DATABASE_URL valueFrom: secretKeyRef: {name: db-credentials, key: DATABASE_URL} resources: requests: {cpu: 100m, memory: 256Mi} readinessProbe: # remplace depends_on httpGet: {path: /, port: 8080} initialDelaySeconds: 5 --- apiVersion: v1 kind: Service metadata: name: web spec: selector: {app: web} ports: [{port: 8080}] --- # --- L'exposition publique (remplace ports: "8080:8080") --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: web annotations: cert-manager.io/issuer: letsencrypt spec: rules: - host: app.example.com http: paths: - path: / pathType: Prefix backend: service: {name: web, port: {number: 8080}} tls: - hosts: [app.example.com] secretName: web-tls ``` Déployez, puis vérifiez : ```bash kubectl apply -f stack.yaml -n kubectl get pods -n ``` Pointez ensuite votre [DNS](/deployer/configurer-votre-dns/) vers la plateforme — et votre application est en ligne, en HTTPS. ## Les pièges de conversion à connaître 1. **Posez vos `resources.requests` dès la conversion** — compose n’en exigeait pas, Kontainers en injecte sinon ([et les facture](/reference/facturation/)) ; 2. **La limite mémoire = la request** : si votre conteneur consommait 800 Mi sous compose, demandez 1 Gi de request, pas 200 Mi ([pourquoi](/reference/mutations-et-politiques/)) ; 3. **`depends_on` n’existe pas** : votre app doit tolérer une base pas encore prête au premier démarrage (elle redémarrera jusqu’à ce que ça passe — c’est normal et sain) ; 4. **Quotas** : 20 pods, 10 vCPU, 20 Gi de requests par namespace ([les chiffres](/reference/quotas-et-limites/)) — largement suffisant pour une stack compose typique, mais à connaître avant de scaler. PostgreSQL : encore mieux que le conteneur Plutôt que de porter votre conteneur `postgres`, Kontainers propose un opérateur PostgreSQL managé (haute dispo, backups self-service) — voir [vos permissions CNPG](/reference/permissions-et-rbac/#postgresql-manag%C3%A9-cnpg) *(guide dédié à venir)*. # Migrer depuis un VPS ou serveur dédié > Faire passer une application installée sur un serveur vers Kontainers : conteneuriser, externaliser l'état, planifier la bascule DNS avec retour arrière. Votre application tourne sur un VPS ou un serveur dédié — installée à la main, gérée en SSH. La migrer vers Kontainers se fait en quatre temps : inventorier, conteneuriser, externaliser l’état, basculer. Vous utilisez déjà docker-compose sur votre serveur ? Passez directement par [Migrer depuis un docker-compose](/migrer/depuis-un-docker-compose/) — la moitié du travail est déjà faite. ## 1. Inventorier ce qui tourne Avant de toucher à quoi que ce soit, listez sur votre serveur : | À inventorier | Comment | Devient sur Kontainers | | -------------------------------- | ----------------------------------------------------- | ------------------------------------------------------------------- | | Les processus / services systemd | `systemctl list-units --type=service --state=running` | Un Deployment par service | | Les ports écoutés | `ss -tlnp` | Services + [Ingress](/deployer/exposer-en-https/) | | Les tâches cron | `crontab -l` (tous les utilisateurs) | CronJobs | | Les fichiers écrits par l’app | uploads, caches, données… | [PVC](/deployer/stockage-persistant/) — ou stockage objet externe | | La base de données | dump + taille | Conteneur + PVC, ou PostgreSQL managé | | La configuration | `/etc/`, variables, fichiers `.env` | [ConfigMaps et Secrets](/deployer/configmaps-et-secrets/) | | Les certificats TLS | souvent certbot | Rien à migrer : [émis automatiquement](/deployer/exposer-en-https/) | ## 2. Conteneuriser l’application Si votre application n’a pas encore d’image Docker, un `Dockerfile` minimal suffit souvent : ```dockerfile FROM node:22-slim # ou python, php, golang… selon votre stack WORKDIR /app COPY . . RUN npm ci --omit=dev ENV NODE_ENV=production EXPOSE 8080 CMD ["node", "server.js"] ``` Deux règles d’or pour un conteneur qui se comporte bien sur Kubernetes : 1. **Ne stockez rien dans le conteneur** : tout ce qui doit survivre va dans un volume ou la base de données ; 2. **Loggez sur la sortie standard** (pas dans des fichiers) : vos logs seront accessibles via `kubectl logs` et la console Rancher. Construisez et poussez l’image vers un registre, puis [déployez-la](/deployer/deployer-un-conteneur/). ## 3. Externaliser l’état * **Base de données** : dump sur le serveur (`pg_dump`, `mysqldump`), déploiement de la base sur Kontainers ([exemple MySQL](/deployer/stockage-persistant/#monter-le-volume-dans-un-pod)), puis restauration du dump via `kubectl exec` ou un Job ; * **Fichiers** (uploads, médias…) : copie vers un [PVC](/deployer/stockage-persistant/) — montez le volume dans un pod temporaire et transférez avec `kubectl cp` ou `rsync` via `kubectl exec` ; * **Configuration** : recréez-la en [ConfigMaps et Secrets](/deployer/configmaps-et-secrets/) — c’est le moment de sortir les mots de passe des fichiers de conf. ## 4. Basculer le DNS (avec retour arrière) 1. **La veille** : abaissez le TTL de vos enregistrements DNS à 300 s ; 2. Déployez et testez la version Kontainers — accessible immédiatement via `kubectl port-forward`, puis via votre Ingress avec un sous-domaine de test (`beta.example.com`) ; 3. **Gel des écritures** sur le serveur, dernière synchronisation des données ; 4. Basculez le DNS vers la plateforme ([Configurer votre DNS](/deployer/configurer-votre-dns/)) — le certificat s’émet automatiquement dans les minutes qui suivent ; 5. **Gardez le serveur allumé quelques jours** : le retour arrière est un simple changement DNS inverse. ## Ce qui change au quotidien | Avant (serveur) | Après (Kontainers) | | --------------------------- | -------------------------------------------------------------- | | `ssh serveur` | `kubectl exec -it deploy/mon-app -- sh` | | `tail -f /var/log/app.log` | `kubectl logs -f deploy/mon-app` | | `systemctl restart mon-app` | `kubectl rollout restart deploy/mon-app` | | certbot + renouvellements | [Automatique](/deployer/exposer-en-https/) | | Sauvegardes à configurer | [Quotidiennes, incluses](/operer/sauvegardes-et-restauration/) | | Surveillance à installer | [Grafana inclus](/operer/acceder-au-monitoring/) | | Mises à jour de l’OS | Plus votre problème | # Monitoring : accès et dashboards > Accéder à votre Grafana, lire les dashboards fournis par namespace et créer vos propres dashboards avec vos métriques custom. Chaque organisation dispose d’un espace **Grafana** managé, alimenté en continu par les métriques de ses namespaces. ## Se connecter 1. Rendez-vous sur ; 2. Authentifiez-vous via le **SSO BlackSwift** (le même compte que [Rancher](/demarrer/premiere-connexion/)). ## Les dashboards fournis Pour chacun de vos namespaces, des dashboards prêts à l’emploi affichent : * **CPU et mémoire** : consommation réelle vs requests de chaque pod — la vue à consulter pour [dimensionner vos requests](/reference/mutations-et-politiques/) ; * **Pods** : état, redémarrages, âge ; * **Stockage** : remplissage de vos volumes persistants ; * **Quotas** : votre consommation par rapport aux [plafonds du namespace](/reference/quotas-et-limites/). Le réflexe dimensionnement Avant d’augmenter une request mémoire (ou après un [OOMKill](/depanner/pod-en-erreur/#oomkilled)), regardez le pic réel de consommation sur le dashboard CPU/mémoire : vous ajusterez au juste besoin — et au juste [coût](/reference/facturation/). ## Vos propres dashboards et métriques Votre espace Grafana n’est pas en lecture seule : * **Créez vos dashboards personnalisés** à partir de toutes les métriques de vos namespaces ; * **Exposez vos métriques applicatives** (Prometheus) : déclarez un `ServiceMonitor` ou un `PodMonitor` dans votre namespace et vos métriques custom remontent automatiquement dans votre Grafana *(guide dédié à venir — la capacité est déjà active, voir [vos permissions](/reference/permissions-et-rbac/#observabilit%C3%A9-et-diagnostic))*. Les métriques custom sont **incluses dans le prix** (fair use). ## Alternative en ligne de commande Pour un aperçu instantané sans quitter le terminal : ```bash # Consommation CPU/mémoire en direct kubectl top pods -n # Consommation vs quotas kubectl describe resourcequota blackswift-standard -n ``` ## Liens utiles * [Consommation et facturation](/operer/consommation-et-facturation/) — traduire ces métriques en euros ; * [État des services BlackSwift](https://status.blackswift.cloud) — si le monitoring lui-même semble indisponible. # CI/CD : déployer sans distribuer de credentials > Les deux mécanismes d'authentification CI/CD de Kontainers : le ServiceAccount bs-namespace-member pour l'outillage in-cluster, et les kubeconfigs permanents pour les pipelines. Le point douloureux de tout pipeline de déploiement Kubernetes : **comment la CI s’authentifie-t-elle** ? Les kubeconfigs personnels expirent et engagent votre identité ; les tokens bricolés traînent dans des variables. Kontainers fournit deux mécanismes propres, chacun pour un usage précis : | Besoin | Mécanisme | Credential à gérer | | ------------------------------------------------------------------------------------------------ | ------------------------------------ | -------------------------- | | Faire tourner un **outil in-cluster** qui pilote le namespace (runner GitLab, opérateur maison…) | ServiceAccount `bs-namespace-member` | **aucun** | | **Déployer depuis un pipeline** (jobs CI — que la CI soit in-cluster ou externe) | Kubeconfig permanent | un secret CI, non expirant | ## Le ServiceAccount `bs-namespace-member` Chaque namespace contient ce ServiceAccount pré-provisionné. Un pod qui tourne avec (`serviceAccountName: bs-namespace-member`) est automatiquement authentifié auprès du cluster, avec des droits couvrant la gestion du namespace ([le détail, colonne SA](/reference/permissions-et-rbac/)). Son usage prévu : **l’outillage qui a réellement besoin de l’API** — typiquement le gestionnaire d’un [runner GitLab in-cluster](/exemples/gitlab-runner/), qui crée et supervise les pods de jobs. Moindre privilège : ne montez pas ce SA dans les pods de jobs Un job de build ou de test n’a pas besoin de parler à l’API Kubernetes — ne lui donnez pas ce pouvoir. Laissez les pods de jobs sous le ServiceAccount `default` du namespace (aucun droit API) : seul l’outil qui orchestre — le runner — porte `bs-namespace-member`. Vos jobs de **déploiement**, eux, s’authentifient explicitement avec un kubeconfig permanent (ci-dessous) : le droit est donné au pipeline qui en a besoin, pas à tous les jobs du projet. ## Le kubeconfig permanent **Testé sur la plateforme.** Pour vos pipelines de déploiement, créez un `BlackSwiftPermanentKubeconfig` : ```yaml apiVersion: blackswift.cloud/v1alpha1 kind: BlackSwiftPermanentKubeconfig metadata: name: cicd ``` ```bash kubectl apply -f permanent-kubeconfig.yaml -n ``` En quelques secondes, deux Secrets apparaissent dans votre namespace : ```bash kubectl get secrets -n | grep bspkcfg # bspkcfg-cicd-kubeconfig Opaque 1 # bspkcfg-cicd-token kubernetes.io/service-account-token 3 ``` Récupérez le kubeconfig : ```bash kubectl get secret bspkcfg-cicd-kubeconfig -n \ -o jsonpath='{.data.kubeconfig}' | base64 -d > ci-kubeconfig.yaml ``` Ce kubeconfig : * porte les droits du ServiceAccount `bs-namespace-member` de votre namespace, **sans jamais expirer** — contrairement au kubeconfig téléchargé depuis Rancher ; * parle **directement à l’API du cluster** (`api.k-bdkbsh.blackswift.cloud`) — il fonctionne même pendant une maintenance de la console Rancher ; * fonctionne depuis n’importe où : jobs d’un runner in-cluster, gitlab.com, GitHub Actions, votre poste. ### L’utiliser dans un pipeline Stockez le contenu du fichier dans une variable CI **masquée et protégée** (GitLab : Settings › CI/CD › Variables, type *File*), puis : .gitlab-ci.yml ```yaml deploy: stage: deploy image: bitnami/kubectl:latest script: - export KUBECONFIG=$KUBE_CONFIG_FILE # variable CI de type File - kubectl set image deploy/mon-app app=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA - kubectl rollout status deploy/mon-app --timeout=120s rules: - if: $CI_COMMIT_TAG ``` Seul ce job de déploiement reçoit la variable — vos jobs de build et de test n’ont aucun accès au cluster. ### Cycle de vie et bonnes pratiques * **Un kubeconfig par usage** (`cicd`, `monitoring-externe`…) : les CR sont multiples et nominatives, la colonne `LAST USED` de `kubectl get blackswiftpermanentkubeconfigs` vous dit lesquels servent encore ; * **Révocation immédiate** : supprimez la CR, les Secrets (et le token) partent avec elle : ```bash kubectl delete blackswiftpermanentkubeconfig cicd -n ``` * **Périmètre** : le kubeconfig n’agit que dans **son** namespace. Pour déployer sur dev et prod depuis un même pipeline, créez-en un par namespace — deux variables CI, deux périmètres étanches ; * Ne le commitez jamais : c’est un credential non expirant. Variable CI protégée uniquement. # Consommation et facturation > Consulter votre consommation Kontainers, comprendre ce qui vous est facturé et piloter vos coûts au quotidien. La grille tarifaire complète est dans la [Référence facturation](/reference/facturation/). Cette page vous montre comment **suivre** et **piloter** votre consommation au quotidien. ## Ce qui détermine votre facture Trois postes, calculés **à l’heure** : 1. **Compute** : les *requests* CPU/mémoire de vos pods **Running** — 0,004 €/h par bundle (0,1 vCPU / 0,2 Go), sur le maximum des deux ; 2. **Stockage** : la capacité **provisionnée** de vos PVC — 0,001 €/Go/h ; 3. **LoadBalancer** : 0,02 €/h par service de ce type, le cas échéant. ## Consulter sa consommation ### Dans le monitoring Vos [dashboards Grafana](/operer/acceder-au-monitoring/) affichent les requests réservées par pod et par namespace — la donnée exacte qui sert de base au calcul. ### En ligne de commande ```bash # Total des requests réservées dans le namespace (base de facturation compute) kubectl describe resourcequota blackswift-standard -n | grep requests # Stockage provisionné kubectl get pvc -n ``` ## Estimer un coût : exemple complet Une application avec 2 replicas de 250 m CPU / 512 Mi RAM + une base de données (500 m / 1 Gi) + 20 Gi de stockage : | Poste | Calcul | Coût mensuel (≈ 720 h) | | ----------------- | ------------------------------- | ---------------------- | | App (×2 replicas) | max(2,5 ; 2,56) → 3 bundles × 2 | 17,28 € | | Base de données | max(5 ; 5) → 5 bundles | 14,40 € | | Stockage 20 Gi | 20 × 0,72 € | 14,40 € | | **Total** | | **≈ 46 € HT / mois** | ## Piloter ses coûts : les 5 leviers 1. **Déclarez des requests réalistes.** Sans déclaration, la plateforme réserve [0,1 vCPU / 200 Mi par conteneur](/reference/mutations-et-politiques/) — parfois trop, parfois trop peu. Basez-vous sur la consommation réelle observée dans le monitoring. 2. **Scalez à zéro ce qui ne sert pas.** Seuls les pods `Running` sont facturés, à l’heure entamée : ```bash kubectl scale deploy/mon-app-de-test --replicas=0 -n ``` Un environnement de dev arrêté la nuit et le week-end coûte \~70 % de moins. 3. **Préférez les CronJobs aux pods qui dorment.** Une tâche quotidienne de quelques minutes est facturée une heure par jour — \~25 fois moins qu’un pod permanent. 4. **Dimensionnez le stockage au besoin réel** : un volume [s’agrandit à chaud](/deployer/stockage-persistant/#agrandir-un-volume) à tout moment, inutile de sur-provisionner. 5. **La CPU sans limite ne coûte rien de plus** : les pics de charge au-delà de votre request ne sont pas facturés — seule la réservation compte. ## Questions fréquentes **Un pod `Completed` ou `Pending` est-il facturé ?** Non — uniquement `Running`. La granularité est l’heure : toute heure où le pod a tourné, même quelques minutes, est due (base : la somme des requests maximales observées sur l’heure). **Les images, le trafic réseau, les certificats ?** Inclus, sans supplément. **Où trouver mes factures ?** Elles sont envoyées par e-mail au contact de facturation de votre organisation. Pour toute question : [support](/depanner/contacter-le-support/). # Exporter vos métriques applicatives > Faire scraper les métriques Prometheus de votre application par la plateforme avec un ServiceMonitor, et les visualiser dans votre Grafana — inclus dans le prix. Votre application expose des métriques au format Prometheus (`/metrics`) ? Déclarez un **ServiceMonitor** et la plateforme les collecte automatiquement — elles apparaissent dans [votre Grafana](/operer/acceder-au-monitoring/), prêtes pour vos dashboards custom. **Inclus dans le prix** (fair use), rien à installer. ## Le trio testé sur la plateforme Une application qui expose `/metrics`, son Service, et le ServiceMonitor : ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: mon-app spec: selector: matchLabels: {app: mon-app} template: metadata: labels: {app: mon-app} spec: containers: - name: app image: registry.exemple.com/acme/mon-app:1.4 ports: - name: http # ← port nommé, référencé par le ServiceMonitor containerPort: 8080 resources: requests: {cpu: 100m, memory: 256Mi} --- apiVersion: v1 kind: Service metadata: name: mon-app labels: {app: mon-app} # ← labels sur le Service : c'est eux que spec: # le ServiceMonitor sélectionne selector: {app: mon-app} ports: - name: http port: 8080 --- apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: name: mon-app spec: selector: matchLabels: {app: mon-app} # ← matche les labels du SERVICE endpoints: - port: http # ← le nom du port du Service path: /metrics interval: 30s ``` Dans la minute qui suit, la plateforme découvre la cible et commence à scraper. Vos métriques sont interrogeables dans Grafana avec leurs labels (`namespace`, `pod`, `job=mon-app`…). ## Les trois pièges classiques 1. **Le ServiceMonitor sélectionne les labels du Service, pas ceux des pods.** Si rien ne remonte, vérifiez : `kubectl get svc mon-app --show-labels` doit afficher les labels attendus par le `selector` du ServiceMonitor ; 2. **`port` désigne le *nom* du port du Service** (`http` ci-dessus), pas son numéro — un port non nommé ne peut pas être référencé ; 3. **L’endpoint doit répondre sans authentification** depuis le cluster : testez `kubectl exec deploy/mon-app -- wget -qO- localhost:8080/metrics | head`. ## Côté application La plupart des frameworks ont une bibliothèque Prometheus prête à l’emploi : [Spring Boot Actuator](https://docs.spring.io/spring-boot/reference/actuator/metrics.html) (Java), [prom-client](https://github.com/siimon/prom-client) (Node.js), [prometheus-client](https://github.com/prometheus/client_python) (Python)… Elles exposent d’office les métriques standard (requêtes HTTP, latences) ; ajoutez vos compteurs métier par-dessus. Pour un workload **sans Service** (CronJob, pods bruts), utilisez un `PodMonitor` — même principe, mais son `selector` matche directement les labels des **pods**. ## Construire un dashboard Dans [votre Grafana](/operer/acceder-au-monitoring/) : **Dashboards → New**, choisissez la source de données proposée, et requêtez vos métriques. Exemples de requêtes PromQL de départ : ```text # Débit de requêtes par seconde rate(http_requests_total{namespace="c-acme-rtximz-prod"}[5m]) # P95 de latence histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) ``` Et les alertes ? Couplées à vos métriques, les alertes ferment la boucle de supervision — parlez de votre besoin au [support](/depanner/contacter-le-support/), les capacités évoluent régulièrement. # Organiser vos environnements > Structurer dev, staging et production en plusieurs namespaces : quand en créer un, comment le demander, et le pattern tooling → dev/prod permis par le réseau intra-organisation. Un même namespace peut héberger **plusieurs applications** sans le moindre problème ; toutefois, créer un namespace dédié reste utile pour : * **Séparer les environnements** (développement, staging, production) ; * Réutiliser les *mêmes noms* de ressources — par exemple un `Service` nommé `api` ou une `ConfigMap` `settings` — sans collisions ; * **Cloisonner les budgets** : chaque namespace a ses propres [quotas](/reference/quotas-et-limites/), donc son propre plafond de dépense. Un namespace suffit souvent Si vos services partagent le même cycle de vie et la même équipe, il est souvent plus simple de les garder dans un seul namespace. Nous conseillons néanmoins de différencier au minimum développement et production. ## Demander un namespace supplémentaire La création est **gratuite** et passe par le [support](/depanner/contacter-le-support/) (validation manuelle). Indiquez : 1. **Identifiant d’organisation complet** (ex. `acme-rtximz`) ; 2. **Nom souhaité** (ex. `staging`) — format `a-z`, `0-9`, `-`, commence par une lettre ; 3. **Usage** (dev, test, prod…). Le namespace créé suivra le modèle : ```text c-- # Exemple : c-acme-rtximz-staging ``` ## Le pattern dev / prod / tooling Une organisation typique : | Namespace | Rôle | | ----------------------- | ----------------------------------------------------------------------------------------------- | | `c-acme-rtximz-dev` | Développement — requests réduites, [scale à zéro la nuit](/operer/consommation-et-facturation/) | | `c-acme-rtximz-prod` | Production — replicas ≥ 2, [HPA et PDB](/operer/scaling-et-disponibilite/) | | `c-acme-rtximz-tooling` | Outillage transverse — CI, jobs d’administration, supervision maison | Le [réseau intra-organisation étant ouvert](/comprendre/isolation-reseau/), le namespace `tooling` peut joindre dev et prod sans configuration : pratique pour un runner CI qui déploie partout, ou un outil de supervision maison. Pour promouvoir une version entre environnements, utilisez **la même image, taguée immuablement** (`v1.4.2`, un digest ou un SHA de commit — pas `latest`) et ne changez entre namespaces que la configuration ([ConfigMaps/Secrets](/deployer/configmaps-et-secrets/)). dev peut joindre prod L’ouverture intra-organisation joue dans les deux sens : un pod de dev peut joindre la base de prod, et [aucune NetworkPolicy client ne peut l’empêcher](/comprendre/isolation-reseau/#le-pi%C3%A8ge-%C3%A0-conna%C3%AEtre--dev-peut-joindre-prod). Protégez-vous par la configuration : des **secrets distincts par environnement** (mots de passe de base différents en dev et en prod), et jamais d’URL de prod dans une config de dev. ## Bien démarrer un nouveau namespace * Votre kubeconfig existant y donne accès immédiatement — mais son **namespace par défaut ne change pas** : pensez au `-n` ou changez de contexte ([rappel](/demarrer/recuperer-votre-kubeconfig/)) ; * Chaque namespace reçoit ses propres quotas, son [Issuer TLS](/deployer/exposer-en-https/), son ServiceAccount CI et sa [sauvegarde quotidienne](/operer/sauvegardes-et-restauration/) — rien à configurer ; * Multiplier les environnements multiplie les plafonds : deux namespaces = 2 × 20 pods, 2 × 10 vCPU… # Sauvegardes et restauration > Ce qui est sauvegardé automatiquement chaque nuit sur Kontainers, comment demander une restauration, et comment compléter avec vos propres sauvegardes applicatives. ## Ce qui est sauvegardé automatiquement Chaque namespace est sauvegardé **chaque nuit à 22h00**, sans configuration de votre part : | Aspect | Détail | | ------------- | ------------------------------------------------------------------------------------------------------------------ | | **Périmètre** | Tout le namespace : ressources Kubernetes (Deployments, Services, ConfigMaps, Secrets…) **et volumes persistants** | | **Fréquence** | Quotidienne, 22h00 | | **Rétention** | **10 jours** | | **Stockage** | Bucket objet S3 dédié, chiffré, **géo-redondant : à plus de 400 km du site de production** | ## Demander une restauration La restauration n’est pas en self-service : elle passe par un [ticket support](/depanner/contacter-le-support/). Indiquez : 1. le **namespace** concerné ; 2. **quoi** restaurer : tout le namespace, une ressource précise, ou un volume (nom du PVC) ; 3. la **date/heure** de la sauvegarde souhaitée (dans la fenêtre de 10 jours) ; 4. la raison — utile pour éviter de restaurer par-dessus une donnée plus récente que le problème. Attention Une restauration de volume écrase les données actuelles par celles de la sauvegarde. Tout ce qui a été écrit entre la sauvegarde et la restauration est perdu — d’où l’intérêt des sauvegardes applicatives ci-dessous pour réduire la fenêtre de perte. ## Compléter avec des sauvegardes applicatives La sauvegarde plateforme est un filet de sécurité quotidien. Pour maîtriser finement votre point de reprise (RPO), doublez-la d’une sauvegarde **applicative** dont vous contrôlez la fréquence et la rétention. Exemple : un dump PostgreSQL toutes les 6 heures via un CronJob : ```yaml apiVersion: batch/v1 kind: CronJob metadata: name: pg-dump spec: schedule: "0 */6 * * *" jobTemplate: spec: template: spec: restartPolicy: Never containers: - name: dump image: postgres:17-alpine command: ["sh", "-c", "pg_dump $DATABASE_URL > /backup/dump-$(date +%Y%m%d-%H%M).sql"] envFrom: - secretRef: name: db-credentials volumeMounts: - name: backup mountPath: /backup volumes: - name: backup persistentVolumeClaim: claimName: pg-backups ``` Avantages : restauration en autonomie (`psql < dump.sql`), granularité de 6 h au lieu de 24 h, pour quelques dizaines de centimes par mois ([facturation à l’heure entamée](/reference/facturation/)). PostgreSQL managé Si vous utilisez l’opérateur PostgreSQL intégré (CNPG), vous pouvez en plus configurer des sauvegardes continues **vers votre propre stockage objet S3** (OVHcloud, Scaleway…), avec sauvegardes à la demande et planifiées que vous pilotez vous-même — [voir le guide](/deployer/postgresql-manage/#sauvegardes-vers-votre-stockage-s3). ## En résumé | Besoin | Solution | | ---------------------------------------- | --------------------------------------------------------- | | Filet de sécurité quotidien | Sauvegarde plateforme (automatique, rien à faire) | | Récupérer l’état d’il y a N jours (≤ 10) | [Ticket de restauration](/depanner/contacter-le-support/) | | RPO fin, restauration autonome | Dumps applicatifs en CronJob | | PostgreSQL | Backups CNPG self-service + filet plateforme | # Scaling et haute disponibilité > Passer de 1 à N replicas proprement sur Kontainers : HorizontalPodAutoscaler, PodDisruptionBudget, et les plafonds réels de l'autoscaling. Un replica unique suffit pour développer ; en production, on veut absorber les pics de charge (**HPA**) et survivre aux opérations de maintenance (**PDB**). Les deux objets sont disponibles en CRUD complet sur Kontainers. ## Prérequis : des probes et des requests L’autoscaling et la haute dispo reposent sur deux fondations : ```yaml containers: - name: web image: mon-app:1.4 resources: requests: # l'HPA calcule en % de la request CPU cpu: 250m memory: 512Mi readinessProbe: # un pod non prêt ne reçoit pas de trafic httpGet: {path: /health, port: 8080} initialDelaySeconds: 5 ``` Sans `requests.cpu`, un HPA basé CPU n’a pas de référence ; sans `readinessProbe`, vos rollouts envoient du trafic vers des pods pas encore prêts. ## HorizontalPodAutoscaler Testé sur la plateforme — ajuste le nombre de replicas selon la charge CPU : ```yaml apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: web spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: web minReplicas: 2 maxReplicas: 6 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 80 # % de la request CPU ``` ```bash kubectl get hpa -n # NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS # web Deployment/web cpu: 12%/80% 2 6 2 ``` À savoir : * **Le plafond réel est votre quota namespace** : la somme des requests de tous vos pods ne peut dépasser [10 vCPU / 20 Gi](/reference/quotas-et-limites/) (et 20 pods). Dimensionnez `maxReplicas` en conséquence : 6 replicas × 250 m = 1,5 vCPU, très à l’aise ; * **Scaler = payer** : chaque replica supplémentaire réserve ses requests ([facturation](/reference/facturation/)) — c’est le but (absorber la charge), mais bornez `maxReplicas` à ce que vous êtes prêt à dépenser ; * La CPU n’ayant [pas de limite](/reference/mutations-et-politiques/), vos pods bursteront déjà au-delà de leur request avant même que l’HPA n’ajoute des replicas : l’HPA gère les charges soutenues, pas les micro-pics ; * L’HPA sait aussi cibler la mémoire (`name: memory`) — attention toutefois : la mémoire redescend rarement, ce qui peut maintenir des replicas inutiles. ## PodDisruptionBudget Le cluster est régulièrement mis à jour (nœud par nœud). Un **PDB** garantit qu’un minimum de vos pods reste disponible pendant ces opérations : ```yaml apiVersion: policy/v1 kind: PodDisruptionBudget metadata: name: web spec: minAvailable: 1 selector: matchLabels: app: web ``` Règles pratiques : * **PDB sans replicas multiples = piège** : avec `replicas: 1` et `minAvailable: 1`, votre unique pod bloque la maintenance — et sera ultimement déplacé quand même. Un PDB accompagne toujours `replicas: 2+` ; * `minAvailable: 1` avec 2 replicas est le réglage simple et sain pour la plupart des applications web. ## La checklist prod | | Objet | Effet | | - | -------------------------------------------------------------------------- | ------------------------------------------------- | | ✅ | `replicas: 2` minimum | Survit à la perte d’un pod ou d’un nœud | | ✅ | `readinessProbe` | Pas de trafic vers un pod pas prêt | | ✅ | HPA `min 2 / max N` | Absorbe les pics de charge | | ✅ | PDB `minAvailable: 1` | Toujours au moins un pod pendant les maintenances | | ✅ | Requests dimensionnées sur le [monitoring](/operer/acceder-au-monitoring/) | Ni gaspillage, ni OOMKill | L’[exemple stack web complète](/exemples/stack-web-complete/) assemble tout ça sur une application réelle.