如何在 Kubernetes 上建立 Elasticsearch、Fluentd 和 Kibana (EFK) 日志栈

介绍

在 Kubernetes 集群上运行多个服务和应用程序时,集中的集群级日志堆可以帮助您快速分类和分析您的 Pods 产生的大量日志数据。

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

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

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

首先,我们将配置和启动可扩展的 Elasticsearch 集群,然后创建 Kibana Kubernetes 服务和部署。

如果您正在寻找一个管理的Kubernetes托管服务,请查看我们的简单的,用于增长的管理的Kubernetes服务(https://www.digitalocean.com/products/kubernetes)。

前提条件

在您开始使用本指南之前,请确保您有以下内容:

  • 具有基于角色访问控制(RBAC)的 Kubernetes 1.10+ 集群启用
  • 确保您的集群具有足够的资源来部署 EFK 堆栈,如果不能通过添加工人节点来扩展您的集群,我们将部署一个 3-Pod Elasticsearch 集群(如果需要的话,您可以扩展到 1),以及一个单一的 Kibana Pod. 每个工人节点也将运行一个 Fluentd Pod. 本指南中的集群由 3 个工人节点和一个受管理的控制平面组成。

一旦您设置了这些组件,您已经准备好开始使用本指南。

步骤 1 - 创建一个名称空间

在我们部署一个 Elasticsearch 集群之前,我们首先将创建一个 Namespace,在其中我们将安装我们的所有日志仪器。Kubernetes 允许您使用名称空间的虚拟集群抽象来分离运行在您的集群中的对象。

首先,使用kubectl来调查群集中的现有名称空间:

1kubectl get namespaces

您应该看到以下三个初始名称空间,这些名称空间在您的 Kubernetes 集群中预先安装:

1[secondary_label Output]
2NAME STATUS AGE
3default Active 5m
4kube-system Active 5m
5kube-public Active 5m

默认 Namespace 包含没有指定 Namespace 创建的对象。 cube-system Namespace 包含 Kubernetes 系统创建和使用的对象,如 cube-dns, cube-proxykubernetes-dashboard

公用名称空间是另一个自动创建的名称空间,可用于存储您想要在整个群集中可读和可访问的对象,即使是未经身份验证的用户。

要创建cube-logging的名称空间,首先打开并编辑名为cube-logging.yaml的文件,使用您最喜欢的编辑器,如 nano:

1nano kube-logging.yaml

在您的编辑器中,粘贴以下的 Namespace 对象 YAML:

1[label kube-logging.yaml]
2kind: Namespace
3apiVersion: v1
4metadata:
5  name: kube-logging

然后保存并关闭文件。

在这里,我们将Kubernetes对象的类型指定为Namespace对象。 要了解有关Namespace对象的更多信息,请参阅官方Kubernetes文档中的Namespaces Walkthrough(https://kubernetes.io/docs/tasks/administer-cluster/namespaces-walkthrough/)。

一旦您创建了kube-logging.yaml Namespace 对象文件,请使用kubectl create 创建使用-f 文件名旗的 Namespace:

1kubectl create -f kube-logging.yaml

你应该看到以下结果:

1[secondary_label Output]
2namespace/kube-logging created

然后您可以确认 Namespace 已成功创建:

1kubectl get namespaces

在此时刻,你应该看到新的cube-logging名称空间:

1[secondary_label Output]
2NAME STATUS AGE
3default Active 23m
4kube-logging Active 1m
5kube-public Active 23m
6kube-system Active 23m

我们现在可以部署一个Elasticsearch集群到这个孤立的日志命名空间中。

步骤 2 — 创建 Elasticsearch StatefulSet

现在我们已经创建了一个名称空间来容纳我们的日志堆栈,我们可以开始部署它的各种组件。

在本指南中,我们使用 3 个 Elasticsearch Pods 来避免在高可用性多节点集群中出现的分裂大脑问题。在高层次上,分裂大脑是当一个或多个节点无法与其他节点进行通信时产生的,并选出多个分裂大师时产生的问题。在 3 个节点中,如果一个节点暂时脱离集群,其他两个节点可以选择一个新的大师,而集群可以继续运作,而最后一个节点试图重新连接。 有关更多信息,请参阅 Elasticsearch 中的集群协调的新时代和 [投票配置(INKL1))。

创造无头无头的服务

首先,我们将创建一个名为elasticsearch的无头Kubernetes服务,该服务将为3Pod定义一个DNS域。

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

1nano elasticsearch_svc.yaml

粘贴在以下Kubernetes服务YAML:

 1[label elasticsearch_svc.yaml]
 2kind: Service
 3apiVersion: v1
 4metadata:
 5  name: elasticsearch
 6  namespace: kube-logging
 7  labels:
 8    app: elasticsearch
 9spec:
10  selector:
11    app: elasticsearch
12  clusterIP: None
13  ports:
14    - port: 9200
15      name: rest
16    - port: 9300
17      name: inter-node

然后保存并关闭文件。

我们在cube-logging Namespace 中定义一个名为elasticsearch服务,并给它一个app:elasticsearch标签,然后将.spec.selector设置为app:elasticsearch,使服务选择具有app:elasticsearch标签的Pods。

最后,我们定义了用于与REST API互动的端口92009300,分别用于节点间通信。

使用kubectl创建服务:

1kubectl create -f elasticsearch_svc.yaml

你应该看到以下结果:

1[secondary_label Output]
2service/elasticsearch created

最后,双重检查是否使用kubectl get成功创建了服务:

1kubectl get services --namespace=kube-logging

你应该看到以下:

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

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

创建国家实体

Kubernetes StatefulSet 允许您将稳定的身份分配给 Pods,并赋予它们稳定的、持久的存储空间。Elasticsearch 需要稳定的存储空间,以便在整个 Pod 进行重新安排和重新启动。

在您最喜爱的编辑器中打开名为 elasticsearch_statefulset.yaml 的文件:

1nano elasticsearch_statefulset.yaml

我们将通过StatefulSet对象定义部分按部分移动,将块粘贴到这个文件中。

开始通过粘贴到以下块:

 1[label elasticsearch_statefulset.yaml]
 2apiVersion: apps/v1
 3kind: StatefulSet
 4metadata:
 5  name: es-cluster
 6  namespace: kube-logging
 7spec:
 8  serviceName: elasticsearch
 9  replicas: 3
10  selector:
11    matchLabels:
12      app: elasticsearch
13  template:
14    metadata:
15      labels:
16        app: elasticsearch

在这个区块中,我们在cube-logging名称空间中定义了一个名为es-cluster的StatefulSet,然后使用serviceName字段将其与我们先前创建的elasticsearch服务相关联。

我们指定3个复印件(Pods)并将matchLabels选项设置为app: elasticseach,然后在.spec.template.metadata部分中反映。

现在我们可以转到对象 spec. 粘贴在前一个区块下面的YAML下面的下一个区块:

 1[label elasticsearch_statefulset.yaml]
 2. . .
 3    spec:
 4      containers:
 5      - name: elasticsearch
 6        image: docker.elastic.co/elasticsearch/elasticsearch:7.2.0
 7        resources:
 8            limits:
 9              cpu: 1000m
10            requests:
11              cpu: 100m
12        ports:
13        - containerPort: 9200
14          name: rest
15          protocol: TCP
16        - containerPort: 9300
17          name: inter-node
18          protocol: TCP
19        volumeMounts:
20        - name: data
21          mountPath: /usr/share/elasticsearch/data
22        env:
23          - name: cluster.name
24            value: k8s-logs
25          - name: node.name
26            valueFrom:
27              fieldRef:
28                fieldPath: metadata.name
29          - name: discovery.seed_hosts
30            value: "es-cluster-0.elasticsearch,es-cluster-1.elasticsearch,es-cluster-2.elasticsearch"
31          - name: cluster.initial_master_nodes
32            value: "es-cluster-0,es-cluster-1,es-cluster-2"
33          - name: ES_JAVA_OPTS
34            value: "-Xms512m -Xmx512m"

在这里,我们在 StatefulSet 中定义了 Pods。我们将容器命名为elasticsearch,然后选择docker.elasticsearch.co/elasticsearch:7.2.0 Docker 图像。 在此时,您可以更改此图像标签以匹配您的内部 Elasticsearch 图像或其他版本。

然后,我们使用资源字段来指定容器需要至少保证 0.1 vCPU,并且可以爆发高达 1 vCPU(在执行初始大摄入或处理负载峰值时限制了Pod的资源使用)。

然后,我们分别打开并命名 REST API 和节点间通信的端口92009300。我们指定一个名为数据volumeMount,将名为数据的 PersistentVolume 安装在路径/usr/share/elasticsearch/data的容器上。

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

  • `集群.名': 弹性搜索集群的名称,在本指南中为"k8s-logs".
  • 节点. name: 节点名称,我们设定为.元数据。 这将决定s-croup-[0,1,2],取决于指定节点的正文。 *`发现.种子_宿主': 此字段会设置一个集群中可种子出节点发现过程的硕士合格节点列表. 在本指南中,由于我们先前配置的无头服务,我们的Pods有"es-cluster-[0, 1,2].elasticsearch.kube-logb.svc.croup.local"的域,因此我们据此设定了这个变量. 利用本地命名空间Kubernetes DNS分辨率,我们可以将它缩短为'es-cluster-[0, 1,2].elasticsearch'. 欲多了解弹性研究发现,请查阅官方弹性研究文件.
  • 集群。初始_master_节点: 本领域还具体列出参加总选举过程的合格主节点. 请注意,对于此字段,您应当用其node.name而不是其主机名来识别节点。 *ES_JAVA_OPTS':我们在此设定为-Xms512m-Xmx512m',它告诉JVM使用最小和最大堆积大小为512 MB. 您应该根据您的集群资源可用性和需求来调整这些参数 。 要多学多学,请参考确定堆积大小. (英语)

我们将粘贴的下一个块看起来如下:

 1[label elasticsearch_statefulset.yaml]
 2. . .
 3      initContainers:
 4      - name: fix-permissions
 5        image: busybox
 6        command: ["sh", "-c", "chown -R 1000:1000 /usr/share/elasticsearch/data"]
 7        securityContext:
 8          privileged: true
 9        volumeMounts:
10        - name: data
11          mountPath: /usr/share/elasticsearch/data
12      - name: increase-vm-max-map
13        image: busybox
14        command: ["sysctl", "-w", "vm.max_map_count=262144"]
15        securityContext:
16          privileged: true
17      - name: increase-fd-ulimit
18        image: busybox
19        command: ["sh", "-c", "ulimit -n 65536"]
20        securityContext:
21          privileged: true

在这个块中,我们定义了几种在主要的elasticsearch应用程序容器之前运行的Init容器,这些Init容器在它们定义的顺序下完成,以便了解更多有关Init容器的信息,请参阅官方的Kubernetes文件(https://kubernetes.io/docs/concepts/workloads/pods/init-containers/)。

第一个命名为fix-permissions,运行一个chown命令,将 Elasticsearch 数据目录的所有者和组更改为1000:1000,即 Elasticsearch 用户的 UID。

第二个命名为increase-vm-max-map,运行命令来增加操作系统对mmap数的限制,默认情况下可能太低,导致内存错误。

接下来要运行的 Init 容器是 increase-fd-ulimit,它运行了 ulimit 命令,以增加最大限度的开放文件描述器。

<$>[注] 注: Elasticsearch Notes for Production Use也提到了因性能原因而禁用交换。 根据您的 Kubernetes 安装或提供商,交换可能已经禁用了。 要检查这一点,‘exec’ 进入运行容器并运行‘cat /proc/swaps’ 列出活跃的交换设备。 如果您在那里看不到任何东西,交换将被禁用。

现在我们已经定义了我们的主要应用程序容器和运行在其前面的Init容器来调节容器操作系统,我们可以将最后一块添加到我们的StatefulSet对象定义文件中:volumeClaimTemplates。

粘贴到以下volumeClaimTemplate块中:

 1[label elasticsearch_statefulset.yaml]
 2. . .
 3  volumeClaimTemplates:
 4  - metadata:
 5      name: data
 6      labels:
 7        app: elasticsearch
 8    spec:
 9      accessModes: [ "ReadWriteOnce" ]
10      storageClassName: do-block-storage
11      resources:
12        requests:
13          storage: 100Gi

在这个块中,我们定义了StatefulSet的volumeClaimTemplates。Kubernetes将使用此来为Pods创建持久体积.在上面的块中,我们将其命名为data(这是我们在以前定义的volumeMount中所指的名称),并给它与我们的StatefulSet相同的app:elasticsearch标签。

然后我们将其访问模式指定为ReadWriteOnce,这意味着它只能通过单个节点进行读写装配。我们在本指南中将存储类定义为Do-block-storage,因为我们使用的是DigitalOcean Kubernetes集群用于演示目的。您应该根据您正在运行Kubernetes集群的位置改变此值。 有关更多信息,请参阅 持久卷文档。

最后,我们指明,我们希望每个 PersistentVolume 尺寸为 100GiB. 您应根据您的生产需求调整此值。

完整的StatefulSet spec应该看起来像这样:

 1[label elasticsearch_statefulset.yaml]
 2apiVersion: apps/v1
 3kind: StatefulSet
 4metadata:
 5  name: es-cluster
 6  namespace: kube-logging
 7spec:
 8  serviceName: elasticsearch
 9  replicas: 3
10  selector:
11    matchLabels:
12      app: elasticsearch
13  template:
14    metadata:
15      labels:
16        app: elasticsearch
17    spec:
18      containers:
19      - name: elasticsearch
20        image: docker.elastic.co/elasticsearch/elasticsearch:7.2.0
21        resources:
22            limits:
23              cpu: 1000m
24            requests:
25              cpu: 100m
26        ports:
27        - containerPort: 9200
28          name: rest
29          protocol: TCP
30        - containerPort: 9300
31          name: inter-node
32          protocol: TCP
33        volumeMounts:
34        - name: data
35          mountPath: /usr/share/elasticsearch/data
36        env:
37          - name: cluster.name
38            value: k8s-logs
39          - name: node.name
40            valueFrom:
41              fieldRef:
42                fieldPath: metadata.name
43          - name: discovery.seed_hosts
44            value: "es-cluster-0.elasticsearch,es-cluster-1.elasticsearch,es-cluster-2.elasticsearch"
45          - name: cluster.initial_master_nodes
46            value: "es-cluster-0,es-cluster-1,es-cluster-2"
47          - name: ES_JAVA_OPTS
48            value: "-Xms512m -Xmx512m"
49      initContainers:
50      - name: fix-permissions
51        image: busybox
52        command: ["sh", "-c", "chown -R 1000:1000 /usr/share/elasticsearch/data"]
53        securityContext:
54          privileged: true
55        volumeMounts:
56        - name: data
57          mountPath: /usr/share/elasticsearch/data
58      - name: increase-vm-max-map
59        image: busybox
60        command: ["sysctl", "-w", "vm.max_map_count=262144"]
61        securityContext:
62          privileged: true
63      - name: increase-fd-ulimit
64        image: busybox
65        command: ["sh", "-c", "ulimit -n 65536"]
66        securityContext:
67          privileged: true
68  volumeClaimTemplates:
69  - metadata:
70      name: data
71      labels:
72        app: elasticsearch
73    spec:
74      accessModes: [ "ReadWriteOnce" ]
75      storageClassName: do-block-storage
76      resources:
77        requests:
78          storage: 100Gi

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

现在,使用kubectl部署 StatefulSet:

1kubectl create -f elasticsearch_statefulset.yaml

你应该看到以下结果:

1[secondary_label Output]
2statefulset.apps/es-cluster created

您可以使用kubectl 部署状态来监控 StatefulSet 发布时:

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

您应该看到下面的输出,因为集群被滚动:

1[secondary_label Output]
2Waiting for 3 pods to be ready...
3Waiting for 2 pods to be ready...
4Waiting for 1 pods to be ready...
5partitioned roll out complete: 3 new pods have been updated...

一旦所有 Pods 都部署了,您可以通过对 REST API 执行请求来检查您的 Elasticsearch 集群是否正常运作。

要做到这一点,先将本地端口9200转发到Elasticsearch节点中的一个端口9200上(es-cluster-0)使用kubectl port-forward:

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

然后,在单独的终端窗口中,对 REST API 执行一个弯曲请求:

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

你应该看到以下结果:

 1[secondary_label Output]
 2{
 3  "cluster_name" : "k8s-logs",
 4  "compressed_size_in_bytes" : 348,
 5  "cluster_uuid" : "QD06dK7CQgids-GQZooNVw",
 6  "version" : 3,
 7  "state_uuid" : "mjNIWXAzQVuxNNOQ7xR-qg",
 8  "master_node" : "IdM5B7cUQWqFgIHXBp0JDg",
 9  "blocks" : { },
10  "nodes" : {
11    "u7DoTpMmSCixOoictzHItA" : {
12      "name" : "es-cluster-1",
13      "ephemeral_id" : "ZlBflnXKRMC4RvEACHIVdg",
14      "transport_address" : "10.244.8.2:9300",
15      "attributes" : { }
16    },
17    "IdM5B7cUQWqFgIHXBp0JDg" : {
18      "name" : "es-cluster-0",
19      "ephemeral_id" : "JTk1FDdFQuWbSFAtBxdxAQ",
20      "transport_address" : "10.244.44.3:9300",
21      "attributes" : { }
22    },
23    "R8E7xcSUSbGbgrhAdyAKmQ" : {
24      "name" : "es-cluster-2",
25      "ephemeral_id" : "9wv6ke71Qqy9vk2LgJTqaA",
26      "transport_address" : "10.244.40.4:9300",
27      "attributes" : { }
28    }
29  },
30...

这表明我们的Elasticsearch集群k8s-logs已成功创建了3个节点:es-cluster-0,es-cluster-1es-cluster-2

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

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

要在 Kubernetes 上启动 Kibana,我们将创建一个名为kibana的服务,以及一个由一个Pod复制件组成的部署。

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

1nano kibana.yaml

点击下面的服务spec:

 1[label kibana.yaml]
 2apiVersion: v1
 3kind: Service
 4metadata:
 5  name: kibana
 6  namespace: kube-logging
 7  labels:
 8    app: kibana
 9spec:
10  ports:
11  - port: 5601
12  selector:
13    app: kibana
14---
15apiVersion: apps/v1
16kind: Deployment
17metadata:
18  name: kibana
19  namespace: kube-logging
20  labels:
21    app: kibana
22spec:
23  replicas: 1
24  selector:
25    matchLabels:
26      app: kibana
27  template:
28    metadata:
29      labels:
30        app: kibana
31    spec:
32      containers:
33      - name: kibana
34        image: docker.elastic.co/kibana/kibana:7.2.0
35        resources:
36          limits:
37            cpu: 1000m
38          requests:
39            cpu: 100m
40        env:
41          - name: ELASTICSEARCH_URL
42            value: http://elasticsearch:9200
43        ports:
44        - containerPort: 5601

然后保存并关闭文件。

在这个规格中,我们在cube-logging名称空间中定义了一个名为kibana的服务,并给了它app: kibana标签。

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

部署规格中,我们定义了一个名为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 Pods 的 IP 地址列表。 有关 Kubernetes DNS 的更多信息,请参阅 DNS for Services and Pods

最后,我们将Kibana的集装箱端口设置为5601,Kibana服务将向其发送请求。

一旦你对Kibana配置感到满意,你可以使用kubectl启动服务和部署:

1kubectl create -f kibana.yaml

你应该看到以下结果:

1[secondary_label Output]
2service/kibana created
3deployment.apps/kibana created

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

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

你应该看到以下结果:

1[secondary_label Output]
2deployment "kibana" successfully rolled out

要访问Kibana接口,我们将再次将本地端口转发到运行Kibana的Kubernetes节点。

1kubectl get pods --namespace=kube-logging
1[secondary_label Output]
2NAME READY STATUS RESTARTS AGE
3es-cluster-0 1/1 Running 0 55m
4es-cluster-1 1/1 Running 0 54m
5es-cluster-2 1/1 Running 0 54m
6kibana-6c9fb4b5b7-plbg2 1/1 Running 0 4m27s

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

将本地端口5601向本地端口5601前进:

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

你应该看到以下结果:

1[secondary_label Output]
2Forwarding from 127.0.0.1:5601 -> 5601
3Forwarding from [::1]:5601 -> 5601

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

1http://localhost:5601

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

Kibana Welcome Screen

现在您可以继续部署EFK堆栈的最终组件:日志收集器,Fluentd。

步骤 4 — 创建 Fluentd DaemonSet

在本指南中,我们将将Fluentd设置为DaemonSet,这是一种Kubernetes工作负载类型,在Kubernetes集群中的每个节点上运行一个特定的Pod副本。 使用这个DaemonSet控制器,我们将在集群中的每个节点上部署一个Fluentd日志代理Pod。 有关此日志架构的更多信息,请参阅[使用节点日志代理](https://kubernetes.io/docs/concepts/cluster-administration/logging/#using-a-node-logging-agent)从官方Kubernetes文件中。

在Kubernetes中,登录到stdoutstderr的容器化应用程序将其日志流捕获并重定向到节点上的JSON文件中。

除了集装箱日志外,Fluentd 代理还会跟踪 Kubernetes 系统组件日志,如 kubelet、kube-proxy 和 Docker 日志. 若要查看由 Fluentd 日志代理收集的完整来源列表,请参阅用于配置日志代理的 kubernetes.conf 文件。

首先,在您最喜欢的文本编辑器中打开名为 'fluentd.yaml' 的文件:

1nano fluentd.yaml

在本指南中,我们将使用由 Fluentd 维护者提供的 Fluentd DaemonSet spec

首先,粘贴下面的 ServiceAccount 定义:

1[label fluentd.yaml]
2apiVersion: v1
3kind: ServiceAccount
4metadata:
5  name: fluentd
6  namespace: kube-logging
7  labels:
8    app: fluentd

在这里,我们创建了一个名为fluentd的服务帐户,Fluentd Pods 将使用它来访问 Kubernetes API. 我们在cube-logging Namespace 中创建一个服务帐户,并再次标记为app: fluentd

接下来,粘贴到以下ClusterRole块:

 1[label fluentd.yaml]
 2. . .
 3---
 4apiVersion: rbac.authorization.k8s.io/v1
 5kind: ClusterRole
 6metadata:
 7  name: fluentd
 8  labels:
 9    app: fluentd
10rules:
11- apiGroups:
12  - ""
13  resources:
14  - pods
15  - namespaces
16  verbs:
17  - get
18  - list
19  - watch

在这里,我们定义了一个名为fluentd的集群角色,我们在podsnamespaces对象上授予getlistwatch权限。集群角色允许您授予访问集群覆盖的Kubernetes资源,如节点。

现在,粘贴到以下ClusterRoleBinding块:

 1[label fluentd.yaml]
 2. . .
 3---
 4kind: ClusterRoleBinding
 5apiVersion: rbac.authorization.k8s.io/v1
 6metadata:
 7  name: fluentd
 8roleRef:
 9  kind: ClusterRole
10  name: fluentd
11  apiGroup: rbac.authorization.k8s.io
12subjects:
13- kind: ServiceAccount
14  name: fluentd
15  namespace: kube-logging

在这个区块中,我们定义了一个名为fluentdClusterRoleBinding,该区块将fluentd ClusterRole 绑定到fluentd服务帐户,从而授予fluentd服务帐户在fluentd集群角色中列出的权限。

在此时刻,我们可以开始粘贴实际的DaemonSet spec:

 1[label fluentd.yaml]
 2. . .
 3---
 4apiVersion: apps/v1
 5kind: DaemonSet
 6metadata:
 7  name: fluentd
 8  namespace: kube-logging
 9  labels:
10    app: fluentd

在这里,我们在cube-logging名称空间中定义一个名为fluentd的DaemonSet,并给它一个app:fluentd标签。

接下来,粘贴在下一节:

 1[label fluentd.yaml]
 2. . .
 3spec:
 4  selector:
 5    matchLabels:
 6      app: fluentd
 7  template:
 8    metadata:
 9      labels:
10        app: fluentd
11    spec:
12      serviceAccount: fluentd
13      serviceAccountName: fluentd
14      tolerations:
15      - key: node-role.kubernetes.io/master
16        effect: NoSchedule
17      containers:
18      - name: fluentd
19        image: fluent/fluentd-kubernetes-daemonset:v1.4.2-debian-elasticsearch-1.1
20        env:
21          - name:  FLUENT_ELASTICSEARCH_HOST
22            value: "elasticsearch.kube-logging.svc.cluster.local"
23          - name:  FLUENT_ELASTICSEARCH_PORT
24            value: "9200"
25          - name: FLUENT_ELASTICSEARCH_SCHEME
26            value: "http"
27          - name: FLUENTD_SYSTEMD_CONF
28            value: disable

在这里,我们匹配在.metadata.labels中定义的app:fluentd标签,然后将fluentd服务帐户分配给DaemonSet。

接下来,我们将定义一个NoSchedule容量,以匹配Kubernetes主节点上的相应污点。这将确保DaemonSet也被部署到Kubernetes主节点上。如果您不想在主节点上运行Fluentd Pod,请删除此容量。 有关Kubernetes污点和容量的更多信息,请从官方Kubernetes文件中查看[色彩和容量](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/)

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

如果您想使用自己的私人或公共Fluentd图像,或使用不同的图像版本,请在容器规格中修改图像标签。

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

  • FLUENT_ELASTICSEARCH_HOST:我们将此设置为先前定义的 Elasticsearch 无头服务地址: elasticsearch.kube-logging.svc.cluster.local. 这将解决为 3 个 Elasticsearch Pods 的 IP 地址列表。 实际 Elasticsearch 主机很可能会是本列表中返回的第一个 IP 地址。 要在集群中分发日志,您需要更改 Fluentd 的 Elasticsearch 输出插件的配置。 要了解更多有关该插件的信息,请参阅 Elasticsearch Output Plugin
  • FLUENT_ELASTICSEARCH_PORT:我们将此

最后,粘贴在以下部分:

 1[label fluentd.yaml]
 2. . .
 3        resources:
 4          limits:
 5            memory: 512Mi
 6          requests:
 7            cpu: 100m
 8            memory: 200Mi
 9        volumeMounts:
10        - name: varlog
11          mountPath: /var/log
12        - name: varlibdockercontainers
13          mountPath: /var/lib/docker/containers
14          readOnly: true
15      terminationGracePeriodSeconds: 30
16      volumes:
17      - name: varlog
18        hostPath:
19          path: /var/log
20      - name: varlibdockercontainers
21        hostPath:
22          path: /var/lib/docker/containers

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

接下来,我们将/var/log/var/lib/docker/containers的主机路径安装到容器中,使用varlogvarlibdockercontainers``volumeMounts

我们在这个块中定义的最后参数是terminationGracePeriodSeconds,在收到SIGTERM信号后,Fluentd 可在 30 秒内优雅关闭。30 秒后,容器会发送一个SIGKILL信号。对于terminationGracePeriodSeconds的默认值为 30 秒,所以在大多数情况下,该参数可以被省略。 有关优雅终止 Kubernetes 工作负载的更多信息,请参阅 Google 的[Kubernetes 最佳实践:终止与 Grace](https://cloud.google.com/blog/products/gcp/kubernetes-best-practices-terminating-with-grace)

整个 Fluentd 规格应该看起来像这样:

 1[label fluentd.yaml]
 2apiVersion: v1
 3kind: ServiceAccount
 4metadata:
 5  name: fluentd
 6  namespace: kube-logging
 7  labels:
 8    app: fluentd
 9---
10apiVersion: rbac.authorization.k8s.io/v1
11kind: ClusterRole
12metadata:
13  name: fluentd
14  labels:
15    app: fluentd
16rules:
17- apiGroups:
18  - ""
19  resources:
20  - pods
21  - namespaces
22  verbs:
23  - get
24  - list
25  - watch
26---
27kind: ClusterRoleBinding
28apiVersion: rbac.authorization.k8s.io/v1
29metadata:
30  name: fluentd
31roleRef:
32  kind: ClusterRole
33  name: fluentd
34  apiGroup: rbac.authorization.k8s.io
35subjects:
36- kind: ServiceAccount
37  name: fluentd
38  namespace: kube-logging
39---
40apiVersion: apps/v1
41kind: DaemonSet
42metadata:
43  name: fluentd
44  namespace: kube-logging
45  labels:
46    app: fluentd
47spec:
48  selector:
49    matchLabels:
50      app: fluentd
51  template:
52    metadata:
53      labels:
54        app: fluentd
55    spec:
56      serviceAccount: fluentd
57      serviceAccountName: fluentd
58      tolerations:
59      - key: node-role.kubernetes.io/master
60        effect: NoSchedule
61      containers:
62      - name: fluentd
63        image: fluent/fluentd-kubernetes-daemonset:v1.4.2-debian-elasticsearch-1.1
64        env:
65          - name:  FLUENT_ELASTICSEARCH_HOST
66            value: "elasticsearch.kube-logging.svc.cluster.local"
67          - name:  FLUENT_ELASTICSEARCH_PORT
68            value: "9200"
69          - name: FLUENT_ELASTICSEARCH_SCHEME
70            value: "http"
71          - name: FLUENTD_SYSTEMD_CONF
72            value: disable
73        resources:
74          limits:
75            memory: 512Mi
76          requests:
77            cpu: 100m
78            memory: 200Mi
79        volumeMounts:
80        - name: varlog
81          mountPath: /var/log
82        - name: varlibdockercontainers
83          mountPath: /var/lib/docker/containers
84          readOnly: true
85      terminationGracePeriodSeconds: 30
86      volumes:
87      - name: varlog
88        hostPath:
89          path: /var/log
90      - name: varlibdockercontainers
91        hostPath:
92          path: /var/lib/docker/containers

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

现在,使用kubectl来滚动DaemonSet:

1kubectl create -f fluentd.yaml

你应该看到以下结果:

1[secondary_label Output]
2serviceaccount/fluentd created
3clusterrole.rbac.authorization.k8s.io/fluentd created
4clusterrolebinding.rbac.authorization.k8s.io/fluentd created
5daemonset.extensions/fluentd created

確認您的 DaemonSet 是否使用「kubectl」成功部署:

1kubectl get ds --namespace=kube-logging

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

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

这表明有3个流动的Pod运行,这相当于我们Kubernetes集群中的节点数量。

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

隨著「kubectl 端口前進」仍然開放,前往「http://localhost:5601」。

在左边的导航菜单中点击 Discover:

Kibana Discover

您应该看到下面的配置窗口:

Kibana Index Pattern Configuration

这允许您定义您想在Kibana中探索的Elasticsearch索引。 要了解更多信息,请参阅Defining your index patterns在官方的Kibana文件中。 目前,我们只需使用logstash-* wildcard 模式来捕捉我们Elasticsearch集群中的所有日志数据。 在文本框中输入logstash-*并点击Next step**

然后你会被带到以下页面:

Kibana Index Pattern Settings

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

现在,在左手导航菜单中点击发现

您应该看到一个 histogram 图表和一些最近的日志条目:

Kibana Incoming Logs

此时,您已成功配置并在您的 Kubernetes 集群中部署 EFK 堆栈. 若要了解如何使用 Kibana 来分析您的日志数据,请参阅 Kibana 用户指南

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

步骤 5 (可选) — 测试集装箱仓库

为了演示一个基本的Kibana使用案例,探索给定的Pod的最新日志,我们将部署一个最小的计数Pod,以打印序列数字。

让我们从创建Pod开始,在你最喜欢的编辑器中打开名为counter.yaml的文件:

1nano counter.yaml

然后,粘贴在以下Pod spec中:

 1[label counter.yaml]
 2apiVersion: v1
 3kind: Pod
 4metadata:
 5  name: counter
 6spec:
 7  containers:
 8  - name: count
 9    image: busybox
10    args: [/bin/sh, -c,
11            'i=0; while true; do echo "$i: $(date)"; i=$((i+1)); sleep 1; done']

保存并关闭文件。

这是一个名为计数的最小Pod,运行一个同时循环,连续打印数字。

使用kubectl部署反对Pod:

1kubectl create -f counter.yaml

一旦 Pod 创建并运行,请返回您的 Kibana 仪表板。

发现页面,在搜索栏中输入kubernetes.pod_name:counter

然后你应该看到一个列表的日志列表的反对Pod:

Counter Logs in Kibana

您可以点击任何日志条目以查看其他元数据,如容器名称、Kubernetes节点、Nameespace 等。

结论

在本指南中,我们展示了如何在 Kubernetes 集群上设置和配置 Elasticsearch、Fluentd 和 Kibana。

在部署此日志堆栈到您的 Kubernetes 生产集群之前,最好根据本指南所示调整资源要求和限制,您还可能想要设置 X-Pack以启用内置的监控和安全功能。

我们在这里使用的日志架构包括3个Elasticsearch Pod,一个单一的Kibana Pod(不负荷均衡),以及一组作为DaemonSet部署的Fluentd Pod。

Kubernetes 还允许更复杂的记录代理架构,这些架构可能更适合您的使用情况。 有关更多信息,请参阅 Kubernetes 文档中的 Logging Architecture

Published At
Categories with 技术
comments powered by Disqus