## Introduction

Lorsqu'un pod utilisant un compte de service Kubernetes ne parvient pas à joindre un service interne, la cause première est rarement le code de l'application. Il s'agit plus souvent d'une mauvaise configuration réseau impliquant les autorisations du compte de service, la résolution DNS ou les politiques réseau. Ce guide propose une approche de dépannage systématique pour les problèmes réseau liés aux comptes de service Kubernetes, avec des exemples pratiques et des commandes que vous pouvez exécuter dans votre propre cluster.

Nous nous concentrons sur le chemin réseau allant d'un pod associé à un compte de service spécifique vers un service Kubernetes. Nous verrons comment vérifier que le compte de service existe, que le pod l'utilise correctement, que le DNS résout le nom du service, que les politiques réseau autorisent le trafic et que les endpoints du service sont joignables. À la fin, vous disposerez d'un processus reproductible pour diagnostiquer et corriger les problèmes réseau courants liés aux comptes de service.

## Inventaire des versions et de l'environnement

Avant de commencer le dépannage, documentez votre environnement. Cela garantit que vous testez sur le bon cluster et que vous comprenez les composants impliqués.

### 1. Noter les versions de Kubernetes et du client

```bash
kubectl version --short
```

Sortie attendue (exemple) :

```text
Client Version: v1.24.3
Server Version: v1.24.3
```

Si les versions du client et du serveur diffèrent considérablement, certaines commandes ou ressources d'API peuvent se comporter différemment. Notez les versions pour référence.

### 2. Confirmer le contexte du cluster et l'espace de noms

Assurez-vous d'opérer dans le bon cluster et le bon espace de noms.

```bash
kubectl config current-context
kubectl get namespaces
```

Vérifiez l'espace de noms où se trouvent votre pod et votre service. Pour ce guide, nous supposons un espace de noms appelé `app-ns`.

### 3. Identifier le pod et son compte de service

Listez les pods avec leurs comptes de service :

```bash
kubectl get pods -n app-ns -o custom-columns=POD:.metadata.name,SERVICE_ACCOUNT:.spec.serviceAccountName
```

Sortie attendue (exemple) :

```text
POD                     SERVICE_ACCOUNT
web-5b7c9d6f8-abcde     web-sa
api-7f8d9c6b5-fghij     api-sa
```

Notez le nom du compte de service pour le pod que vous dépannez. Dans cet exemple, nous utiliserons `web-sa`.

### 4. Vérifier l'existence et les détails du compte de service

Vérifiez que le compte de service existe et notez ses secrets (le cas échéant) et les rôles associés.

```bash
kubectl get serviceaccount web-sa -n app-ns -o yaml
```

Recherchez les champs `secrets` et `imagePullSecrets`. Si le compte de service n'a aucun secret listé, c'est normal dans Kubernetes 1.24+ où les secrets de jeton ne sont pas automatiquement créés. Le pod peut toujours s'authentifier à l'aide de jetons projetés.

### 5. Vérifier la topologie réseau

Comprenez comment les pods communiquent. Les plugins CNI courants (Calico, Flannel, Cilium) ont des comportements par défaut différents. Vérifiez les ressources NetworkPolicy dans l'espace de noms :

```bash
kubectl get networkpolicies -n app-ns
```

S'il en existe, elles peuvent bloquer le trafic. Nous les examinerons plus tard.

### Prérequis pour le dépannage

Vous avez besoin d'un accès `kubectl` au cluster et, idéalement, de la permission d'exécuter des commandes dans les pods. Vous devez également avoir une compréhension de base des services Kubernetes, des endpoints et du DNS.

## Chemin de configuration sûr

Pour illustrer le dépannage, nous avons besoin d'une configuration fonctionnelle. Nous allons créer un exemple minimal : un compte de service, un déploiement utilisant ce compte de service et un service que le pod doit joindre.

### 1. Créer un compte de service

Définissez un compte de service dans `sa.yaml` :

```yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: web-sa
  namespace: app-ns
```

Appliquez-le :

```bash
kubectl apply -f sa.yaml
```

Sortie attendue :

```text
serviceaccount/web-sa created
```

### 2. Créer un déploiement utilisant le compte de service

Créez `deploy.yaml` :

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
  namespace: app-ns
spec:
  replicas: 1
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      serviceAccountName: web-sa
      containers:
      - name: web
        image: nginx:alpine
        ports:
        - containerPort: 80
```

Appliquez :

```bash
kubectl apply -f deploy.yaml
```

Vérifiez que le pod est en cours d'exécution :

```bash
kubectl get pods -n app-ns -l app=web
```

Sortie attendue :

```text
NAME                     READY   STATUS    RESTARTS   AGE
web-5b7c9d6f8-abcde      1/1     Running   0          30s
```

### 3. Créer un service

Définissez `service.yaml` pour un service interne auquel le pod web doit accéder. Nous utilisons un simple service ClusterIP :

```yaml
apiVersion: v1
kind: Service
metadata:
  name: api
  namespace: app-ns
spec:
  selector:
    app: api
  ports:
  - port: 80
    targetPort: 8080
```

Appliquez-le :

```bash
kubectl apply -f service.yaml
```

Vérifiez :

```bash
kubectl get services -n app-ns
```

Sortie attendue :

```text
NAME   TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
api    ClusterIP   10.96.100.50    <none>        80/TCP    10s
```

### 4. Créer un pod backend pour le service

Pour que le service ait des endpoints, créez un déploiement backend simple :

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
  namespace: app-ns
spec:
  replicas: 1
  selector:
    matchLabels:
      app: api
  template:
    metadata:
      labels:
        app: api
    spec:
      containers:
      - name: api
        image: hashicorp/http-echo:latest
        args:
        - "-text=API is up"
        - "-listen=:8080"
        ports:
        - containerPort: 8080
```

Appliquez et attendez qu'il soit prêt.

Cette configuration nous donne un environnement contrôlé pour tester la connectivité. Le pod web utilise le compte de service `web-sa`. Nous n'avons pas encore configuré de politiques réseau, donc par défaut tout le trafic est autorisé.

## Vérification et diagnostics

Nous allons maintenant vérifier systématiquement chaque couche du chemin réseau.

### 1. Confirmer que le pod utilise le bon compte de service

Vérifiez la spécification du pod :

```bash
kubectl get pod -n app-ns -l app=web -o jsonpath='{.items[0].spec.serviceAccountName}'
```

Sortie attendue :

```text
web-sa
```

Si la sortie est vide, le pod utilise le compte de service `default` de l'espace de noms.

### 2. Vérifier le jeton du compte de service monté dans le pod

Kubernetes projette un jeton de compte de service dans le pod à l'emplacement `/var/run/secrets/kubernetes.io/serviceaccount`. Vérifiez qu'il existe :

```bash
kubectl exec -n app-ns <nom-du-pod-web> -- ls /var/run/secrets/kubernetes.io/serviceaccount
```

Sortie attendue :

```text
ca.crt  namespace  token
```

Si ces fichiers sont absents, le pod peut ne pas être en mesure de s'authentifier auprès de l'API server, ce qui peut affecter certaines fonctionnalités réseau (mais pas le trafic direct de service à service).

### 3. Résolution DNS à l'intérieur du pod

Depuis le pod web, tentez de résoudre le nom DNS du service. Le nom DNS complet d'un service dans le même espace de noms est `<service>.<espace-de-noms>.svc.cluster.local`. Pour un espace de noms différent, c'est également `<service>.<espace-de-noms>.svc.cluster.local`, mais le nom court `<service>` fonctionne dans le même espace de noms.

```bash
kubectl exec -n app-ns <nom-du-pod-web> -- nslookup api
```

Sortie attendue (exemple) :

```text
Server:         10.96.0.10
Address:        10.96.0.10#53

Name:   api.app-ns.svc.cluster.local
Address: 10.96.100.50
```

Si `nslookup` n'est pas disponible, utilisez `getent hosts api` ou `cat /etc/resolv.conf` pour vérifier la configuration DNS.

Vérifiez `resolv.conf` :

```bash
kubectl exec -n app-ns <nom-du-pod-web> -- cat /etc/resolv.conf
```

Sortie attendue (exemple) :

```text
nameserver 10.96.0.10
search app-ns.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
```

Le serveur de noms doit être l'adresse IP du service DNS du cluster (généralement `kube-dns`). Si le DNS échoue, l'adresse IP du service peut ne pas être renvoyée.

### 4. Tester la connectivité vers l'IP et le port du service

Utilisez `curl` ou `wget` depuis le pod pour accéder au service. Comme notre backend renvoie un message, nous pouvons nous attendre à une réponse.

```bash
kubectl exec -n app-ns <nom-du-pod-web> -- curl -s http://api
```

Sortie attendue :

```text
API is up
```

Si `curl` n'est pas disponible, utilisez `nc -zv api 80` ou `telnet api 80` pour tester la connectivité TCP.

Si cela échoue, vérifiez les endpoints du service :

```bash
kubectl get endpoints -n app-ns api
```

Sortie attendue (exemple) :

```text
NAME   ENDPOINTS           AGE
api    10.244.1.5:8080     5m
```

Si les endpoints sont vides, le sélecteur du service ne correspond à aucun pod. Vérifiez les libellés des pods backend :

```bash
kubectl get pods -n app-ns -l app=api --show-labels
```

### 5. Vérifier les politiques réseau (NetworkPolicy)

Si des NetworkPolicies existent, elles peuvent bloquer le trafic. Listez-les :

```bash
kubectl get networkpolicies -n app-ns
```

Si elles sont présentes, inspectez-les. Par exemple, une politique peut refuser tout le trafic entrant vers les pods portant le libellé `app=api` sauf depuis des sources spécifiques. Vous pouvez tester en créant temporairement une politique autorisant tout (uniquement dans un environnement de test) :

```yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all
  namespace: app-ns
spec:
  podSelector: {}
  ingress:
  - {}
  egress:
  - {}
  policyTypes:
  - Ingress
  - Egress
```

Appliquez-la, retestez la connectivité, puis supprimez-la. Si la connectivité fonctionne avec cette politique, vous savez que les politiques précédentes étaient la cause.

### 6. Vérifier les autorisations du compte de service (RBAC) pour l'accès à l'API

Parfois, le pod doit appeler l'API Kubernetes (par exemple, pour lister les services). Cela nécessite des rôles RBAC liés au compte de service. Vérifiez si le compte de service a une liaison de rôle :

```bash
kubectl get rolebindings,clusterrolebindings -n app-ns -o json | jq '.items[] | select(.subjects[]?.name=="web-sa")'
```

Si aucune liaison n'existe, le pod ne peut pas effectuer d'appels API. Pour tester l'accès API, exécutez :

```bash
kubectl exec -n app-ns <nom-du-pod-web> -- curl -s -k -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" https://kubernetes.default.svc/api/v1/namespaces/app-ns/services
```

Si vous obtenez une erreur 403 Forbidden, le compte de service n'a pas les autorisations RBAC nécessaires. Notez que le trafic de service à service ne nécessite pas l'accès à l'API, mais certaines applications en dépendent.

### 7. Inspecter kube-proxy et les règles de service

Sur le nœud (si vous y avez accès), vérifiez les règles iptables pour l'IP du service :

```bash
sudo iptables-save | grep 10.96.100.50
```

Cela montre les règles DNAT qui acheminent le trafic vers l'IP du pod. Si les règles sont absentes, kube-proxy peut être défaillant.

## Modes de défaillance et récupération

Modes de défaillance courants et comment s'en remettre.

### Défaillance 1 : Faute de frappe ou absence du nom du compte de service

Si la spécification du pod fait référence à un compte de service inexistant, le pod ne démarrera pas avec une erreur du type :

```text
Error creating: pods "web-..." is forbidden: error looking up service account app-ns/web-sa: serviceaccount "web-sa" not found
```

**Récupération :** Créez le compte de service ou corrigez le nom dans le déploiement. Appliquez le changement et le pod redémarrera automatiquement.

### Défaillance 2 : Échec de la résolution DNS

Symptômes : `nslookup` renvoie `server can't find api: NXDOMAIN` ou expire.

Causes possibles :

- Les pods CoreDNS ne sont pas en cours d'exécution : vérifiez `kubectl get pods -n kube-system -l k8s-app=kube-dns`.
- Problèmes de configuration CoreDNS : vérifiez les journaux `kubectl logs -n kube-system -l k8s-app=kube-dns`.
- Le fichier `resolv.conf` du pod est incorrect en raison d'un `dnsPolicy` ou `dnsConfig` personnalisé.

**Récupération :** Assurez-vous que CoreDNS est sain. Si les paramètres DNS du pod sont incorrects, ajustez la spécification du pod. Pour un test rapide, vous pouvez temporairement utiliser directement l'IP ClusterIP du service : `curl http://10.96.100.50`.

### Défaillance 3 : Politique réseau bloquant le trafic

Si les NetworkPolicies refusent le trafic, les pods ne peuvent pas se connecter. Vérifiez en examinant les politiques et leurs sélecteurs. Souvent, une politique est destinée à n'autoriser que des espaces de noms ou des pods spécifiques.

**Récupération :** Modifiez la NetworkPolicy pour autoriser le trafic provenant des libellés ou de l'espace de noms du pod web. Par exemple, pour autoriser le trafic depuis les pods portant le libellé `app=web` :

```yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-web-to-api
  namespace: app-ns
spec:
  podSelector:
    matchLabels:
      app: api
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: web
    ports:
    - port: 8080
```

Appliquez et retestez.

### Défaillance 4 : Endpoints du service vides

Si `kubectl get endpoints` ne montre aucun endpoint, le sélecteur du service ne correspond à aucun pod en cours d'exécution. Vérifiez que les pods backend ont les bons libellés et sont dans l'état Ready.

**Récupération :** Corrigez les libellés ou assurez-vous que les pods backend sont en cours d'exécution. Par exemple, si les pods backend ont le libellé `app=api-v2` mais que le service sélectionne `app=api`, mettez à jour le sélecteur du service.

### Défaillance 5 : Restrictions RBAC sur les appels API

Si le pod doit accéder à l'API Kubernetes et obtient des erreurs 403, créez un rôle et une liaison de rôle appropriés.

Exemple de rôle pour autoriser la lecture des services :

```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: app-ns
  name: service-reader
rules:
- apiGroups: [""]
  resources: ["services"]
  verbs: ["get", "list"]
```

Liaison de rôle :

```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  namespace: app-ns
  name: web-sa-service-reader
subjects:
- kind: ServiceAccount
  name: web-sa
  namespace: app-ns
roleRef:
  kind: Role
  name: service-reader
  apiGroup: rbac.authorization.k8s.io
```

Appliquez et retestez l'accès API.

### Stratégies de retour en arrière

Conservez toujours une sauvegarde des fichiers YAML d'origine avant d'apporter des modifications. Si un changement casse la connectivité, revenez à la version précédente en utilisant `kubectl apply -f previous.yaml`. Pour les NetworkPolicies, vous pouvez supprimer la politique fautive et réappliquer celle qui est connue comme bonne.

### Vérifications de récupération

Après tout changement, exécutez la séquence de vérification suivante :

- `kubectl get pods -n app-ns` - assurez-vous que les pods sont en cours d'exécution et prêts.
- `kubectl get endpoints -n app-ns api` - assurez-vous que les endpoints sont renseignés.
- `kubectl exec -n app-ns <pod-web> -- curl -s http://api` - attendez la réponse du backend.
- `kubectl exec -n app-ns <pod-web> -- nslookup api` - attendez la bonne IP.

## Liste de contrôle des opérations

Utilisez cette liste de contrôle pour un dépannage et une maintenance reproductibles.

| Étape | Action | Commande | Résultat attendu |
|------|--------|---------|-----------------|
| 1 | Vérifier l'existence du compte de service | `kubectl get sa web-sa -n app-ns` | Compte de service listé |
| 2 | Confirmer que le pod utilise le bon SA | `kubectl get pod <pod> -n app-ns -o jsonpath='{.spec.serviceAccountName}'` | Affiche `web-sa` |
| 3 | Vérifier les fichiers de jeton du pod | `kubectl exec <pod> -n app-ns -- ls /var/run/secrets/kubernetes.io/serviceaccount` | Liste `ca.crt`, `namespace`, `token` |
| 4 | Tester la résolution DNS | `kubectl exec <pod> -n app-ns -- nslookup api` | Renvoie l'IP du service |
| 5 | Tester la connectivité au service | `kubectl exec <pod> -n app-ns -- curl -s http://api` | Renvoie la réponse attendue |
| 6 | Vérifier les endpoints | `kubectl get endpoints api -n app-ns` | Endpoints non vides |
| 7 | Vérifier les politiques réseau | `kubectl get networkpolicies -n app-ns` | Examiner toute politique |
| 8 | Vérifier les liaisons RBAC | `kubectl get rolebindings,clusterrolebindings -n app-ns -o json \| jq '.items[] \| select(.subjects[]?.name=="web-sa")'` | Liaisons si l'accès API est nécessaire |
| 9 | Examiner les journaux CoreDNS | `kubectl logs -n kube-system -l k8s-app=kube-dns` | Aucune erreur |

Effectuez ces vérifications chaque fois qu'un problème réseau survient, après tout changement de configuration et lors des audits de routine.

## Pièges courants et comment les éviter

Même les administrateurs Kubernetes expérimentés peuvent négliger certains détails qui conduisent à des échecs de réseau des comptes de service. Voici les erreurs les plus fréquentes et comment les éviter.

### 1. Supposer que la politique réseau par défaut autorise tout le trafic

De nombreuses équipes activent une NetworkPolicy de refus par défaut au niveau de l'espace de noms, puis oublient d'ajouter une règle d'autorisation pour leurs charges de travail. Un pod qui fonctionnait hier peut soudainement perdre la connectivité après l'application d'une politique.

**Pourquoi cela arrive :** Les NetworkPolicies de Kubernetes sont additives : si une politique sélectionne un pod, le pod est isolé et seul le trafic explicitement autorisé par l'union des politiques est permis.

**Comment éviter :** Documentez toujours les NetworkPolicies et leur effet prévu. Lors du dépannage, vérifiez les politiques qui sélectionnent les pods source ou destination. Utilisez `kubectl describe networkpolicy <nom> -n <espace-de-noms>` pour voir les règles exactes.

### 2. Ignorer les domaines de recherche DNS et ndots

L'option `ndots:5` dans `/etc/resolv.conf` amène le résolveur à essayer le nom complet avec les domaines de recherche avant de tenter le nom absolu. Cela peut conduire à des recherches DNS inattendues et à des retards si vous utilisez fréquemment des noms d'hôtes externes sans point final.

**Pourquoi cela arrive :** Kubernetes injecte les domaines de recherche du cluster dans chaque pod par défaut. Pour un nom comme `api`, le résolveur essaie d'abord `api.app-ns.svc.cluster.local`, puis `api.svc.cluster.local`, etc., avant de revenir au nom absolu.

**Comment éviter :** Soyez conscient de ce comportement lors du débogage des problèmes DNS. Utilisez des noms de domaine pleinement qualifiés (FQDN) se terminant par un point pour contourner les domaines de recherche, ou ajustez `dnsConfig` dans la spécification du pod si nécessaire.

### 3. Oublier que les comptes de service n'affectent pas directement le réseau de service à service

Une idée fausse courante est qu'un compte de service est nécessaire pour qu'un pod atteigne un autre service. En réalité, le trafic de service à service au sein du cluster repose sur le DNS, kube-proxy et les politiques réseau, et non sur le compte de service. Le compte de service n'a d'importance que lorsque le pod doit s'authentifier auprès de l'API Kubernetes ou de systèmes externes à l'aide de son jeton.

**Pourquoi cela arrive :** La documentation regroupe souvent la configuration du compte de service avec la sécurité générale des pods et le réseau, ce qui prête à confusion.

**Comment éviter :** Séparez les préoccupations. Si un pod ne peut pas joindre un service, vérifiez d'abord le DNS et les politiques réseau. N'enquêtez sur le RBAC et les jetons de compte de service que si le pod échoue à accéder à l'API Kubernetes ou à des services externes nécessitant une authentification.

### 4. Utiliser un objet Endpoint obsolète pour le débogage

Après avoir corrigé un sélecteur de service ou les libellés des pods backend, l'objet Endpoints peut ne pas se mettre à jour immédiatement. Vérifier `kubectl get endpoints` trop tôt peut montrer des endpoints vides même si le service est maintenant correctement configuré.

**Pourquoi cela arrive :** Le contrôleur Endpoints a une boucle de réconciliation et peut prendre quelques secondes pour se mettre à jour.

**Comment éviter :** Attendez que le contrôleur se synchronise, ou surveillez l'objet endpoints avec `kubectl get endpoints -n app-ns -w` pour voir la mise à jour en temps réel.

### 5. Négliger l'impact des services NodePort ou LoadBalancer sur la communication interne

Lorsqu'un service est exposé en NodePort ou LoadBalancer, il possède toujours une ClusterIP. Les pods internes doivent utiliser la ClusterIP ou le nom DNS ; utiliser le port du nœud depuis un pod peut échouer si le pare-feu du nœud ou la politique réseau bloque ce port.

**Pourquoi cela arrive :** Les développeurs testent parfois depuis un pod en utilisant l'IP du nœud et le NodePort, qui peuvent ne pas être joignables en raison des politiques réseau ou des règles de pare-feu du nœud.

**Comment éviter :** Utilisez toujours le nom DNS du service ou la ClusterIP depuis l'intérieur du cluster. Réservez NodePort et LoadBalancer au trafic externe.

## Conclusion

Le dépannage du réseau des comptes de service Kubernetes nécessite une approche méthodique. Commencez par vérifier la configuration du compte de service et du pod, puis testez la résolution DNS et la connectivité, et enfin inspectez les politiques réseau et le RBAC. Les commandes pratiques et les exemples de ce guide fournissent un processus reproductible pour identifier et résoudre la plupart des problèmes. En suivant la liste de contrôle des opérations, vous pouvez rapidement diagnostiquer les problèmes et rétablir la communication entre services.

N'oubliez pas de documenter votre environnement et de tester les changements dans un espace de noms sûr et isolé avant de les déployer en production. Avec ces outils, vous pouvez gérer en toute confiance le réseau des comptes de service dans Kubernetes.