这篇学习笔记是基于杜宽老师在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/
评论区