Comment configurer une pile de journalisation Elasticsearch, Fluentd et Kibana (EFK) sur Kubernetes

introduction

Lorsque vous exécutez plusieurs services et applications sur un cluster Kubernetes, une pile de journalisation centralisée au niveau du cluster peut vous aider à trier et à analyser rapidement le volume important de données de journalisation générées par vos pods. Une solution de journalisation centralisée populaire est la pileElasticsearch,Fluentd etKibana (EFK).

Elasticsearch est un moteur de recherche en temps réel, distribué et évolutif qui permet une recherche en texte intégral et structurée, ainsi que des analyses. Il est couramment utilisé pour indexer et rechercher dans de grands volumes de données de journal, mais peut également être utilisé pour rechercher de nombreux types de documents.

Elasticsearch est généralement déployé aux côtés deKibana, une interface puissante de visualisation de données et un tableau de bord pour Elasticsearch. Kibana vous permet d'explorer les données de votre journal Elasticsearch via une interface Web et de créer des tableaux de bord et des requêtes permettant de répondre rapidement aux questions et d'obtenir un aperçu des applications Kubernetes.

Dans ce didacticiel, nous utiliseronsFluentd pour collecter, transformer et expédier les données de journal au backend Elasticsearch. Fluentd est un collecteur de données open source populaire que nous allons configurer sur nos nœuds Kubernetes pour extraire les fichiers journaux du conteneur, filtrer et transformer les données de journal et les transmettre au cluster Elasticsearch, où elles seront indexées et stockées.

Nous allons commencer par configurer et lancer un cluster Elasticsearch évolutif, puis créer le service Kibana Kubernetes and Deployment. Pour conclure, nous allons configurer Fluentd en tant que DaemonSet pour qu’il s’exécute sur tous les nœuds de travail Kubernetes.

Conditions préalables

Avant de commencer avec ce guide, assurez-vous de disposer des éléments suivants:

  • Un cluster Kubernetes 1.10+ avec le contrôle d'accès basé sur les rôles (RBAC) activé

    • Assurez-vous que votre cluster dispose de suffisamment de ressources pour déployer la pile EFK. Si ce n'est pas le cas, mettez-le à l'échelle en ajoutant des nœuds de travail. Nous allons déployer un cluster Elasticsearch à 3 pods (vous pouvez le réduire à 1 si nécessaire), ainsi qu’un seul pod Kibana. Chaque nœud de travail exécutera également un pod Fluentd. Le cluster de ce guide est constitué de 3 nœuds d'ouvrier et d'un plan de contrôle géré.

  • L'outil de ligne de commandekubectl installé sur votre ordinateur local, configuré pour se connecter à votre cluster. Vous pouvez en savoir plus sur l'installation dekubectlin the official documentation.

Une fois ces composants configurés, vous êtes prêt à commencer par ce guide.

[[step-1 -—- creation-a-namespace]] == Étape 1 - Création d'un espace de noms

Avant de déployer un cluster Elasticsearch, nous allons d’abord créer un espace de noms dans lequel nous installerons l’ensemble de nos instruments de journalisation. Kubernetes vous permet de séparer les objets en cours d'exécution dans votre cluster à l'aide d'une abstraction de «cluster virtuel» appelée Namespaces. Dans ce guide, nous allons créer un espace de nomskube-logging dans lequel nous installerons les composants de la pile EFK. Cet espace de noms nous permettra également de nettoyer et de supprimer rapidement la pile de journalisation sans perte de fonction pour le cluster Kubernetes.

Pour commencer, examinez d'abord les espaces de noms existants dans votre cluster à l'aide dekubectl:

kubectl get namespaces

Vous devriez voir les trois espaces de noms initiaux suivants, préinstallés avec votre cluster Kubernetes:

OutputNAME          STATUS    AGE
default       Active    5m
kube-system   Active    5m
kube-public   Active    5m

L'espace de nomsdefault héberge les objets créés sans spécifier d'espace de noms. L'espace de nomskube-system contient des objets créés et utilisés par le système Kubernetes, tels quekube-dns,kube-proxy etkubernetes-dashboard. Il est recommandé de garder cet espace de noms propre et de ne pas le polluer avec vos charges de travail d’application et d’instrumentation.

L'espace de nomskube-public est un autre espace de noms créé automatiquement qui peut être utilisé pour stocker des objets que vous souhaitez voir lisibles et accessibles dans tout le cluster, même pour les utilisateurs non authentifiés.

Pour créer l'espace de nomskube-logging, ouvrez et modifiez d'abord un fichier appelékube-logging.yaml à l'aide de votre éditeur préféré, tel que nano:

nano kube-logging.yaml

Dans votre éditeur, collez l’objet Namespace suivant YAML:

kube-logging.yaml

kind: Namespace
apiVersion: v1
metadata:
  name: kube-logging

Ensuite, enregistrez et fermez le fichier.

Ici, nous spécifions leskind de l'objet Kubernetes en tant qu'objetNamespace. Pour en savoir plus sur les objetsNamespace, consultez lesNamespaces Walkthrough dans la documentation officielle de Kubernetes. Nous spécifions également la version de l'API Kubernetes utilisée pour créer l'objet (v1), et lui attribuons unname,kube-logging.

Une fois que vous avez créé le fichier d'objet d'espace de nomskube-logging.yaml, créez l'espace de noms à l'aide dekubectl create avec l'indicateur de nom de fichier-f:

kubectl create -f kube-logging.yaml

Vous devriez voir la sortie suivante:

Outputnamespace/kube-logging created

Vous pouvez ensuite confirmer que l'espace de noms a été créé avec succès:

kubectl get namespaces

À ce stade, vous devriez voir le nouvel espace de nomskube-logging:

OutputNAME           STATUS    AGE
default        Active    23m
kube-logging   Active    1m
kube-public    Active    23m
kube-system    Active    23m

Nous pouvons maintenant déployer un cluster Elasticsearch dans cet espace de noms de journalisation isolé.

[[step-2 -—- Creating-the-elasticsearch-statefulset]] == Étape 2 - Création de Elasticsearch StatefulSet

Maintenant que nous avons créé un espace de noms pour héberger notre pile de journalisation, nous pouvons commencer à déployer ses divers composants. Nous allons d’abord commencer par déployer un cluster Elasticsearch à 3 nœuds.

Dans ce guide, nous utilisons 3 Elasticsearch Pods pour éviter le problème de «division du cerveau» qui se produit dans les clusters hautement disponibles à plusieurs nœuds. À haut niveau, le «cerveau divisé» se produit lorsqu'un ou plusieurs nœuds ne peuvent pas communiquer avec les autres et que plusieurs maîtres «divisés» sont élus. Avec 3 nœuds, si l'un d'entre eux est déconnecté temporairement du cluster, les deux autres nœuds peuvent élire un nouveau maître et le cluster peut continuer à fonctionner pendant que le dernier nœud tente de se rejoindre. Pour en savoir plus, consultezA new era for cluster coordination in Elasticsearch etVoting configurations.

Création du service sans tête

Pour commencer, nous allons créer un service Kubernetes headless appeléelasticsearch qui définira un domaine DNS pour les 3 pods. Un service headless n'effectue pas d'équilibrage de charge ou possède une adresse IP statique; pour en savoir plus sur les services headless, consultez lesKubernetes documentation officiels.

Ouvrez un fichier appeléelasticsearch_svc.yaml en utilisant votre éditeur préféré:

nano elasticsearch_svc.yaml

Collez dans le service Kubernetes YAML suivant:

elasticsearch_svc.yaml

kind: Service
apiVersion: v1
metadata:
  name: elasticsearch
  namespace: kube-logging
  labels:
    app: elasticsearch
spec:
  selector:
    app: elasticsearch
  clusterIP: None
  ports:
    - port: 9200
      name: rest
    - port: 9300
      name: inter-node

Ensuite, enregistrez et fermez le fichier.

Nous définissons unService appeléelasticsearch dans l'espace de nomskube-logging, et lui donnons le labelapp: elasticsearch. Nous définissons ensuite les.spec.selector surapp: elasticsearch afin que le service sélectionne les pods avec l'étiquetteapp: elasticsearch. Lorsque nous associons notre Elasticsearch StatefulSet à ce service, le service renvoie des enregistrements DNS A qui pointent vers des pods Elasticsearch avec le labelapp: elasticsearch.

Nous définissons ensuiteclusterIP: None, ce qui rend le service sans tête. Enfin, nous définissons les ports9200 et9300 qui sont utilisés respectivement pour interagir avec l'API REST et pour la communication inter-nœuds.

Créez le service en utilisantkubectl:

kubectl create -f elasticsearch_svc.yaml

Vous devriez voir la sortie suivante:

Outputservice/elasticsearch created

Enfin, vérifiez que le service a été créé avec succès à l'aide dekubectl get:

kubectl get services --namespace=kube-logging

Vous devriez voir ce qui suit:

OutputNAME            TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)             AGE
elasticsearch   ClusterIP   None                 9200/TCP,9300/TCP   26s

Maintenant que nous avons configuré notre service headless et un domaine.elasticsearch.kube-logging.svc.cluster.local stable pour nos pods, nous pouvons continuer et créer le StatefulSet.

Créer le StatefulSet

Kubernetes StatefulSet vous permet d'attribuer une identité stable aux pods et de leur accorder un stockage stable et persistant. Elasticsearch nécessite un stockage stable pour conserver les données tout au long de la replanification et du redémarrage du pod. Pour en savoir plus sur la charge de travail StatefulSet, consultez la pageStatefulsets de la documentation Kubernetes.

Ouvrez un fichier appeléelasticsearch_statefulset.yaml dans votre éditeur préféré:

nano elasticsearch_statefulset.yaml

Nous allons parcourir section par section la définition de l'objet StatefulSet, en collant des blocs dans ce fichier.

Commencez par coller dans le bloc suivant:

elasticsearch_statefulset.yaml

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: es-cluster
  namespace: kube-logging
spec:
  serviceName: elasticsearch
  replicas: 3
  selector:
    matchLabels:
      app: elasticsearch
  template:
    metadata:
      labels:
        app: elasticsearch

Dans ce bloc, nous définissons un StatefulSet appelées-cluster dans l'espace de nomskube-logging. Nous l'associons ensuite à notre serviceelasticsearch précédemment créé en utilisant le champserviceName. Cela garantit que chaque pod du StatefulSet sera accessible à l'aide de l'adresse DNS suivante:es-cluster-[0,1,2].elasticsearch.kube-logging.svc.cluster.local, où[0,1,2] correspond à l'ordinal entier attribué au pod.

Nous spécifions 3replicas (Pods) et positionnons le sélecteurmatchLabels surapp: elasticseach, que nous reflétons ensuite dans la section.spec.template.metadata. Les champs.spec.selector.matchLabels et.spec.template.metadata.labels doivent correspondre.

Nous pouvons maintenant passer à la spécification d'objet. Collez dans le bloc suivant de YAML immédiatement en dessous du bloc précédent:

elasticsearch_statefulset.yaml

. . .
    spec:
      containers:
      - name: elasticsearch
        image: docker.elastic.co/elasticsearch/elasticsearch:7.2.0
        resources:
            limits:
              cpu: 1000m
            requests:
              cpu: 100m
        ports:
        - containerPort: 9200
          name: rest
          protocol: TCP
        - containerPort: 9300
          name: inter-node
          protocol: TCP
        volumeMounts:
        - name: data
          mountPath: /usr/share/elasticsearch/data
        env:
          - name: cluster.name
            value: k8s-logs
          - name: node.name
            valueFrom:
              fieldRef:
                fieldPath: metadata.name
          - name: discovery.seed_hosts
            value: "es-cluster-0.elasticsearch,es-cluster-1.elasticsearch,es-cluster-2.elasticsearch"
          - name: cluster.initial_master_nodes
            value: "es-cluster-0,es-cluster-1,es-cluster-2"
          - name: ES_JAVA_OPTS
            value: "-Xms512m -Xmx512m"

Ici, nous définissons les pods dans le StatefulSet. Nous nommons les conteneurselasticsearch et choisissons l'image Docker dedocker.elastic.co/elasticsearch/elasticsearch:7.2.0. À ce stade, vous pouvez modifier cette balise d'image pour qu'elle corresponde à votre propre image interne Elasticsearch ou à une version différente. Notez que pour les besoins de ce guide, seul Elasticsearch7.2.0 a été testé.

Nous utilisons ensuite le champresources pour spécifier que le conteneur a besoin d'au moins 0,1 vCPU qui lui est garanti, et peut exploser jusqu'à 1 vCPU (ce qui limite l'utilisation des ressources du pod lors de la réalisation d'une acquisition initiale importante ou du traitement d'un pic de charge. ). Vous devez modifier ces valeurs en fonction de votre charge anticipée et des ressources disponibles. Pour en savoir plus sur les demandes de ressources et les limites, consultez lesKubernetes Documentation officiels.

Nous ouvrons et nommons ensuite les ports9200 et9300 pour l'API REST et la communication inter-nœuds, respectivement. Nous spécifions unvolumeMount appelédata qui montera le PersistentVolume nommédata sur le conteneur au chemin/usr/share/elasticsearch/data. Nous définirons les VolumeClaims pour cet StatefulSet dans un bloc YAML ultérieur.

Enfin, nous définissons des variables d’environnement dans le conteneur:

  • cluster.name: le nom du cluster Elasticsearch, qui dans ce guide estk8s-logs.

  • node.name: le nom du nœud, que nous avons défini dans le champ.metadata.name en utilisantvalueFrom. Cela se résoudra enes-cluster-[0,1,2], en fonction de l’ordinal attribué au nœud.

  • discovery.seed_hosts: ce champ définit une liste de nœuds éligibles maître dans le cluster qui amorceront le processus de découverte de nœuds. Dans ce guide, grâce au service headless que nous avons configuré précédemment, nos pods ont des domaines de la formees-cluster-[0,1,2].elasticsearch.kube-logging.svc.cluster.local, nous définissons donc cette variable en conséquence. En utilisant la résolution DNS Kubernetes de l'espace de noms local, nous pouvons raccourcir cela àes-cluster-[0,1,2].elasticsearch. Pour en savoir plus sur la découverte Elasticsearch, consultez lesElasticsearch documentation officiels.

  • cluster.initial_master_nodes: ce champ spécifie également une liste de nœuds éligibles au maître qui participeront au processus d'élection du maître. Notez que pour ce champ, vous devez identifier les nœuds par leurnode.name, et non par leurs noms d'hôte.

  • ES_JAVA_OPTS: Ici, nous définissons ceci sur-Xms512m -Xmx512m, ce qui indique à la JVM d'utiliser une taille de segment minimale et maximale de 512 Mo. Vous devez ajuster ces paramètres en fonction de la disponibilité et des besoins en ressources de votre cluster. Pour en savoir plus, consultezSetting the heap size.

Le bloc suivant dans lequel nous allons coller ressemble à ce qui suit:

elasticsearch_statefulset.yaml

. . .
      initContainers:
      - name: fix-permissions
        image: busybox
        command: ["sh", "-c", "chown -R 1000:1000 /usr/share/elasticsearch/data"]
        securityContext:
          privileged: true
        volumeMounts:
        - name: data
          mountPath: /usr/share/elasticsearch/data
      - name: increase-vm-max-map
        image: busybox
        command: ["sysctl", "-w", "vm.max_map_count=262144"]
        securityContext:
          privileged: true
      - name: increase-fd-ulimit
        image: busybox
        command: ["sh", "-c", "ulimit -n 65536"]
        securityContext:
          privileged: true

Dans ce bloc, nous définissons plusieurs conteneurs d'initialisation qui s'exécutent avant le conteneur d'application principal deelasticsearch. Ces conteneurs d'initialisation s'exécutent chacun dans l'ordre dans lequel ils ont été définis. Pour en savoir plus sur Init Containers, consultez lesKubernetes Documentation officiels.

Le premier, nomméfix-permissions, exécute une commandechown pour changer le propriétaire et le groupe du répertoire de données Elasticsearch en1000:1000, l'UID de l'utilisateur Elasticsearch. Par défaut, Kubernetes monte le répertoire de données en tant queroot, ce qui le rend inaccessible à Elasticsearch. Pour en savoir plus sur cette étape, consultez «https://www.elastic.co/guide/fr/elasticsearch/reference/current/docker.html#_notes_for_production_use_and_defaults&Notes pour l'utilisation de la production et les valeurs par défaut]».

Le second, nomméincrease-vm-max-map, exécute une commande pour augmenter les limites du système d’exploitation sur le nombre de mmap, qui par défaut peut être trop faible, ce qui entraîne des erreurs de mémoire insuffisante. Pour en savoir plus sur cette étape, consultez lesElasticsearch documentation officiels.

Le prochain conteneur d'initialisation à exécuter estincrease-fd-ulimit, qui exécute la commandeulimit pour augmenter le nombre maximum de descripteurs de fichiers ouverts. Pour en savoir plus sur cette étape, consultez les «https://www.elastic.co/guide/fr/elasticsearch/reference/current/docker.html#_notes_for_production_use_and_defaults[ Notes d'utilisation de la production et des valeurs par défaut]» dans la documentation officielle Elasticsearch.

[.note] #Note: ElasticsearchNotes for Production Use mentionne également la désactivation de la permutation pour des raisons de performances. En fonction de votre installation ou de votre fournisseur Kubernetes, la permutation peut déjà être désactivée. Pour vérifier cela,exec dans un conteneur en cours d'exécution et exécutezcat /proc/swaps pour répertorier les périphériques d'échange actifs. Si vous ne voyez rien là-bas, le swap est désactivé.
#

Maintenant que nous avons défini notre conteneur d'application principal et les conteneurs d'initialisation qui s'exécutent avant lui pour régler le système d'exploitation du conteneur, nous pouvons ajouter le dernier élément à notre fichier de définition d'objet StatefulSet: lesvolumeClaimTemplates.

Collez dans le blocvolumeClaimTemplate suivant:

elasticsearch_statefulset.yaml

. . .
  volumeClaimTemplates:
  - metadata:
      name: data
      labels:
        app: elasticsearch
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: do-block-storage
      resources:
        requests:
          storage: 100Gi

Dans ce bloc, nous définissons lesvolumeClaimTemplates du StatefulSet. Kubernetes l'utilisera pour créer des volumes persistants pour les pods. Dans le bloc ci-dessus, nous le nommonsdata (qui est lename auquel nous nous référons dans l'étiquettevolumeMount+`s defined previously), and give it the same `+app: elasticsearch comme notre StatefulSet.

Nous spécifions ensuite son mode d'accès commeReadWriteOnce, ce qui signifie qu'il ne peut être monté en lecture-écriture que par un seul nœud. Nous définissons la classe de stockage commedo-block-storage dans ce guide car nous utilisons un cluster DigitalOcean Kubernetes à des fins de démonstration. Vous devez modifier cette valeur en fonction de l'emplacement où vous exécutez votre cluster Kubernetes. Pour en savoir plus, consultez la documentation dePersistent Volume.

Enfin, nous spécifions que nous préférerions que chaque volume persistant ait une taille de 100 Go. Vous devez ajuster cette valeur en fonction de vos besoins de production.

La spécification complète de StatefulSet devrait ressembler à ceci:

elasticsearch_statefulset.yaml

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: es-cluster
  namespace: kube-logging
spec:
  serviceName: elasticsearch
  replicas: 3
  selector:
    matchLabels:
      app: elasticsearch
  template:
    metadata:
      labels:
        app: elasticsearch
    spec:
      containers:
      - name: elasticsearch
        image: docker.elastic.co/elasticsearch/elasticsearch:7.2.0
        resources:
            limits:
              cpu: 1000m
            requests:
              cpu: 100m
        ports:
        - containerPort: 9200
          name: rest
          protocol: TCP
        - containerPort: 9300
          name: inter-node
          protocol: TCP
        volumeMounts:
        - name: data
          mountPath: /usr/share/elasticsearch/data
        env:
          - name: cluster.name
            value: k8s-logs
          - name: node.name
            valueFrom:
              fieldRef:
                fieldPath: metadata.name
          - name: discovery.seed_hosts
            value: "es-cluster-0.elasticsearch,es-cluster-1.elasticsearch,es-cluster-2.elasticsearch"
          - name: cluster.initial_master_nodes
            value: "es-cluster-0,es-cluster-1,es-cluster-2"
          - name: ES_JAVA_OPTS
            value: "-Xms512m -Xmx512m"
      initContainers:
      - name: fix-permissions
        image: busybox
        command: ["sh", "-c", "chown -R 1000:1000 /usr/share/elasticsearch/data"]
        securityContext:
          privileged: true
        volumeMounts:
        - name: data
          mountPath: /usr/share/elasticsearch/data
      - name: increase-vm-max-map
        image: busybox
        command: ["sysctl", "-w", "vm.max_map_count=262144"]
        securityContext:
          privileged: true
      - name: increase-fd-ulimit
        image: busybox
        command: ["sh", "-c", "ulimit -n 65536"]
        securityContext:
          privileged: true
  volumeClaimTemplates:
  - metadata:
      name: data
      labels:
        app: elasticsearch
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: do-block-storage
      resources:
        requests:
          storage: 100Gi

Une fois que vous êtes satisfait de votre configuration Elasticsearch, enregistrez et fermez le fichier.

Maintenant, déployez le StatefulSet en utilisantkubectl:

kubectl create -f elasticsearch_statefulset.yaml

Vous devriez voir la sortie suivante:

Outputstatefulset.apps/es-cluster created

Vous pouvez surveiller le StatefulSet lors de son déploiement à l'aide dekubectl rollout status:

kubectl rollout status sts/es-cluster --namespace=kube-logging

Vous devriez voir la sortie suivante à mesure que le cluster est déployé:

OutputWaiting for 3 pods to be ready...
Waiting for 2 pods to be ready...
Waiting for 1 pods to be ready...
partitioned roll out complete: 3 new pods have been updated...

Une fois que tous les pods ont été déployés, vous pouvez vérifier que votre cluster Elasticsearch fonctionne correctement en effectuant une requête sur l'API REST.

Pour ce faire, transférez d'abord le port local9200 vers le port9200 sur l'un des nœuds Elasticsearch (es-cluster-0) en utilisantkubectl port-forward:

kubectl port-forward es-cluster-0 9200:9200 --namespace=kube-logging

Ensuite, dans une fenêtre de terminal distincte, effectuez une requêtecurl sur l'API REST:

curl http://localhost:9200/_cluster/state?pretty

Vous devriez voir la sortie suivante:

Output{
  "cluster_name" : "k8s-logs",
  "compressed_size_in_bytes" : 348,
  "cluster_uuid" : "QD06dK7CQgids-GQZooNVw",
  "version" : 3,
  "state_uuid" : "mjNIWXAzQVuxNNOQ7xR-qg",
  "master_node" : "IdM5B7cUQWqFgIHXBp0JDg",
  "blocks" : { },
  "nodes" : {
    "u7DoTpMmSCixOoictzHItA" : {
      "name" : "es-cluster-1",
      "ephemeral_id" : "ZlBflnXKRMC4RvEACHIVdg",
      "transport_address" : "10.244.8.2:9300",
      "attributes" : { }
    },
    "IdM5B7cUQWqFgIHXBp0JDg" : {
      "name" : "es-cluster-0",
      "ephemeral_id" : "JTk1FDdFQuWbSFAtBxdxAQ",
      "transport_address" : "10.244.44.3:9300",
      "attributes" : { }
    },
    "R8E7xcSUSbGbgrhAdyAKmQ" : {
      "name" : "es-cluster-2",
      "ephemeral_id" : "9wv6ke71Qqy9vk2LgJTqaA",
      "transport_address" : "10.244.40.4:9300",
      "attributes" : { }
    }
  },
...

Cela indique que notre cluster Elasticsearchk8s-logs a été créé avec succès avec 3 nœuds:es-cluster-0,es-cluster-1 etes-cluster-2. Le nœud maître actuel estes-cluster-0.

Maintenant que votre cluster Elasticsearch est opérationnel, vous pouvez maintenant configurer un frontal Kibana pour celui-ci.

[[step-3 -—- creating-the-kibana-deployment-and-service]] == Étape 3 - Création du déploiement et du service Kibana

Pour lancer Kibana sur Kubernetes, nous allons créer un service appelékibana et un déploiement composé d'un réplica de pod. Vous pouvez mettre à l'échelle le nombre de réplicas en fonction de vos besoins de production et éventuellement spécifier un typeLoadBalancer pour le service afin d'équilibrer la charge des demandes dans les pods de déploiement.

Cette fois, nous allons créer le service et le déploiement dans le même fichier. Ouvrez un fichier appelékibana.yaml dans votre éditeur préféré:

nano kibana.yaml

Collez dans la spécification de service suivante:

kibana.yaml

apiVersion: v1
kind: Service
metadata:
  name: kibana
  namespace: kube-logging
  labels:
    app: kibana
spec:
  ports:
  - port: 5601
  selector:
    app: kibana
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kibana
  namespace: kube-logging
  labels:
    app: kibana
spec:
  replicas: 1
  selector:
    matchLabels:
      app: kibana
  template:
    metadata:
      labels:
        app: kibana
    spec:
      containers:
      - name: kibana
        image: docker.elastic.co/kibana/kibana:7.2.0
        resources:
          limits:
            cpu: 1000m
          requests:
            cpu: 100m
        env:
          - name: ELASTICSEARCH_URL
            value: http://elasticsearch:9200
        ports:
        - containerPort: 5601

Ensuite, enregistrez et fermez le fichier.

Dans cette spécification, nous avons défini un service appelékibana dans l’espace de nomskube-logging et lui avons donné l’étiquetteapp: kibana.

Nous avons également spécifié qu'il doit être accessible sur le port5601 et utiliser le libelléapp: kibana pour sélectionner les pods cibles du service.

Dans la spécificationDeployment, nous définissons un déploiement appelékibana et spécifions que nous aimerions 1 réplique de pod.

Nous utilisons l'image dedocker.elastic.co/kibana/kibana:7.2.0. À ce stade, vous pouvez utiliser votre propre image Kibana privée ou publique.

Nous spécifions que nous voulons au moins 0,1 vCPU garanti au pod, avec une limite de 1 vCPU. Vous pouvez modifier ces paramètres en fonction de votre charge anticipée et des ressources disponibles.

Ensuite, nous utilisons la variable d'environnementELASTICSEARCH_URL pour définir le point de terminaison et le port du cluster Elasticsearch. En utilisant le DNS Kubernetes, ce point de terminaison correspond à son nom de serviceelasticsearch. Ce domaine résoudra en une liste d'adresses IP pour les 3 pods Elasticsearch. Pour en savoir plus sur le DNS Kubernetes, consultezDNS for Services and Pods.

Enfin, nous définissons le port de conteneur de Kibana sur5601, vers lequel le servicekibana transmettra les requêtes.

Une fois que vous êtes satisfait de votre configuration Kibana, vous pouvez déployer le service et le déploiement à l'aide dekubectl:

kubectl create -f kibana.yaml

Vous devriez voir la sortie suivante:

Outputservice/kibana created
deployment.apps/kibana created

Vous pouvez vérifier que le déploiement a réussi en exécutant la commande suivante:

kubectl rollout status deployment/kibana --namespace=kube-logging

Vous devriez voir la sortie suivante:

Outputdeployment "kibana" successfully rolled out

Pour accéder à l'interface Kibana, nous allons à nouveau transférer un port local au nœud Kubernetes exécutant Kibana. Récupérez les détails du pod Kibana en utilisantkubectl get:

kubectl get pods --namespace=kube-logging
OutputNAME                      READY     STATUS    RESTARTS   AGE
es-cluster-0              1/1       Running   0          55m
es-cluster-1              1/1       Running   0          54m
es-cluster-2              1/1       Running   0          54m
kibana-6c9fb4b5b7-plbg2   1/1       Running   0          4m27s

Ici, nous observons que notre Kibana Pod est appelékibana-6c9fb4b5b7-plbg2.

Transférez le port local5601 vers le port5601 sur ce pod:

kubectl port-forward kibana-6c9fb4b5b7-plbg2 5601:5601 --namespace=kube-logging

Vous devriez voir la sortie suivante:

OutputForwarding from 127.0.0.1:5601 -> 5601
Forwarding from [::1]:5601 -> 5601

Maintenant, dans votre navigateur Web, visitez l'URL suivante:

http://localhost:5601

Si vous voyez la page d’accueil Kibana suivante, vous avez correctement déployé Kibana dans votre cluster Kubernetes:

Kibana Welcome Screen

Vous pouvez maintenant passer au déploiement du dernier composant de la pile EFK: le collecteur de journaux, Fluentd.

[[step-4 -—- Creating-the-fluentd-daemonset]] == Étape 4 - Création du Fluentd DaemonSet

Dans ce guide, nous allons configurer Fluentd en tant que DaemonSet, qui est un type de charge de travail Kubernetes qui exécute une copie d’un pod donné sur chaque nœud du cluster Kubernetes. À l'aide de ce contrôleur DaemonSet, nous allons déployer un pod de l'agent de journalisation Fluentd sur chaque nœud de notre cluster. Pour en savoir plus sur cette architecture de journalisation, consultez «https://kubernetes.io/docs/concepts/cluster-administration/logging/#using-a-node-logging-agent[Using un agent de journalisation de nœud]» du Kubernetes officiel docs.

Dans Kubernetes, les applications conteneurisées qui se connectent àstdout etstderr voient leurs flux de journaux capturés et redirigés vers des fichiers JSON sur les nœuds. Le pod Fluentd suivra ces fichiers journaux, filtrera les événements du journal, transformera les données du journal et les expédiera au backend de journalisation Elasticsearch que nous avons déployé dansStep 2.

Outre les journaux de conteneur, l'agent Fluentd répertorie les journaux de composants système Kubernetes tels que les journaux kubelet, kube-proxy et Docker. Pour voir une liste complète des sources suivies par l'agent de journalisation Fluentd, consultez le fichierkubernetes.conf utilisé pour configurer l'agent de journalisation. Pour en savoir plus sur la journalisation dans les clusters Kubernetes, consultez «https://kubernetes.io/docs/concepts/cluster-administration/logging/#logging-at-the-node-level[Logging au niveau du nœud]» du responsable Documentation Kubernetes.

Commencez par ouvrir un fichier appeléfluentd.yaml dans votre éditeur de texte préféré:

nano fluentd.yaml

Une fois encore, nous allons coller bloc par bloc dans les définitions d’objet Kubernetes, en fournissant un contexte au fur et à mesure. Dans ce guide, nous utilisons lesFluentd DaemonSet spec fournis par les responsables de Fluentd. Une autre ressource utile fournie par les responsables de Fluentd estKuberentes Fluentd.

Tout d’abord, collez la définition suivante de ServiceAccount:

fluentd.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: fluentd
  namespace: kube-logging
  labels:
    app: fluentd

Ici, nous créons un compte de service appeléfluentd que les pods Fluentd utiliseront pour accéder à l'API Kubernetes. Nous le créons dans l'espace de nomskube-logging et lui attribuons à nouveau l'étiquetteapp: fluentd. Pour en savoir plus sur les comptes de service dans Kubernetes, consultezConfigure Service Accounts for Pods dans la documentation officielle de Kubernetes.

Ensuite, collez le blocClusterRole suivant:

fluentd.yaml

. . .
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: fluentd
  labels:
    app: fluentd
rules:
- apiGroups:
  - ""
  resources:
  - pods
  - namespaces
  verbs:
  - get
  - list
  - watch

Ici, nous définissons un ClusterRole appeléfluentd auquel nous accordons les autorisationsget,list etwatch sur les objetspods etnamespaces. ClusterRoles vous permet d'accorder l'accès à des ressources Kubernetes de type cluster, telles que des nœuds. Pour en savoir plus sur le contrôle d'accès basé sur les rôles et les rôles de cluster, consultezUsing RBAC Authorization dans la documentation officielle de Kubernetes.

Maintenant, collez le blocClusterRoleBinding suivant:

fluentd.yaml

. . .
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: fluentd
roleRef:
  kind: ClusterRole
  name: fluentd
  apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
  name: fluentd
  namespace: kube-logging

Dans ce bloc, nous définissons unClusterRoleBinding appeléfluentd qui lie le ClusterRolefluentd au compte de servicefluentd. Cela accorde aufluentd ServiceAccount les autorisations répertoriées dans le rôle de clusterfluentd.

À ce stade, nous pouvons commencer à coller dans les spécifications de DaemonSet:

fluentd.yaml

. . .
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd
  namespace: kube-logging
  labels:
    app: fluentd

Ici, nous définissons un DaemonSet appeléfluentd dans l'espace de nomskube-logging et lui donnons le labelapp: fluentd.

Ensuite, collez dans la section suivante:

fluentd.yaml

. . .
spec:
  selector:
    matchLabels:
      app: fluentd
  template:
    metadata:
      labels:
        app: fluentd
    spec:
      serviceAccount: fluentd
      serviceAccountName: fluentd
      tolerations:
      - key: node-role.kubernetes.io/master
        effect: NoSchedule
      containers:
      - name: fluentd
        image: fluent/fluentd-kubernetes-daemonset:v1.4.2-debian-elasticsearch-1.1
        env:
          - name:  FLUENT_ELASTICSEARCH_HOST
            value: "elasticsearch.kube-logging.svc.cluster.local"
          - name:  FLUENT_ELASTICSEARCH_PORT
            value: "9200"
          - name: FLUENT_ELASTICSEARCH_SCHEME
            value: "http"
          - name: FLUENTD_SYSTEMD_CONF
            value: disable

Ici, nous faisons correspondre le libelléapp: fluentd défini dans.metadata.labels, puis attribuons au DaemonSet le compte de servicefluentd. Nous sélectionnons également lesapp: fluentd comme pods gérés par ce DaemonSet.

Ensuite, nous définissons une toléranceNoSchedule pour correspondre à la teinte équivalente sur les nœuds maîtres Kubernetes. Cela garantira que le DaemonSet sera également déployé vers les maîtres Kubernetes. Si vous ne voulez pas exécuter de Fluentd Pod sur vos nœuds maîtres, supprimez cette tolérance. Pour en savoir plus sur les tolérances et les méthodes de Kubernetes, consultez «https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/[Taints and Tolerations]» dans les documents officiels de Kubernetes.

Ensuite, nous commençons à définir le conteneur Pod, que nous appelonsfluentd.

Nous utilisons lesofficial v1.4.2 Debian image fournis par les responsables de Fluentd. Si vous souhaitez utiliser votre propre image Fluentd privée ou publique, ou utiliser une autre version d'image, modifiez la baliseimage dans les spécifications du conteneur. Le Dockerfile et le contenu de cette image sont disponibles dans Fluentd'sfluentd-kubernetes-daemonset Github repo.

Ensuite, nous configurons Fluentd en utilisant certaines variables d’environnement:

  • FLUENT_ELASTICSEARCH_HOST: Nous définissons ceci sur l'adresse du service sans tête Elasticsearch définie précédemment:elasticsearch.kube-logging.svc.cluster.local. Cela résultera en une liste d'adresses IP pour les 3 pods Elasticsearch. L'hôte Elasticsearch sera probablement la première adresse IP renvoyée dans cette liste. Pour distribuer les journaux sur le cluster, vous devez modifier la configuration du plug-in Elasticsearch Output de Fluentd. Pour en savoir plus sur ce plugin, consultezElasticsearch Output Plugin.

  • FLUENT_ELASTICSEARCH_PORT: Nous définissons ceci sur le port Elasticsearch que nous avons configuré précédemment,9200.

  • FLUENT_ELASTICSEARCH_SCHEME: Nous définissons ceci surhttp.

  • FLUENTD_SYSTEMD_CONF: Nous définissons ceci surdisable pour supprimer la sortie liée àsystemd qui n'est pas configuré dans le conteneur.

Enfin, collez dans la section suivante:

fluentd.yaml

. . .
        resources:
          limits:
            memory: 512Mi
          requests:
            cpu: 100m
            memory: 200Mi
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
      terminationGracePeriodSeconds: 30
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers

Ici, nous spécifions une limite de mémoire de 512 Mio sur le FluentD Pod et nous la garantissons pour 0,1 vCPU et 200 Mo de mémoire. Vous pouvez ajuster ces limites et demandes de ressources en fonction du volume de journal prévu et des ressources disponibles.

Ensuite, nous montons les chemins d'hôte/var/log et/var/lib/docker/containers dans le conteneur en utilisant lesvarlog etvarlibdockercontainersvolumeMounts. Cesvolumes sont définis à la fin du bloc.

Le dernier paramètre que nous définissons dans ce bloc estterminationGracePeriodSeconds, ce qui donne à Fluentd 30 secondes pour s'arrêter en douceur lors de la réception d'un signalSIGTERM. Après 30 secondes, les conteneurs reçoivent un signalSIGKILL. La valeur par défaut pourterminationGracePeriodSeconds est 30s, donc dans la plupart des cas, ce paramètre peut être omis. Pour en savoir plus sur la résiliation harmonieuse des charges de travail Kubernetes, consultez les «meilleures pratiques de Google: https://cloud.google.com/blog/products/gcp/kubernetes-best-practices-terminating-with-grace[ de Kubernetes: se terminer avec grâce]."

L'ensemble des spécifications Fluentd devrait ressembler à ceci:

fluentd.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: fluentd
  namespace: kube-logging
  labels:
    app: fluentd
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: fluentd
  labels:
    app: fluentd
rules:
- apiGroups:
  - ""
  resources:
  - pods
  - namespaces
  verbs:
  - get
  - list
  - watch
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: fluentd
roleRef:
  kind: ClusterRole
  name: fluentd
  apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
  name: fluentd
  namespace: kube-logging
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd
  namespace: kube-logging
  labels:
    app: fluentd
spec:
  selector:
    matchLabels:
      app: fluentd
  template:
    metadata:
      labels:
        app: fluentd
    spec:
      serviceAccount: fluentd
      serviceAccountName: fluentd
      tolerations:
      - key: node-role.kubernetes.io/master
        effect: NoSchedule
      containers:
      - name: fluentd
        image: fluent/fluentd-kubernetes-daemonset:v1.4.2-debian-elasticsearch-1.1
        env:
          - name:  FLUENT_ELASTICSEARCH_HOST
            value: "elasticsearch.kube-logging.svc.cluster.local"
          - name:  FLUENT_ELASTICSEARCH_PORT
            value: "9200"
          - name: FLUENT_ELASTICSEARCH_SCHEME
            value: "http"
          - name: FLUENTD_SYSTEMD_CONF
            value: disable
        resources:
          limits:
            memory: 512Mi
          requests:
            cpu: 100m
            memory: 200Mi
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
      terminationGracePeriodSeconds: 30
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers

Une fois que vous avez fini de configurer Fluentd DaemonSet, enregistrez et fermez le fichier.

Maintenant, déployez le DaemonSet en utilisantkubectl:

kubectl create -f fluentd.yaml

Vous devriez voir la sortie suivante:

Outputserviceaccount/fluentd created
clusterrole.rbac.authorization.k8s.io/fluentd created
clusterrolebinding.rbac.authorization.k8s.io/fluentd created
daemonset.extensions/fluentd created

Vérifiez que votre DaemonSet s'est déroulé avec succès à l'aide dekubectl:

kubectl get ds --namespace=kube-logging

Vous devriez voir la sortie d'état suivante:

OutputNAME      DESIRED   CURRENT   READY     UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
fluentd   3         3         3         3            3                     58s

Cela indique que 3fluentdpods sont en cours d'exécution, ce qui correspond au nombre de nœuds dans notre cluster Kubernetes.

Nous pouvons maintenant vérifier que Kibana vérifie que les données du journal sont correctement collectées et envoyées à Elasticsearch.

Avec leskubectl port-forward toujours ouverts, accédez àhttp://localhost:5601.

Cliquez surDiscover dans le menu de navigation de gauche:

Kibana Discover

Vous devriez voir la fenêtre de configuration suivante:

Kibana Index Pattern Configuration

Cela vous permet de définir les index Elasticsearch que vous souhaitez explorer dans Kibana. Pour en savoir plus, consultezDefining your index patterns dans la documentation officielle de Kibana. Pour l'instant, nous allons simplement utiliser le modèle génériquelogstash-* pour capturer toutes les données de journal dans notre cluster Elasticsearch. Entrezlogstash-* dans la zone de texte et cliquez surNext step.

Vous serez alors amené à la page suivante:

Kibana Index Pattern Settings

Cela vous permet de configurer le champ que Kibana utilisera pour filtrer les données de journal par heure. Dans la liste déroulante, sélectionnez le champ@timestamp et appuyez surCreate index pattern.

Maintenant, appuyez surDiscover dans le menu de navigation de gauche.

Vous devriez voir un graphique d'histogramme et quelques entrées de journal récentes:

Kibana Incoming Logs

À ce stade, vous avez correctement configuré et déployé la pile EFK sur votre cluster Kubernetes. Pour savoir comment utiliser Kibana pour analyser vos données de journal, consultez lesKibana User Guide.

Dans la section facultative suivante, nous déploierons un compteur simple qui imprimera des nombres sur stdout et trouvera ses journaux dans Kibana.

[[step-5-optional -—- testing-container-logging]] == Étape 5 (Facultatif) - Test de la journalisation du conteneur

Pour illustrer un cas d'utilisation de base de Kibana consistant à explorer les derniers journaux d'un pod donné, nous allons déployer un compteur minimal qui imprime des numéros séquentiels sur stdout.

Commençons par créer le pod. Ouvrez un fichier appelécounter.yaml dans votre éditeur préféré:

nano counter.yaml

Ensuite, collez dans la spécification de pod suivante:

counter.yaml

apiVersion: v1
kind: Pod
metadata:
  name: counter
spec:
  containers:
  - name: count
    image: busybox
    args: [/bin/sh, -c,
            'i=0; while true; do echo "$i: $(date)"; i=$((i+1)); sleep 1; done']

Enregistrez et fermez le fichier.

Il s'agit d'un pod minimal appelécounter qui exécute une bouclewhile, imprimant des nombres séquentiellement.

Déployez le podcounter à l'aide dekubectl:

kubectl create -f counter.yaml

Une fois le pod créé et en cours d'exécution, revenez à votre tableau de bord Kibana.

À partir de la pageDiscover, dans la barre de recherche, entrezkubernetes.pod_name:counter. Cela filtre les données du journal pour les pods nomméscounter.

Vous devriez alors voir une liste d'entrées de journal pour le podcounter:

Counter Logs in Kibana

Vous pouvez cliquer sur l'une des entrées du journal pour voir des métadonnées supplémentaires telles que le nom du conteneur, le noeud Kubernetes, l'espace de noms, etc.

Conclusion

Dans ce guide, nous avons montré comment configurer et configurer Elasticsearch, Fluentd et Kibana sur un cluster Kubernetes. Nous avons utilisé une architecture de journalisation minimale consistant en un seul agent de journalisation fonctionnant sur chaque nœud de travail Kubernetes.

Avant de déployer cette pile de journalisation dans votre cluster de production Kubernetes, il est préférable d’ajuster les exigences en matière de ressources et les limites indiquées dans le présent guide. Vous pouvez également configurerX-Pack pour activer les fonctions de surveillance et de sécurité intégrées.

L’architecture de journalisation que nous avons utilisée ici consiste en 3 pods Elasticsearch, un seul pod Kibana (non équilibré) et un ensemble de pods Fluentd déployés en tant que DaemonSet. Vous souhaiterez peut-être adapter cette configuration en fonction de votre cas d'utilisation en production. Pour en savoir plus sur la mise à l'échelle de votre pile Elasticsearch et Kibana, consultezScaling Elasticsearch.

Kubernetes permet également des architectures d'agent de journalisation plus complexes et mieux adaptées à votre cas d'utilisation. Pour en savoir plus, consultezLogging Architecture de la documentation Kubernetes.

Related