前言

上一期主要和大家介绍从全局维度考虑如何去构建K8s中的日志系统,本期我们从实践角度出发来一步步构建K8s中的日志监控体系。构建日志系统的第一步是如何去产生这些日志,而这也往往是最繁杂最困难的一步。

2009年阿里云春节上班第一天,在北京一间连暖气都没有的办公室里,一帮工程师一边口呼白气,一边敲出了“飞天”的第一行代码。“飞天”作为阿里云的核心技术平台,其英文名Apsara——来自吴哥王朝的阿仆萨罗飞天仙女的名字。

阿里云飞天系统的第一行代码就是为了编写一个日志系统,而现在apsara logging的日志库应用在飞天所有的系统中,包括盘古、女娲、伏羲、洛神...

Kubernetes中日志重要性

通常日志最基础的作用是记录程序的运行轨迹,在此之上会衍生出非常多的功能,例如线上监控、告警、运营分析、安全分析等等(详情可以参见第一篇文章),这些功能反过来也对日志具备一定的要求,我们需要尽可能的将日志规范化,以减少收集、解析、分析的代价。

在Kubernetes中,环境的动态性很强,日志基本上都是易失的,因此需要实时将日志采集到中心的存储中,为了配合日志采集,对于日志的输出、采集会有更多的要求。

下述我们列举了Kubernetes中,日志输出的常见注意事项(其中标记 (*)的是Kubernetes中特有的项目):

  1. 如何选择日志等级
  2. 日志内容规范
  3. 合理控制日志输出量
  4. 选择多种日志输出目标
  5. 控制日志性能消耗
  6. 如何选择日志库
  7. 日志形态选择(*)
  8. 日志是否落盘以及落盘介质(*)
  9. 如何保证日志存储周期(*)

1. 如何选择日志等级

日志等级是用来区分日志对应事件严重程度的说明,这是所有日志中必须具备的一个选项。通常日志会分为6个不同的等级:

  • FATAL(致命):用来输出非常严重或预期中不会发生的错误,遇到此种错误应当立即报警并人工介入处理。
  • ERROR (错误):非预期中的错误,此种错误可能导致部分系统异常但不会影响核心业务和系统正常运行。
  • WARN(警告):潜在的危险或值得关注的信息(比较核心的路径)。
  • INFO(信息):应用执行过程中的详细信息,一般通过该信息可以看到每个请求的主要执行过程。
  • DEBUG(调试):用于线下调试的日志信息,用于分析应用执行逻辑,线上应用切勿开启。
  • TRACE(跟踪):输出最细致的运行轨迹,可能包含涉及的数据内容。

作为程序员,一定要合理设置日志等级,个人在开发过程中总结以下几点经验:

  1. FATAL类型日志一定是非常严重的错误、需要人工处理的场景打印的。
  2. ERROR和WARNING的区别很多程序员难以选择,可以从告警角度考虑:ERROR一般需要告警,WARNING不需要。
  3. 日志等级一方面是为了能够表示日志的严重程度,另一方面也是为了控制应用程序的日志输出量,通常线上只能打开INFO或WARN的日志。
  4. DEBUG日志可以多打,方便分析问题。
  5. 所有用户请求日志,必须记录。
  6. 对于不确定的外部系统调用,日志需尽可能覆盖周全。
  7. 程序中的日志库需要具备运行期间变更日志等级的能力,方便在遇到问题需要分析时临时更改日志等级。
  8. 通常在新功能上线,涉及的日志可适当提升一个等级,方便实时观察和监控,待稳定后再调整到正常(记得加上注释,方便改回来)。

2. 日志内容规范

通常在没有约束的情况下,程序员的发挥天马行空,各种日志内容都会出现,这些只有开发自己才能看懂的日志很难进行分析和告警。因此我们需要一个日志顶向下的规范来约束项目中的开发人员,让所有的日志看起来是一个人打印的而且是易于分析的。

2.1 日志的字段

日志中通常必备的字段有:Time、Level、Location。对于特定模块/流程/业务,还需要有一些Common的字段,例如:

  1. 如果使用Trace系统,可以把TraceID附加到日志中。
  2. 固定的流程需要附加对应的字段,例如订单的生命周期中,一定要有订单号、用户ID等信息,这些信息可以通过Context附加到对应流程的日志实例上。
  3. HTTP请求需要记录:URL、Method、Status、Latency、Inflow、OutFlow、ClientIP、UserAgent等,详情可以参考Nginx日志格式。
  4. 如果多个模块的日志都打印到同一个流/文件中,必须有字段标识模块名。

日志的字段规约最好由运维平台/中间件平台自顶向下推动,约束每个模块/流程的程序员按照规定打印日志。

2.2 日志表现形式

通常我们建议使用KeyValue对形式的日志格式,比如我们阿里的飞天日志库采用的就是这种形式:

[2019-12-30 21:45:30.611992]    [WARNING]       [958] [block_writer.cpp:671]  path:pangu://localcluster/index/3/prom/7/1577711464522767696_0_1577711517     min_time:1577712000000000       max_time:1577715600000000       normal_count:27595      config:prom     start_line:57315569     end_line:57343195       latency(ms):42  type:AddBlock

KeyValue对的日志可以完全自解析且易于理解,同时便于日志采集时自动解析。

另外推荐的是JSON日志格式,支持以JSON格式输出的日志库很多,而且大部分的日志采集Agent都支持JSON格式的日志收集。

{"addr":"tcp://0.0.0.0:10010","caller":"main.go:98","err":"listen tcp: address tcp://0.0.0.0:10010: too many colons in address","level":"error","msg":"Failed to listen","ts":"2019-03-08T10:02:47.469421Z"}

  • 注意:绝大部分场景不建议使用非可读的日志格式(例如ProtoBuf、Binlog等)。

2.3 单条日志换行问题

非必要情况下,尽量不要一条日志输出成多行,这种对于采集、解析和索引的代价都比较高。

3. 合理控制日志输出量

日志的输出量直接影响到磁盘使用以及对于应用的性能消耗,日志太多也不利于查看、采集、分析;日志太少不利于监控,同时在出现问题的时候没办法调查。一般线上应用需合理控制日志的数据量:

  1. 服务入口的请求和响应日志没有特殊原因都要输出并采集,采集的字段可以根据需求调整。
  2. 错误日志一般都要打印,如果太多,可以使用采样方式打印。
  3. 减少无效日志输出,尤其是循环中打印日志的情况需尽量减少。
  4. 请求型的日志(比如Ingress、Nginx访问日志)一般不超过5MB/s(500字节每条,不超过1W/s),应用程序日志不超过200KB/s(2KB每条,不超过100条/s)。

4. 选择多种日志输出目标

建议一个应用不同类型的日志输出到不同的目标(文件),这样便于分类采集、查看和监控。例如:

  1. 访问日志单独放到一个文件,如果域名不多,可以按照一个域名一个文件的形式。
  2. 错误类的日志单独放一个文件,单独配置监控告警。
  3. 调用外部系统的日志单独放一个文件,便于后续对账、审计。
  4. 中间件通常都由统一的平台提供,日志一般单独打印一个文件。

5. 控制日志性能消耗

日志作为业务系统的辅助模块,一定不能影响到业务正常的工作,因此日志模块的性能消耗需要单独额外注意,一般在选择/开发日志库时,需要对日志库进行性能测试,确保正常情况下日志的性能消耗不超过整体CPU占用的5%。

  • 注意:一定要确保日志打印是异步的,不能阻塞业务系统运行。

6. 如何选择日志库

开源的日志库非常多,基本每个语言都有数十种,选择一个符合公司/业务需求的日志库需要精挑细选,有一个简单的指导原则是尽可能使用比较流行的日志库的稳定版本,入坑的几率要小一点。例如:

  1. Java 使用 Log4J、LogBack。
  2. Golang 使用 go-kit。
  3. Python默认集成的日志库大部分场景都够用,建议阅读一下CookBook。
  4. C++ 推荐使用 spdlog,高性能、跨平台。

7. 日志形态选择

在虚拟机/物理机的场景中,绝大部分应用都以文件的形式输出日志(只有一些系统应用输出到syslog/journal);而在容器场景中,多了一个标准输出的方式,应用把日志打到stdout或stderr上,日志会自动进入到docker的日志模块,可以通过 docker logs 或 kubectl logs 直接查看。

容器的标准输出只适应于比较单一的应用,例如K8s中的一些系统组件,线上的服务类应用通常都会涉及到多个层级(中间件)、和各种服务交互,一般日志都会分为好几类,如果全部打印到容器的标准输出,很难区分处理。

同时容器标准输出对于DockerEngine的性能消耗特别大,实测10W/s的日志量会额外占用DockerEngine 1个核心的CPU(单核100%)。

8. 日志是否落盘以及落盘介质

在Kubernetes中,还可以将日志库直接对接日志系统,日志打印的时候不落盘而直接传输到日志系统后端。这种使用方式免去了日志落盘、Agent采集的过程,整体性能会高很多。这种方式我们一般只建议日志量极大的场景使用,普通情况下还是直接落盘,相比直接发送到后端的方式,落盘增加了一层文件缓存,在网络失败的情况下还能缓存一定的数据,在日志系统不可用的情况下我们的研发运维同学可以直接查看文件的日志,提高整体的可靠性。

Kubernetes提供了多种存储方式,一般在云上,都会提供本地存储、远程文件存储、对象存储等方式。由于日志写入的QPS很高,和应用也直接相关,如果使用远程类型的存储,会额外多2-3次网络通信开销。我们一般建议使用本地存储的方式,可以使用HostVolume或者EmptyDir的方式,这样对于写入和采集的性能影响会尽可能的小。

9. 如何保证日志存储周期

相比传统虚拟机/物理机的场景,Kubernetes对于节点、应用层提供了强大的调度、容错、缩/扩容能力,我们通过Kubernetes很容易就能让应用获得高可靠运行、极致弹性。这些优势带来的一个现象是:节点动态创建/删除、容器动态创建/删除,这样日志也会随时销毁,没办法保证日志的存储周期能够满足DevOps、审计等相关的需求。

在动态的环境下实现日志的长期存储只能通过中心化的日志存储来实现,通过实时的日志采集方式,将各个节点、各个容器的日志在秒级内采集到日志中心系统上,即使节点/容器挂掉也能够通过日志还原当时的现场。

总结

日志输出是日志系统建设中非常重要的环节,公司/产品线一定要遵循一个统一的日志规范,这样才能保证后续日志采集、分析、监控、可视化能够顺利进行。

后面的章节会介绍如何为Kubernetes规划日志采集和存储的最佳实践,敬请期待。

将输出结果以json类型打印在控制台上_系列文章:Kubernetes中日志的正确输出姿势...相关推荐

  1. 日志库 winston 的学习笔记 - logger.info 打印到控制台上的实现原理

    if (process.env.NODE_ENV !== 'production') {logger.add(new winston.transports.Console({format: winst ...

  2. opencv将Mat读入的图像的像素值打印在控制台上

    //将Mat读入的图像像素值打印在控制台上,这里的Img为单通道 方法一:cv::Mat Img; IplImage *src;src=&IplImage(Img);for(int i=0;i ...

  3. JDBC查询所有记录打印到控制台上

    package cn.itcast.jdbc;import java.sql.Connection; import java.sql.DriverManager; import java.sql.Re ...

  4. xml文件 卷积神经网络_理解卷积神经网络中的输入与输出形状(Keras实现)

    即使我们从理论上理解了卷积神经网络,在实际进行将数据拟合到网络时,很多人仍然对其网络的输入和输出形状(shape)感到困惑.本文章将帮助你理解卷积神经网络的输入和输出形状. 让我们看看一个例子.CNN ...

  5. python中能够处理的最大整数是_实例讲解Python中整数的最大值输出

    在Python中可以存储很大的值,如下面的Python示例程序: x = 10000000000000000000000000000000000000000000; x = x + 1 print ( ...

  6. python求三个整数最大值_实例讲解Python中整数的最大值输出

    实例讲解Python中整数的最大值输出 在Python中可以存储很大的值,如下面的Python示例程序: x = 1000000000000000000000000000000000000000000 ...

  7. javaweb输出所有学生信息_遍历工作表中所有形状并输出信息

    大家好,我们今日讲解"VBA信息获取与处理"教程中第十九个专题"工作表中对SHAPE信息的获取及处理"的第1节"遍历工作表中所有形状并输出信息&quo ...

  8. 日志的log中如何输出变量_如何在kubernetes中优雅的输出日志

    背景 我们经常需要在kubernetes中运行一些任务性质的Job或者Pod.在调试过程中,我们对日志有如下两个需求 需求一:日志输出到stdout.因为stdout的输出,可以非常方便的通过kube ...

  9. url的post请求 Content-Type:application/json类型 Java后端接收(^_^)

    Content-Type为application/json时,假设前台传输的数据为data: {name:'wyc',age:12} 第一种情况在springmvc框架下 处理方法为前台post请求, ...

最新文章

  1. Java多线程-线程的调度(合并)
  2. 看不懂花书?博士教你如何深入深度学习,从编程基础到完整的项目实战
  3. Python学习进程
  4. 侣信即时通讯系统的技术解析
  5. tensorflow 启动Session(tf.Session(),tf.InteractivesSession(),tf.train.Supervisor().managed_session() )
  6. javascript测试框架mocha
  7. python爬虫之路scrapy
  8. 安装oracle11g未找到文件WFMLRSVCApp.ear文件
  9. 源码安装 nginx 并设置为 service
  10. Thinking in Java 14.7 动态代理
  11. vs2017远程编译linux教程,Visual Studio 2017 远程编译调试 Linux 上已存在的通过 Samba 共享的 CMake 工程...
  12. 前端开源项目周报0103
  13. 巧用 10分钟邮箱 申请小红伞 免费KEY 92天
  14. Fujitsu Lifebook U1010安装XP TabletPC 2005完全攻略
  15. 数据可视化大屏demo制作(画图)
  16. TMOS系统之Trunks
  17. 【绘画素材】日系插画“人物表情”素材参考!告别脸部僵硬~
  18. C. Pythagorean Triples
  19. 网络安全意识 | 以人为本,安全意识工作大有可为
  20. 合肥通用职业技术学院计算机专业,2020年安徽高考专科提前批及大专录取时间及录取结果查询...

热门文章

  1. 【Android】 认识反射机制(Reflection)
  2. Vue之webpack之vue
  3. tensorflow之数据加载
  4. 网卡重启影响nfs吗_NFS性能优化 不完整介绍
  5. ipv6单播地址包括哪两种类型_探秘联接|技术小课堂之BRAS设备IPv6地址分配方式...
  6. java程序设计比赛心得体会_对Java程序设计的感想.doc
  7. nodejs连接mysql哪个版本_nodejs连接mysql
  8. ubuntu16.04 安装kicad5.1
  9. 2021湖南高考成绩分段查询,2021年湖南高考成绩排名查询系统,湖南高考位次排名表...
  10. mysql startswith_Java startsWith()方法