精神世界 發表於 2022-5-15 18:13:00

kubernetes部署EFK(k8s)

<p>Kubernetes 中比较流行的日志收集解决方案是&nbsp;<code>Elasticsearch</code>、<code>Fluentd</code>&nbsp;和&nbsp;<code>Kibana</code>(EFK)技术栈,也是官方现在比较推荐的一种方案。</p>
<p><code>Elasticsearch</code>&nbsp;是一个实时的、分布式的可扩展的搜索引擎,允许进行全文、结构化搜索,它通常用于索引和搜索大量日志数据,也可用于搜索许多不同类型的文档。</p>
<p>Elasticsearch 通常与&nbsp;<code>Kibana</code>&nbsp;一起部署,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>&lt;none&gt;      <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,指定标签&nbsp;<code>app=elasticsearch</code>,当我们将 Elasticsearch StatefulSet 与此服务关联时,服务将返回带有标签&nbsp;<code>app=elasticsearch</code>的 Elasticsearch Pods 的 DNS A 记录,然后设置&nbsp;<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>&lt;none&gt;      <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:节点的名称,通过&nbsp;<code>metadata.name</code>&nbsp;来获取。这将解析为 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>此外&nbsp;Elastisearch Notes for Production Use&nbsp;文档还提到了由于性能原因最好禁用 swap,当然对于 Kubernetes 集群而言,最好也是禁用 swap 分区的。</blockquote>
<p>我们这里使用 volumeClaimTemplates 来定义持久化模板,Kubernetes 会使用它为 Pod 创建 PersistentVolume,设置访问模式为<code>ReadWriteOnce</code>,这意味着它只能被 mount 到单个节点上进行读写,然后最重要的是使用了一个 StorageClass 对象,这里我们就直接使用前面创建的 NFS 类型&nbsp;的 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> -&gt; <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> -&gt; <span style="color: rgba(128, 0, 128, 1)">9200<br></span></pre>
<p>&nbsp;#然后,在另外的终端窗口中,执行如下请求:</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>&lt;none&gt;      <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>   &lt;none&gt;      <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 中配置都比较简单,唯一需要注意的是我们使用&nbsp;<code>ELASTICSEARCH_HOSTS</code>&nbsp;这个环境变量来设置Elasticsearch 集群的端点和端口,直接使用 Kubernetes DNS 即可,此端点对应服务名称为 elasticsearch,由于是一个 headless service,所以该域将解析为3个 Elasticsearch Pod 的 IP 地址列表。</p>
<p>如果 Pod 已经是 Running 状态了,证明应用已经部署成功了,然后可以通过 NodePort 来访问 Kibana 这个服务,在浏览器中打开<code>http://&lt;任意节点IP&gt;:31139</code>即可,如有部署了ingress-control也可以通过 部署ingress进行访问。看到如下欢迎界面证明 Kibana 已经成功部署到了 Kubernetes集群之中。</p>
<p>3、<strong>部署 Fluentd</strong></p>
<p><code>Fluentd</code>&nbsp;是一个高效的日志聚合器,是用 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>&lt;source&gt;<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
&lt;parse&gt;                              # 多行格式化成JSON
    @type multi_format                   # 使用 multi-format-parser 解析器插件
    &lt;pattern&gt;
      format json                        # JSON 解析器
      time_key time                      # 指定事件时间的时间字段
      time_format %Y-%m-%dT%H:%M:%S.%NZ# 时间格式
    &lt;/pattern&gt;
    &lt;pattern&gt;
      format /^(?&lt;time&gt;.+) (?&lt;stream&gt;stdout|stderr) [^ ]* (?&lt;log&gt;.*)$/
      time_format %Y-%m-%dT%H:%M:%S.%N%:z
    &lt;/pattern&gt;
&lt;/parse&gt;
&lt;/source&gt;</span></pre>
</div>
<p>上面配置部分参数说明如下:</p>
<ul>
<li>id:表示引用该日志源的唯一标识符,该标识可用于进一步过滤和路由结构化日志数据</li>
<li>type:Fluentd 内置的指令,<code>tail</code>&nbsp;表示 Fluentd 从上次读取的位置通过 tail 不断获取数据,另外一个是&nbsp;<code>http</code>&nbsp;表示通过一个 GET 请求来收集数据。</li>
<li>path:<code>tail</code>&nbsp;类型下的特定参数,告诉 Fluentd 采集&nbsp;<code>/var/log/containers</code>&nbsp;目录下的所有日志,这是 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>&lt;match **&gt;<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>
&lt;buffer&gt;<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>&lt;/buffer&gt;</pre>
</div>
<ul>
<li>match:标识一个目标标签,后面是一个匹配日志源的正则表达式,我们这里想要捕获所有的日志并将它们发送给 Elasticsearch,所以需要配置成<code>**</code>。</li>
<li>id:目标的一个唯一标识符。</li>
<li>type:支持的输出插件标识符,我们这里要输出到 Elasticsearch,所以配置成 elasticsearch,这是 Fluentd 的一个内置插件。</li>
<li>log_level:指定要捕获的日志级别,我们这里配置成&nbsp;<code>info</code>,表示任何该级别或者该级别以上(INFO、WARNING、ERROR)的日志都将被路由到 Elsasticsearch。</li>
<li>host/port:定义 Elasticsearch 的地址,也可以配置认证信息,我们的 Elasticsearch 不需要认证,所以这里直接指定 host 和 port 即可。</li>
<li>logstash_format:Elasticsearch 服务对日志数据构建反向索引进行搜索,将 logstash_format 设置为&nbsp;<code>true</code>,Fluentd 将会以 logstash 格式来转发结构化的日志数据。</li>
<li>Buffer: Fluentd 允许在目标不可用时进行缓存,比如,如果网络出现故障或者 Elasticsearch 不可用的时候。缓冲区配置也有助于降低磁盘的 IO。</li>
</ul>
<p><strong>过滤</strong></p>
<p>由于 Kubernetes 集群中应用太多,也还有很多历史数据,所以我们可以只将某些应用的日志进行收集,比如我们只采集具有&nbsp;<code>logging=true</code>&nbsp;这个 Label 标签的 Pod 日志,这个时候就需要使用 filter,如下所示:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)"># 删除无用的属性
</span>&lt;filter kubernetes.**&gt;<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>&lt;/filter&gt;<span style="color: rgba(0, 0, 0, 1)">
# 只保留具有logging</span>=<span style="color: rgba(0, 0, 0, 1)">true标签的Pod日志
</span>&lt;filter kubernetes.**&gt;<span style="color: rgba(0, 0, 0, 1)">
@id filter_log
@type grep
</span>&lt;regexp&gt;<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>&lt;/regexp&gt;
&lt;/filter&gt;</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>|-
    &lt;system&gt;<span style="color: rgba(0, 0, 0, 1)">
      root_dir </span>/tmp/fluentd-buffers/
    &lt;/system&gt;<span style="color: rgba(0, 0, 0, 1)">
containers.input.conf: </span>|-
    &lt;source&gt;<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
      &lt;parse&gt;                                 # 多行格式化成JSON
      @type multi_format                  # 使用 multi-format-parser 解析器插件
      &lt;pattern&gt;
          format json                         # JSON解析器
          time_key time                     # 指定事件时间的时间字段
          time_format %Y-%m-%dT%H:%M:%S.%NZ   # 时间格式
      &lt;/pattern&gt;
      &lt;pattern&gt;
          format /^(?&lt;time&gt;.+) (?&lt;stream&gt;stdout|stderr) [^ ]* (?&lt;log&gt;.*)$/
          time_format %Y-%m-%dT%H:%M:%S.%N%:z
      &lt;/pattern&gt;
      &lt;/parse&gt;
    &lt;/source&gt;
    # 在日志输出中检测异常,并将其作为一条日志转发
    # </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)">
    &lt;match raw.kubernetes.**&gt;         # 匹配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
    &lt;/match&gt;


    &lt;filter **&gt;# 拼接日志
      @id filter_concat
      @type concat                # Fluentd Filter 插件,用于连接多个事件中分隔的多行日志。
      key message
      multiline_end_regexp /\n$/# 以换行符“\n”拼接
      separator ""
    &lt;/filter&gt;


    # 添加 Kubernetes metadata 数据
    &lt;filter kubernetes.**&gt;
      @id filter_kubernetes_metadata
      @type kubernetes_metadata
    &lt;/filter&gt;


    # 修复 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)">
    &lt;filter kubernetes.**&gt;
      @id filter_parser
      @type parser                # multi-format-parser多格式解析器插件
      key_name log                # 在要解析的记录中指定字段名称。
      reserve_data true         # 在解析结果中保留原始键值对。
      remove_key_name_field true# key_name 解析成功后删除字段。
      &lt;parse&gt;
      @type multi_format
      &lt;pattern&gt;
          format json
      &lt;/pattern&gt;
      &lt;pattern&gt;
          format none
      &lt;/pattern&gt;
      &lt;/parse&gt;
    &lt;/filter&gt;


    # 删除一些多余的属性
    &lt;filter kubernetes.**&gt;
      @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
    &lt;/filter&gt;


    # 只保留具有logging=true标签的Pod日志
    &lt;filter kubernetes.**&gt;
      @id filter_log
      @type grep
      &lt;regexp&gt;
      key $.kubernetes.labels.logging
      pattern ^true$
      &lt;/regexp&gt;
    &lt;/filter&gt;

###### 监听配置,一般用于日志聚合用 ######
forward.input.conf: |-
    # 监听通过TCP发送的消息
    &lt;source&gt;
      @id forward
      @type forward
    &lt;/source&gt;


output.conf: |-
    &lt;match **&gt;
      @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
      &lt;buffer&gt;
      @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
      &lt;/buffer&gt;
    &lt;/match&gt;</span></pre>
</div>
<p>上面配置文件中我们只配置了 docker 容器日志目录,收集到数据经过处理后发送到&nbsp;<code>elasticsearch:9200</code>&nbsp;服务。</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 了,但是我们这里是过滤了只采集具有&nbsp;<code>logging=true</code>&nbsp;标签的 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 页面,点击左侧最下面的&nbsp;<code>management</code>&nbsp;图标,然后点击 Kibana 下面的&nbsp;<code>Index Patterns</code>&nbsp;开始导入索引数据:</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>&nbsp;</p>
<p>&nbsp;至此我们就成功部署了EFK来收集k8s集群的日志。</p><br><br>
来源:https://www.cnblogs.com/zjl-throb/p/16273904.html
頁: [1]
查看完整版本: kubernetes部署EFK(k8s)