Introduction
Les comptes de service Kubernetes constituent la couche d'identité pour les charges de travail s'exécutant à l'intérieur d'un cluster. Chaque pod se voit attribuer un compte de service, et cette identité détermine les opérations de l'API Kubernetes que le pod peut effectuer. Dans les clusters de production, le nombre de comptes de service ainsi que leurs jetons, secrets et liaisons RBAC associés peut croître rapidement, entraînant une dégradation des performances, des risques de sécurité et des difficultés opérationnelles.
Cet article fournit un guide pratique pour la planification de capacité des comptes de service Kubernetes du point de vue d'un administrateur. Il s'adresse aux ingénieurs DevOps, aux SRE et aux équipes techniques de startups qui doivent comprendre comment les comptes de service consomment les ressources du cluster, définir des limites appropriées et planifier la montée en charge.
Nous aborderons :
- Comment les comptes de service consomment les ressources du cluster et pourquoi la planification de capacité est importante
- Des méthodes concrètes pour compter et mesurer les comptes de service et les objets associés
- Comment définir et appliquer des quotas et des limites
- Comment surveiller et vérifier l'état des comptes de service
- Les modes de défaillance courants et les procédures de récupération
- Une liste de contrôle opérationnelle prête à l'emploi
L'objectif est la sécurité opérationnelle : observer avant de modifier, limiter le rayon d'impact, utiliser des espaces réservés au lieu de secrets, vérifier le résultat et documenter la procédure de récupération si l'état attendu n'est pas atteint.
Impact des comptes de service sur la capacité du cluster
Les comptes de service eux-mêmes sont des objets légers, mais ils sont étroitement liés à plusieurs autres ressources Kubernetes qui peuvent consommer une capacité significative du cluster :
- Objets ServiceAccount : stockés dans etcd, chaque compte de service consomme de la mémoire et du stockage.
- Secrets (secrets de jetons hérités) : avant Kubernetes 1.24, chaque compte de service générait automatiquement un secret de jeton. Ces secrets sont stockés dans etcd et peuvent occuper un espace considérable.
- Jetons (jetons de volume projeté) : depuis Kubernetes 1.24, les jetons de compte de service sont limités dans le temps et émis via l'API TokenRequest. Ils ne sont pas stockés comme secrets, mais le kube-apiserver doit gérer l'émission et la validation des jetons, ce qui ajoute une charge CPU et mémoire.
- RoleBindings et ClusterRoleBindings : chaque liaison fait référence à un compte de service. L'autoriseur RBAC doit évaluer ces liaisons pour chaque requête API, donc un grand nombre de liaisons peut ralentir les requêtes API.
- Pods : chaque pod monte un jeton de compte de service (sauf si désactivé). Le kubelet doit projeter ce jeton dans le pod, et l'identité du pod est utilisée pour le contrôle d'admission et l'autorisation.
La planification de capacité pour les comptes de service implique donc non seulement de compter les objets ServiceAccount, mais aussi d'estimer la croissance des secrets, jetons et liaisons associés, et de comprendre leur impact sur etcd et le serveur API.
Exemples de consommation de ressources
Quantifions l'impact avec un exemple réaliste.
Supposons un cluster avec :
- 10 000 comptes de service
- Chaque compte de service a 1 secret de jeton hérité (si Kubernetes < 1.24) ou 1 jeton projeté (si >= 1.24)
- Chaque compte de service est lié à au moins un RoleBinding ou ClusterRoleBinding
Stockage etcd :
- Un objet ServiceAccount fait généralement 1 à 2 Ko.
- Un objet Secret (jeton hérité) fait environ 2 à 5 Ko (inclut les données du jeton et les métadonnées).
- Donc pour 10 000 comptes de service avec secrets hérités, le stockage etcd total pour ces objets est d'environ 30 à 70 Mo. Cela peut sembler faible, mais les performances d'etcd se dégradent à mesure que la base de données grossit, et des écritures fréquentes (par exemple, rotations de secrets) peuvent amplifier les E/S.
Mémoire du serveur API :
- Le serveur API met en cache la plupart des objets en mémoire pour des lectures efficaces. Chaque objet en cache consomme de la mémoire.
- Un objet ServiceAccount peut utiliser environ 500 octets en cache.
- Un objet Secret peut utiliser environ 1 Ko en cache.
- Donc 10 000 comptes de service + 10 000 secrets pourraient ajouter 15 Mo à la mémoire du serveur API. C'est gérable, mais si vous avez 100 000 ou 1 000 000, cela devient significatif.
Coût d'autorisation RBAC :
- L'autoriseur RBAC construit une carte des liaisons vers les rôles. Le nombre de liaisons affecte directement le temps d'autorisation d'une requête.
- Avec de nombreuses liaisons, l'autoriseur peut devoir parcourir une longue liste pour trouver les règles applicables. Cela peut ajouter des millisecondes à chaque requête API.
- Dans un cluster très actif, un nombre élevé de liaisons peut devenir un goulot d'étranglement.
Taux d'émission de jetons :
- Lors de l'utilisation de jetons projetés, chaque pod demande un jeton au serveur API au démarrage. Le serveur API doit émettre et signer ces jetons.
- L'opération de signature de jeton est gourmande en CPU. Si vous créez de nombreux pods simultanément, le taux d'émission de jetons peut faire grimper l'utilisation du CPU.
Ainsi, la planification de capacité doit tenir compte de toutes ces dimensions.
Observer l'utilisation actuelle des comptes de service
Avant d'apporter des modifications, vous devez mesurer l'état actuel. Utilisez des commandes en lecture seule pour collecter des données.
Compter les comptes de service
kubectl get serviceaccounts --all-namespaces | wc -l
Cela donne un compte approximatif. Pour obtenir un nombre plus précis par espace de noms :
kubectl get serviceaccounts --all-namespaces -o json | jq '.items | length'
Remarque : l'espace de noms default contient le compte de service default créé automatiquement pour chaque espace de noms.
Compter les secrets de jetons hérités
Si vous utilisez Kubernetes < 1.24, chaque compte de service obtient automatiquement un secret de jeton. Comptez-les avec :
kubectl get secrets --all-namespaces --field-selector type=kubernetes.io/service-account-token | wc -l
Sur les clusters plus récents, cette commande peut renvoyer zéro car des jetons projetés sont utilisés à la place.
Compter les RoleBindings et ClusterRoleBindings
kubectl get rolebindings --all-namespaces | wc -l
kubectl get clusterrolebindings --all-namespaces | wc -l
Vérifier la taille de la base de données etcd
Si vous avez accès à etcd, vérifiez la taille de sa base de données et l'état des points de terminaison :
ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
endpoint status --write-out=table
Regardez la colonne DB SIZE. Le quota etcd par défaut est de 2 Gio (configurable via --quota-backend-bytes). Si vous approchez du quota, les écritures échoueront, y compris la création de comptes de service.
Vérifier les métriques du serveur API
Si vous avez metrics-server ou Prometheus, interrogez les métriques pertinentes :
apiserver_request_duration_seconds(surtout pour les opérations gourmandes en RBAC)etcd_db_total_size_in_bytesapiserver_storage_objects(par type de ressource)
Par exemple, pour voir le nombre de comptes de service stockés :
apiserver_storage_objects{resource="serviceaccounts"}
Cela donne un compte direct du point de vue du serveur API.
Définir des limites et des quotas
Pour empêcher une croissance illimitée, vous devez définir des quotas de ressources et des limites sur les espaces de noms. C'est l'une des mesures de planification de capacité les plus efficaces.
Quotas au niveau de l'espace de noms
Vous pouvez créer un ResourceQuota qui limite le nombre de comptes de service et de secrets dans un espace de noms.
Exemple quota.yaml :
apiVersion: v1
kind: ResourceQuota
metadata:
name: service-account-quota
namespace: my-app
spec:
hard:
serviceaccounts: "100"
secrets: "200"
Appliquez-le :
kubectl apply -f quota.yaml
Essayez ensuite de créer plus de comptes de service que permis. Vous verrez une erreur comme :
Error from server (Forbidden): error when creating "sa.yaml": serviceaccounts "test-sa" is forbidden: exceeded quota: service-account-quota, requested: serviceaccounts=1, used: serviceaccounts=100, limited: serviceaccounts=100
Limites au niveau du cluster
Il n'existe pas de quota intégré à l'échelle du cluster pour les comptes de service, mais vous pouvez utiliser des contrôleurs d'admission comme OPA Gatekeeper ou Kyverno pour appliquer des politiques.
Par exemple, avec Kyverno, vous pouvez créer une politique de cluster qui limite le nombre total de comptes de service :
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: limit-serviceaccounts
spec:
validationFailureAction: enforce
background: false
rules:
- name: limit-serviceaccount-count
match:
resources:
kinds:
- ServiceAccount
validate:
message: "Trop de comptes de service dans l'espace de noms"
pattern:
metadata:
labels:
app.kubernetes.io/name: "?*"
Cependant, ceci est un exemple simple ; une politique plus robuste utiliserait un contexte pour compter les comptes de service existants et refuser si au-dessus d'un seuil. Kyverno prend en charge de telles vérifications via des règles deny avec conditions.
Durées de vie et rotation des jetons
Lors de l'utilisation de jetons projetés, vous pouvez définir une durée de vie de jeton plus courte pour limiter l'exposition et réduire la fenêtre de vol de jeton. La durée de vie par défaut est de 1 heure, mais elle peut être configurée via le drapeau --service-account-max-token-expiration sur le kube-apiserver. La durée de vie du jeton par pod peut être définie dans la spécification du pod :
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
serviceAccountName: my-sa
containers:
- name: app
image: nginx
volumeMounts:
- mountPath: /var/run/secrets/tokens
name: my-token
volumes:
- name: my-token
projected:
sources:
- serviceAccountToken:
path: my-token
expirationSeconds: 3600
Définir expirationSeconds à une valeur plus basse (par exemple, 600 secondes) force une rotation plus fréquente des jetons, ce qui peut augmenter la charge du serveur API mais réduit le risque qu'un jeton volé soit utile.
Désactiver le montage automatique des jetons
Tous les pods n'ont pas besoin de communiquer avec l'API Kubernetes. Vous pouvez désactiver le montage automatique des jetons de compte de service au niveau du compte de service ou du pod pour réduire l'émission de jetons et la surcharge de montage.
Au niveau du compte de service :
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-sa
automountServiceAccountToken: false
Au niveau du pod :
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
serviceAccountName: my-sa
automountServiceAccountToken: false
containers:
- name: app
image: nginx
Cela réduit le nombre de jetons demandés et montés, soulageant une partie de la pression sur le serveur API et le kubelet.
Surveillance et vérification
Après avoir appliqué les limites, vous devez vérifier qu'elles sont efficaces et que le cluster reste sain.
Vérifier l'utilisation des quotas
kubectl describe resourcequota service-account-quota -n my-app
La sortie montre l'utilisation par rapport aux limites strictes.
Surveiller la latence du serveur API
Surveillez la métrique apiserver_request_duration_seconds pour les opérations RBAC. Si vous constatez une augmentation après l'ajout de nombreuses liaisons, vous devrez peut-être faire évoluer le serveur API ou réduire les liaisons.
Tester l'émission de jetons
Vous pouvez simuler la création de pods et mesurer le temps d'émission des jetons. Par exemple, créez un pod de test et mesurez le temps entre la création et l'état prêt :
time kubectl run test-pod --image=nginx --restart=Never
Une émission de jeton lente peut indiquer une surcharge du serveur API.
Vérifier la santé d'etcd
Vérifiez régulièrement la santé d'etcd :
ETCDCTL_API=3 etcdctl endpoint health
Si etcd passe en lecture seule en raison du quota, toutes les mutations échoueront, y compris la création de comptes de service.
Modes de défaillance et récupération
Défaillance : quota de comptes de service dépassé
Symptôme : kubectl create serviceaccount échoue avec l'erreur : exceeded quota.
Récupération :
- Vérifiez l'utilisation actuelle :
kubectl get serviceaccounts -n my-app | wc -l - Identifiez les comptes de service obsolètes et supprimez-les :
kubectl delete serviceaccount obsolete-sa -n my-app
- Ou augmentez temporairement le quota si nécessaire, mais réévaluez après le nettoyage.
Défaillance : quota etcd dépassé
Symptôme : le serveur API renvoie des erreurs etcdserver: mvcc: database space exceeded et les écritures échouent.
Récupération :
- Exécutez immédiatement la compaction et la défragmentation d'etcd :
ETCDCTL_API=3 etcdctl compact <révision-actuelle>
ETCDCTL_API=3 etcdctl defrag
- Identifiez les objets volumineux et nettoyez (par exemple, anciens secrets).
- Augmentez le quota etcd si nécessaire, mais comprenez d'abord ce qui a causé la croissance.
Défaillance : serveur API surchargé par les demandes de jetons
Symptôme : CPU élevé sur le serveur API, latence accrue, pods bloqués dans ContainerCreating.
Récupération :
- Réduisez l'émission de jetons en désactivant le montage automatique lorsque possible.
- Ajoutez plus de réplicas du serveur API ou augmentez le CPU/la mémoire.
- Utilisez la mise en cache des demandes de jetons si disponible.
Défaillance : retards d'autorisation RBAC
Symptôme : réponses API lentes même lorsque etcd et le CPU vont bien ; les décisions d'autorisation prennent du temps.
Récupération :
- Réduisez le nombre de liaisons de rôles en consolidant les rôles.
- Utilisez des règles d'agrégation pour simplifier les rôles de cluster.
- Activez la mise en cache de l'autorisation RBAC si prise en charge (par exemple, dans certaines distributions Kubernetes).
Défaillance : jeton de compte de service expiré
Symptôme : les pods signalent des erreurs d'authentification lors de l'appel à l'API : Unauthorized.
Récupération :
- Vérifiez l'expiration du jeton du pod :
kubectl exec my-pod -- cat /var/run/secrets/kubernetes.io/serviceaccount/token | cut -d'.' -f2 | base64 -d | jq .exp - Si expiré, supprimez le pod pour forcer sa recréation avec un nouveau jeton.
- Pour les pods à longue durée de vie, utilisez des mécanismes d'identité de charge de travail (par exemple, les rôles IAM AWS pour les comptes de service) pour éviter les problèmes de durée de vie des jetons.
Liste de contrôle opérationnelle
Utilisez cette liste de contrôle régulièrement pour vous assurer que la capacité des comptes de service est sous contrôle.
Contrôles quotidiens
- [ ] Surveiller
apiserver_storage_objects{resource="serviceaccounts"}pour détecter des pics inattendus. - [ ] Vérifier le taux d'erreurs du serveur API pour les erreurs de dépassement de quota.
- [ ] Vérifier la santé d'etcd :
etcdctl endpoint health.
Contrôles hebdomadaires
- [ ] Examiner les quotas des espaces de noms :
kubectl get resourcequota --all-namespaces. - [ ] Identifier les espaces de noms avec un nombre élevé de comptes de service :
kubectl get serviceaccounts --all-namespaces -o json | jq -r '.items[] | .metadata.namespace' | sort | uniq -c | sort -nr | head. - [ ] Rechercher les comptes de service inutilisés (non référencés par un pod) : croiser avec les spécifications de pods.
Contrôles mensuels
- [ ] Analyser la tendance de la taille de la base de données etcd et prévoir la croissance.
- [ ] Examiner les paramètres d'expiration des jetons et les politiques de rotation.
- [ ] Auditer les liaisons RBAC pour les entrées trop permissives ou obsolètes.
Ajustements de la planification de capacité
- [ ] Si le nombre de comptes de service approche des limites de quota, prévoir d'augmenter le quota ou d'optimiser.
- [ ] Si la taille d'etcd dépasse 70 % du quota, prévoir une compaction/défragmentation ou augmenter le quota.
- [ ] Si la latence du serveur API se dégrade, envisager une mise à l'échelle ou une réduction des liaisons.
Stratégies de mise à l'échelle
Lorsque votre cluster se développe, voici des stratégies pour faire évoluer la gestion des comptes de service.
Automatiser le cycle de vie des comptes de service
Utilisez des outils comme Terraform, GitOps (ArgoCD/Flux) ou des contrôleurs personnalisés pour créer et supprimer des comptes de service dans le cadre du déploiement d'applications. Cela évite les comptes de service orphelins.
Utiliser la segmentation des espaces de noms
Créez des espaces de noms distincts pour différentes équipes ou applications, chacun avec son propre quota. Cela limite le rayon d'impact d'une seule application.
Adopter l'identité de charge de travail
Lorsque possible, utilisez les rôles IAM des fournisseurs de cloud (par exemple, IAM Roles for Service Accounts sur EKS, Workload Identity sur GKE, Azure AD Pod Identity) pour réduire la dépendance aux jetons de compte de service Kubernetes. Cela peut éliminer la surcharge de gestion des jetons et améliorer la sécurité.
Nettoyer régulièrement les objets obsolètes
Planifiez une tâche cron pour supprimer les comptes de service et secrets inutilisés. Par exemple, utilisez le contrôleur TTL intégré de Kubernetes pour les ressources terminées, ou écrivez un script personnalisé.
Conclusion
La planification de capacité pour les comptes de service Kubernetes ne se limite pas à compter les objets ; elle nécessite de comprendre l'interaction entre les comptes de service, les secrets, les jetons et RBAC, et leur impact sur etcd et le serveur API. En observant l'utilisation actuelle, en définissant des quotas appropriés, en surveillant les métriques clés et en mettant en place des procédures de récupération, vous pouvez maintenir votre cluster en bonne santé à mesure qu'il évolue.
Les principes opérationnels soulignés tout au long de cet article sont : observer avant de modifier, limiter le rayon d'impact, vérifier les résultats et documenter les chemins de récupération. En suivant les exemples pratiques et la liste de contrôle, vous pouvez élaborer une stratégie de gestion des comptes de service robuste qui favorise à la fois la sécurité et les performances.
Comme prochaine étape, commencez par mesurer votre utilisation actuelle des comptes de service avec les commandes fournies, puis définissez des quotas d'espace de noms et surveillez l'impact. Au fil du temps, itérez sur votre plan de capacité à mesure que votre cluster évolue.