如何在 Kubernetes 上设置 Elasticsearch、Fluentd 和 Kibana (EFK) 日志堆栈

介绍

在 Kubernetes 集群上运行多个服务和应用程序时,集中的集群级日志堆栈可以帮助您快速整理和分析 Pod 产生的大量日志数据。一种流行的集中记录的解决方案是Ë lasticsearch,˚F luentd和ķ ibana(EFK)堆栈。

Elasticsearch是一个实时、分布式和可扩展的搜索引擎,支持全文和结构化搜索以及分析。它通常用于索引和搜索大量日志数据,但也可用于搜索许多不同类型的文档。

Elasticsearch 通常与Kibana一起部署,Kibana是 Elasticsearch的强大数据可视化前端和仪表板。Kibana 允许您通过 Web 界面探索您的 Elasticsearch 日志数据,并构建仪表板和查询以快速回答问题并深入了解您的 Kubernetes 应用程序。

在本教程中,我们将使用Fluentd收集、转换日志数据并将其传送到 Elasticsearch 后端。Fluentd 是一个流行的开源数据收集器,我们将在我们的 Kubernetes 节点上设置它来跟踪容器日志文件、过滤和转换日志数据,并将其传送到 Elasticsearch 集群,在那里它会被索引和存储。

我们将首先配置和启动一个可扩展的 Elasticsearch 集群,然后创建 Kibana Kubernetes 服务和部署。最后,我们将 Fluentd 设置为 DaemonSet,以便它在每个 Kubernetes 工作节点上运行。

先决条件

在您开始阅读本指南之前,请确保您已准备好以下内容:

  • 启用了基于角色的访问控制 (RBAC) 的 Kubernetes 1.10+ 集群

    • 确保您的集群有足够的资源来推出 EFK 堆栈,如果没有,请通过添加工作节点来扩展您的集群。我们将部署一个 3 Pod Elasticsearch 集群(如有必要,您可以将其缩减为 1),以及一个 Kibana Pod。每个工作节点也将运行一个 Fluentd Pod。本指南中的集群由 3 个工作节点和一个托管控制平面组成。
  • kubectl安装在本地机器上命令行工具,配置为连接到集群。您可以kubectl 在官方文档中阅读有关安装的更多信息

设置好这些组件后,您就可以开始使用本指南了。

步骤 1 — 创建命名空间

在我们推出 Elasticsearch 集群之前,我们将首先创建一个命名空间,我们将在其中安装所有日志记录工具。Kubernetes 允许您使用称为命名空间的“虚拟集群”抽象来分离集群中运行的对象。在本指南中,我们将创建一个kube-logging命名空间,我们将在其中安装 EFK 堆栈组件。这个命名空间还允许我们快速清理和删除日志堆栈,而不会丢失 Kubernetes 集群的任何功能。

首先,首先使用以下命令调查集群中的现有命名空间kubectl

  • kubectl get namespaces

您应该会看到以下三个初始命名空间,它们预装在您的 Kubernetes 集群中:

Output
NAME STATUS AGE default Active 5m kube-system Active 5m kube-public Active 5m

default那些没有指定一个命名空间创建命名空间的房子的对象。kube-system命名空间包含由Kubernetes系统,如创建和使用的对象kube-dnskube-proxykubernetes-dashboard保持此命名空间干净并且不会被您的应用程序和检测工作负载污染是一种很好的做法。

kube-public命名空间是另一个自动创建命名空间可用于你想为可读的,在整个集群访问,甚至到未经授权的用户存储对象。

要创建kube-logging命名空间,首先kube-logging.yaml使用您喜欢的编辑器打开并编辑一个名为nano 的文件:

  • nano kube-logging.yaml

在编辑器中,粘贴以下命名空间对象 YAML:

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

然后,保存并关闭文件。

在这里,我们将 Kubernetes 对象指定kind为一个Namespace对象。要了解有关Namespace对象的更多信息,请参阅官方 Kubernetes 文档中命名空间演练我们还指定用于创建对象 ( v1)的 Kubernetes API 版本,并为其指定一个name, kube-logging

一旦你创建了kube-logging.yaml命名空间对象文件,使用创建命名空间kubectl create-f文件名标志:

  • kubectl create -f kube-logging.yaml

您应该看到以下输出:

Output
namespace/kube-logging created

然后您可以确认命名空间已成功创建:

  • kubectl get namespaces

此时,您应该看到新的kube-logging命名空间:

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

我们现在可以将 Elasticsearch 集群部署到这个隔离的日志命名空间中。

第 2 步 – 创建 Elasticsearch StatefulSet

现在我们已经创建了一个命名空间来容纳我们的日志堆栈,我们可以开始推出它的各种组件。我们将首先部署一个 3 节点 Elasticsearch 集群。

在本指南中,我们使用 3 个 Elasticsearch Pod 来避免在高可用、多节点集群中发生的“裂脑”问题。在高层次上,当一个或多个节点无法与其他节点通信时,就会出现“裂脑”,并且选出了多个“裂脑”主节点。对于 3 个节点,如果一个节点暂时与集群断开连接,其他两个节点可以选举一个新的主节点,并且在最后一个节点尝试重新加入时,集群可以继续运行。要了解更多信息,请参阅ElasticsearchVoting 配置中集群协调的新时代

创建无头服务

首先,我们将创建一个无头 Kubernetes 服务elasticsearch,该服务将为 3 个Pod定义一个 DNS 域。Headless 服务不执行负载均衡或具有静态 IP;要了解有关无头服务的更多信息,请参阅官方Kubernetes 文档

elasticsearch_svc.yaml使用您喜欢的编辑器打开一个名为的文件

  • nano elasticsearch_svc.yaml

粘贴以下 Kubernetes 服务 YAML:

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

然后,保存并关闭文件。

我们Namespace 中定义了一个Service被调用者,并为其赋予了标签。然后我们将 设置为以便 Service 选择带有标签的Pod 当我们将 Elasticsearch StatefulSet 与此服务关联时,该服务将返回指向带有标签的Elasticsearch Pod 的 DNS A 记录elasticsearchkube-loggingapp: elasticsearch.spec.selectorapp: elasticsearchapp: elasticsearchapp: elasticsearch

然后我们设置clusterIP: None,这使服务无头。最后,我们定义端口9200端口9300,分别用于与 REST API 交互和节点间通信。

使用kubectl以下方法创建服务

  • kubectl create -f elasticsearch_svc.yaml

您应该看到以下输出:

Output
service/elasticsearch created

最后,使用kubectl get以下命令仔细检查服务是否已成功创建

kubectl get services --namespace=kube-logging

您应该看到以下内容:

Output
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE elasticsearch ClusterIP None <none> 9200/TCP,9300/TCP 26s

现在我们已经.elasticsearch.kube-logging.svc.cluster.local为 Pod设置了无头服务和稳定域,我们可以继续创建 StatefulSet。

创建 StatefulSet

Kubernetes StatefulSet 允许您为 Pod 分配一个稳定的身份并授予它们稳定、持久的存储。Elasticsearch 需要稳定的存储来在 Pod 重新调度和重新启动时持久保存数据。要了解有关 StatefulSet 工作负载的更多信息,请参阅Kubernetes 文档中Statefulsets页面。

打开一个elasticsearch_statefulset.yaml在你最喜欢的编辑器中调用的文件

  • nano elasticsearch_statefulset.yaml

我们将逐节浏览 StatefulSet 对象定义,将块粘贴到此文件中。

首先粘贴以下块:

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

在这个块中,我们定义了一个es-clusterkube-logging命名空间中调用的 StatefulSet 然后我们elasticsearch使用该serviceName字段将其与我们之前创建的服务相关联这确保了 StatefulSet 中的每个 Pod 都可以使用以下 DNS 地址访问:es-cluster-[0,1,2].elasticsearch.kube-logging.svc.cluster.local,其中[0,1,2]对应于 Pod 分配的整数序数。

我们指定 3 replicas(Pods) 并将matchLabels选择器设置app: elasticseach,然后我们将其镜像到.spec.template.metadata部分中。.spec.selector.matchLabels.spec.template.metadata.labels字段必须匹配。

我们现在可以转到对象规范。将下面的 YAML 块粘贴到上一个块的正下方:

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"

这里我们定义了 StatefulSet 中的 Pod。我们为容器命名elasticsearch并选择docker.elastic.co/elasticsearch/elasticsearch:7.2.0Docker 镜像。此时,您可以修改此图像标签以对应于您自己的内部 Elasticsearch 图像,或者不同的版本。请注意,就本指南而言,仅对 Elasticsearch7.2.0进行了测试。

然后我们使用该resources字段来指定容器至少需要 0.1 个 vCPU 保证,并且可以突增到 1 个 vCPU(这在执行初始大量摄取或处理负载峰值时限制了 Pod 的资源使用)。您应该根据预期的负载和可用资源修改这些值。要了解有关资源请求和限制的更多信息,请参阅官方Kubernetes 文档

我们再打开,并将其命名端口92009300分别为REST API和节点间通信。我们指定了一个volumeMount调用data,它将把 PersistentVolume 挂载data到路径上的容器中/usr/share/elasticsearch/data我们将在后面的 YAML 块中为这个 StatefulSet 定义 VolumeClaims。

最后,我们在容器中设置一些环境变量:

  • cluster.name:Elasticsearch 集群的名称,在本指南中为k8s-logs.
  • node.name:节点的名称,我们.metadata.name使用valueFrom. 这将解析为es-cluster-[0,1,2],具体取决于节点的分配序号。
  • discovery.seed_hosts:此字段设置集群中主节点的列表,这些节点将为节点发现过程提供种子。在本指南中,由于我们之前配置的无头服务,我们的 Pod 具有形式为 的域es-cluster-[0,1,2].elasticsearch.kube-logging.svc.cluster.local,因此我们相应地设置了这个变量。使用本地命名空间 Kubernetes DNS 解析,我们可以将其缩短为es-cluster-[0,1,2].elasticsearch. 要了解有关 Elasticsearch 发现的更多信息,请参阅官方Elasticsearch 文档
  • cluster.initial_master_nodes:该字段还指定了将参与主选举过程的主合格节点列表。请注意,对于此字段,您应该通过节点node.name而不是主机名来标识节点
  • ES_JAVA_OPTS:这里我们设置-Xms512m -Xmx512m它告诉 JVM 使用 512 MB 的最小和最大堆大小。您应该根据集群的资源可用性和需求调整这些参数。要了解更多信息,请参阅设置堆大小

我们将粘贴的下一个块如下所示:

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

在这个块中,我们定义了几个在主elasticsearch应用程序容器之前运行的初始化容器。这些 Init Containers 每个都按照它们定义的顺序运行完成。要了解有关 Init Containers 的更多信息,请参阅官方Kubernetes 文档

第一个名为 named fix-permissions,运行chown命令将 Elasticsearch 数据目录的所有者和组更改1000:1000为 Elasticsearch 用户的 UID。默认情况下,Kubernetes 将数据目录挂载为root,这使得 Elasticsearch 无法访问它。要了解有关此步骤的更多信息,请参阅 Elasticsearch 的“生产使用说明和默认值”

第二个, namedincrease-vm-max-map运行一个命令来增加操作系统对 mmap 计数的限制,默认情况下它可能太低,导致内存不足错误。要了解有关此步骤的更多信息,请参阅Elasticsearch官方文档

下一个要运行的 Init Container 是increase-fd-ulimit,它运行ulimit命令以增加打开的文件描述符的最大数量。要了解有关此步骤的更多信息,请参阅Elasticsearch 官方文档中的“生产使用和默认值注意事项”。

注意: Elasticsearch Notes for Production Use还提到出于性能原因禁用交换。根据您的 Kubernetes 安装或提供商,交换可能已被禁用。要检查这一点,请exec进入正在运行的容器并运行cat /proc/swaps以列出活动的交换设备。如果您在那里什么也看不到,则交换已禁用。

现在我们已经定义了我们的主应用程序容器和在它之前运行以调整容器操作系统的初始化容器,我们可以将最后一部分添加到我们的 StatefulSet 对象定义文件中:volumeClaimTemplates.

粘贴到以下volumeClaimTemplate块中:

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

在这个块中,我们定义了 StatefulSet 的volumeClaimTemplates. Kubernetes 将使用它为 Pod 创建 PersistentVolumes。在上面的块中,我们给它命名data(这是name我们在volumeMount之前定义s 中所指的 ),并给它与app: elasticsearch我们的 StatefulSet相同的标签。

然后我们将其访问模式指定为ReadWriteOnce,这意味着它只能由单个节点以读写方式挂载。我们按照do-block-storage本指南定义存储类,因为我们使用 DigitalOcean Kubernetes 集群进行演示。您应该根据运行 Kubernetes 集群的位置更改此值。要了解更多信息,请参阅Persistent Volume文档。

最后,我们指定我们希望每个 PersistentVolume 的大小为 100GiB。您应该根据您的生产需要调整此值。

完整的 StatefulSet 规范应如下所示:

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

一旦您对 Elasticsearch 配置感到满意,请保存并关闭文件。

现在,使用kubectl以下命令部署 StatefulSet

  • kubectl create -f elasticsearch_statefulset.yaml

您应该看到以下输出:

Output
statefulset.apps/es-cluster created

您可以使用以下方法监控 StatefulSet 的推出kubectl rollout status

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

随着集群的推出,您应该会看到以下输出:

Output
Waiting 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...

部署完所有 Pod 后,您可以通过对 REST API 执行请求来检查您的 Elasticsearch 集群是否正常运行。

为此,首先使用以下命令将本地端口转发92009200Elasticsearch 节点之一 ( es-cluster-0)的端口kubectl port-forward

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

然后,在单独的终端窗口中,curl针对 REST API执行请求:

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

您应该会看到以下输出:

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" : { } } }, ...

这表明,我们的Elasticsearch集群k8s-logs已成功与3个节点创建:es-cluster-0es-cluster-1,和es-cluster-2当前主节点是es-cluster-0.

现在您的 Elasticsearch 集群已启动并运行,您可以继续为其设置 Kibana 前端。

第 3 步 – 创建 Kibana 部署和服务

为了在 Kubernetes 上启动 Kibana,我们将创建一个名为 的服务kibana,以及一个由一个 Pod 副本组成的部署。您可以根据生产需求扩展副本的数量,并可以选择LoadBalancer为服务指定一种类型,以跨部署 pod 对请求进行负载平衡。

这一次,我们将在同一个文件中创建服务和部署。打开一个kibana.yaml在你喜欢的编辑器中调用的文件

  • nano kibana.yaml

粘贴以下服务规范:

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

然后,保存并关闭文件。

在这个规范中,我们定义了一个kibanakube-logging命名空间中调用的服务,并给了它app: kibana标签。

我们还指定它应该可以在端口上访问5601并使用app: kibana标签来选择服务的目标 Pod。

Deployment规范中,我们定义了一个名为的部署kibana并指定我们想要 1 个 Pod 副本。

我们使用docker.elastic.co/kibana/kibana:7.2.0图像。此时,您可以替换您自己的私有或公共 Kibana 映像来使用。

我们指定我们希望为 Pod 保证至少 0.1 个 vCPU,突增到 1 个 vCPU 的限制。您可以根据预期的负载和可用资源更改这些参数。

接下来,我们使用ELASTICSEARCH_URL环境变量来设置 Elasticsearch 集群的端点和端口。使用 Kubernetes DNS,此端点对应于其服务名称elasticsearch该域将解析为 3 个 Elasticsearch Pod 的 IP 地址列表。要了解有关 Kubernetes DNS 的更多信息,请参阅服务和Pod 的 DNS

最后,我们将 Kibana 的容器端口设置为5601kibanaService 会将请求转发到该端口

一旦您对 Kibana 配置感到满意,您就可以使用以下命令推出服务和部署kubectl

  • kubectl create -f kibana.yaml

您应该看到以下输出:

Output
service/kibana created deployment.apps/kibana created

您可以通过运行以下命令来检查部署是否成功:

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

您应该看到以下输出:

Output
deployment "kibana" successfully rolled out

要访问 Kibana 界面,我们将再次将本地端口转发到运行 Kibana 的 Kubernetes 节点。使用kubectl get以下命令获取 Kibana Pod 详细信息

  • kubectl get pods --namespace=kube-logging
Output
NAME 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

在这里,我们观察到我们的 Kibana Pod 被称为kibana-6c9fb4b5b7-plbg2

将本地端口转发到此 Pod5601上的端口5601

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

您应该看到以下输出:

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

现在,在您的 Web 浏览器中,访问以下 URL:

http://localhost:5601

如果您看到以下 Kibana 欢迎页面,则您已成功将 Kibana 部署到您的 Kubernetes 集群中:

Kibana 欢迎屏幕

您现在可以继续推出 EFK 堆栈的最后一个组件:日志收集器 Fluentd。

第 4 步 – 创建 Fluentd DaemonSet

在本指南中,我们将 Fluentd 设置为 DaemonSet,它是一种 Kubernetes 工作负载类型,在 Kubernetes 集群中的每个节点上运行给定 Pod 的副本。使用这个 DaemonSet 控制器,我们将在集群中的每个节点上推出一个 Fluentd 日志代理 Pod。要了解有关此日志记录架构的更多信息,请参阅Kubernetes 官方文档中的使用节点日志记录代理”。

在 Kubernetes 中,容器化应用程序登录stdoutstderr捕获其日志流并重定向到节点上的 JSON 文件。Fluentd Pod 将跟踪这些日志文件、过滤日志事件、转换日志数据,并将其发送到我们在步骤 2 中部署的 Elasticsearch 日志后端

除了容器日志之外,Fluentd 代理还会跟踪 Kubernetes 系统组件日志,如 kubelet、kube-proxy 和 Docker 日志。要查看 Fluentd 日志代理跟踪的完整源列表,请查阅kubernetes.conf用于配置日志代理文件。要了解有关 Kubernetes 集群中日志记录的更多信息,请参阅Kubernetes 官方文档中的“节点级别的日志记录”。

首先打开一个fluentd.yaml在您喜欢的文本编辑器中调用的文件

  • nano fluentd.yaml

再一次,我们将逐块粘贴 Kubernetes 对象定义,并在进行过程中提供上下文。在本指南中,我们使用Fluentd 维护人员提供Fluentd DaemonSet 规范Fluentd 维护者提供的另一个有用资源是Kuberentes Fluentd

首先,粘贴以下 ServiceAccount 定义:

流利的.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: fluentd
  namespace: kube-logging
  labels:
    app: fluentd

在这里,我们创建了一个名为fluentdFluentd Pod 将用于访问 Kubernetes API的服务帐户我们在kube-loggingNamespace 中创建它并再次给它标签app: fluentd要了解有关 Kubernetes 中服务帐户的更多信息,请参阅官方 Kubernetes 文档中的Pod配置服务帐户

接下来,粘贴以下ClusterRole块:

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

在这里我们定义了一个名为ClusterRolefluentd这是我们授予getlist以及watch对权限podsnamespaces对象。ClusterRoles 允许您授予对集群范围的 Kubernetes 资源(如节点)的访问权限。要了解有关基于角色的访问控制和集群角色的更多信息,请参阅官方 Kubernetes 文档中的Using RBAC Authorization

现在,粘贴以下ClusterRoleBinding块:

流利的.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

在此块中,我们定义了一个ClusterRole绑定服务帐户ClusterRoleBinding调用这将授予ServiceAccount集群角色中列出的权限fluentdfluentdfluentdfluentdfluentd

此时我们可以开始粘贴实际的 DaemonSet 规范:

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

在这里,我们定义了一个fluentdkube-loggingNamespace 中调用的 DaemonSet并为其赋予app: 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

在这里,我们匹配中app: fluentd定义标签.metadata.labels,然后为 DaemonSet 分配fluentd服务帐户。我们还选择了app: fluentd作为此 DaemonSet 管理的 Pod。

接下来,我们定义一个NoSchedule容忍度以匹配 Kubernetes 主节点上的等效污点。这将确保 DaemonSet 也被推出到 Kubernetes 主节点。如果您不想在主节点上运行 Fluentd Pod,请删除此容忍度。要了解更多关于Kubernetes污点和tolerations,咨询“污点和Tolerations从官方Kubernetes文档”。

接下来,我们开始定义 Pod 容器,我们称之为fluentd.

我们使用Fluentd 维护者提供官方 v1.4.2 Debian 映像如果您想使用自己的私有或公共 Fluentd 镜像,或者使用不同的镜像版本,请修改image容器规范中标签。该镜像的 Dockerfile 和内容可在 Fluentd 的fluentd-kubernetes-daemonset Github repo 中找到

接下来,我们使用一些环境变量配置 Fluentd:

  • FLUENT_ELASTICSEARCH_HOST:我们将其设置为之前定义的 Elasticsearch headless Service 地址:elasticsearch.kube-logging.svc.cluster.local这将解析为 3 个 Elasticsearch Pod 的 IP 地址列表。实际的 Elasticsearch 主机很可能是此列表中返回的第一个 IP 地址。要跨集群分发日志,您需要修改 Fluentd 的 Elasticsearch Output 插件的配置。要了解有关此插件的更多信息,请参阅Elasticsearch 输出插件
  • FLUENT_ELASTICSEARCH_PORT:我们将其设置为我们之前配置的 Elasticsearch 端口,9200
  • FLUENT_ELASTICSEARCH_SCHEME: 我们将其设置为http.
  • FLUENTD_SYSTEMD_CONF:我们将其设置disable为抑制与systemd未在容器中设置相关的输出

最后,粘贴在以下部分:

流利的.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

这里我们在 FluentD Pod 上指定了 512 MiB 的内存限制,并保证它有 0.1vCPU 和 200MiB 的内存。您可以根据预期的日志量和可用资源调整这些资源限制和请求。

接下来,我们来挂载/var/log/var/lib/docker/containers主机路径到使用的容器varlogvarlibdockercontainers volumeMounts这些volumes是在块的末尾定义的。

我们在这个块中定义的最后一个参数是terminationGracePeriodSeconds,它让 Fluentd 在收到SIGTERM信号时有 30 秒的时间优雅地关闭30 秒后,向容器发送SIGKILL信号。的默认值为terminationGracePeriodSeconds30s,因此大多数情况下可以省略此参数。要了解有关优雅终止 Kubernetes 工作负载的更多信息,请参阅 Google 的“ Kubernetes 最佳实践:优雅终止”。

整个 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

完成 Fluentd DaemonSet 的配置后,保存并关闭文件。

现在,使用kubectl以下命令推出 DaemonSet

  • kubectl create -f fluentd.yaml

您应该看到以下输出:

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

使用以下命令验证您的 DaemonSet 是否成功推出kubectl

  • kubectl get ds --namespace=kube-logging

您应该看到以下状态输出:

Output
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE fluentd 3 3 3 3 3 <none> 58s

这表明有 3 个fluentdPod 正在运行,这与我们 Kubernetes 集群中的节点数相对应。

我们现在可以检查 Kibana 以验证日志数据是否被正确收集并传送到 Elasticsearch。

kubectl port-forward仍然打开的情况下,导航到http://localhost:5601

点击查看在左侧导航菜单:

Kibana 探索

您应该会看到以下配置窗口:

Kibana 索引模式配置

这允许您定义要在 Kibana 中探索的 Elasticsearch 索引。要了解更多信息,请参阅Kibana 官方文档中的定义索引模式现在,我们将只使用logstash-*通配符模式来捕获 Elasticsearch 集群中的所有日志数据。logstash-*在文本框中输入并单击下一步

然后,您将被带到以下页面:

Kibana 索引模式设置

这允许您配置 Kibana 将使用哪个字段按时间过滤日志数据。在下拉列表中,选择@timestamp字段,然后点击Create index pattern

现在,命中发现在左侧导航菜单。

您应该会看到一个直方图和一些最近的日志条目:

Kibana 传入日志

此时,您已在 Kubernetes 集群上成功配置并推出 EFK 堆栈。要了解如何使用 Kibana 分析日志数据,请参阅Kibana 用户指南

在下一个可选部分中,我们将部署一个简单的计数器 Pod,它将数字打印到标准输出,并在 Kibana 中找到它的日志。

第 5 步(可选)——测试容器日志

为了演示探索给定 Pod 的最新日志的基本 Kibana 用例,我们将部署一个最小计数器 Pod,它将序列号打印到标准输出。

让我们从创建 Pod 开始。打开一个counter.yaml在你喜欢的编辑器中调用的文件

  • nano counter.yaml

然后,粘贴以下 Pod 规范:

计数器.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']

保存并关闭文件。

这是一个最小的 Pod counter,它运行一个while循环,按顺序打印数字。

counter使用kubectl以下命令部署Pod

  • kubectl create -f counter.yaml

创建 Pod 并运行后,导航回您的 Kibana 仪表板。

在“发现”页面中,在搜索栏中输入kubernetes.pod_name:counter这会过滤名为 的 Pod 的日志数据counter

然后,您应该会看到counterPod的日志条目列表

Kibana 中的计数器日志

You can click into any of the log entries to see additional metadata like the container name, Kubernetes node, Namespace, and more.

Conclusion

In this guide we’ve demonstrated how to set up and configure Elasticsearch, Fluentd, and Kibana on a Kubernetes cluster. We’ve used a minimal logging architecture that consists of a single logging agent Pod running on each Kubernetes worker node.

Before deploying this logging stack into your production Kubernetes cluster, it’s best to tune the resource requirements and limits as indicated throughout this guide. You may also want to set up X-Pack to enable built-in monitoring and security features.

我们在这里使用的日志架构由 3 个 Elasticsearch Pod、一个 Kibana Pod(未负载平衡)和一组作为 DaemonSet 推出的 Fluentd Pod 组成。您可能希望根据您的生产用例扩展此设置。要了解有关扩展 Elasticsearch 和 Kibana 堆栈的更多信息,请参阅扩展 Elasticsearch

Kubernetes 还允许更复杂的日志代理架构,这些架构可能更适合您的用例。要了解更多信息,请参阅Kubernetes 文档中的日志架构

觉得文章有用?

点个广告表达一下你的爱意吧 !😁