目 录CONTENT

文章目录

容器日志收集

简中仙
2023-06-29 / 0 评论 / 0 点赞 / 120 阅读 / 0 字 / 正在检测是否收录...
温馨提示:
本文最后更新于2024-01-14,若内容或图片失效,请留言反馈。 本文如有错误或者侵权的地方,欢迎您批评指正!

这篇学习笔记是基于杜宽老师在51CTO上发布的视频课程制作的。在此,感谢杜宽老师的分享和教学。如有侵权,请及时联系我。版权归原作者所有,未经允许不得转载或使用。

一、使用 EFK 收集控制台日志

1、部署 Elasticsearch+Fluentd+Kibana

本次实验的环境如下,服务器可用资源 2 核 4G 以上

# kubectl get node 
NAME           STATUS   ROLES    AGE   VERSION 
k8s-master01   Ready    <none>   88d   v1.21.0 
k8s-master02   Ready    <none>   88d   v1.21.0 
k8s-master03   Ready    <none>   88d   v1.21.0 
k8s-node01      Ready    <none>   88d   v1.21.0 
k8s-node02      Ready    <none>   88d   v1.21.0

下载需要的部署文件

# git clone https://github.com/dotbalo/k8s.git 
# cd k8s/efk-7.10.2/

创建 EFK 所用的命名空间

[root@k8s-master01 efk-7.10.2]# kubectl create -f create-logging-namespace.yaml  
namespace/logging created

创建 Elasticsearch 集群(企业内已有 ELK 平台可以不创建)

[root@k8s-master01 efk-7.10.2]# kubectl create -f es-service.yaml  
service/elasticsearch-logging created 
[root@k8s-master01 efk-7.10.2]# kubectl create -f es-statefulset.yaml  
serviceaccount/elasticsearch-logging created 
clusterrole.rbac.authorization.k8s.io/elasticsearch-logging created 
clusterrolebinding.rbac.authorization.k8s.io/elasticsearch-logging created 
statefulset.apps/elasticsearch-logging created

创建 Kibana(企业内已有 ELK 平台可以不创建)

[root@k8s-master01 efk-7.10.2]# kubectl create -f kibana-deployment.yaml -f kibana-service.yaml  
deployment.apps/kibana-logging created 
service/kibana-logging created

由于在 Kubernetes 集群中,我们可能并不需要对所有的机器都采集日志,所以可以更改Fluentd 的部署文件如下,添加一个 NodeSelector,只部署至需要采集的主机即可

# grep "nodeSelector" fluentd-es-ds.yaml -A 3 
      nodeSelector: 
        fluentd: "true" 
  ...

之后给需要采集日志的节点打上一个标签,以 k8s-node01 为例

# kubectl label node k8s-node01 fluentd=true 
# kubectl get node -l fluentd=true --show-labels 
NAME         STATUS   ROLES    AGE   VERSION   LABELS 
k8s-node01   Ready    <none>   88d   v1.21.0   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,fluentd=true,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node01,kubernetes.io/os=linux,node.kubernetes.io/node=

创建 Fluentd

[root@k8s-master01 efk-7.10.2]# kubectl create -f fluentd-es-ds.yaml -f fluentd-es-configmap.yaml  
serviceaccount/fluentd-es created 
clusterrole.rbac.authorization.k8s.io/fluentd-es created 
clusterrolebinding.rbac.authorization.k8s.io/fluentd-es created 
daemonset.apps/fluentd-es-v3.1.1 created 
configmap/fluentd-es-config-v0.2.1 created

Fluentd 的 ConfigMap 有个字段需要注意,在 fluentd-es-configmap.yaml 最后有一个output.conf

output.conf: |- 
    <match **> 
      ... 
      host elasticsearch-logging 
      port 9200 
  ...

2、Kibana 使用

确认创建的 Pod 都已经成功启动

# kubectl get po -n logging 
NAME                              READY   STATUS    RESTARTS   AGE 
elasticsearch-logging-0           1/1     Running   0          15h 
elasticsearch-logging-1           1/1     Running   0          15h 
fluentd-es-v3.1.1-p4zbk           1/1     Running   17         15h 
kibana-logging-75bb6cccf5-pscg5   1/1     Running   7          15h

接下来查看 Kibana 暴露的端口号,访问 Kibana

# kubectl get svc -n logging 
NAME                    TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)             AGE 
elasticsearch-logging   ClusterIP   None             <none>        9200/TCP,9300/TCP   15h 
kibana-logging          NodePort    192.168.148.65   <none>        5601:32664/TCP       15h

使用任意一个部署了 kube-proxy 服务的节点的 IP+32664 端口即可访问 Kibana

二、使用 Filebeat 收集自定义文件日志

1、创建 Kafka 和 Logstash

首先需要部署 Kafka 和 Logstash 至 Kubernetes 集群,如果企业内已经有比较成熟的技术栈,可以无需部署,直接将 Filebeat 的输出指向外部 Kafka 集群即可

# cd filebeat 
# helm install zookeeper zookeeper/ -n logging 
# kubectl get po -n logging -l app.kubernetes.io/name=zookeeper
NAME          READY   STATUS    RESTARTS   AGE 
zookeeper-0   1/1     Running   0          51s 
 
# helm  install kafka kafka/ -n logging 
# kubectl get po -n logging -l app.kubernetes.io/component=kafka 
NAME      READY   STATUS    RESTARTS   AGE 
kafka-0   1/1     Running   0          43s

待 Pod 都正常后,创建 Logstash 服务

# kubectl create -f logstash-service.yaml -f logstash-cm.yaml -f logstash.yaml -n logging 
service/logstash-service created 
configmap/logstash-configmap created 
deployment.apps/logstash-deployment created

需要注意 logstash-cm.yaml 文件中的一些配置

  • input:数据来源,本次示例配置的是 Kakfa;
  • input.kafka.bootstrap_servers:Kafka 地址,由于是安装在集群内部的,可以直接使用Kafka 集群的 Service 接口,如果是外部地址,按需配置即可;
  • input.kafka.topics:Kafka 的 topic,需要和 Filebeat 输出的 topic 一致;
  • input.kafka.type:定义一个 type,可以用于 logstash 输出至不同的 Elasticsearch 集群;
  • output:数据输出至哪里,本次示例输出至 Elasticsearch 集群,在里面配置了一个判断语句,当 type 为 filebeat-sidecar 时,将会输出至 Elasticsearch 集群,并且 index 为 filebeat-xxx。

2、注入 Filebeat Sidecar

接下来创建一个模拟程序

# kubectl create -f app.yaml -nlogging

该程序会一直在/opt/date.log 文件输出当前日期,配置如下

		  command: 
            - sh 
            - -c 
            - while true; do date >> /opt/date.log; sleep 2;done

成功启动后,可以查看该文件的内容

# kubectl  get po -nlogging 
NAME                   READY   STATUS    RESTARTS   AGE 
app-6dd64bdc55-8wzjc   1/1     Running   0          19m 
# kubectl logs app-6dd64bdc55-8wzjc  -nlogging
# kubectl  exec app-6dd64bdc55-8wzjc -nlogging -- tail -1 /opt/date.log 
Wed Jul  10 09:10:06 UTC 2021

此时如果去 Kibana 检索该日志,是无法查到该日志的,因为 Fluentd 无法采集内部的日志文件。接下来改造该程序,添加 Filebeat 至该部署文件(只展示了部分内容)

# cat app-filebeat.yaml  
... 
      containers: 
        - name: filebeat                         
          image: registry.cn-beijing.aliyuncs.com/dotbalo/filebeat:7.10.2  
          imagePullPolicy: IfNotPresent 
          env: 
            - name: podIp 
              valueFrom: 
                fieldRef: 
                  apiVersion: v1 
                  fieldPath: status.podIP 
            - name: podName 
              valueFrom: 
                fieldRef: 
                  apiVersion: v1 
                  fieldPath: metadata.name 
            - name: podNamespace 
              valueFrom: 
                fieldRef: 
                  apiVersion: v1 
                  fieldPath: metadata.namespace 
            - name: podDeployName 
              value: app 
            - name: TZ 
              value: "Asia/Shanghai" 
          securityContext: 
            runAsUser: 0 
          volumeMounts: 
            - name: logpath 
              mountPath: /data/log/app/ 
            - name: filebeatconf 
              mountPath: /usr/share/filebeat/filebeat.yml  
              subPath: usr/share/filebeat/filebeat.yml 
        - name: app 
          image: registry.cn-beijing.aliyuncs.com/dotbalo/alpine:3.6  
          imagePullPolicy: IfNotPresent 
          volumeMounts: 
            - name: logpath 
              mountPath: /opt/ 
          env: 
            - name: TZ 
              value: "Asia/Shanghai" 
            - name: LANG 
              value: C.UTF-8 
            - name: LC_ALL 
              value: C.UTF-8 
          command: 
            - sh 
            - -c 
            - while true; do date >> /opt/date.log; sleep 2;  done  
      volumes: 
        - name: logpath 
          emptyDir: {} 
        - name: filebeatconf 
          configMap: 
            name: filebeatconf 
            items: 
              - key: filebeat.yml 
                path: usr/share/filebeat/filebeat.yml

可以看到在 Deployment 部署文件中,添加了 Volumes 配置,并配置了一个名为 logpath 的volume,将其挂载到了应用容器的/opt/目录和 Filebeat 的/data/log/app/目录,这样同一个 Pod内的两个容器就实现了目录的共享。

之后创建一个 Filebeat 的配置文件,采集该目录下的日志即可

# cat filebeat-cm.yaml  
apiVersion: v1 
kind: ConfigMap 
metadata: 
  name: filebeatconf 
data: 
  filebeat.yml: |- 
    filebeat.inputs: 
    - input_type: log 
      paths: 
        - /data/log/*/*.log 
      tail_files: true 
      fields: 
        pod_name: '${podName}' 
        pod_ip: '${podIp}' 
        pod_deploy_name: '${podDeployName}' 
        pod_namespace: '${podNamespace}' 
    output.kafka: 
      hosts: ["kafka:9092"] 
      topic: "filebeat-sidecar" 
      codec.json: 
        pretty: false 
      keep_alive: 30s

需要注意 paths 是配置的共享目录,output.kafka 需要和 logstash 的 kafka 为同一个集群,并且 topic 和 logstash 消费的 topic 为同一个。之后注入 Filebeat

# kubectl apply -f filebeat-cm.yaml -f app-filebeat.yaml -n logging 
configmap/filebeatconf created 
deployment.apps/app configured

三、Loki 初体验

Loki:主服务器,负责日志的存储和查询,参考了Prometheus的服务发现机制,将标签添加到日志流,而不是像其它平台一样进行全文索引;

Promtail:负责收集日志并将其发送给Loki,主要用于发现采集目标以及添加对应Label,最终发送给Loki;

Grafana:用来展示或查询相关日志,可以在页面查询指定标签Pod的日志。

1、安装 Loki Stack

Loki 提供了 Helm 的安装方式,可以直接下载包进行安装即可,首先添加并更新 Loki 的 Helm仓库

# helm repo add grafana https://grafana.github.io/helm-charts 
"grafana" has been added to your repositories 
# helm repo update 
Hang tight while we grab the latest from your chart repositories... 
... 
...Successfully got an update from the "grafana" chart repository

创建 Loki Namespace

# kubectl create ns loki 
namespace/loki created

创建 Loki Stack

# helm upgrade --install loki grafana/loki-stack  --set grafana.enabled=true --set grafana.service.type=NodePort -n loki 
NAME: loki 
NAMESPACE: loki 
STATUS: deployed 
REVISION: 1 
NOTES: 
The Loki stack has been deployed to your cluster. Loki can now be added as a datasource in Grafana. 
 
See http://docs.grafana.org/features/datasources/loki/ for more detail.

查看 Pod 状态

# kubectl get po -n loki 
NAME                            READY   STATUS    RESTARTS   AGE 
loki-0                          1/1     Running   0          4m23s
loki-grafana-5b57955f9d-48z6l   1/1     Running   0          4m23s 
loki-promtail-2n24m             1/1     Running   0          4m23s 
loki-promtail-59slx             1/1     Running   0          4m23s 
loki-promtail-6flzq             1/1     Running   0          4m23s 
loki-promtail-gq2hk             1/1     Running   0          4m23s 
loki-promtail-sqwtv             1/1     Running   0          4m23s

查看 Grafana 的 Service 暴露的端口号

# kubectl get svc -n loki 
NAME            TYPE        CLUSTER-IP        EXTERNAL-IP   PORT(S)        AGE 
loki            ClusterIP   192.168.67.18     <none>        3100/TCP       7m54s 
loki-grafana    NodePort    192.168.103.121   <none>        80:31053/TCP   7m54s 
loki-headless   ClusterIP   None              <none>        3100/TCP       7m54s

之后通过任意一个安装了 kube-proxy 的节点的 IP 加上 31053 即可访问 Grafana

查看 Grafana 密码(账号 admin)

# kubectl get secret --namespace loki loki-grafana -o jsonpath="{.data.admin-password}" | base64 --decode ; echo 
eD47DAbzhyPLgDeSM8C0LvBi3DksU73vZND8t4h0

其它安装配置可以参考:https://grafana.com/docs/loki/latest/installation/helm/

2、Loki 语法入门

Loki 是参考 Prometheus 进行设计的,所以查询语法和 PromQL 类似,比如查询命名空间为kube-system 下的所有 Pod 的日志,只需要在 Log browser 输入{namespace="kube-system"},然后点击 Run query 即可

可以看到每条日志都被添加了很多的标签,之后可以通过标签过滤日志,比如查看命名空间为 kube-system,且 Pod 名称包含 calico 的日志

{namespace="kube-system", pod=~"calico.*"}

相当于=~,类似的语法还有:

  • =~ :正则模糊匹配
  • = :完全匹配
  • != :不等于
  • !~ :正则模糊不等于

Loki 语法同时支持 Pipeline,比如需要过滤日志包含 avg 字段的日志

{namespace="kube-system", pod=~"calico.*"} |~ "avg"

还可以使用 logfmt 对日志进行格式化,然后可以进行值的判断,比如找到包含 avg 字段,并且 longest 的值大于 16ms 的日志

{namespace="kube-system", pod=~"calico.*"} |~ "avg" | logfmt | longest > 16ms

得到的日志和上个结果相比,只有 longest 大于 16ms 的日志,其它的均已被过滤掉

值匹配可以判断多个条件,比如 longest 大于 16ms 且 avg 大于 6ms 的日志

{namespace="kube-system", pod=~"calico.*"} |~ "avg" | logfmt | longest > 16ms and avg > 6ms

上述演示的语法为最基本也是最常用的语法,如果想要了解更多复杂的语法,可以查看LogQL 的官方文档:https://grafana.com/docs/loki/latest/logql/

0

评论区