Le problème
Avant le monitoring, débugger c'était:
- SSH sur le master
-
kubectl get podspour trouver le pod qui plante -
kubectl logs <pod>pour voir les logs - Répéter pour chaque pod
- Perdre 30 minutes pour comprendre ce qui se passe
Résultat: c'était insupportable avec 10+ pods qui tournent.
La solution: Loki + Fluent Bit + Grafana
L'idée c'est simple:
- Fluent Bit: collecte les logs de TOUS les pods
- Loki: stocke les logs (comme ElasticSearch mais en plus simple)
- Grafana: interface web pour chercher et visualiser
Tout centralisé, recherche en temps réel, c'est le rêve.
Architecture
┌─────────────────────────────────────────┐
│ Cluster Kubernetes │
│ │
│ ┌────────┐ ┌────────┐ ┌────────┐ │
│ │ Pod 1 │ │ Pod 2 │ │ Pod N │ │
│ └───┬────┘ └───┬────┘ └───┬────┘ │
│ │ │ │ │
│ └───────────┼────────────┘ │
│ ▼ │
│ ┌──────────────┐ │
│ │ Fluent Bit │ │
│ │ (DaemonSet) │ │
│ └──────┬───────┘ │
│ │ │
│ ▼ │
│ ┌──────────────┐ │
│ │ Loki │ │
│ └──────┬───────┘ │
│ │ │
│ ▼ │
│ ┌──────────────┐ │
│ │ Grafana │ │
│ └──────────────┘ │
└─────────────────────────────────────────┘
Installation de Loki
Voici mon manifest Kubernetes pour Loki:
apiVersion: v1
kind: ConfigMap
metadata:
name: loki-config
namespace: logging
data:
loki.yaml: |
auth_enabled: false
server:
http_listen_port: 3100
log_level: info
common:
path_prefix: /loki
storage:
filesystem:
chunks_directory: /loki/chunks
rules_directory: /loki/rules
replication_factor: 1
schema_config:
configs:
- from: 2020-10-24
store: tsdb
object_store: filesystem
schema: v13
index:
prefix: index_
period: 24h
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: loki
namespace: logging
spec:
serviceName: loki
replicas: 1
template:
spec:
containers:
- name: loki
image: grafana/loki:3.0.0
args:
- -config.file=/etc/loki/loki.yaml
ports:
- containerPort: 3100
volumeMounts:
- name: config
mountPath: /etc/loki
- name: storage
mountPath: /loki
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
volumes:
- name: config
configMap:
name: loki-config
volumeClaimTemplates:
- metadata:
name: storage
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 5Gi
Le StatefulSet c'est important pour garder les données même si le pod redémarre.
Configuration de Fluent Bit
Fluent Bit tourne en DaemonSet (= 1 pod par node) pour collecter les logs:
apiVersion: v1
kind: ConfigMap
metadata:
name: fluent-bit-config
namespace: logging
data:
fluent-bit.conf: |
[SERVICE]
Flush 5
Log_Level info
[INPUT]
Name tail
Path /var/log/containers/*.log
Parser docker
Tag kube.*
Refresh_Interval 5
Mem_Buf_Limit 5MB
[FILTER]
Name kubernetes
Match kube.*
Kube_URL https://kubernetes.default.svc:443
Merge_Log On
Keep_Log Off
[OUTPUT]
Name loki
Match *
Host loki
Port 3100
Labels job=fluentbit
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluent-bit
namespace: logging
spec:
template:
spec:
serviceAccountName: fluent-bit
containers:
- name: fluent-bit
image: fluent/fluent-bit:3.1.9
volumeMounts:
- name: varlog
mountPath: /var/log
readOnly: true
- 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
Explication:
-
[INPUT]: Lit les logs dans/var/log/containers/*.log -
[FILTER]: Ajoute les métadonnées Kubernetes (namespace, pod name, etc.) -
[OUTPUT]: Envoie tout à Loki
Le hostPath monte les dossiers de logs de l'hôte dans le container.
Permissions RBAC pour Fluent Bit
Fluent Bit a besoin de permissions pour lire les infos Kubernetes:
apiVersion: v1
kind: ServiceAccount
metadata:
name: fluent-bit
namespace: logging
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: fluent-bit
rules:
- apiGroups: [""]
resources: ["pods", "namespaces"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: fluent-bit
subjects:
- kind: ServiceAccount
name: fluent-bit
namespace: logging
roleRef:
kind: ClusterRole
name: fluent-bit
apiGroup: rbac.authorization.k8s.io
Sans ça, Fluent Bit peut pas enrichir les logs avec les infos des pods.
Installation de Grafana
apiVersion: v1
kind: ConfigMap
metadata:
name: grafana-datasources
namespace: logging
data:
datasources.yaml: |
apiVersion: 1
datasources:
- name: Loki
type: loki
access: proxy
url: http://loki:3100
isDefault: true
jsonData:
maxLines: 1000
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: grafana
namespace: logging
spec:
replicas: 1
template:
spec:
containers:
- name: grafana
image: grafana/grafana:10.0.0
ports:
- containerPort: 3000
env:
- name: GF_SECURITY_ADMIN_USER
value: "admin"
- name: GF_SECURITY_ADMIN_PASSWORD
value: "monmotdepasse"
- name: GF_PATHS_PROVISIONING
value: /etc/grafana/provisioning
volumeMounts:
- name: grafana-datasources
mountPath: /etc/grafana/provisioning/datasources
volumes:
- name: grafana-datasources
configMap:
name: grafana-datasources
---
apiVersion: v1
kind: Service
metadata:
name: grafana
namespace: logging
spec:
type: NodePort
ports:
- port: 3000
targetPort: 3000
nodePort: 30300
selector:
app: grafana
Le ConfigMap configure automatiquement Loki comme datasource.
Déploiement avec Ansible
Mon rôle Ansible qui déploie tout:
- name: Créer namespace logging
command: kubectl create namespace logging
environment:
KUBECONFIG: /etc/kubernetes/admin.conf
ignore_errors: yes
- name: Déployer Loki
command: kubectl apply -f /vagrant/manifests/logging/loki-values.yml
environment:
KUBECONFIG: /etc/kubernetes/admin.conf
- name: Attendre que Loki démarre
command: kubectl wait --for=condition=ready pod/loki-0 -n logging --timeout=120s
environment:
KUBECONFIG: /etc/kubernetes/admin.conf
- name: Déployer Fluent Bit + Grafana
command: kubectl apply -f /vagrant/manifests/logging/fluentbit-grafana.yaml
environment:
KUBECONFIG: /etc/kubernetes/admin.conf
- name: Afficher les infos de connexion
debug:
msg:
- "Stack de Logging déployée!"
- "Grafana: http://192.168.56.10:30300"
- "Username: admin"
- "Password: monmotdepasse"
Utilisation dans Grafana
Une fois connecté à Grafana (http://192.168.56.10:30300):
Explore → Loki
Requête de base:
{job="fluentbit"}
Ça affiche tous les logs.
Filtrer par namespace:
{job="fluentbit", namespace="apps"}
Filtrer par pod:
{job="fluentbit", pod="apicatalogue-xyz"}
Chercher des erreurs:
{job="fluentbit"} |= "error"
Combiner plusieurs filtres:
{job="fluentbit", namespace="apps"} |= "error" != "404"
Créer un dashboard
- Dans Grafana: Dashboards → New Dashboard
- Add visualization
- Query:
sum by (pod) (count_over_time({job="fluentbit"}[5m]))
- Ça affiche le nombre de logs par pod sur 5 minutes
Pratique pour voir quel pod spam les logs.
Les erreurs que j'ai faites
Erreur #1: Pas assez de ressources pour Loki
Au début j'avais mis:
resources:
limits:
memory: 128Mi
Loki plantait dès qu'il y avait trop de logs.
Solution: Au moins 512Mi de RAM.
Erreur #2: Fluent Bit sans permissions
J'avais oublié le ServiceAccount et les ClusterRole.
Résultat: Fluent Bit tournait mais envoyait rien à Loki.
Solution: Toujours créer les permissions RBAC.
Erreur #3: Parser les logs JSON manuellement
Au début je parsais pas les logs JSON de Gunicorn.
Résultat: tout en texte brut, impossible de filtrer par niveau de log.
Solution: Ajouter un parser dans la config Fluent Bit.
Vérifier que ça marche
# Logs de Loki
kubectl logs -n logging loki-0
# Logs de Fluent Bit (sur tous les nodes)
kubectl logs -n logging daemonset/fluent-bit
# Tester l'API Loki directement
curl http://192.168.56.10:30300/loki/api/v1/query_range?query={job="fluentbit"}
Requêtes LogQL avancées
Top 10 des pods qui loguent le plus:
topk(10, sum by (pod) (count_over_time({job="fluentbit"}[1h])))
Logs d'erreurs des 10 dernières minutes:
{job="fluentbit"} |= "ERROR" [10m]
Rate de logs par seconde:
rate({job="fluentbit"}[1m])
Conclusion
Cette stack m'a vraiment changé la vie pour le debug:
- Plus besoin de SSH
- Recherche instantanée dans tous les logs
- Dashboards custom
- Historique complet (tant que le PVC tient)
Le seul défaut: ça consomme pas mal de ressources (Loki + Grafana = ~700MB de RAM).
Mais ça vaut le coup, vraiment.
Code complet: [GitHub]
Top comments (0)