四、EFK日志系统

日志级别

日志打印通常有四种级别,从高到底分别是:ERROR、WARN、INFO、DEBUG。如果开启了某一级别的日志后,就不会打印比它级别低的日志

DEBUG:可以打印出最详细的日志信息,主要用于开发过程中打印一些运行信息。 
INFO 可以打印一些你感兴趣的或者重要的信息,这个可以用于生产环境中输出程序运行的一些重要信息,但是不能滥用,避免打印过多的日志。 
WARNING 表明发生了一些暂时不影响运行的错误,会出现潜在错误的情形,有些信息不是错误信息,但是也要给程序员的一些提示 
ERROR 可以打印错误和异常信息,如果不想输出太多的日志,可以使用这个级别,这一级就是比较重要的错误了,软件的某些功能已经不能继续执行了。

4.1、方案及组件

Kubernetes 中比较流行的日志收集解决方案是 Elasticsearch、Fluentd 和Kibana(EFK)技术栈,也是官方推荐的一种方案

EFK

在 Kubernetes 集群上运行多个服务和应用程序时,日志收集系统可以帮助你快速分类和分析由Pod 生成的大量日志数据。Kubernetes 中比较流行的日志收集解决方案是 Elasticsearch、Fluentd 和Kibana(EFK)技术栈,也是官方推荐的一种方案。

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

	kibana 可以把 Elasticsearch 采集到的数据通过dashboard(仪表板)可视化展示出来。Kibana 允许你通过 Web 界面浏览Elasticsearch 日志数据,也可自定义查询条件快速检索出 elasticccsearch 中的日志数据。

	Fluentd 是一个流行的开源数据收集器,我们在 Kubernetes 集群节点上安装 Fluentd,通过获取容器日志文件、过滤和转换日志数据,然后将数据传递到 Elasticsearch 集群,在该集群中对其进行索引和存储。

ELK

##	只是收集日志的组件不一样
Logstash:是一个完全开源的工具,他可以对你的日志进行收集、过滤,并将其存储供以后使用(支持动态的从各种数据源搜集数据,并对数据进行过滤、分析、丰富、统一格式等操作。)。

其它方案

ELK 日志流程可以有多种方案(不同组件可自由组合,根据自身业务配置),常见有以下: 
 
 Logstash(采集、处理)—> ElasticSearch (存储)—>Kibana (展示) 
 Logstash(采集)—> Logstash(聚合、处理)—> ElasticSearch (存储)—>Kibana (展示) 
 Filebeat(采集、处理)—> ElasticSearch (存储)—>Kibana (展示) 
 Filebeat(采集)—> Logstash(聚合、处理)—> ElasticSearch (存储)—>Kibana (展示) 
 Filebeat(采集)—> Kafka/Redis(消峰) —> Logstash(聚合、处理)—> ElasticSearch (存储)—>Kibana (展示)

4.1.1、elasticsearch介绍

Elasticsearch 是一个分布式的免费开源搜索和分析引擎,适用于包括文本、数字、地理空间、结构化和非结构化数据等在内的所有类型的数据。

4.1.2、filebeat简介

filebeat 是 Beats 中的一员。 
 	Filebeat 是用于转发和收集日志数据的轻量级传送工具。Filebeat 监视你指定的日志文件或位置,收集日志事件,并将它们转发到 Elasticsearch 或 Logstash 中。
 	
 	Beats 是一个轻量级日志采集器,Beats 家族有 6 个成员,早期的 ELK 架构中使用 Logstash 收集、解析日志,但是 Logstash 对内存、cpu、io 等资源消耗比较高。相比 Logstash,Beats 所占系统的 CPU和内存几乎可以忽略不计。



目前 Beats 包含六种工具: 
1、Packetbeat:网络数据(收集网络流量数据) 
2、Metricbeat:指标(收集系统、进程和文件系统级别的 CPU 和内存使用情况等数据) 
3、Filebeat:日志文件(收集文件数据) 
4、Winlogbeat:windows 事件日志(收集 Windows 事件日志数据) 
5、Auditbeat:审计数据(收集审计日志) 
6、Heartbeat:运行时间监控(收集系统运行时的数据)

filebeat传输方案

1、output.elasticsearch 
如果你希望使用 filebeat 直接向 elasticsearch 输出数据,需要配置 output.elasticsearch 
output.elasticsearch: 
	hosts: ["192.168.40.180:9200"] 


2、output.logstash 
如果使用 filebeat 向 logstash 输出数据,然后由 logstash 再向 elasticsearch 输出数据,需要配置 output.logstash。 logstash 和 filebeat 一起工作时,如果 logstash 忙于处理数据,会通知FileBeat 放慢读取速度。一旦拥塞得到解决,FileBeat 将恢复到原来的速度并继续传播。这样,可以减少管道超负荷的情况。 
output.logstash: 
	hosts: ["192.168.40.180:5044"] 


3、output.kafka 
如果使用 filebeat 向 kafka 输出数据,然后由 logstash 作为消费者拉取 kafka 中的日志,并再向elasticsearch 输出数据,需要配置 output.logstash 
output.kafka: 
	enabled: true 
	hosts: ["192.168.40.180:9092"] 
	topic: elfk8stest

4.1.3、logstash简介

logstash:是一个开源的数据收集引擎,具有实时的管道功能。

Logstash 可以动态地将来自不同数据源的数据统一起来,并将数据标准化到你所选择的目的地。

Logstash 是一个应用程序日志、事件的传输、处理、管理和搜索的平台。你可以用它来统一对应用程序日志进行收集管理,提供 Web 接口用于查询和统计。

Logstash 有两个必要元素:input 和 output ,一个可选元素:filter。 这三个元素,分别代表
Logstash 事件处理的三个阶段:输入 > 过滤器 > 输出
	输入:采集各种样式、大小和来源的数据(kafka、file、syslog)
	过滤器:实时解析和转换数据
	输出:选择你的存储,导出你的数据(es)

4.2、安装elasticsearch 组件

在安装 Elasticsearch 集群之前,我们先创建一个名称空间,在这个名称空间下安装日志收工具elasticsearch、fluentd、kibana。我们创建一个 kube-logging 名称空间,将 EFK 组件安装到该名称空间中。

创建名称空间

vim kube-logging.yaml

kind: Namespace 
apiVersion: v1 
metadata: 
  name: kube-logging

4.2.1、通过statefulset部署es

我们需要部署一个有 3 个节点的 Elasticsearch 集群。并且创建 Storageclass,实现存储类动态供给,我们使用 3 个 Elasticsearch Pods 可以避免高可用中的多节点群集中发生的“裂脑”的问题。 Elasticsearch 脑裂可参考如下:
https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-node.html#split-brain

创建 headless service 服务

vim elasticsearch_svc.yaml 

kind: Service 
apiVersion: v1 
metadata: 
  name: elasticsearch
  namespace: kube-logging 
  labels: 
    app: elasticsearch 
spec: 
  selector: 
    app: elasticsearch 
  clusterIP: None 
  ports: 
  - port: 9200 
    name: rest 
  - port: 9300 
    name: inter-node

4.2.2、k8s部署动态存储

安装nfs

##创建动态存储步骤如下
	1、安装后端存储nfs
	2、部署nfs供应商
	3、创建storageclass存储类

##	安装后端存储nfs(两个节点都安装启动)
yum install nfs-utils -y 
systemctl start nfs
systemctl enable nfs.service 

##	master01 上创建一个 nfs 共享目录 
mkdir /data/v1 -p  
[root@omaster01 ~]# vim /etc/exports 
/data/v1 192.168.163.130/24(rw,no_root_squash) #加载配置,使配置生效 

exportfs -arv 
systemctl restart nfs

创建 nfs 作为存储的供应商

## 创建 sa 

vim serviceaccount.yaml 

apiVersion: v1 
kind: ServiceAccount 
metadata: 
  name: nfs-provisioner
  

##	对 sa 做 rbac 授权
kubectl apply -f rbac.yaml

vim rbac.yaml
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: nfs-provisioner-runner
rules:
  - apiGroups: [""]
    resources: ["persistentvolumes"]
    verbs: ["get", "list", "watch", "create", "delete"]
  - apiGroups: [""]
    resources: ["persistentvolumeclaims"]
    verbs: ["get", "list", "watch", "update"]
  - apiGroups: ["storage.k8s.io"]
    resources: ["storageclasses"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["events"]
    verbs: ["create", "update", "patch"]
  - apiGroups: [""]
    resources: ["services", "endpoints"]
    verbs: ["get"]
  - apiGroups: ["extensions"]
    resources: ["podsecuritypolicies"]
    resourceNames: ["nfs-provisioner"]
    verbs: ["use"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: run-nfs-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-provisioner
    namespace: default
roleRef:
  kind: ClusterRole
  name: nfs-provisioner-runner
  apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-provisioner
rules:
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-provisioner
    namespace: default
roleRef:
  kind: Role
  name: leader-locking-nfs-provisioner
  apiGroup: rbac.authorization.k8s.io

通过 deployment 创建 pod 用来运行 nfs-provisioner

vim deployment-nfs.yaml

kind: Deployment
apiVersion: apps/v1
metadata:
  name: nfs-provisioner
spec:
  selector:
    matchLabels:
      app: nfs-provisioner
  replicas: 1
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: nfs-provisioner
    spec:
      serviceAccount: nfs-provisioner
      containers:
        - name: nfs-provisioner
          image: registry.cn-beijing.aliyuncs.com/mydlq/nfs-subdir-external-provisioner:v4.0.0
          imagePullPolicy: IfNotPresent
          volumeMounts:
            - name: nfs-client-root
              mountPath: /persistentvolumes
          env:
            - name: PROVISIONER_NAME
              value: example.com/nfs
            - name: NFS_SERVER
              value: 192.168.163.130
            - name: NFS_PATH
              value: /data/v1
      volumes:
        - name: nfs-client-root
          nfs:
            server: 192.168.163.130
            path: /data/v1

创建 storageclass

vim storageclass.yaml

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: do-block-storage
provisioner: example.com/nfs

4.2.3、安装 elasticsearch 集群

statefulset源清单文件

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: es-cluster
  namespace: kube-logging
spec:
  serviceName: elasticsearch
  replicas: 3
  selector:
    matchLabels:
      app: elasticsearch
  template:
    metadata:
      labels:
        app: elasticsearch
    spec:
      containers:
      - name: elasticsearch
        image: elasticsearch:7.16.2
        imagePullPolicy: IfNotPresent
        resources:
            limits:
              cpu: 1000m
            requests:
              cpu: 100m
        ports:
        - containerPort: 9200
          name: rest
          protocol: TCP
        - containerPort: 9300
          name: inter-node
          protocol: TCP
        volumeMounts:
        - name: data
          mountPath: /usr/share/elasticsearch/data
        env:
          - name: cluster.name
            value: k8s-logs
          - name: node.name
            valueFrom:
              fieldRef:
                fieldPath: metadata.name
          - name: discovery.seed_hosts
            value: "es-cluster-0.elasticsearch.kube-logging.svc.cluster.local,es-cluster-1.elasticsearch.kube-logging.svc.cluster.local,es-cluster-2.elasticsearch.kube-logging.svc.cluster.local"
          - name: cluster.initial_master_nodes
            value: "es-cluster-0,es-cluster-1,es-cluster-2"
          - name: ES_JAVA_OPTS
            value: "-Xms512m -Xmx512m"
      initContainers:
      - name: fix-permissions
        image: busybox
        imagePullPolicy: IfNotPresent
        command: ["sh", "-c", "chown -R 1000:1000 /usr/share/elasticsearch/data"]
        securityContext:
          privileged: true
        volumeMounts:
        - name: data
          mountPath: /usr/share/elasticsearch/data
      - name: increase-vm-max-map
        image: busybox
        imagePullPolicy: IfNotPresent
        command: ["sysctl", "-w", "vm.max_map_count=262144"]
        securityContext:
          privileged: true
      - name: increase-fd-ulimit
        image: busybox
        imagePullPolicy: IfNotPresent
        command: ["sh", "-c", "ulimit -n 65536"]
        securityContext:
          privileged: true
  volumeClaimTemplates:
  - metadata:
      name: data
      labels:
        app: elasticsearch
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: do-block-storage
      resources:
        requests:
          storage: 2Gi

4.2.4、安装 kibana 可视化

apiVersion: v1
kind: Service
metadata:
  name: kibana
  namespace: kube-logging
  labels:
    app: kibana
spec:
  ports:
  - port: 5601
  selector:
    app: kibana
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kibana
  namespace: kube-logging
  labels:
    app: kibana
spec:
  replicas: 1
  selector:
    matchLabels:
      app: kibana
  template:
    metadata:
      labels:
        app: kibana
    spec:
      containers:
      - name: kibana
        image: docker.elastic.co/kibana/kibana:7.2.0
        imagePullPolicy: IfNotPresent
        resources:
          limits:
            cpu: 1000m
          requests:
            cpu: 100m
        env:
          - name: ELASTICSEARCH_URL
            value: http://elasticsearch.kube-logging.svc.cluster.local:9200
        ports:
        - containerPort: 5601

4.2.5、安装 fluentd 组件

我们使用 daemonset 控制器部署 fluentd 组件,这样可以保证集群中的每个节点都可以运行同样fluentd 的 pod 副本,这样就可以收集 k8s 集群中每个节点的日志
	在 k8s 集群中,容器应用程序的输入输出日志会重定向到 node 节点里的 json 文件中,fluentd 可以 tail 和过滤以及把日志转换成指定的格式发送到 elasticsearch 集群中。
	除了容器日志,fluentd 也可以采集 kubelet、kube-proxy、docker 的日志。

fluentd资源清单文件

apiVersion: v1
kind: ServiceAccount
metadata:
  name: fluentd
  namespace: kube-logging
  labels:
    app: fluentd
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: fluentd
  labels:
    app: fluentd
rules:
- apiGroups:
  - ""
  resources:
  - pods
  - namespaces
  verbs:
  - get
  - list
  - watch
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: fluentd
roleRef:
  kind: ClusterRole
  name: fluentd
  apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
  name: fluentd
  namespace: kube-logging
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd
  namespace: kube-logging
  labels:
    app: fluentd
spec:
  selector:
    matchLabels:
      app: fluentd
  template:
    metadata:
      labels:
        app: fluentd
    spec:
      serviceAccount: fluentd
      serviceAccountName: fluentd
      tolerations:
      - key: node-role.kubernetes.io/master
        effect: NoSchedule
      containers:
      - name: fluentd
        image: fluent/fluentd-kubernetes-daemonset:v1.4.2-debian-elasticsearch-1.1
        imagePullPolicy: IfNotPresent
        env:
          - name:  FLUENT_ELASTICSEARCH_HOST
            value: "elasticsearch.kube-logging.svc.cluster.local"
          - name:  FLUENT_ELASTICSEARCH_PORT
            value: "9200"
          - name: FLUENT_ELASTICSEARCH_SCHEME
            value: "http"
          - name: FLUENTD_SYSTEMD_CONF
            value: disable
        resources:
          limits:
            memory: 512Mi
          requests:
            cpu: 100m
            memory: 200Mi
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
      terminationGracePeriodSeconds: 30
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers