云原生技术的宗旨是帮助企业组织在现代动态的环境下(公有云,私有云和混合云)高效管理应用的全生命周期, 围绕kubernetes,用户可以灵活选择各个模块组件,搭建平台以满足多样需求。 云原生风景图完整地展示了企业实践云原生所需要处理的问题。 这张图很有参考意义。

图中将日志,监控和链路追踪都放到了监控和分析,这是一个趋势,背负着统一OpenTraceOpenCensueOpenTelemetry就是为了实现MetricsTracingLogging的融合。 这三者的融合才是一个完整的用户故事。基于 Metrics 的告警及时发现异常,通过 Tracing 定位问题的模块,根据模块的日志找到问题的根源, 最后基于问题,调整 Metrics(增加或者调整告警的阀值等),以便下次可以更早发现或预防问题。

日志,监控和链路追踪三中平台的基本架构是一致的:数据收集 -> 数据处理 -> 数据展示(告警),主要差异是数据源的不一致, 所以搭建三套平台显然很冗余,也不便于维护管理。但是很可惜,目前很没有一套成熟的方案,不过相信随着社区的发展,不会等太久。

结合最近的工作,本片博文,我将给大家简单介绍EFK Stack日志平台,并如何通过Helm快速部署一套EFK集群。 本文内容较为浅显,适合刚接触日志平台的人阅读🤣(因为笔者也没有EFK丰富的实践经验,splunk的使用经验不知道能不能算)。EFK涉及的组件文档十分完善, 还请以文档作为主要参考。

主流的方案

  1. Splunk Enterprise:Splunk 公司产品,生态丰富且成熟度较高,SPEL 查询语言功能强大,维护方便,缺点是收费;
  2. Elastic Stack(ELK Stack):由 Elasticsearch,Logstash,Kibana,Filebeats 组件组成,生态丰富,不完全开源, 需要订阅才能解锁一些高级(🤣不常用)功能;
  3. EFK Stack: 和Elastic Stack的组件类似,不过日志收集器使用的是fluented,fluented对容器应用的支持很完善,和kubernetes集群更加匹配;
  4. PLG Stack(Grafana Loki): 由 Promtail,Loki 和 Grafana 组成,Grafana 强大的可视化加成,借鉴了 Prometheus 的设计思路;

EFK 方案

kubernetes 官方中 Elasticsarch 插件包含 Elasticsearch,Fluented 和 kibana 三个,Elasticsearch 负责存储日志数据的存储和索引查询, Fluented 从集群收集数据并发送到 Elasticsearch,Kibana 提供数据的可视化功能。

组件介绍

Elasticsearch(es)

一般来说,根据集群的规模和日志量来部署 es 集群,数据存储在不同节点的碎片中,集群有多个类型节点组成,以提高可用性,有以下类型的节点:

  • 主节点(Master node):集群控制器,最少需要 3 个,采用的主从模式
  • 数据节点(Data node):保存索引数据并且执行数据查询任务
  • 提取节点(Ingest node):通过 ingest pipeline 对文档执行预处理操作,以便在索引文档之前对文档进行转换或者增强。
  • 协调节点(Coordinationg node):协调保存在不同节点数据的搜索请求,索引请求之类的操作,这种类型的节点需要足够的内存和 CPU 资源;

一个节点默认情况下可以同时是多个类型的节点,当集群增大时,最好将其分开配置。下图显示如何将数据存储在主碎片和副本碎片中, 以便在节点之间分散负载并提高数据的可用性。

每个碎片中的数据存储在一个反向索引中(inverted index),下图展示了数据如何存储在反向索引中:

Fluented

Fluented 是开源日志收集器,是 CNCF 的毕业项目,通过丰富的插件系统实现多种日志源的收集,处理和转发功能。

fluentd 既可以作为日志收集器安装到每一个结点上, 也可以作为一个服务端收集各个结点上报的日志流。 你甚至也可以在各个结点上都部署 fluentd 收集日志,然后上报到一个 fluentd 集群做统一处理, 然后再转发到最终的日志存储服务器。

fluented 的核心是各种命令块(directives),每种命令块完成一项功能,最终通过组合这些命令块以 pipline 的形式处理和分发日志。

主要命令有:

  • source:收集日志源文件,如通过一下配置收集 kubernetes 所有容器日志,如果通过容器方式部署,需要将/var/lib/docker/containers(默认配置)目录挂在到容器中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<source>
@id fluentd-containers.log
@type tail # Fluentd 内置的输入方式,其原理是不停地从源文件中获取新的日志。
path /var/log/containers/*.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>
  • match: 指定动作,通过 tag 匹配 source,然后执行指定命令来分发日志,match 是从上往下依次匹配的,一旦一个日志流被匹配上, 就会停止配置,以下配置将日志数据发送到 es 集群中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<match **>                # 唯一标识符,后面匹配一个日志源,**代表全部数据
@id elasticsearch # 唯一标识符
@type elasticsearch # 支持输出插件的标识符,elasticsearch是一个内置插件
@log_level info # 指定需要捕获日志的级别
include_tag_key true
type_name fluentd
host "#{ENV['OUTPUT_HOST']}" # es主机
port "#{ENV['OUTPUT_PORT']}" # es主机端口
logstash_format true
<buffer> # Fluentd 允许在目标不可用时进行缓存,比如,如果网络出现故障或者 Elasticsearch 不可用的时候。缓冲区配置也有助于降低磁盘的 IO。
@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 "#{ENV['OUTPUT_BUFFER_CHUNK_LIMIT']}"
queue_limit_length "#{ENV['OUTPUT_BUFFER_QUEUE_LIMIT']}"
overflow_action block
</buffer>

  • filter: 对数据进行过滤,其和 match 不同,可以通过 filter 串联成 pipeline,对数据进行窜行处理。比如我们只采集具有logging=true标签的 Pod 的日志, 以下是示例配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 删除无用的属性
<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>

fluented配置文件可能比较繁琐,不过通过部署fluented-elastic版本,来简化配置,我们只需要在默认配置文件进行少量的修改即可。

kibana

Kibana是一个类似Grafana的数据可视化组件。

使用 Helm 部署

部署前,你需要准备好 kubernetes 集群并且安装 helm,建议创建新的 namespace 部署这些组件。

Step1:安装 es 集群 es以Statefulset的方式部署以保证稳定的,持久的存储。选择bitnami的chart,国内能够正常拉取默认镜像。 更具需求修改配置文件。

1
helm install elasticsearch bitami/elasticsearch -f custom.yaml

Step2:安装 fluented-elasticsearch 通过Daemonset方式部署,收集每个节点的数据,kiwigrid仓库默认中的镜像国内能够正常拉取。

1
2
3
helm repo add kiwigrid https://kiwigrid.github.io
helm update
helm install fluented kiwigrid/fluentd-elasticsearch -f custom.yaml

Step3:安装 kibana 直接通过Deoplyment方式部署。

1
helm install kibana bitnami/kibana -f custom.yaml

step4:验证是否部署成功

下图是部署后Pod列表:

1
2
3
4
5
6
7
8
9
10
11
12
➜  Temp kubectl get pods
NAME READY STATUS RESTARTS AGE
elasticsearch-1594201009-elasticsearch-coordinating-only-6s55cs 1/1 Running 0 6d1h
elasticsearch-1594201009-elasticsearch-coordinating-only-6t7k6v 1/1 Running 0 6d1h
elasticsearch-1594201009-elasticsearch-data-0 1/1 Running 0 6d1h
elasticsearch-1594201009-elasticsearch-data-1 1/1 Running 0 6d1h
elasticsearch-1594201009-elasticsearch-master-0 1/1 Running 0 6d1h
elasticsearch-1594201009-elasticsearch-master-1 1/1 Running 0 6d1h
fluentd-elasticsearch-1594208939-8kmzz 1/1 Running 0 5d9h
fluentd-elasticsearch-1594208939-h4znp 1/1 Running 0 5d9h
fluentd-elasticsearch-1594208939-tcs9c 1/1 Running 0 5d9h
kibana-1594207892-54b9cbd57-hsfss

这里创建一个简单的应用,定时往标准输出打印日志:

counter.yaml
1
2
3
4
5
6
7
8
9
10
11
apiVersion: v1
kind: Pod
metadata:
name: counter
labels:
logging: "true"
spec:
containers:
- name: count
image: busybox
args: [/bin/sh,-c,'i=0;while true;do echo "$i: $(date)"; i=$((i+1)); sleep 1;done']

部署测试pod,通过log可以看到日志输出。

1
2
3
4
5
6
7
8
9
10
11
12
➜  Temp kubectl create -f counter.yaml 

➜ Temp kubectl logs counter
0: Thu Jul 9 06:21:27 UTC 2020
1: Thu Jul 9 06:21:28 UTC 2020
2: Thu Jul 9 06:21:29 UTC 2020
3: Thu Jul 9 06:21:30 UTC 2020
4: Thu Jul 9 06:21:31 UTC 2020
5: Thu Jul 9 06:21:32 UTC 2020
6: Thu Jul 9 06:21:33 UTC 2020
7: Thu Jul 9 06:21:34 UTC 2020
...

在kibana中添加对应的索引,即可看到对应的数据:

everything-all-in 这里介绍一个比较方便的 chart stable/elastic-stack,这个 chart 和并了以上上个 chart,同时还可以使用其他组件进行替换,如可以使用 Filebeats 替换 fluented。

1
$ helm install efk-stack stable/elastic-stack --set logstash.enabled=false --set fluentd.enabled=true --set fluentd-elasticsearch.enabled=true

总结

本文,给大家介绍了日志平台方案 EFK,并通过Helm部署了实验环境,不涉及到参数调优等介绍, 也不涉及到es的查询和kibnama如何使用,有splunk或类似平台使用经验的读者能够很快上手。

参考

  1. docker容器日志管理最佳实践
  2. EFK vs PLG Stack