
Popeye scanne votre cluster Kubernetes en temps réel et génère un rapport de santé avec un score de 0 à 100. L'outil détecte les mauvaises configurations courantes : images taguées latest, absence de limites CPU/mémoire, pods sans probes, conteneurs root, secrets inutilisés. En 30 secondes, vous savez ce qui doit être corrigé.
Ce guide vous montre comment :
- Installer Popeye via mise, Homebrew ou binaire
- Lancer un scan et interpréter les résultats
- Filtrer l'analyse par namespace ou type de ressource
- Personnaliser les règles avec spinach.yaml
- Intégrer dans une CI/CD avec seuil de score minimal
Pourquoi auditer son cluster avec Popeye
Section intitulée « Pourquoi auditer son cluster avec Popeye »Kubernetes accepte de déployer des manifestes même s'ils violent les bonnes pratiques. Un Deployment sans limites de ressources ? Accepté. Une image nginx:latest ? Déployée. Un pod qui tourne en root ? Aucune erreur.
Ces configurations fonctionnent... jusqu'au jour où :
- Un pod consomme toute la mémoire du node et fait crasher les autres
- Une mise à jour automatique de
latestcasse l'application - Une vulnérabilité exploite les privilèges root du conteneur
Popeye détecte ces problèmes avant qu'ils ne surviennent en analysant ce qui tourne réellement dans le cluster (pas les fichiers YAML sur disque).
| Ce que Popeye vérifie | Exemple de problème détecté |
|---|---|
| Images | Tag latest, image sans tag |
| Ressources | Pas de requests/limits CPU/mémoire |
| Probes | Pas de liveness/readiness probe |
| Sécurité | Container root, pas de NetworkPolicy |
| RBAC | ClusterRoles/Roles inutilisés |
| Ressources orphelines | ConfigMaps, Secrets non référencés |
Installation
Section intitulée « Installation »Popeye est un binaire Go statique, sans dépendance ni démon à démarrer. Il s'installe sur votre poste et se connecte au cluster avec votre kubeconfig courant, exactement comme kubectl. Trois voies existent selon vos habitudes : un gestionnaire de versions, un gestionnaire de paquets système, ou le binaire téléchargé directement depuis les releases GitHub.
Avec mise (recommandé)
Section intitulée « Avec mise (recommandé) »mise épingle la version outil par outil et la restaure automatiquement dans chaque projet, ce qui évite qu'un collègue et la CI n'obtiennent pas le même rapport.
mise install popeye@latestmise use popeye@latestAvec Homebrew
Section intitulée « Avec Homebrew »Sur macOS et Linux, le tap de l'auteur fournit la formule officielle. La version installée suit alors le rythme de brew upgrade, sans épinglage possible par projet.
brew install derailed/popeye/popeyeBinaire précompilé
Section intitulée « Binaire précompilé »C'est la méthode à retenir pour un runner de CI, où l'on veut une version figée et aucun gestionnaire de paquets à installer. L'archive est téléchargée par son numéro de version explicite, jamais par un tag mouvant.
# Télécharger depuis GitHubcurl -LO https://github.com/derailed/popeye/releases/download/v0.22.1/popeye_linux_amd64.tar.gztar -xzf popeye_linux_amd64.tar.gzsudo mv popeye /usr/local/bin/Vérification :
popeye versionVersion: 0.22.1Commit: 35b554961dcdabfa7aba6bd070673c6c24c70884Date: 2025-01-28T15:31:22ZPremier scan
Section intitulée « Premier scan »Popeye utilise votre kubeconfig actif (comme kubectl). Lancez un scan complet :
popeyeRésultat sur un cluster Kind fraîchement créé :
GENERAL [KIND-POPEYE-TEST]┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅ · Connectivity.............................✅ · MetricServer.............................💥
CLUSTER (1 SCANNED) 💥 0 😱 0 🔊 0 ✅ 1 100٪┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅ · Version..................................✅ ✅ [POP-406] K8s version OK.
DEPLOYMENTS (2 SCANNED) 💥 0 😱 1 🔊 1 ✅ 0 50٪┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅ · demo/nginx-bad...........................😱 🐳 nginx 😱 [POP-101] Image tagged "latest" in use. 😱 [POP-106] No resources requests/limits defined. · demo/nginx-good..........................🔊 🐳 nginx 🔊 [POP-108] Unnamed port 8080.
SUMMARY┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅Your cluster score: A (91)Comprendre le rapport
Section intitulée « Comprendre le rapport »Le rapport se lit à deux vitesses. Le score global en fin de sortie donne la tendance et sert de garde-fou en CI ; les codes POP-xxx ligne par ligne indiquent quoi corriger et où. Un piège classique consiste à ne regarder que le score : un cluster noté A peut très bien héberger un pod privilégié, parce qu'un seul problème sur des dizaines de ressources conformes pèse peu dans la moyenne.
Niveaux de sévérité
Section intitulée « Niveaux de sévérité »Popeye classe chaque constat sur quatre niveaux, du simple conseil au problème bloquant. C'est ce niveau, et non le nombre de lignes, qui détermine l'impact sur le score et ce que vous devez traiter en priorité.
| Icône | Niveau | Signification | Action |
|---|---|---|---|
| ✅ | OK | Ressource conforme | Rien à faire |
| 🔊 | Info | Recommandation | Amélioration optionnelle |
| 😱 | Warn | Problème potentiel | À corriger |
| 💥 | Error | Problème critique | Action immédiate requise |
Codes d'erreur courants
Section intitulée « Codes d'erreur courants »Chaque règle porte un identifiant stable de la forme POP-xxx, regroupé par familles : la série 100 rassemble les constats sur les conteneurs (image, probes, ressources), la série 300 ceux liés aux ServiceAccounts et aux privilèges, la série 400 les références manquantes. Ces codes sont la clé de la personnalisation, puisque c'est par eux que l'on exclut une règle dans spinach.yaml.
| Code | Message | Explication | Solution |
|---|---|---|---|
| POP-101 | Image tagged "latest" | L'image peut changer sans préavis | Spécifier une version : nginx:1.27.0 |
| POP-102 | No probes defined | Kubernetes ne sait pas si l'app est saine | Ajouter liveness/readiness probes |
| POP-106 | No resources requests/limits | Le pod peut consommer toutes les ressources | Définir requests et limits |
| POP-107 | No resource limits | Limits absentes (requests présentes) | Ajouter limits CPU/memory |
| POP-300 | Uses "default" ServiceAccount | Risque de permissions excessives | Créer un ServiceAccount dédié |
| POP-302 | Pod running as root | Risque de sécurité | Ajouter runAsNonRoot: true |
| POP-400 | Unable to locate reference | ConfigMap/Secret inutilisé | Supprimer ou utiliser la ressource |
| POP-1204 | Not secured by NetworkPolicy | Trafic non filtré | Ajouter une NetworkPolicy |
Calcul du score
Section intitulée « Calcul du score »Le score (0-100) est calculé par type de ressource, puis agrégé. Chaque problème réduit le score selon sa sévérité :
- Error : forte pénalité
- Warn : pénalité moyenne
- Info : faible impact
Un cluster de production devrait viser 80+ (note B minimum).
| Score | Note | Interprétation |
|---|---|---|
| 90-100 | A | Excellent, peu de corrections |
| 80-89 | B | Bon, quelques améliorations |
| 70-79 | C | Acceptable, corrections recommandées |
| 60-69 | D | Insuffisant, action requise |
| 0-59 | F | Critique, problèmes majeurs |
Filtrer l'analyse
Section intitulée « Filtrer l'analyse »Un scan complet sur un cluster de production produit des centaines de lignes, dont une bonne partie concerne des composants système que vous ne maîtrisez pas. Les filtres servent donc à deux choses : rendre le rapport lisible, et cibler le périmètre dont votre équipe est réellement responsable. Ils se combinent librement.
Par namespace
Section intitulée « Par namespace »Sans option, Popeye n'analyse que le namespace courant de votre contexte kubeconfig. -n en cible un autre, -A bascule sur l'ensemble du cluster.
# Un seul namespacepopeye -n demo
# Tous les namespacespopeye -APar type de ressource
Section intitulée « Par type de ressource »Utilisez l'option -s (sections) avec les alias de ressources :
# Pods et Deployments uniquementpopeye -n demo -s po,deploy| Ressource | Alias |
|---|---|
| Pod | po |
| Deployment | dp, deploy |
| Service | svc |
| ConfigMap | cm |
| Secret | sec |
| ServiceAccount | sa |
| Ingress | ing |
| StatefulSet | sts |
| DaemonSet | ds |
| Job | job |
| CronJob | cj |
| PersistentVolumeClaim | pvc |
| NetworkPolicy | np |
| HorizontalPodAutoscaler | hpa |
Par niveau de sévérité
Section intitulée « Par niveau de sévérité »Afficher uniquement les problèmes warn et plus :
popeye -l warnValeurs possibles : ok, info, warn, error
Formats de sortie
Section intitulée « Formats de sortie »Le format se choisit avec -o et détermine à qui s'adresse le rapport. La console convient à une inspection manuelle, le JSON à un traitement automatisé, le HTML à un partage avec une équipe qui n'a pas accès au cluster. Ce choix est aussi ce qui rend Popeye utilisable en CI, où une sortie colorée avec des émojis n'a aucun intérêt.
Console (défaut)
Section intitulée « Console (défaut) »Sortie interactive avec couleurs et icônes, destinée à la lecture directe dans un terminal.
popeyeJSON (pour traitement automatisé)
Section intitulée « JSON (pour traitement automatisé) »Le JSON contient l'intégralité des constats avec leur code et leur niveau, ce qui permet de les rejouer dans un script ou de les archiver pour suivre l'évolution.
popeye -o json > rapport.jsonHTML (pour partage)
Section intitulée « HTML (pour partage) »Le rendu HTML produit un fichier autonome, à envoyer à une équipe applicative ou à joindre à une revue d'architecture. L'option --save écrit le rapport dans le répertoire désigné par POPEYE_REPORT_DIR.
POPEYE_REPORT_DIR=\$(pwd) popeye --save --out html --output-file rapport.htmlScore seul (pour CI/CD)
Section intitulée « Score seul (pour CI/CD) »Ce format n'émet que le score numérique, ce qui facilite sa récupération dans une variable de pipeline ou son envoi vers un tableau de bord.
popeye -o score# Retourne uniquement le chiffre, ex: 91Autres formats
Section intitulée « Autres formats »Quatre formats complémentaires couvrent des besoins plus spécifiques, notamment l'affichage dans l'onglet de tests d'un serveur d'intégration ou la remontée vers une solution de métriques :
yaml: rapport YAMLjunit: format JUnit pour intégration CIjurassic: texte sans couleurs ni icônesprometheus: métriques Prometheus
Personnaliser les règles avec spinach.yaml
Section intitulée « Personnaliser les règles avec spinach.yaml »Créez un fichier spinach.yaml pour adapter les règles à votre contexte :
# Configuration Popeyepopeye: # Seuils d'utilisation des ressources allocations: cpu: underPercUtilization: 200 # Alerte si CPU sous-utilisé > 200% overPercUtilization: 50 # Alerte si CPU sur-utilisé > 50% memory: underPercUtilization: 200 overPercUtilization: 50
# Exclusions excludes: # Exclusions globales (tous les linters) global: fqns: [rx:^kube-] # Ignorer kube-system, kube-public... codes: ["300"] # Ignorer le code POP-300
# Exclusions par linter linters: pods: instances: - fqns: [kube-system/coredns] codes: ["302"] # Ignorer "running as root" pour CoreDNS
namespaces: codes: ["100"] # Ignorer namespace inactif
# Seuils pour les pods resources: pod: restarts: 5 # Alerte si > 5 restarts limits: cpu: 80 memory: 75
# Registries autorisés (tout autre registre = erreur) registries: - docker.io - ghcr.io - quay.io - registry.k8s.io
# Modifier la sévérité d'un code overrides: - code: "206" # PodDisruptionBudget manquant severity: 1 # Réduire à Info (1=info, 2=warn, 3=error)Lancez Popeye avec ce fichier :
popeye -f spinach.yamlIntégration CI/CD
Section intitulée « Intégration CI/CD »Popeye analysant un cluster vivant et non des fichiers, il ne se branche pas sur une pull request comme un linter de manifestes : il s'exécute après le déploiement, ou sur un déclencheur planifié. Le job a besoin d'un kubeconfig valide, donc de credentials stockés en secret, avec des droits en lecture seule. L'option --min-score transforme ensuite le rapport en garde-fou, en faisant échouer le job sous un seuil que vous fixez.
GitHub Actions
Section intitulée « GitHub Actions »Le workflow ci-dessous combine un déclenchement sur main et un scan quotidien planifié, puis conserve le rapport JSON en artefact pour pouvoir comparer d'un jour sur l'autre.
name: Kubernetes Lint
on: push: branches: [main] schedule: - cron: '0 6 * * *' # Tous les jours à 6h
jobs: popeye: runs-on: ubuntu-latest steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- name: Setup kubeconfig run: | mkdir -p ~/.kube echo "\${{ secrets.KUBECONFIG }}" | base64 -d > ~/.kube/config
- name: Install Popeye run: | curl -LO https://github.com/derailed/popeye/releases/download/v0.22.1/popeye_linux_amd64.tar.gz tar -xzf popeye_linux_amd64.tar.gz sudo mv popeye /usr/local/bin/
- name: Run Popeye run: | popeye -A --min-score 80 -o json > popeye-report.json
- name: Upload Report uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: popeye-report path: popeye-report.jsonL'option --min-score 80 fait échouer le job si le score est inférieur à 80.
GitLab CI
Section intitulée « GitLab CI »Ici l'image officielle évite l'étape d'installation, et la règle limite l'exécution aux pipelines planifiés. Le seuil est plus bas et --force-exit-zero neutralise l'échec : c'est le réglage à adopter au démarrage, tant que le rapport sert à mesurer plutôt qu'à bloquer.
popeye: stage: validate image: quay.io/derailed/popeye:v0.22.1@sha256:ee3e66d023e736eec64edd6ac86a36e33b6a389ff508994ba356cc20b756e5c4 script: - popeye -A --min-score 75 --force-exit-zero -o yaml rules: - if: \$CI_PIPELINE_SOURCE == "schedule"Exécution dans le cluster
Section intitulée « Exécution dans le cluster »Popeye peut aussi tourner dans le cluster via un CronJob, ce qui supprime le besoin d'exporter un kubeconfig vers un runner externe. L'identité utilisée devient alors un ServiceAccount dédié, auquel un ClusterRole n'accorde que les verbes get et list : Popeye ne modifie jamais rien, il n'a donc aucune raison d'obtenir davantage. Le manifeste ci-dessous crée le namespace, l'identité, les droits et la tâche planifiée en une seule fois.
apiVersion: v1kind: Namespacemetadata: name: popeye---apiVersion: v1kind: ServiceAccountmetadata: name: popeye namespace: popeye---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata: name: popeyerules: - apiGroups: [""] resources: [configmaps, endpoints, namespaces, nodes, persistentvolumes, persistentvolumeclaims, pods, secrets, serviceaccounts, services] verbs: [get, list] - apiGroups: [apps] resources: [daemonsets, deployments, statefulsets, replicasets] verbs: [get, list] - apiGroups: [networking.k8s.io] resources: [ingresses, networkpolicies] verbs: [get, list] - apiGroups: [autoscaling] resources: [horizontalpodautoscalers] verbs: [get, list] - apiGroups: [policy] resources: [poddisruptionbudgets] verbs: [get, list] - apiGroups: [rbac.authorization.k8s.io] resources: [clusterroles, clusterrolebindings, roles, rolebindings] verbs: [get, list] - apiGroups: [metrics.k8s.io] resources: [pods, nodes] verbs: [get, list]---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata: name: popeyesubjects: - kind: ServiceAccount name: popeye namespace: popeyeroleRef: kind: ClusterRole name: popeye apiGroup: rbac.authorization.k8s.io---apiVersion: batch/v1kind: CronJobmetadata: name: popeye namespace: popeyespec: schedule: "0 */6 * * *" # Toutes les 6 heures concurrencyPolicy: Forbid jobTemplate: spec: template: spec: serviceAccountName: popeye restartPolicy: Never containers: - name: popeye image: quay.io/derailed/popeye:v0.22.1 args: - -A - -o - yaml - --force-exit-zero resources: limits: cpu: 500m memory: 256Mikubectl apply -f popeye-cronjob.yamlBonnes pratiques
Section intitulée « Bonnes pratiques »Un scan Popeye ne coûte presque rien, ce qui rend l'outil facile à adopter mais aussi facile à laisser dériver. Les quatre réflexes qui suivent visent le même objectif : obtenir un rapport que l'équipe lit vraiment, plutôt qu'une liste de centaines de constats que plus personne n'ouvre.
Exécuter régulièrement
Section intitulée « Exécuter régulièrement »Planifiez un scan quotidien ou après chaque déploiement majeur. Les problèmes s'accumulent vite.
Commencer par un namespace
Section intitulée « Commencer par un namespace »Sur un grand cluster, analysez d'abord un namespace critique :
popeye -n production -l warnDocumenter les exclusions
Section intitulée « Documenter les exclusions »Si vous excluez des règles dans spinach.yaml, documentez pourquoi :
excludes: linters: pods: instances: # CoreDNS tourne en root par design (upstream) # Voir: https://github.com/coredns/coredns/issues/... - fqns: [kube-system/coredns] codes: ["302"]Surveiller le score dans le temps
Section intitulée « Surveiller le score dans le temps »Exportez les rapports JSON et suivez l'évolution du score. Un score qui baisse indique une dette technique croissante.
Dépannage
Section intitulée « Dépannage »Les incidents rencontrés avec Popeye tiennent rarement à l'outil lui-même : ce sont surtout des questions de droits RBAC, de composants absents du cluster ou de volume de ressources à parcourir. Le tableau associe chaque symptôme à sa cause la plus fréquente.
| Problème | Cause probable | Solution |
|---|---|---|
MetricServer 💥 | metrics-server non installé | Installer metrics-server ou ignorer |
| Timeout sur grand cluster | Trop de ressources à scanner | Filtrer par namespace -n |
| Permissions insuffisantes | RBAC manquant | Vérifier les droits get/list |
| Score très bas sur kube-system | Composants système non optimisés | Exclure via spinach.yaml |
À retenir
Section intitulée « À retenir »- Popeye analyse le cluster en temps réel (pas les fichiers YAML)
- Le score de 0 à 100 donne une vue d'ensemble rapide
- Les codes POP-xxx identifient précisément chaque problème
- spinach.yaml permet d'adapter les règles à votre contexte
- Intégrez dans la CI/CD avec
--min-scorepour bloquer les régressions - Popeye est en lecture seule : il ne modifie jamais vos ressources