359 lines
16 KiB
Markdown
359 lines
16 KiB
Markdown
---
|
||
reviewers:
|
||
- piosz
|
||
- x13n
|
||
title: Architecture de Journalisation d'évènements (logging)
|
||
content_template: templates/concept
|
||
weight: 60
|
||
---
|
||
|
||
{{% capture overview %}}
|
||
|
||
La journalisation des évènements systèmes et d'applications peut aider à
|
||
comprendre ce qui se passe dans un cluster. Les journaux sont particulièrement
|
||
utiles pour débogguer les problèmes et surveiller l'activité du cluster. La
|
||
plupart des application modernes ont un mécanisme de journalisation
|
||
d'évènements, et la plupart des environnements d'exécution de conteneurs ont été
|
||
conçus pour supporter la journalisation des évènements. La méthode de
|
||
journalisation la plus facile et la plus répandue pour des applications
|
||
conteneurisées est d'écrire dans les flux de sortie standard et d'erreur
|
||
standard (`stdout` et `stderr`).
|
||
|
||
Malgré cela, la fonctionnalité de journalisation fournie nativement par
|
||
l'environnement d'exécution de conteneurs n'est pas suffisante comme solution
|
||
complète de journalisation. Quand un conteneur crash, quand un Pod est expulsé
|
||
ou quand un nœud disparaît, il est utile de pouvoir accéder au journal
|
||
d'événement de l'application. C'est pourquoi les journaux doivent avoir leur
|
||
propre espace de stockage et un cycle de vie indépendamment des nœuds, Pods ou
|
||
conteneurs. Ce concept est appelé _journalisation des évènements au niveau du
|
||
cluster_ (cluster-level-logging). Un backend dédié pour stocker, analyser et
|
||
faire des requêtes est alors nécessaire. Kubernetes n'offre pas nativement de
|
||
solution de stockage pour les journaux mais il est possible d'intégrer de
|
||
nombreuses solutions de journalisation d'évènements dans un cluster Kubernetes.
|
||
|
||
|
||
{{% /capture %}}
|
||
|
||
|
||
{{% capture body %}}
|
||
|
||
L'architecture de journalisation des évènements au niveau du cluster est décrite
|
||
en considérant qu'un backend de journalisation est présent à l'intérieur ou à
|
||
l'extérieur du cluster. Même sans avoir l'intention de journaliser les
|
||
évènements au niveau du cluster, il est intéressant de savoir comment les
|
||
journaux sont conservés et gérés au niveau d'un nœud.
|
||
|
||
## Journalisation simple d'évènements dans Kubernetes
|
||
|
||
Dans cette section, on va utiliser un exemple simple de journalisation
|
||
d'évènements avec le flux de sortie standard. Cette démonstration utilise un
|
||
manifeste pour un Pod avec un seul conteneur qui écrit du texte sur le flux
|
||
de sortie standard toutes les secondes.
|
||
|
||
{{< codenew file="debug/counter-pod.yaml" >}}
|
||
|
||
Pour lancer ce Pod, utilisez la commande suivante :
|
||
|
||
```shell
|
||
kubectl apply -f https://k8s.io/examples/debug/counter-pod.yaml
|
||
```
|
||
Le résultat est :
|
||
```
|
||
pod/counter created
|
||
```
|
||
|
||
Pour récupérer les événements du conteneur d'un pod, utilisez la commande
|
||
`kubectl logs` de la manière suivante :
|
||
|
||
```shell
|
||
kubectl logs counter
|
||
```
|
||
Le résultat est :
|
||
```
|
||
0: Mon Jan 1 00:00:00 UTC 2001
|
||
1: Mon Jan 1 00:00:01 UTC 2001
|
||
2: Mon Jan 1 00:00:02 UTC 2001
|
||
...
|
||
```
|
||
|
||
Utilisez `kubectl logs` pour récupérer les évènements de l'instanciation
|
||
précédente d'un Pod en utilisant l'option `--previous` quand par exemple le
|
||
conteneur a crashé.
|
||
|
||
Si le Pod a plusieurs conteneurs, il faut spécifier le nom du conteneur dont on
|
||
veut récupérer le journal d'évènement. Dans notre exemple le conteneur s'appelle
|
||
`count` donc vous pouvez utiliser `kubectl logs counter count`. Plus de détails
|
||
dans la [documentation de `kubectl
|
||
logs`] (/docs/reference/generated/kubectl/kubectl-commands#logs)
|
||
|
||
## Journalisation d'évènements au niveau du nœud
|
||
|
||

|
||
|
||
Tout ce qu'une application conteneurisée écrit sur `stdout` ou `stderr` est pris
|
||
en compte et redirigé par l'environment d'exécution des conteneurs. Par exemple,
|
||
Docker redirige ces deux flux à un [driver de journalisation
|
||
(EN)](https://docs.docker.com/config/containers/logging/configure/) qui est
|
||
configuré dans Kubernetes pour écrire dans un fichier au format json.
|
||
|
||
{{< note >}} Le driver json de Docker traite chaque ligne comme un message
|
||
différent. Avec ce driver il n'y a pas de support direct pour des messages
|
||
multi-lignes. Il faut donc traiter les messages multi-lignes au niveau de
|
||
l'agent de journalisation ou plus haut. {{< /note >}}
|
||
|
||
Par défaut quand un conteneur redémarre, le kubelet ne conserve le journal que
|
||
du dernier conteneur terminé. Quand un Pod est expulsé d'un nœud, tous ses
|
||
conteneurs sont aussi expulsés avec leurs journaux d'évènements.
|
||
|
||
Quand on utilise la journalisation d'évènements au niveau du nœud, il faut
|
||
prendre garde à mettre en place une politique de rotation des journaux adéquate
|
||
afin qu'ils n'utilisent pas tout l'espace de stockage du nœud. Kubernetes n'a
|
||
pas en charge la rotation des journaux, c'est à l'outil de déploiement de le
|
||
prendre en compte.
|
||
|
||
Par exemple, dans les clusters Kubernetes déployés avec le script `kube-up.sh`
|
||
[`logrotate`](https://linux.die.net/man/8/logrotate) est configuré pour
|
||
s'exécuter toutes les heures. Il est aussi possible de configurer
|
||
l'environnement d'exécution des conteneurs pour que la rotation des journaux
|
||
s'exécute automatiquement, e.g. en utilisant le paramètre `log-opt` de Docker.
|
||
Dans le script `kube-up.sh`, c'est cette méthode qui est utilisée pour des
|
||
images COS sur GCP et sinon c'est la première méthode dans tous les autres cas.
|
||
Quel que soit la méthode choisie par `kube-up.sh` la rotation est configurée par
|
||
defaut quand la taille d'un journal atteint 10 Mo.
|
||
|
||
Ce [script][cosConfigureHelper] montre de manière détaillée comment `kube-up.sh`
|
||
met en place la journalisation d'évènement pour des images COS sur GCP.
|
||
|
||
Quand [`kubectl logs`](/docs/reference/generated/kubectl/kubectl-commands#logs)
|
||
s'exécute comme dans le premier exemple de journalisation simple le kubelet du
|
||
nœud gère la requête et lit directement depuis le fichier de journal et retourne
|
||
son contenu dans la réponse.
|
||
|
||
{{< note >}} Si un système externe a effectué la rotation des journaux, seul le
|
||
contenu du dernier fichier journal sera disponible avec `kubectl logs`. Par
|
||
exemple quand le journal atteint 10 Mo, `logrotate` effectue une rotation, il y a
|
||
alors 2 fichers, un de 10 Mo et un de vide, à ce moment là `kubectl logs`
|
||
retournera une réponse vide. {{< /note >}}
|
||
|
||
[cosConfigureHelper]: https://github.com/kubernetes/kubernetes/blob/{{< param
|
||
"githubbranch" >}}/cluster/gce/gci/configure-helper.sh
|
||
|
||
### Journalisation des évènements des composants système
|
||
|
||
Il y a deux types de composants système selon qu'ils s'exécutent dans un
|
||
conteneur ou pas.
|
||
|
||
Par exemple :
|
||
|
||
* Le scheduler Kubernetes et kube-proxy s'exécutent dans un conteneur.
|
||
* Kubelet et l'environment d'exécution de conteneurs, comme par exemple
|
||
Docker, ne s'exécutent pas dans un conteneur.
|
||
|
||
Sur les systèmes avec systemd, kubelet et l'environment d'exécution de
|
||
conteneurs écrivent dans journald. Si systemd n'est pas présent, ils écrivent
|
||
dans un fichier `.log` dans le répertoire `/var/log`.
|
||
|
||
Les composants système qui s'exécutent dans un conteneur écrivent toujours dans
|
||
le répertoire `/var/log`, en contournant le mécanisme de journalisation par
|
||
défaut. Ils utilisent la bibliothèque de journalisation [klog][klog]. Les
|
||
conventions pour la sévérité des évènements pour ces composants se trouvent dans
|
||
cette [documentation sur les conventions de journalisation des évènements dans
|
||
kubernetes]
|
||
(https://github.com/kubernetes/community/blob/master/contributors/devel/sig-instrumentation/logging.md).
|
||
|
||
De la même manière que les journaux des conteneurs, les journaux des composants
|
||
systèmes doivent avoir une politique de rotation. Dans un cluster créé avec
|
||
le script `kube-up.sh`, les journaux ont une rotation journalière ou quand leur
|
||
taille atteint 100 Mo.
|
||
|
||
[klog]: https://github.com/kubernetes/klog
|
||
|
||
## Architecture de journalisation des évènements au niveau du cluster
|
||
|
||
Kubernetes ne fournit pas de solution native pour la journalisation des
|
||
évènements au niveau du cluster. Mais il y a différentes approches qui peuvent
|
||
être considérées :
|
||
|
||
* Utiliser un agent de journalisation au niveau du nœud sur chacun des nœuds.
|
||
* Inclure un conteneur side-car pour journaliser les évènements du Pod
|
||
applicatif.
|
||
* Envoyer les évènements directement a un backend depuis l'application.
|
||
|
||
### Utiliser un agent de journalisation au niveau du nœud
|
||
|
||

|
||
|
||
Vous pouvez implémenter une journalisation au niveau du cluster en incluant un
|
||
_agent de journalisation au niveau du nœud_ sur chacun des nœuds. L'agent de
|
||
journalisation est un outil dédié qui met à disposition ou envoie les journaux à
|
||
un backend. Communément l'agent de journalisation est un conteneur qui a accès
|
||
au répertoire qui contient les journaux des conteneurs applicatifs sur ce nœud.
|
||
|
||
Comme l'agent de journalisation doit s'exécuter sur chacun des nœuds, on utilise
|
||
soit un DaemonSet, soit un manifeste de Pod, soit un processus dédié natif sur
|
||
le nœud. Ces deux dernières options sont obsolètes et fortement découragées.
|
||
|
||
Utiliser un agent de journalisation au niveau du nœud est l'approche la plus
|
||
commune et recommandée pour un cluster Kubernetes parce qu'un seul agent par
|
||
nœud est créé et qu'aucune modification dans l'application n'est nécessaire.
|
||
Mais cette approche _ne fonctionne correctement que pour les flux standard de
|
||
sortie et d'erreurs des applications_.
|
||
|
||
Kubernetes ne définit pas d'agent de journalisation, mais deux agents de
|
||
journalisation optionnels sont fournis avec la version de Kubernetes :
|
||
[Stackdriver (EN)](/docs/user-guide/logging/stackdriver) pour utiliser sur
|
||
Google Cloud Platform, et [Elasticsearch
|
||
(EN)](/docs/user-guide/logging/elasticsearch). Les deux utilisent
|
||
[fluentd](http://www.fluentd.org/) avec une configuration spécifique comme agent
|
||
sur le nœud. Les liens précédents fournissent plus d'informations et les
|
||
instructions pour les utiliser et configurer.
|
||
|
||
### Inclure un conteneur side-car pour journaliser les évènements du Pod applicatif
|
||
|
||
Vous pouvez utiliser un conteneur side-car d'une des manières suivantes :
|
||
|
||
* Le conteneur side-car diffuse les journaux de l'application sur son propre
|
||
`stdout`.
|
||
* Le conteneur side-car exécute un agent de journalisation qui est configuré
|
||
pour récupérer les journaux du conteneur applicatif.
|
||
|
||
#### Conteneur side-car diffusant (Streaming sidecar container)
|
||
|
||

|
||
|
||
Comme le conteneur side-car diffuse les journaux sur ses propres flux `stdout`
|
||
et `stderr`, on peut bénéficier du kubelet et de l'agent de journalisation qui
|
||
s'exécute déjà sur chaque nœud. Les conteneurs side-car lisent les journaux
|
||
depuis un fichier, un socket ou bien journald. Chaque conteneur side-car écrit
|
||
son journal sur son propre flux `stdout` ou `stderr`.
|
||
|
||
Cette méthode permet de séparer les flux de journaux de différentes
|
||
parties de votre application même si elles ne supportent pas d'écrire sur
|
||
`stdout` ou `stderr`. La logique de rediriger les journaux est minime et
|
||
le surcoût est non significatif. De plus comme les flux standards `stdout` et
|
||
`stderr` sont gérés par kubelet, les outils natifs comme `kubectl logs` peuvent
|
||
être utilisés.
|
||
|
||
Regardez l'exemple qui suit.
|
||
|
||
Un Pod exécute un unique conteneur et ce conteneur écrit dans deux fichiers de
|
||
journaux différents en utilisant deux format différents. Voici le manifeste du
|
||
Pod :
|
||
|
||
{{< codenew file="admin/logging/two-files-counter-pod.yaml" >}}
|
||
|
||
Il serait très désordonné d'avoir des évènements avec des formats différents
|
||
dans le même journal en redirigeant les évènements dans le flux de sortie
|
||
`stdout` d'un seul conteneur. Il est plutôt souhaitable d'utiliser deux
|
||
conteneurs side-car, un pour chaque type de journaux. Chaque conteneur side-car
|
||
suit un des fichiers et renvoie les évènements sur son propre `stdout`.
|
||
|
||
Ci-dessous se trouve le manifeste pour un Pod avec deux conteneurs side-car.
|
||
|
||
{{< codenew file="admin/logging/two-files-counter-pod-streaming-sidecar.yaml"
|
||
>}}
|
||
|
||
Quand ce Pod s'exécute, chaque journal peut être diffusé séparément en
|
||
utilisant les commandes suivantes :
|
||
|
||
```shell
|
||
kubectl logs counter count-log-1
|
||
```
|
||
```
|
||
0: Mon Jan 1 00:00:00 UTC 2001
|
||
1: Mon Jan 1 00:00:01 UTC 2001
|
||
2: Mon Jan 1 00:00:02 UTC 2001
|
||
...
|
||
```
|
||
|
||
```shell
|
||
kubectl logs counter count-log-2
|
||
```
|
||
```
|
||
Mon Jan 1 00:00:00 UTC 2001 INFO 0
|
||
Mon Jan 1 00:00:01 UTC 2001 INFO 1
|
||
Mon Jan 1 00:00:02 UTC 2001 INFO 2
|
||
...
|
||
```
|
||
|
||
L'agent au niveau du nœud installé dans le cluster récupère les deux flux de
|
||
journaux sans aucune configuration supplémentaire. Il est possible de configurer
|
||
l'agent pour qu'il analyse syntaxiquement les évènements en fonction du
|
||
conteneur source.
|
||
|
||
Notez que bien que la consommation en CPU et mémoire soit faible ( de l'ordre de
|
||
quelques milicores pour la CPU et quelques mégaoctets pour la mémoire), ecrire
|
||
les évènements dans un fichier et les envoyer ensuite dans `stdout` peut doubler
|
||
l'espace disque utilisé. Quand une application écrit dans un seul fichier de
|
||
journal il est préférable de configurer `/dev/stdout` comme destination plutôt
|
||
que d'implémenter un conteneur side-car diffusant.
|
||
|
||
Les conteneurs side-car peuvent être utilisés pour faire la rotation des
|
||
journaux quand l'application n'en est pas capable elle même. Un exemple serait
|
||
un petit conteneur side-car qui effectuerait cette rotation périodiquement.
|
||
Toutefois il est recommandé d'utiliser `stdout` et `stderr` directement et de
|
||
laisser la rotation et les politiques de rétentions au kubelet.
|
||
|
||
### Conteneur side-car avec un agent de journalisation
|
||
|
||

|
||
|
||
Quand un agent de journalisation au niveau du nœud n'est pas assez flexible pour
|
||
votre utilisation vous pouvez créer un conteneur side-car avec un agent de
|
||
journalisation séparé que vous avez configuré spécialement pour qu'il s'exécute
|
||
avec votre application.
|
||
|
||
{{< note >}}
|
||
Utiliser un agent de journalisation dans un conteneur side-car peut entraîner
|
||
une consommation de ressource significative. De plus vous n'avez plus accès aux
|
||
journaux avec la commande `kubectl` parce qu'ils ne sont plus gérés par
|
||
kubelet.
|
||
{{< /note >}}
|
||
|
||
Comme exemple, vous pouvez utiliser
|
||
[Stackdriver](/docs/tasks/debug-application-cluster/logging-stackdriver/) où
|
||
fluentd est l'agent de journalisation. Ci-dessous se trouvent deux
|
||
configurations qui implémentent cette méthode.
|
||
|
||
Le premier fichier contient un
|
||
[ConfigMap](/docs/tasks/configure-pod-container/configure-pod-configmap/) pour
|
||
configurer fluentd.
|
||
|
||
{{< codenew file="admin/logging/fluentd-sidecar-config.yaml" >}}
|
||
|
||
{{< note >}}
|
||
La configuration de fluentd est hors du cadre de cet article. Vous trouverez
|
||
des informations pour configurer fluentd dans la [documentation officielle de
|
||
fluentd](http://docs.fluentd.org/).
|
||
{{< /note >}}
|
||
|
||
Le second fichier est un manifeste pour un Pod avec un conteneur side-car qui
|
||
exécute fluentd. Le Pod monte un volume où fluentd peut récupérer sa
|
||
configuration.
|
||
|
||
{{< codenew file="admin/logging/two-files-counter-pod-agent-sidecar.yaml" >}}
|
||
|
||
Apres quelques minutes, les évènements apparaîtront dans l'interface de
|
||
Stackdriver.
|
||
|
||
Ce n'est qu'un exemple et vous pouvez remplacer fluentd par n'importe quel
|
||
agent de journalisation qui lit depuis n'importe quelle source de votre
|
||
application.
|
||
|
||
### Envoyer les évènements directement depuis l'application.
|
||
|
||

|
||
|
||
Vous pouvez implémenter la journalisation au niveau cluster en mettant à
|
||
disposition ou en envoyant les journaux directement depuis chaque application;
|
||
Toutefois l'implémentation de ce mécanisme de journalisation est hors du cadre
|
||
de Kubernetes.
|
||
|
||
|
||
{{% /capture %}}
|