Introduction
La journalisation Kubernetes est une capacité opérationnelle fondamentale qui permet aux développeurs et aux ingénieurs DevOps de comprendre le comportement des applications, de diagnostiquer les pannes et de surveiller la santé du cluster. Cependant, de nombreuses équipes sont confrontées à des journaux fragmentés, à une absence d'architecture claire et à un débogage improvisé. Cet article fournit un guide pratique, axé sur des exemples, de l'architecture de journalisation Kubernetes. Vous apprendrez comment les journaux circulent des conteneurs vers un backend centralisé, comment configurer les agents de journalisation courants et comment résoudre les problèmes typiques à l'aide de commandes concrètes et de sorties attendues.
Nous nous concentrons sur des scénarios réels : un cluster à nœud unique simple pour l'apprentissage, un cluster de production multi-nœuds, et les étapes pour évoluer d'une journalisation de base vers une configuration centralisée robuste. À la fin, vous serez en mesure de concevoir, mettre en œuvre et vérifier une solution de journalisation répondant aux besoins opérationnels.
Inventaire des versions et de l'environnement
Avant de plonger dans la journalisation, vous devez connaître votre environnement. Cette section fournit des commandes pour inventorier les versions, les nœuds et les composants de journalisation existants. Un inventaire précis évite les configurations incompatibles et vous aide à identifier ce qui est déjà en place.
Informations sur Kubernetes et les nœuds
Commencez par confirmer la version de Kubernetes et les détails des nœuds. Cela est essentiel car les versions des agents de journalisation ont souvent des contraintes de compatibilité avec l'API Kubernetes et le runtime de conteneur.
kubectl version --short
# Sortie attendue similaire à :
# Client Version: v1.25.3
# Server Version: v1.25.3
kubectl get nodes -o wide
# Exemple de sortie :
# NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
# node1 Ready control-plane 10d v1.25.3 192.168.1.10 <none> Ubuntu 22.04.1 LTS 5.15.0-56-generic containerd://1.6.8
Composants de journalisation existants
Vérifiez si des agents de journalisation sont déjà déployés. Les agents courants incluent Fluent Bit, Fluentd, Filebeat ou Vector. Recherchez des DaemonSets (un par nœud) ou des Deployments.
kubectl get daemonsets --all-namespaces | grep -E 'fluent|filebeat|log|vector'
kubectl get deployments --all-namespaces | grep -E 'fluent|filebeat|log|vector'
# Si rien ne retourne, aucun agent commun n'est déployé.
# Vérifiez également les ressources personnalisées liées à la journalisation
kubectl get crd | grep -E 'fluent|log|elastic'
Emplacement des journaux du runtime de conteneur
Comprendre où les journaux des conteneurs sont stockés sur le nœud est crucial pour la configuration de l'agent. Pour containerd (le défaut dans de nombreux clusters), les journaux se trouvent généralement sous /var/log/containers (liens symboliques) et /var/log/pods. Pour Docker, ils se trouvent sous /var/lib/docker/containers.
# Sur un nœud, listez les fichiers journaux d'exemple
ls -l /var/log/containers/ | head -5
# Exemple : total 0
# lrwxrwxrwx 1 root root 107 Jan 5 10:00 app-nginx-6d4b7c9f5-abcde_default_nginx-abc123.log -> /var/log/pods/default_app-nginx-6d4b7c9f5-abcde/nginx/0.log
Vérification pratique
Exécutez un pod de test qui produit des journaux et vérifiez qu'ils sont écrits à l'emplacement attendu.
kubectl run logtest --image=busybox --restart=Never -- sh -c 'while true; do echo "Hello from logtest at $(date)"; sleep 5; done'
kubectl logs -f logtest
# Vous devriez voir une sortie répétée.
# Sur le nœud, trouvez le fichier journal pour ce pod
ls /var/log/pods/default_logtest/ | grep logtest
# Exemple : logtest.log
cat /var/log/pods/default_logtest/logtest/0.log
# Affiche la même sortie que kubectl logs.
Nettoyez le pod de test :
kubectl delete pod logtest
Chemin de configuration sécurisé
La configuration des agents de journalisation peut avoir un impact sur toutes les applications d'un nœud si elle est mal effectuée. Cette section décrit un chemin sûr et incrémental : d'un test sur un seul nœud à un déploiement complet, avec vérification à chaque étape.
Choix d'un agent de journalisation
Fluent Bit est un choix populaire et léger pour Kubernetes. Il est efficace et s'intègre bien avec de nombreux backends. Nous utiliserons ici Fluent Bit comme exemple, mais les principes s'appliquent à d'autres agents.
Étape 1 : Déployer Fluent Bit en tant que DaemonSet dans un namespace de test
Commencez avec une configuration minimale qui lit les journaux des conteneurs et les sort vers stdout (pour vérification). Créez un fichier fluent-bit-test.yaml :
apiVersion: v1
kind: Namespace
metadata:
name: logging-test
---
apiVersion: v1
kind: ConfigMap
metadata:
name: fluent-bit-config
namespace: logging-test
data:
fluent-bit.conf: |
[SERVICE]
Flush 1
Daemon off
Log_Level info
[INPUT]
Name tail
Path /var/log/containers/*.log
Parser docker
Tag kube.*
[OUTPUT]
Name stdout
Match *
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluent-bit
namespace: logging-test
spec:
selector:
matchLabels:
app: fluent-bit
template:
metadata:
labels:
app: fluent-bit
spec:
containers:
- name: fluent-bit
image: fluent/fluent-bit:2.1.10
volumeMounts:
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
- name: config
mountPath: /fluent-bit/etc/
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
- name: config
configMap:
name: fluent-bit-config
Appliquez et vérifiez :
kubectl apply -f fluent-bit-test.yaml
kubectl get pods -n logging-test -o wide
# Attendez-vous à un pod fluent-bit par nœud, statut Running.
# Vérifiez les journaux pour voir s'il collecte
kubectl logs -n logging-test daemonset/fluent-bit --tail=20
# Vous devriez voir des entrées de journal d'autres pods s'ils existent, ou au moins les journaux de démarrage de l'agent lui-même.
Étape 2 : Ajouter un filtre de métadonnées Kubernetes
Les journaux bruts des conteneurs manquent de noms de pods, de namespaces et d'étiquettes. Ajoutez un filtre Kubernetes pour enrichir les journaux. Mettez à jour la ConfigMap fluent-bit.conf pour inclure :
[FILTER]
Name kubernetes
Match kube.*
Kube_URL https://kubernetes.default.svc:443
Kube_CA_File /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
Kube_Token_File /var/run/secrets/kubernetes.io/serviceaccount/token
Kube_Tag_Prefix kube.var.log.containers.
Merge_Log On
Merge_Log_Key log_processed
Ce filtre nécessite que le pod ait accès à l'API Kubernetes. Le compte de service par défaut dans le namespace peut avoir besoin de permissions RBAC. Pour les tests, vous pouvez créer un ClusterRole et un ClusterRoleBinding pour autoriser la lecture des pods. Créez rbac.yaml :
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: fluent-bit-read
rules:
- apiGroups: [""]
resources: ["pods", "namespaces"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: fluent-bit-read
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: fluent-bit-read
subjects:
- kind: ServiceAccount
name: default
namespace: logging-test
Appliquez et redémarrez Fluent Bit :
kubectl apply -f rbac.yaml
kubectl rollout restart daemonset/fluent-bit -n logging-test
kubectl logs -n logging-test daemonset/fluent-bit --tail=10
# Recherchez des lignes contenant "kubernetes": {"pod_name": ...}
Étape 3 : Envoyer les journaux vers un backend centralisé (exemple Elasticsearch)
Remplacez la sortie stdout par Elasticsearch. Assurez-vous d'avoir une instance Elasticsearch accessible depuis le cluster. Pour les tests, vous pouvez déployer un Elasticsearch à nœud unique dans le même namespace ou utiliser un endpoint externe. Mettez à jour la section de sortie dans fluent-bit.conf :
[OUTPUT]
Name es
Match *
Host elasticsearch.logging-test.svc
Port 9200
Index kubernetes-logs
Type _doc
Logstash_Format On
Logstash_Prefix kubernetes-logs
Retry_Limit False
Appliquez la mise à jour de la configuration et redémarrez :
kubectl apply -f fluent-bit-test.yaml
kubectl rollout restart daemonset/fluent-bit -n logging-test
kubectl logs -n logging-test daemonset/fluent-bit --tail=10
# Vous ne devriez voir aucune erreur et peut-être des messages sur la connexion à Elasticsearch.
Vérifiez que les données arrivent dans Elasticsearch :
# Si vous avez un accès curl à Elasticsearch
curl -X GET 'http://elasticsearch.logging-test.svc:9200/_cat/indices/kubernetes-logs*?v'
# Sortie attendue comme :
# health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
# green open kubernetes-logs-2024.01.05 aBcDeFgHiJkLmNoPqRs 1 0 100 0 200kb 200kb
Étape 4 : Passer au namespace de production et mettre à l'échelle
Une fois que la configuration de test fonctionne, répliquez-la dans un namespace de production avec des limites de ressources appropriées, des sélecteurs de nœuds et des contextes de sécurité. Envisagez d'utiliser le chart Helm officiel pour une gestion plus facile.
helm repo add fluent https://fluent.github.io/helm-charts
helm install fluent-bit fluent/fluent-bit --namespace logging --create-namespace
Vérifiez toujours après chaque modification :
kubectl get pods -n logging -o wide
kubectl logs -n logging daemonset/fluent-bit --tail=20
Vérification et diagnostics
Une fois la journalisation configurée, vous devez vérifier qu'elle fonctionne correctement et diagnostiquer les problèmes lorsque les journaux sont manquants ou mal formés. Cette section fournit des commandes de diagnostic et leur interprétation.
Vérifier la collecte de journaux pour un pod spécifique
Créez un pod de test avec un motif de journal connu :
kubectl run log-check --image=busybox --restart=Never -- sh -c 'for i in $(seq 1 10); do echo "UNIQUE_MARKER_$i"; sleep 1; done'
Attendez la fin, puis vérifiez si les journaux apparaissent dans la sortie de Fluent Bit. Si vous utilisez la sortie stdout, vérifiez les journaux de Fluent Bit :
kubectl logs -n logging-test daemonset/fluent-bit --since=5m | grep UNIQUE_MARKER
# Vous devriez voir les 10 lignes.
Si vous utilisez Elasticsearch, interrogez pour le marqueur :
curl -X GET 'http://elasticsearch.logging-test.svc:9200/kubernetes-logs*/_search?q=log:UNIQUE_MARKER_1&pretty'
Scénarios de défaillance courants et diagnostics
- Aucun journal d'aucun pod
- Vérifiez si les pods Fluent Bit sont en cours d'exécution :
kubectl get pods -n logging - Vérifiez les journaux de Fluent Bit :
kubectl logs -n logging daemonset/fluent-bit --tail=50 - Recherchez des erreurs comme "permission denied" sur les fichiers journaux. Assurez-vous que le DaemonSet a des volumes hostPath montés correctement et que le conteneur s'exécute en tant que root (ou a les permissions appropriées).
- Journaux manquants pour des pods spécifiques
- Le plugin d'entrée tail peut ne pas avoir commencé à lire le fichier s'il existait auparavant. Assurez-vous que Fluent Bit a un accès en lecture et que le motif de chemin correspond. Vérifiez le
Pathdans la configuration d'entrée. Généralement, le chemin est/var/log/containers/*.log. - Vérifiez que le fichier journal du pod existe sur le nœud :
ls -l /var/log/containers/ | grep <pod-name>
- Les journaux sont tronqués ou illisibles
- Le parseur peut être mal configuré. Si vous utilisez le parseur Docker, assurez-vous qu'il correspond au runtime de conteneur. Pour containerd, vous pourriez avoir besoin d'un parseur différent ou d'aucun parseur.
- Augmentez
Buffer_SizeetMem_Buf_Limitdans le plugin d'entrée pour gérer de grandes lignes de journal.
- Fluent Bit ne peut pas se connecter au backend
- Vérifiez les politiques réseau et le DNS des services. Depuis le cluster, testez la connectivité :
kubectl run curl-test --image=curlimages/curl --rm -it --restart=Never -- curl -v http://elasticsearch.logging-test.svc:9200 - Assurez-vous que le service backend est dans le même namespace ou accessible via FQDN.
Surveillance des métriques de Fluent Bit
Fluent Bit expose des métriques internes qui peuvent aider à diagnostiquer les problèmes de performance. Activez l'endpoint de métriques dans la configuration :
[SERVICE]
HTTP_Server On
HTTP_Listen 0.0.0.0
HTTP_Port 2020
Ensuite, exposez le port dans le DaemonSet et interrogez les métriques :
kubectl port-forward -n logging daemonset/fluent-bit 2020:2020
curl http://localhost:2020/api/v1/metrics/prometheus
# Recherchez des métriques comme fluentbit_input_records_total, fluentbit_output_errors_total
Modes de défaillance et récupération
Même avec un pipeline de journalisation bien configuré, des pannes surviennent. Cette section décrit les modes de défaillance courants, leurs symptômes et les étapes de récupération.
Mode de défaillance 1 : Pression disque due à des journaux volumineux
Symptôme : L'utilisation du disque du nœud augmente rapidement, provoquant éventuellement des évictions de pods.
Diagnostic :
df -h /var/log
# Vérifiez l'utilisation du disque sur le nœud où les journaux sont stockés.
du -sh /var/log/containers/* | sort -rh | head -10
# Identifiez quel pod génère le plus de journaux.
Récupération :
- Implémentez la rotation des journaux au niveau du runtime de conteneur (généralement configuré via les paramètres kubelet comme
--container-log-max-sizeet--container-log-max-files). Par exemple, définissez dans la config kubelet :containerLogMaxSize: "10Mi",containerLogMaxFiles: 5. - Ajoutez des limites de ressources à l'agent de journalisation pour éviter qu'il ne consomme trop de CPU/mémoire.
- Implémentez une limitation de débit dans Fluent Bit en utilisant le filtre
Throttle.
Mode de défaillance 2 : CrashLoopBackOff de l'agent de journalisation
Symptôme : kubectl get pods -n logging montre des pods Fluent Bit en CrashLoopBackOff.
Diagnostic :
kubectl describe pod -n logging -l app=fluent-bit
# Regardez les événements pour les messages d'erreur (par exemple, OOMKilled, erreur de config).
kubectl logs -n logging <fluent-bit-pod> --previous
# Vérifiez les journaux du conteneur précédent pour la raison du crash.
Causes courantes et récupération :
- Mauvaise configuration : Corrigez la syntaxe du fichier de config ou les paramètres invalides. Validez la config avec
fluent-bit -c /path/to/conf --dry-runlocalement. - Mémoire insuffisante : Augmentez la limite de mémoire dans la spec du DaemonSet ou réduisez la mise en mémoire tampon.
- Problèmes de permissions : Assurez-vous que le conteneur s'exécute avec un contexte de sécurité approprié pour lire les journaux de l'hôte. Par exemple, définissez
runAsUser: 0si nécessaire, mais préférez utiliser un utilisateur dédié avec un accès de groupe aux répertoires de journaux.
Mode de défaillance 3 : Backend indisponible (perte de données)
Symptôme : Les journaux n'apparaissent pas dans le backend, et les journaux de Fluent Bit montrent des erreurs de connexion répétées ou des tentatives épuisées.
Diagnostic :
kubectl logs -n logging daemonset/fluent-bit --tail=50 | grep -i 'error\|retry\|failed'
# Recherchez des messages comme "[error] [output:es:es.0] connection refused"
Récupération :
- Remettez le service backend en ligne (par exemple, Elasticsearch).
- Assurez-vous que Fluent Bit a la logique de nouvelle tentative activée et une limite de nouvelles tentatives suffisante. Dans le plugin de sortie, définissez
Retry_Limitsur une valeur élevée ouFalsepour des tentatives infinies (avec prudence, car cela peut accumuler un tampon disque). - Envisagez d'utiliser un tampon disque pour conserver les journaux pendant les pannes du backend. Dans Fluent Bit, configurez
storage.pathet utilisezstorage.type filesystemdans la sortie.
Mode de défaillance 4 : Perte de journaux due à la suppression d'un pod
Symptôme : Lorsqu'un pod est supprimé, ses journaux sont retirés du nœud (avec la configuration par défaut). Si l'agent de journalisation ne les a pas encore traités, les journaux sont perdus.
Prévention :
- Assurez-vous que l'agent de journalisation traite les journaux rapidement et dispose d'un tampon suffisamment grand. Surveillez la métrique
fluentbit_output_retries_failed_total. - Utilisez un stockage persistant pour les journaux sur le nœud, mais cela est rare. Il est préférable de diffuser les journaux dès qu'ils sont produits.
- Pour les applications critiques, envisagez une journalisation au niveau de l'application vers un puits distant directement pour éviter la dépendance aux fichiers journaux du nœud.
Liste de contrôle des opérations
Cette liste de contrôle résume les opérations quotidiennes et périodiques essentielles pour maintenir une configuration de journalisation Kubernetes saine.
Vérifications quotidiennes
- [ ] Vérifier que les pods de l'agent de journalisation sont en cours d'exécution :
kubectl get pods -n logging -o wide(attendu : tous Ready 1/1). - [ ] Vérifier les journaux d'erreur de l'agent :
kubectl logs -n logging daemonset/fluent-bit --tail=20 | grep -i error(attendu : aucune erreur). - [ ] Confirmer que les journaux circulent vers le backend le cas échéant : interroger le backend pour les documents récents (par exemple, Elasticsearch :
curl -s 'http://elasticsearch:9200/kubernetes-logs-*/_count?pretty' | grep count).
Vérifications hebdomadaires
- [ ] Inspecter l'utilisation du disque sur les nœuds :
df -h /var/logsur tous les nœuds ; s'assurer que l'utilisation est inférieure à 70 %. - [ ] Examiner l'utilisation des ressources de l'agent de journalisation :
kubectl top pods -n logging(nécessite metrics server). - [ ] Vérifier la dérive de configuration : comparer la ConfigMap en cours d'exécution avec l'état souhaité versionné.
Vérifications mensuelles
- [ ] Appliquer les mises à jour de sécurité de l'image de l'agent de journalisation : vérifier les nouvelles versions et mettre à jour le chart Helm ou la balise d'image.
- [ ] Tester la rotation et la rétention des journaux : simuler un pod à volume de journalisation élevé et s'assurer que la rotation fonctionne et que les anciens journaux sont supprimés.
- [ ] Examiner les permissions RBAC des comptes de service de journalisation ; s'assurer du moindre privilège.
Runbook de réponse aux incidents
- La journalisation s'est complètement arrêtée
- Vérifier les pods de l'agent :
kubectl get pods -n logging - Si CrashLoopBackOff, inspecter les journaux et les événements ; se référer au Mode de défaillance 2.
- Si aucun pod, vérifier le DaemonSet/Deployment :
kubectl get daemonset -n logging - S'assurer que les sélecteurs de nœuds ou les tolérances n'excluent pas tous les nœuds.
- Problèmes de backend (Elasticsearch en panne)
- Vérifier les pods Elasticsearch :
kubectl get pods -n elastic - Vérifier le stockage :
kubectl get pvc -n elastic - Redémarrer si nécessaire, puis vérifier que Fluent Bit se reconnecte.
- Latence élevée de recherche de journaux
- Vérifier l'utilisation des ressources du backend :
kubectl top pods -n elastic - Mettre à l'échelle les nœuds de données Elasticsearch ou augmenter le tas.
- Optimiser les paramètres d'index (par exemple, le nombre de fragments).
Conclusion
Une journalisation Kubernetes efficace nécessite de comprendre l'architecture, une configuration minutieuse et une surveillance proactive. Nous avons couvert l'ensemble du cycle de vie : de l'inventaire de l'environnement à la configuration sécurisée, à la vérification, à la récupération après défaillance et aux opérations continues. En suivant les exemples pratiques et les commandes fournies, vous pouvez construire un pipeline de journalisation résilient qui vous aide à résoudre plus rapidement les problèmes d'application et à maintenir la santé du cluster.
Rappelez-vous que la journalisation n'est pas un système à configurer et à oublier. Examinez régulièrement votre configuration, surveillez la santé de tous les composants et ajustez à mesure que votre cluster évolue. Commencez par la liste de contrôle fournie et adaptez-la à votre environnement spécifique. Avec ces outils et pratiques, vous serez bien équipé pour relever les défis de la gestion des journaux dans Kubernetes.
Comme prochaine étape, choisissez un composant à faible risque (comme l'activation du filtre de métadonnées Kubernetes) et testez-le dans un namespace non productif. Documentez le changement, vérifiez la sortie, puis planifiez un déploiement progressif vers la production. En procédant par étapes incrémentales et en vérifiant chacune, vous vous assurez que votre journalisation reste une source de vérité fiable pour vos applications.