kubernetes部署EFK(k8s)
<p>Kubernetes 中比较流行的日志收集解决方案是 <code>Elasticsearch</code>、<code>Fluentd</code> 和 <code>Kibana</code>(EFK)技术栈,也是官方现在比较推荐的一种方案。</p><p><code>Elasticsearch</code> 是一个实时的、分布式的可扩展的搜索引擎,允许进行全文、结构化搜索,它通常用于索引和搜索大量日志数据,也可用于搜索许多不同类型的文档。</p>
<p>Elasticsearch 通常与 <code>Kibana</code> 一起部署,Kibana 是 Elasticsearch 的一个功能强大的数据可视化 Dashboard,Kibana 允许你通过 web 界面来浏览 Elasticsearch 日志数据。</p>
<p><code>Fluentd</code>是一个流行的开源数据收集器,我们将在 Kubernetes 集群节点上安装 Fluentd,通过获取容器日志文件、过滤和转换日志数据,然后将数据传递到 Elasticsearch 集群,在该集群中对其进行索引和存储。</p>
<p>我们先来配置启动一个可扩展的 Elasticsearch 集群,然后在 Kubernetes 集群中创建一个 Kibana 应用,最后通过 DaemonSet 来运行 Fluentd,以便它在每个 Kubernetes 工作节点上都可以运行一个 Pod。</p>
<p>1、<strong>创建 Elasticsearch 集群</strong></p>
<p>在创建 Elasticsearch 集群之前,我们先创建一个命名空间,我们将在其中安装所有日志相关的资源对象。</p>
<p>新建一个 kube-logging.yaml 文件</p>
<div class="cnblogs_code">
<pre>vim kube-<span style="color: rgba(0, 0, 0, 1)">logging.yaml
apiVersion: v1
kind: Namespace
metadata:
name: logging<br></span></pre>
<pre>$ kubectl create -f kube-logging.yaml
namespace/logging created
$ kubectl get ns
NAME STATUS AGE
default Active 244d
istio-system Active 100d
kube-ops Active 179d
kube-public Active 244d
kube-system Active 244d
logging Active 4h
monitoring Active 35d</pre>
</div>
<p>现在创建了一个命名空间来存放我们的日志相关资源,接下来可以部署 EFK 相关组件,首先开始部署一个3节点的 Elasticsearch 集群。</p>
<p>一个关键点是我们应该设置参数<code>discover.zen.minimum_master_nodes=N/2+1</code>,其中<code>N</code>是 Elasticsearch 集群中符合主节点的节点数,比如我们这里3个节点,意味着<code>N</code>应该设置为2。这样,如果一个节点暂时与集群断开连接,则另外两个节点可以选择一个新的主节点,并且集群可以在最后一个节点尝试重新加入时继续运行,在扩展 Elasticsearch 集群时,一定要记住这个参数。</p>
<p>首先创建一个名为 elasticsearch 的无头服务,新建文件 elasticsearch-svc.yaml,文件内容如下</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">kind: Service
apiVersion: v1
metadata:
name: elasticsearch
</span><span style="color: rgba(0, 0, 255, 1)">namespace</span><span style="color: rgba(0, 0, 0, 1)">: logging
labels:
app: elasticsearch
spec:
selector:
app: elasticsearch
clusterIP: None
ports:
</span>- port: <span style="color: rgba(128, 0, 128, 1)">9200</span><span style="color: rgba(0, 0, 0, 1)">
name: rest
</span>- port: <span style="color: rgba(128, 0, 128, 1)">9300</span><span style="color: rgba(0, 0, 0, 1)">
name: inter</span>-<span style="color: rgba(0, 0, 0, 1)">node
$ kubectl create </span>-f elasticsearch-<span style="color: rgba(0, 0, 0, 1)">svc.yaml
service</span>/<span style="color: rgba(0, 0, 0, 1)">elasticsearch created
$ kubectl </span><span style="color: rgba(0, 0, 255, 1)">get</span> services --<span style="color: rgba(0, 0, 255, 1)">namespace</span>=<span style="color: rgba(0, 0, 0, 1)">logging
Output
NAME TYPE CLUSTER</span>-IP EXTERNAL-<span style="color: rgba(0, 0, 0, 1)">IP PORT(S) AGE
elasticsearch ClusterIP None </span><none> <span style="color: rgba(128, 0, 128, 1)">9200</span>/TCP,<span style="color: rgba(128, 0, 128, 1)">9300</span>/TCP 26s</pre>
</div>
<p>定义了一个名为 elasticsearch 的 Service,指定标签 <code>app=elasticsearch</code>,当我们将 Elasticsearch StatefulSet 与此服务关联时,服务将返回带有标签 <code>app=elasticsearch</code>的 Elasticsearch Pods 的 DNS A 记录,然后设置 <code>clusterIP=None</code>,将该服务设置成无头服务。最后,我们分别定义端口9200、9300,分别用于与 REST API 交互,以及用于节点间通信。</p>
<p>现在我们已经为 Pod 设置了无头服务和一个稳定的域名<code>.elasticsearch.logging.svc.cluster.local</code>,接下来我们通过 StatefulSet 来创建具体的 Elasticsearch 的 Pod 应用。</p>
<p>Kubernetes StatefulSet 允许我们为 Pod 分配一个稳定的标识和持久化存储,Elasticsearch 需要稳定的存储来保证 Pod 在重新调度或者重启后的数据依然不变,所以需要使用 StatefulSet 来管理 Pod。</p>
<p>新建名为 elasticsearch-statefulset.yaml 的资源清单文件,首先粘贴下面内容:</p>
<div class="cnblogs_code">
<pre>apiVersion: apps/<span style="color: rgba(0, 0, 0, 1)">v1
kind: StatefulSet
metadata:
name: es
</span><span style="color: rgba(0, 0, 255, 1)">namespace</span><span style="color: rgba(0, 0, 0, 1)">: logging
spec:
serviceName: elasticsearch
replicas: </span><span style="color: rgba(128, 0, 128, 1)">3</span><span style="color: rgba(0, 0, 0, 1)">
selector:
matchLabels:
app: elasticsearch
template:
metadata:
labels:
app: elasticsearch
spec:
nodeSelector:
es: log
initContainers:
</span>- name: increase-vm-max-<span style="color: rgba(0, 0, 0, 1)">map
image: busybox
command: [</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">sysctl</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">-w</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">vm.max_map_count=262144</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">]
securityContext:
privileged: </span><span style="color: rgba(0, 0, 255, 1)">true</span>
- name: increase-fd-<span style="color: rgba(0, 0, 0, 1)">ulimit
image: busybox
command: [</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">sh</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">-c</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">ulimit -n 65536</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">]
securityContext:
privileged: </span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">
containers:
</span>-<span style="color: rgba(0, 0, 0, 1)"> name: elasticsearch
image: docker.elastic.co</span>/elasticsearch/elasticsearch:<span style="color: rgba(128, 0, 128, 1)">7.6</span>.<span style="color: rgba(128, 0, 128, 1)">2</span><span style="color: rgba(0, 0, 0, 1)">
ports:
</span>-<span style="color: rgba(0, 0, 0, 1)"> name: rest
containerPort: </span><span style="color: rgba(128, 0, 128, 1)">9200</span>
-<span style="color: rgba(0, 0, 0, 1)"> name: inter
containerPort: </span><span style="color: rgba(128, 0, 128, 1)">9300</span><span style="color: rgba(0, 0, 0, 1)">
resources:
limits:
cpu: 1000m
requests:
cpu: 1000m
volumeMounts:
</span>-<span style="color: rgba(0, 0, 0, 1)"> name: data
mountPath: </span>/usr/share/elasticsearch/<span style="color: rgba(0, 0, 0, 1)">data
env:
</span>-<span style="color: rgba(0, 0, 0, 1)"> name: cluster.name
value: k8s</span>-<span style="color: rgba(0, 0, 0, 1)">logs
</span>-<span style="color: rgba(0, 0, 0, 1)"> name: node.name
valueFrom:
fieldRef:
fieldPath: metadata.name
</span>-<span style="color: rgba(0, 0, 0, 1)"> name: cluster.initial_master_nodes
value: </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">es-0,es-1,es-2</span><span style="color: rgba(128, 0, 0, 1)">"</span>
-<span style="color: rgba(0, 0, 0, 1)"> name: discovery.zen.minimum_master_nodes
value: </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">2</span><span style="color: rgba(128, 0, 0, 1)">"</span>
-<span style="color: rgba(0, 0, 0, 1)"> name: discovery.seed_hosts
value: </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">elasticsearch</span><span style="color: rgba(128, 0, 0, 1)">"</span>
-<span style="color: rgba(0, 0, 0, 1)"> name: ES_JAVA_OPTS
value: </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">-Xms512m -Xmx512m</span><span style="color: rgba(128, 0, 0, 1)">"</span>
-<span style="color: rgba(0, 0, 0, 1)"> name: network.host
value: </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">0.0.0.0</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
volumeClaimTemplates:
</span>-<span style="color: rgba(0, 0, 0, 1)"> metadata:
name: data
labels:
app: elasticsearch
spec:
accessModes: [ </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">ReadWriteOnce</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)"> ]
storageClassName: rook</span>-ceph-<span style="color: rgba(0, 0, 0, 1)">block
resources:
requests:
storage: 50Gi
$ kubectl create </span>-f elasticsearch-<span style="color: rgba(0, 0, 0, 1)">statefulset.yaml
statefulset.apps</span>/<span style="color: rgba(0, 0, 0, 1)">es created
$ kubectl </span><span style="color: rgba(0, 0, 255, 1)">get</span> sts -<span style="color: rgba(0, 0, 0, 1)">n logging
NAME READY AGE
es </span><span style="color: rgba(128, 0, 128, 1)">3</span>/<span style="color: rgba(128, 0, 128, 1)">3</span><span style="color: rgba(0, 0, 0, 1)"> 83m
$ kubectl </span><span style="color: rgba(0, 0, 255, 1)">get</span> pods -<span style="color: rgba(0, 0, 0, 1)">n logging
NAME READY STATUS RESTARTS AGE
es</span>-<span style="color: rgba(128, 0, 128, 1)">0</span> <span style="color: rgba(128, 0, 128, 1)">1</span>/<span style="color: rgba(128, 0, 128, 1)">1</span> Running <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)"> 83m
es</span>-<span style="color: rgba(128, 0, 128, 1)">1</span> <span style="color: rgba(128, 0, 128, 1)">1</span>/<span style="color: rgba(128, 0, 128, 1)">1</span> Running <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)"> 82m
es</span>-<span style="color: rgba(128, 0, 128, 1)">2</span> <span style="color: rgba(128, 0, 128, 1)">1</span>/<span style="color: rgba(128, 0, 128, 1)">1</span> Running <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)"> 81m
$ kubectl </span><span style="color: rgba(0, 0, 255, 1)">get</span> svc -<span style="color: rgba(0, 0, 0, 1)">n logging
NAME TYPE CLUSTER</span>-IP EXTERNAL-<span style="color: rgba(0, 0, 0, 1)">IP PORT(S) AGE
elasticsearch ClusterIP None </span><none> <span style="color: rgba(128, 0, 128, 1)">9200</span>/TCP,<span style="color: rgba(128, 0, 128, 1)">9300</span>/TCP 20h</pre>
</div>
<p>该部分是定义 StatefulSet 中的 Pod,暴露了9200和9300两个端口,注意名称要和上面定义的 Service 保持一致。然后通过 volumeMount 声明了数据持久化目录,下面我们再来定义 VolumeClaims。最后就是我们在容器中设置的一些环境变量了:</p>
<ul>
<li>cluster.name:Elasticsearch 集群的名称,我们这里命名成 k8s-logs。</li>
<li>node.name:节点的名称,通过 <code>metadata.name</code> 来获取。这将解析为 es-,取决于节点的指定顺序。</li>
<li>discovery.seed_hosts:此字段用于设置在 Elasticsearch 集群中节点相互连接的发现方法。由于我们之前配置的无头服务,我们的 Pod 具有唯一的 DNS 域<code>es-.elasticsearch.logging.svc.cluster.local</code>,因此我们相应地设置此变量。要了解有关 Elasticsearch 发现的更多信息,请参阅 Elasticsearch 官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-discovery.html。</li>
<li>discovery.zen.minimum_master_nodes:我们将其设置为<code>(N/2) + 1</code>,<code>N</code>是我们的群集中符合主节点的节点的数量。我们有3个 Elasticsearch 节点,因此我们将此值设置为2(向下舍入到最接近的整数)。要了解有关此参数的更多信息,请参阅官方 Elasticsearch 文档:https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-node.html#split-brain。</li>
<li>ES_JAVA_OPTS:这里我们设置为<code>-Xms512m -Xmx512m</code>,告诉<code>JVM</code>使用<code>512 MB</code>的最小和最大堆。您应该根据群集的资源可用性和需求调整这些参数。要了解更多信息,请参阅设置堆大小的相关文档:https://www.elastic.co/guide/en/elasticsearch/reference/current/heap-size.html。</li>
</ul>
<p>这里我们定义了几个在主应用程序之前运行的 Init 容器,这些初始容器按照定义的顺序依次执行,执行完成后才会启动主应用容器。</p>
<p>第一个名为 increase-vm-max-map 的容器用来增加操作系统对<code>mmap</code>计数的限制,默认情况下该值可能太低,导致内存不足的错误,要了解更多关于该设置的信息,可以查看 Elasticsearch 官方文档说明:https://www.elastic.co/guide/en/elasticsearch/reference/current/vm-max-map-count.html。</p>
<p>最后一个初始化容器是用来执行<code>ulimit</code>命令增加打开文件描述符的最大数量的。</p>
<blockquote>此外 Elastisearch Notes for Production Use 文档还提到了由于性能原因最好禁用 swap,当然对于 Kubernetes 集群而言,最好也是禁用 swap 分区的。</blockquote>
<p>我们这里使用 volumeClaimTemplates 来定义持久化模板,Kubernetes 会使用它为 Pod 创建 PersistentVolume,设置访问模式为<code>ReadWriteOnce</code>,这意味着它只能被 mount 到单个节点上进行读写,然后最重要的是使用了一个 StorageClass 对象,这里我们就直接使用前面创建的 NFS 类型 的 StorageClass 对象即可。最后,我们指定了每个 PersistentVolume 的大小为 50GB,我们可以根据自己的实际需要进行调整该值。</p>
<p>Pods 部署完成后,我们可以通过请求一个 REST API 来检查 Elasticsearch 集群是否正常运行。使用下面的命令将本地端口9200 转发到 Elasticsearch 节点(如es-0)对应的端口:</p>
<div class="cnblogs_code">
<pre>$ kubectl port-forward es-<span style="color: rgba(128, 0, 128, 1)">0</span> <span style="color: rgba(128, 0, 128, 1)">9200</span>:<span style="color: rgba(128, 0, 128, 1)">9200</span> --<span style="color: rgba(0, 0, 255, 1)">namespace</span>=<span style="color: rgba(0, 0, 0, 1)">logging
Forwarding </span><span style="color: rgba(0, 0, 255, 1)">from</span> <span style="color: rgba(128, 0, 128, 1)">127.0</span>.<span style="color: rgba(128, 0, 128, 1)">0.1</span>:<span style="color: rgba(128, 0, 128, 1)">9200</span> -> <span style="color: rgba(128, 0, 128, 1)">9200</span><span style="color: rgba(0, 0, 0, 1)">
Forwarding </span><span style="color: rgba(0, 0, 255, 1)">from</span> [::<span style="color: rgba(128, 0, 128, 1)">1</span>]:<span style="color: rgba(128, 0, 128, 1)">9200</span> -> <span style="color: rgba(128, 0, 128, 1)">9200<br></span></pre>
<p> #然后,在另外的终端窗口中,执行如下请求:</p>
<pre>curl http://localhost:9200/_cluster/state?pretty</pre>
<pre><span style="color: rgba(128, 0, 128, 1)">#当然也可不用这个方式直接进入到容器内执行</span></pre>
</div>
<p>看到上面的信息就表明我们名为 k8s-logs 的 Elasticsearch 集群成功创建了3个节点:es-0,es-1,和es-2,当前主节点是 es-0。</p>
<p>2、<strong>创建 Kibana 服务</strong></p>
<p>Elasticsearch 集群启动成功了,接下来我们可以来部署 Kibana 服务,新建一个名为 kibana.yaml 的文件,对应的文件内容如下:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">apiVersion: v1
kind: Service
metadata:
name: kibana
</span><span style="color: rgba(0, 0, 255, 1)">namespace</span><span style="color: rgba(0, 0, 0, 1)">: logging
labels:
app: kibana
spec:
ports:
</span>- port: <span style="color: rgba(128, 0, 128, 1)">5601</span><span style="color: rgba(0, 0, 0, 1)">
type: NodePort
selector:
app: kibana
</span>---<span style="color: rgba(0, 0, 0, 1)">
apiVersion: apps</span>/<span style="color: rgba(0, 0, 0, 1)">v1
kind: Deployment
metadata:
name: kibana
</span><span style="color: rgba(0, 0, 255, 1)">namespace</span><span style="color: rgba(0, 0, 0, 1)">: logging
labels:
app: kibana
spec:
selector:
matchLabels:
app: kibana
template:
metadata:
labels:
app: kibana
spec:
nodeSelector:
es: log
containers:
</span>-<span style="color: rgba(0, 0, 0, 1)"> name: kibana
image: docker.elastic.co</span>/kibana/kibana:<span style="color: rgba(128, 0, 128, 1)">7.6</span>.<span style="color: rgba(128, 0, 128, 1)">2</span><span style="color: rgba(0, 0, 0, 1)">
resources:
limits:
cpu: 1000m
requests:
cpu: 1000m
env:
</span>-<span style="color: rgba(0, 0, 0, 1)"> name: ELASTICSEARCH_HOSTS
value: http:</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">elasticsearch:9200</span>
<span style="color: rgba(0, 0, 0, 1)"> ports:
</span>- containerPort: <span style="color: rgba(128, 0, 128, 1)">5601</span><span style="color: rgba(0, 0, 0, 1)">
$ kubectl create </span>-<span style="color: rgba(0, 0, 0, 1)">f kibana.yaml
service</span>/<span style="color: rgba(0, 0, 0, 1)">kibana created
deployment.apps</span>/<span style="color: rgba(0, 0, 0, 1)">kibana created
$ kubectl </span><span style="color: rgba(0, 0, 255, 1)">get</span> pods --<span style="color: rgba(0, 0, 255, 1)">namespace</span>=<span style="color: rgba(0, 0, 0, 1)">logging
NAME READY STATUS RESTARTS AGE
es</span>-<span style="color: rgba(128, 0, 128, 1)">0</span> <span style="color: rgba(128, 0, 128, 1)">1</span>/<span style="color: rgba(128, 0, 128, 1)">1</span> Running <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)"> 85m
es</span>-<span style="color: rgba(128, 0, 128, 1)">1</span> <span style="color: rgba(128, 0, 128, 1)">1</span>/<span style="color: rgba(128, 0, 128, 1)">1</span> Running <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)"> 84m
es</span>-<span style="color: rgba(128, 0, 128, 1)">2</span> <span style="color: rgba(128, 0, 128, 1)">1</span>/<span style="color: rgba(128, 0, 128, 1)">1</span> Running <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)"> 83m
kibana</span>-5c565c47dd-xj4bd <span style="color: rgba(128, 0, 128, 1)">1</span>/<span style="color: rgba(128, 0, 128, 1)">1</span> Running <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)"> 80m
$ kubectl </span><span style="color: rgba(0, 0, 255, 1)">get</span> svc -<span style="color: rgba(0, 0, 0, 1)">n logging
NAME TYPE CLUSTER</span>-IP EXTERNAL-<span style="color: rgba(0, 0, 0, 1)">IP PORT(S) AGE
elasticsearch ClusterIP None </span><none> <span style="color: rgba(128, 0, 128, 1)">9200</span>/TCP,<span style="color: rgba(128, 0, 128, 1)">9300</span>/<span style="color: rgba(0, 0, 0, 1)">TCP 3h22m
kibana NodePort </span><span style="color: rgba(128, 0, 128, 1)">10.111</span>.<span style="color: rgba(128, 0, 128, 1)">223.99</span> <none> <span style="color: rgba(128, 0, 128, 1)">5601</span>:<span style="color: rgba(128, 0, 128, 1)">31139</span>/TCP 3h20m</pre>
</div>
<p>上面我们定义了两个资源对象,一个 Service 和 Deployment,为了测试方便,我们将 Service 设置为了 NodePort 类型,Kibana Pod 中配置都比较简单,唯一需要注意的是我们使用 <code>ELASTICSEARCH_HOSTS</code> 这个环境变量来设置Elasticsearch 集群的端点和端口,直接使用 Kubernetes DNS 即可,此端点对应服务名称为 elasticsearch,由于是一个 headless service,所以该域将解析为3个 Elasticsearch Pod 的 IP 地址列表。</p>
<p>如果 Pod 已经是 Running 状态了,证明应用已经部署成功了,然后可以通过 NodePort 来访问 Kibana 这个服务,在浏览器中打开<code>http://<任意节点IP>:31139</code>即可,如有部署了ingress-control也可以通过 部署ingress进行访问。看到如下欢迎界面证明 Kibana 已经成功部署到了 Kubernetes集群之中。</p>
<p>3、<strong>部署 Fluentd</strong></p>
<p><code>Fluentd</code> 是一个高效的日志聚合器,是用 Ruby 编写的,并且可以很好地扩展。对于大部分企业来说,Fluentd 足够高效并且消耗的资源相对较少,另外一个工具<code>Fluent-bit</code>更轻量级,占用资源更少,但是插件相对 Fluentd 来说不够丰富,所以整体来说,Fluentd 更加成熟,使用更加广泛,所以我们这里也同样使用 Fluentd 来作为日志收集工具。</p>
<h3 id="header-230a-6493" data-fold="unfold"><strong>工作原理</strong></h3>
<p>Fluentd 通过一组给定的数据源抓取日志数据,处理后(转换成结构化的数据格式)将它们转发给其他服务,比如 Elasticsearch、对象存储等等。Fluentd 支持超过300个日志存储和分析服务,所以在这方面是非常灵活的。主要运行步骤如下:</p>
<ul>
<li>首先 Fluentd 从多个日志源获取数据</li>
<li>结构化并且标记这些数据</li>
<li>然后根据匹配的标签将数据发送到多个目标服务去</li>
<li><img src="https://img2022.cnblogs.com/blog/2636555/202205/2636555-20220515175719417-496790112.png" alt="" loading="lazy"></li>
</ul>
<h3 id="header-f0e9-57eb" data-fold="unfold"><strong>配置</strong></h3>
<p>一般来说我们是通过一个配置文件来告诉 Fluentd 如何采集、处理数据的,下面简单和大家介绍下 Fluentd 的配置方法。</p>
<p><strong>日志源配置</strong></p>
<p>比如我们这里为了收集 Kubernetes 节点上的所有容器日志,就需要做如下的日志源配置</p>
<div class="cnblogs_code">
<pre><source><span style="color: rgba(0, 0, 0, 1)">
@id fluentd</span>-<span style="color: rgba(0, 0, 0, 1)">containers.log
@type tail # Fluentd 内置的输入方式,其原理是不停地从源文件中获取新的日志。
path </span>/<span style="color: rgba(0, 0, 255, 1)">var</span>/log/containers<span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">.log # 挂载的服务器Docker容器日志地址
pos_file /var/log/es-containers.log.pos
tag raw.kubernetes.* # 设置日志标签
read_from_head true
<parse> # 多行格式化成JSON
@type multi_format # 使用 multi-format-parser 解析器插件
<pattern>
format json # JSON 解析器
time_key time # 指定事件时间的时间字段
time_format %Y-%m-%dT%H:%M:%S.%NZ# 时间格式
</pattern>
<pattern>
format /^(?<time>.+) (?<stream>stdout|stderr) [^ ]* (?<log>.*)$/
time_format %Y-%m-%dT%H:%M:%S.%N%:z
</pattern>
</parse>
</source></span></pre>
</div>
<p>上面配置部分参数说明如下:</p>
<ul>
<li>id:表示引用该日志源的唯一标识符,该标识可用于进一步过滤和路由结构化日志数据</li>
<li>type:Fluentd 内置的指令,<code>tail</code> 表示 Fluentd 从上次读取的位置通过 tail 不断获取数据,另外一个是 <code>http</code> 表示通过一个 GET 请求来收集数据。</li>
<li>path:<code>tail</code> 类型下的特定参数,告诉 Fluentd 采集 <code>/var/log/containers</code> 目录下的所有日志,这是 docker 在 Kubernetes 节点上用来存储运行容器 stdout 输出日志数据的目录。</li>
<li>pos_file:检查点,如果 Fluentd 程序重新启动了,它将使用此文件中的位置来恢复日志数据收集。</li>
<li>tag:用来将日志源与目标或者过滤器匹配的自定义字符串,Fluentd 匹配源/目标标签来路由日志数据。</li>
</ul>
<p><strong>路由配置</strong></p>
<p>上面是日志源的配置,接下来看看如何将日志数据发送到 Elasticsearch:</p>
<div class="cnblogs_code">
<pre><match **><span style="color: rgba(0, 0, 0, 1)">
@id elasticsearch
@type elasticsearch
@log_level info
include_tag_key </span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">
type_name fluentd
host </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">#{ENV['OUTPUT_HOST']}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
port </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">#{ENV['OUTPUT_PORT']}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
logstash_format </span><span style="color: rgba(0, 0, 255, 1)">true</span>
<buffer><span style="color: rgba(0, 0, 0, 1)">
@type file
path </span>/<span style="color: rgba(0, 0, 255, 1)">var</span>/log/fluentd-buffers/<span style="color: rgba(0, 0, 0, 1)">kubernetes.system.buffer
flush_mode interval
retry_type exponential_backoff
flush_thread_count </span><span style="color: rgba(128, 0, 128, 1)">2</span><span style="color: rgba(0, 0, 0, 1)">
flush_interval 5s
retry_forever
retry_max_interval </span><span style="color: rgba(128, 0, 128, 1)">30</span><span style="color: rgba(0, 0, 0, 1)">
chunk_limit_size </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">#{ENV['OUTPUT_BUFFER_CHUNK_LIMIT']}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
queue_limit_length </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">#{ENV['OUTPUT_BUFFER_QUEUE_LIMIT']}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
overflow_action block
</span></buffer></pre>
</div>
<ul>
<li>match:标识一个目标标签,后面是一个匹配日志源的正则表达式,我们这里想要捕获所有的日志并将它们发送给 Elasticsearch,所以需要配置成<code>**</code>。</li>
<li>id:目标的一个唯一标识符。</li>
<li>type:支持的输出插件标识符,我们这里要输出到 Elasticsearch,所以配置成 elasticsearch,这是 Fluentd 的一个内置插件。</li>
<li>log_level:指定要捕获的日志级别,我们这里配置成 <code>info</code>,表示任何该级别或者该级别以上(INFO、WARNING、ERROR)的日志都将被路由到 Elsasticsearch。</li>
<li>host/port:定义 Elasticsearch 的地址,也可以配置认证信息,我们的 Elasticsearch 不需要认证,所以这里直接指定 host 和 port 即可。</li>
<li>logstash_format:Elasticsearch 服务对日志数据构建反向索引进行搜索,将 logstash_format 设置为 <code>true</code>,Fluentd 将会以 logstash 格式来转发结构化的日志数据。</li>
<li>Buffer: Fluentd 允许在目标不可用时进行缓存,比如,如果网络出现故障或者 Elasticsearch 不可用的时候。缓冲区配置也有助于降低磁盘的 IO。</li>
</ul>
<p><strong>过滤</strong></p>
<p>由于 Kubernetes 集群中应用太多,也还有很多历史数据,所以我们可以只将某些应用的日志进行收集,比如我们只采集具有 <code>logging=true</code> 这个 Label 标签的 Pod 日志,这个时候就需要使用 filter,如下所示:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)"># 删除无用的属性
</span><filter kubernetes.**><span style="color: rgba(0, 0, 0, 1)">
@type record_transformer
remove_keys $.docker.container_id,$.kubernetes.container_image_id,$.kubernetes.pod_id,$.kubernetes.namespace_id,$.kubernetes.master_url,$.kubernetes.labels.pod</span>-template-<span style="color: rgba(0, 0, 0, 1)">hash
</span></filter><span style="color: rgba(0, 0, 0, 1)">
# 只保留具有logging</span>=<span style="color: rgba(0, 0, 0, 1)">true标签的Pod日志
</span><filter kubernetes.**><span style="color: rgba(0, 0, 0, 1)">
@id filter_log
@type grep
</span><regexp><span style="color: rgba(0, 0, 0, 1)">
key $.kubernetes.labels.logging
pattern </span>^<span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">$
</span></regexp>
</filter></pre>
</div>
<h3 id="header-f9eb-5ab2" data-fold="unfold"><strong>安装</strong></h3>
<p>要收集 Kubernetes 集群的日志,直接用 DasemonSet 控制器来部署 Fluentd 应用,这样,它就可以从 Kubernetes 节点上采集日志,确保在集群中的每个节点上始终运行一个 Fluentd 容器。当然可以直接使用 Helm 来进行一键安装,为了能够了解更多实现细节,我们这里还是采用手动方法来进行安装。</p>
<p>首先,我们通过 ConfigMap 对象来指定 Fluentd 配置文件,新建 fluentd-configmap.yaml 文件,文件内容如下:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">kind: ConfigMap
apiVersion: v1
metadata:
name: fluentd</span>-<span style="color: rgba(0, 0, 0, 1)">config
</span><span style="color: rgba(0, 0, 255, 1)">namespace</span><span style="color: rgba(0, 0, 0, 1)">: logging
data:
system.conf: </span>|-
<system><span style="color: rgba(0, 0, 0, 1)">
root_dir </span>/tmp/fluentd-buffers/
</system><span style="color: rgba(0, 0, 0, 1)">
containers.input.conf: </span>|-
<source><span style="color: rgba(0, 0, 0, 1)">
@id fluentd</span>-<span style="color: rgba(0, 0, 0, 1)">containers.log
@type tail # Fluentd 内置的输入方式,其原理是不停地从源文件中获取新的日志。
path </span>/<span style="color: rgba(0, 0, 255, 1)">var</span>/log/containers<span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">.log # 挂载的服务器Docker容器日志地址
pos_file /var/log/es-containers.log.pos
tag raw.kubernetes.* # 设置日志标签
read_from_head true
<parse> # 多行格式化成JSON
@type multi_format # 使用 multi-format-parser 解析器插件
<pattern>
format json # JSON解析器
time_key time # 指定事件时间的时间字段
time_format %Y-%m-%dT%H:%M:%S.%NZ # 时间格式
</pattern>
<pattern>
format /^(?<time>.+) (?<stream>stdout|stderr) [^ ]* (?<log>.*)$/
time_format %Y-%m-%dT%H:%M:%S.%N%:z
</pattern>
</parse>
</source>
# 在日志输出中检测异常,并将其作为一条日志转发
# </span><span style="color: rgba(0, 128, 0, 1); text-decoration: underline">https://github.com/GoogleCloudPlatform/fluent-plugin-detect-exceptions</span><span style="color: rgba(0, 128, 0, 1)">
<match raw.kubernetes.**> # 匹配tag为raw.kubernetes.**日志信息
@id raw.kubernetes
@type detect_exceptions # 使用detect-exceptions插件处理异常栈信息
remove_tag_prefix raw # 移除 raw 前缀
message log
stream stream
multiline_flush_interval 5
max_bytes 500000
max_lines 1000
</match>
<filter **># 拼接日志
@id filter_concat
@type concat # Fluentd Filter 插件,用于连接多个事件中分隔的多行日志。
key message
multiline_end_regexp /\n$/# 以换行符“\n”拼接
separator ""
</filter>
# 添加 Kubernetes metadata 数据
<filter kubernetes.**>
@id filter_kubernetes_metadata
@type kubernetes_metadata
</filter>
# 修复 ES 中的 JSON 字段
# 插件地址:</span><span style="color: rgba(0, 128, 0, 1); text-decoration: underline">https://github.com/repeatedly/fluent-plugin-multi-format-parser</span><span style="color: rgba(0, 128, 0, 1)">
<filter kubernetes.**>
@id filter_parser
@type parser # multi-format-parser多格式解析器插件
key_name log # 在要解析的记录中指定字段名称。
reserve_data true # 在解析结果中保留原始键值对。
remove_key_name_field true# key_name 解析成功后删除字段。
<parse>
@type multi_format
<pattern>
format json
</pattern>
<pattern>
format none
</pattern>
</parse>
</filter>
# 删除一些多余的属性
<filter kubernetes.**>
@type record_transformer
remove_keys $.docker.container_id,$.kubernetes.container_image_id,$.kubernetes.pod_id,$.kubernetes.namespace_id,$.kubernetes.master_url,$.kubernetes.labels.pod-template-hash
</filter>
# 只保留具有logging=true标签的Pod日志
<filter kubernetes.**>
@id filter_log
@type grep
<regexp>
key $.kubernetes.labels.logging
pattern ^true$
</regexp>
</filter>
###### 监听配置,一般用于日志聚合用 ######
forward.input.conf: |-
# 监听通过TCP发送的消息
<source>
@id forward
@type forward
</source>
output.conf: |-
<match **>
@id elasticsearch
@type elasticsearch
@log_level info
include_tag_key true
host elasticsearch
port 9200
logstash_format true
logstash_prefix k8s# 设置 index 前缀为 k8s
request_timeout 30s
<buffer>
@type file
path /var/log/fluentd-buffers/kubernetes.system.buffer
flush_mode interval
retry_type exponential_backoff
flush_thread_count 2
flush_interval 5s
retry_forever
retry_max_interval 30
chunk_limit_size 2M
queue_limit_length 8
overflow_action block
</buffer>
</match></span></pre>
</div>
<p>上面配置文件中我们只配置了 docker 容器日志目录,收集到数据经过处理后发送到 <code>elasticsearch:9200</code> 服务。</p>
<p>然后新建一个 fluentd-daemonset.yaml 的文件,文件内容如下:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">apiVersion: v1
kind: ServiceAccount
metadata:
name: fluentd</span>-<span style="color: rgba(0, 0, 0, 1)">es
</span><span style="color: rgba(0, 0, 255, 1)">namespace</span><span style="color: rgba(0, 0, 0, 1)">: logging
labels:
k8s</span>-app: fluentd-<span style="color: rgba(0, 0, 0, 1)">es
kubernetes.io</span>/cluster-service: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">true</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
addonmanager.kubernetes.io</span>/<span style="color: rgba(0, 0, 0, 1)">mode: Reconcile
</span>---<span style="color: rgba(0, 0, 0, 1)">
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io</span>/<span style="color: rgba(0, 0, 0, 1)">v1
metadata:
name: fluentd</span>-<span style="color: rgba(0, 0, 0, 1)">es
labels:
k8s</span>-app: fluentd-<span style="color: rgba(0, 0, 0, 1)">es
kubernetes.io</span>/cluster-service: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">true</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
addonmanager.kubernetes.io</span>/<span style="color: rgba(0, 0, 0, 1)">mode: Reconcile
rules:
</span>-<span style="color: rgba(0, 0, 0, 1)"> apiGroups:
</span>- <span style="color: rgba(128, 0, 0, 1)">""</span><span style="color: rgba(0, 0, 0, 1)">
resources:
</span>- <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">namespaces</span><span style="color: rgba(128, 0, 0, 1)">"</span>
- <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">pods</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
verbs:
</span>- <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">get</span><span style="color: rgba(128, 0, 0, 1)">"</span>
- <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">watch</span><span style="color: rgba(128, 0, 0, 1)">"</span>
- <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">list</span><span style="color: rgba(128, 0, 0, 1)">"</span>
---<span style="color: rgba(0, 0, 0, 1)">
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io</span>/<span style="color: rgba(0, 0, 0, 1)">v1
metadata:
name: fluentd</span>-<span style="color: rgba(0, 0, 0, 1)">es
labels:
k8s</span>-app: fluentd-<span style="color: rgba(0, 0, 0, 1)">es
kubernetes.io</span>/cluster-service: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">true</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
addonmanager.kubernetes.io</span>/<span style="color: rgba(0, 0, 0, 1)">mode: Reconcile
subjects:
</span>-<span style="color: rgba(0, 0, 0, 1)"> kind: ServiceAccount
name: fluentd</span>-<span style="color: rgba(0, 0, 0, 1)">es
</span><span style="color: rgba(0, 0, 255, 1)">namespace</span><span style="color: rgba(0, 0, 0, 1)">: logging
apiGroup: </span><span style="color: rgba(128, 0, 0, 1)">""</span><span style="color: rgba(0, 0, 0, 1)">
roleRef:
kind: ClusterRole
name: fluentd</span>-<span style="color: rgba(0, 0, 0, 1)">es
apiGroup: </span><span style="color: rgba(128, 0, 0, 1)">""</span>
---<span style="color: rgba(0, 0, 0, 1)">
apiVersion: apps</span>/<span style="color: rgba(0, 0, 0, 1)">v1
kind: DaemonSet
metadata:
name: fluentd</span>-<span style="color: rgba(0, 0, 0, 1)">es
</span><span style="color: rgba(0, 0, 255, 1)">namespace</span><span style="color: rgba(0, 0, 0, 1)">: logging
labels:
k8s</span>-app: fluentd-<span style="color: rgba(0, 0, 0, 1)">es
kubernetes.io</span>/cluster-service: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">true</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
addonmanager.kubernetes.io</span>/<span style="color: rgba(0, 0, 0, 1)">mode: Reconcile
spec:
selector:
matchLabels:
k8s</span>-app: fluentd-<span style="color: rgba(0, 0, 0, 1)">es
template:
metadata:
labels:
k8s</span>-app: fluentd-<span style="color: rgba(0, 0, 0, 1)">es
kubernetes.io</span>/cluster-service: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">true</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
# 此注释确保如果节点被驱逐,fluentd不会被驱逐,支持关键的基于 pod 注释的优先级方案。
annotations:
scheduler.alpha.kubernetes.io</span>/critical-pod: <span style="color: rgba(128, 0, 0, 1)">''</span><span style="color: rgba(0, 0, 0, 1)">
spec:
serviceAccountName: fluentd</span>-<span style="color: rgba(0, 0, 0, 1)">es
containers:
</span>- name: fluentd-<span style="color: rgba(0, 0, 0, 1)">es
image: quay.io</span>/fluentd_elasticsearch/fluentd:v3.<span style="color: rgba(128, 0, 128, 1)">0.1</span><span style="color: rgba(0, 0, 0, 1)">
env:
</span>-<span style="color: rgba(0, 0, 0, 1)"> name: FLUENTD_ARGS
value: </span>--no-supervisor -<span style="color: rgba(0, 0, 0, 1)">q
resources:
limits:
memory: 500Mi
requests:
cpu: 100m
memory: 200Mi
volumeMounts:
</span>-<span style="color: rgba(0, 0, 0, 1)"> name: varlog
mountPath: </span>/<span style="color: rgba(0, 0, 255, 1)">var</span>/<span style="color: rgba(0, 0, 0, 1)">log
</span>-<span style="color: rgba(0, 0, 0, 1)"> name: varlibdockercontainers
mountPath: </span>/data/docker/<span style="color: rgba(0, 0, 0, 1)">containers
readOnly: </span><span style="color: rgba(0, 0, 255, 1)">true</span>
- name: config-<span style="color: rgba(0, 0, 0, 1)">volume
mountPath: </span>/etc/fluent/<span style="color: rgba(0, 0, 0, 1)">config.d
nodeSelector:
beta.kubernetes.io</span>/fluentd-ds-ready: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">true</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
tolerations:
</span>- <span style="color: rgba(0, 0, 255, 1)">operator</span><span style="color: rgba(0, 0, 0, 1)">: Exists
terminationGracePeriodSeconds: </span><span style="color: rgba(128, 0, 128, 1)">30</span><span style="color: rgba(0, 0, 0, 1)">
volumes:
</span>-<span style="color: rgba(0, 0, 0, 1)"> name: varlog
hostPath:
path: </span>/<span style="color: rgba(0, 0, 255, 1)">var</span>/<span style="color: rgba(0, 0, 0, 1)">log
</span>-<span style="color: rgba(0, 0, 0, 1)"> name: varlibdockercontainers
hostPath:
path: </span>/data/docker/<span style="color: rgba(0, 0, 0, 1)">containers
</span>- name: config-<span style="color: rgba(0, 0, 0, 1)">volume
configMap:
name: fluentd</span>-config</pre>
</div>
<p>我们将上面创建的 fluentd-config 这个 ConfigMap 对象通过 volumes 挂载到了 Fluentd 容器中,另外为了能够灵活控制哪些节点的日志可以被收集,所以我们这里还添加了一个 nodSelector 属性:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">nodeSelector:
beta.kubernetes.io</span>/fluentd-ds-ready: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">true</span><span style="color: rgba(128, 0, 0, 1)">"</span></pre>
</div>
<p>意思就是要想采集节点的日志,那么我们就需要给节点打上上面的标签,比如我们这里只给节点4和节点6打上了该标签:</p>
<p>如果你需要在其他节点上采集日志,则需要给对应节点打上标签,使用如下命令:<code>kubectl label nodes node名 beta.kubernetes.io/fluentd-ds-ready=true</code>。</p>
<p>另外由于我们的集群使用的是 kubeadm 搭建的,默认情况下 master 节点有污点,所以如果要想也收集 master 节点的日志,则需要添加上容忍:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">tolerations:
</span>- <span style="color: rgba(0, 0, 255, 1)">operator</span>: Exists</pre>
</div>
<p>ds的yaml文件要改</p>
<div class="cnblogs_code">
<pre>$ kubectl create -f fluentd-<span style="color: rgba(0, 0, 0, 1)">configmap.yaml
configmap </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">fluentd-config</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)"> created
$ kubectl create </span>-f fluentd-<span style="color: rgba(0, 0, 0, 1)">daemonset.yaml
serviceaccount </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">fluentd-es</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)"> created
clusterrole.rbac.authorization.k8s.io </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">fluentd-es</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)"> created
clusterrolebinding.rbac.authorization.k8s.io </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">fluentd-es</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)"> created
daemonset.apps </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">fluentd-es</span><span style="color: rgba(128, 0, 0, 1)">"</span> created</pre>
</div>
<p>Fluentd 启动成功后,这个时候就可以发送日志到 ES 了,但是我们这里是过滤了只采集具有 <code>logging=true</code> 标签的 Pod 日志,所以现在还没有任何数据会被采集。</p>
<p>下面我们部署一个简单的测试应用, 新建 counter.yaml 文件,文件内容如下:</p>
<div class="cnblogs_code">
<pre>$ kubectl create -<span style="color: rgba(0, 0, 0, 1)">f counter.yaml
$ kubectl </span><span style="color: rgba(0, 0, 255, 1)">get</span><span style="color: rgba(0, 0, 0, 1)"> pods
NAME READY STATUS RESTARTS AGE
counter </span><span style="color: rgba(128, 0, 128, 1)">1</span>/<span style="color: rgba(128, 0, 128, 1)">1</span> Running <span style="color: rgba(128, 0, 128, 1)">0</span> 9h</pre>
</div>
<p>Pod 创建并运行后,回到 Kibana Dashboard 页面,点击左侧最下面的 <code>management</code> 图标,然后点击 Kibana 下面的 <code>Index Patterns</code> 开始导入索引数据:</p>
<div class="cnblogs_code">
<pre>curl -s <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">127.0.0.1:9200/_cat/indices?v</span><span style="color: rgba(128, 0, 0, 1)">"</span></pre>
</div>
<p>查看索引是否存在</p>
<p><img src="https://img2022.cnblogs.com/blog/2636555/202205/2636555-20220515181127420-208277523.png" alt="" loading="lazy"></p>
<p> </p>
<p> 至此我们就成功部署了EFK来收集k8s集群的日志。</p><br><br>
来源:https://www.cnblogs.com/zjl-throb/p/16273904.html
頁:
[1]