查看原文
其他

日志源解析|K8s 集群部署 CLS 日志服务原理及场景实现

kahing、willyi 腾讯云原生 2022-03-18

kahing,腾讯云CLS产品经理,负责CLS云原生日志的云产品接入工作。

willyi,腾讯云高级开发工程师,负责CLS云原生日志服务云原生生态建设,致力于大规模云原生设施的落地和实践工作。

简介


日志服务 (Cloud Log Service, CLS)支持采集自建K8s集群上的日志,在进行日志采集前,需要在K8s自建集群上通过CRD定义日志采集配置(LogConfig),并部署安装Log-Provisoner,Log-Agent,以及LogListener。针对使用腾讯云容器服务(Tencent Kubernetes Engine ,TKE)的用户, 可参见 [1]TKE开启日志采集文档,通过控制台快速接入并使用日志服务。

什么是CLS 日志服务?

腾讯云日志服务(Cloud Log Service,CLS)是腾讯云提供的一站式日志服务平台,提供了从日志采集、日志存储到日志检索,图表分析、监控告警、日志投递等多项服务,协助用户通过日志来解决业务运维、服务监控、日志审计等场景问题。

CLS 日志服务https://cloud.tencent.com/product/cls

什么是TKE 容器服务?

腾讯云容器服务(Tencent Kubernetes Engine,TKE)是基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,您可以在托管的云服务器实例集群上轻松运行应用程序。
TKE 容器服务:https://cloud.tencent.com/product/tke

前提条件

下面我们来看几个典型的差异化配置场景。在如下的例子
  1. Kubernetes 1.10 及以上版本集群
  2. 开通日志服务, 创建日志集与日志主题, 并获取日志主题ID(topicId),详细配置请参见[2]创建日志主题 文档
  3. 获取日志主题所在地域的域名(CLS_HOST),详细CLS域名列表请参见 [3]可用地域 文档
  4. 获取访问CLS侧鉴权所需的API密钥ID(TmpSecretId)以及API密钥Key(TmpSecretKey),请前往 [4]API密钥管理 查看

K8s日志采集原理

K8s集群上部署日志采集主要涉及Log-Provisoner,Log-Agent,LogListener三个组件,以及一个LogConfig采集配置。

  • LogConfig:日志采集配置,定义了日志从哪里被采集, 采集后如何解析, 解析后投递至CLS侧的哪个日志主题
  • Log-Provisoner:将LogConfig中定义日志采集配置信息同步至CLS侧
  • Log-Agent:监听LogConfig和节点上容器的变化, 动态计算容器中的日志文件在节点宿主机上的实际位置
  • LogListener:采集节点宿主机上的相应日志文件内容,解析并上传至CLS侧

部署步骤

  1. 定义LogConfig资源类型

  2. 定义LogConfig对象

  3. 创建LogConfig对象

  4. 配置CLS鉴权ConfigMap

  5. 部署cls-provisione

  6. 部署log-agent与loglistener


定义LogConfig资源类型

使用K8s中的Custom Resource Definition(CRD)定义 LogConfig资源类型。
以Master节点路径/usr/local/为例:wget下载CRD.yaml声明文件,使用kubectl定义LogConfig资源类型。操作命令如下:
# wget https://mirrors.tencent.com/install/cls/k8s/CRD.yaml
# kubectl create -f /usr/local/CRD.yaml

定义LogConfig对象

通过创建LogConfig对象定义日志采集配置,即日志从哪里被采集, 采集后如何解析, 解析后投递至CLS侧的哪个日志主题。
以Master节点路径/usr/local/为例:wget下载LogConfig.yaml声明文件
# wget https://mirrors.tencent.com/install/cls/k8s/LogConfig.yaml

LogConfig.yaml声明文件主要分为两个部分:

  • clsDetail:定义日志解析格式,以及目标日志主题ID(topicId)
  • inputDetail:定义采集日志源,即日志从哪里被采集

注意:需将clsDetail中的topicId项配置为您创建的日志主题ID

以下将对日志解析格式以及日志源的配置进行说明:

日志解析格式

CLS支持以下几种日志解析格式:
  • 单行全文格式
  • 多行全文格式
  • 完全正则格式
  • JSON格式
  • 分隔符格式

单行全文格式

单行全文日志是指一行日志内容为一条完整的日志。日志服务在采集的时候,将使用换行符\n来作为一条日志日志的结束符。为了统一结构化管理,每条日志都会存在一个默认的键值__CONTENT__,但日志数据本身不再进行日志结构化处理,也不会提取日志字段,日志属性的时间项由日志采集的时间决定。
假设一条日志原始数据为:
Tue Jan 22 12:08:15 CST 2019 Installed: libjpeg-turbo-static-1.2.90-6.el7.x86_64

LogConfig配置参考示例如下:

apiVersion: cls.cloud.tencent.com/v1
kind: LogConfig
spec:
  clsDetail:
    topicId: xxxxxx-xx-xx-xx-xxxxxxxx
    # 单行日志
    logType: minimalist_log

采集到日志服务的数据为:

__CONTENT__:Tue Jan 22 12:08:15 CST 2019 Installed: libjpeg-turbo-static-1.2.90-6.el7.x86_64

多行全文格式

多行全文日志是指一条完整的日志数据可能跨占多行(例如 Java  stacktrace)。在这种情况下,以换行符\n 为日志的结束标识符就显得有些不合理,为了能让日志系统明确区分开每条日志,采用首行正则的方式进行匹配,当某行日志匹配上预先设置的正则表达式,就认为是一条日志的开头,而下一个行首出现作为该条日志的结束标识符。

多行全文也会设置一个默认的键值__CONTENT__,但日志数据本身不再进行日志结构化处理,也不会提取日志字段,日志属性的时间项由日志采集的时间决定。

假设一条多行日志原始数据为:
2019-12-15 17:13:06,043 [main] ERROR com.test.logging.FooFactory:
java.lang.NullPointerException
    at com.test.logging.FooFactory.createFoo(FooFactory.java:15)
    at com.test.logging.FooFactoryTest.test(FooFactoryTest.java:11)

LogConfig配置的参考如下:

apiVersion: cls.cloud.tencent.com/v1
kind: LogConfig
spec:
  clsDetail:
    topicId: xxxxxx-xx-xx-xx-xxxxxxxx
    # 多行日志
    logType: multiline_log
    extractRule:
      # 只有以日期时间开头的行才被认为是新一条日志的开头,否则就添加换行符\n并追加到当前日志的尾部
      beginningRegex: \d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2},\d{3}\s.+

采集到日志服务的数据为:

__CONTENT__:2019-12-15 17:13:06,043 [main] ERROR com.test.logging.FooFactory:\njava.lang.NullPointerException\n    at com.test.logging.FooFactory.createFoo(FooFactory.java:15)\n    at com.test.logging.FooFactoryTest.test(FooFactoryTest.java:11)

单行-完全正则格式

单行完全正则格式通常用来处理结构化的日志,指将一条完整日志按正则方式提取多个 key-value 的日志解析模式。
假设一条日志原始数据为:
10.135.46.111 - - [22/Jan/2019:19:19:30 +0800] "GET /my/course/1 HTTP/1.1" 127.0.0.1 200 782 9703 "http://127.0.0.1/course/explore?filter%5Btype%5D=all&filter%5Bprice%5D=all&filter%5BcurrentLevelId%5D=all&orderBy=studentNum" "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:64.0) Gecko/20100101 Firefox/64.0"  0.354 0.354

LogConfig配置的参考如下:

apiVersion: cls.cloud.tencent.com/v1
kind: LogConfig
spec:
  clsDetail:
    topicId: xxxxxx-xx-xx-xx-xxxxxxxx
    # 完全正则格式
    logType: fullregex_log
    extractRule:
      # 正则表达式,会根据()捕获组提取对应的value
      logRegex: (\S+)[^\[]+(\[[^:]+:\d+:\d+:\d+\s\S+)\s"(\w+)\s(\S+)\s([^"]+)"\s(\S+)\s(\d+)\s(\d+)\s(\d+)\s"([^"]+)"\s"([^"]+)"\s+(\S+)\s(\S+).*
      beginningRegex: (\S+)[^\[]+(\[[^:]+:\d+:\d+:\d+\s\S+)\s"
(\w+)\s(\S+)\s([^"]+)"\s(\S+)\s(\d+)\s(\d+)\s(\d+)\s"([^"]+)"\s"([^"]+)"\s+(\S+)\s(\S+).*
      # 提取的key列表,与提取的value的一一对应
      keys:  ['remote_addr','time_local','request_method','request_url','http_protocol','http_host','status','request_length','body_bytes_sent','http_referer','http_user_agent','request_time','upstream_response_time']

采集到日志服务的数据为:

body_bytes_sent: 9703
http_host: 127.0.0.1
http_protocol: HTTP/1.1
http_referer: http://127.0.0.1/course/explore?filter%5Btype%5D=all&filter%5Bprice%5D=all&filter%5BcurrentLevelId%5D=all&orderBy=studentNum
http_user_agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:64.0) Gecko/20100101 Firefox/64.0
remote_addr: 10.135.46.111
request_length: 782
request_method: GET
request_time: 0.354
request_url: /my/course/1
status: 200
time_local: [22/Jan/2019:19:19:30 +0800]
upstream_response_time: 0.354

多行-完全正则格式

多行-完全正则模式适用于日志文本中一条完整的日志数据跨占多行(例如 Java 程序日志),可按正则表达式提取为多个 key-value 键值的日志解析模式。若不需要提取 key-value,请参阅多行全文格式进行配置。假设一条日志原始数据为:

[2018-10-01T10:30:01,000] [INFO] java.lang.Exception: exception happened
   at TestPrintStackTrace.f(TestPrintStackTrace.java:3)
   at TestPrintStackTrace.g(TestPrintStackTrace.java:7)
   at TestPrintStackTrace.main(TestPrintStackTrace.java:16)

LogConfig 配置的参考如下:

apiVersion: cls.cloud.tencent.com/v1
kind: LogConfig
spec: 
  clsDetail: 
    topicId: xxxxxx-xx-xx-xx-xxxxxxxx
        #多行-完全正则格式
        logType: multiline_fullregex_log
        extractRule: 
      #行首完全正则表达式,只有以日期时间开头的行才被认为是新一条日志的开头,否则就添加换行符\n并追加到当前日志的尾部
            beginningRegex: \[\d+-\d+-\w+:\d+:\d+,\d+\]\s\[\w+\]\s.*
      #正则表达式,会根据()捕获组提取对应的value
      logRegex: \[(\d+-\d+-\w+:\d+:\d+,\d+)\]\s\[(\w+)\]\s(.*)
            # 提取的 key 列表,与提取的 value 的一一对应
            keys: 
      - time 
      - level 
      - msg

根据提取的 key,采集到日志服务的数据为:

time:2018-10-01T10:30:01,000`
level:INFO`
msg:java.lang.Exception: exception happened
   at TestPrintStackTrace.f(TestPrintStackTrace.java:3)
   at TestPrintStackTrace.g(TestPrintStackTrace.java:7)
   at TestPrintStackTrace.main(TestPrintStackTrace.java:16)

JSON 格式

JSON 格式日志会自动提取首层的 key 作为对应字段名,首层的 value 作为对应的字段值,以该方式将整条日志进行结构化处理,每条完整的日志以换行符\n为结束标识符。

假设一条 JSON 日志原始数据为:
{"remote_ip":"10.135.46.111","time_local":"22/Jan/2019:19:19:34 +0800","body_sent":23,"responsetime":0.232,"upstreamtime":"0.232","upstreamhost":"unix:/tmp/php-cgi.sock","http_host":"127.0.0.1","method":"POST","url":"/event/dispatch","request":"POST /event/dispatch HTTP/1.1","xff":"-","referer":"http://127.0.0.1/my/course/4","agent":"Mozilla/5.0 (Windows NT 10.0; WOW64; rv:64.0) Gecko/20100101 Firefox/64.0","response_code":"200"}

LogConfig配置的参考如下:

apiVersion: cls.cloud.tencent.com/v1
kind: LogConfig
spec:
  clsDetail:
    topicId: xxxxxx-xx-xx-xx-xxxxxxxx
    # JSON格式日志
    logType: json_log

采集到日志服务的数据为:

agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:64.0) Gecko/20100101 Firefox/64.0
body_sent: 23
http_host: 127.0.0.1
method: POST
referer: http://127.0.0.1/my/course/4
remote_ip: 10.135.46.111
request: POST /event/dispatch HTTP/1.1
response_code: 200
responsetime: 0.232
time_local: 22/Jan/2019:19:19:34 +0800
upstreamhost: unix:/tmp/php-cgi.sock
upstreamtime: 0.232
url: /event/dispatch
xff: -

分隔符格式

分隔符日志是指一条日志数据可以根据指定的分隔符将整条日志进行结构化处理,每条完整的日志以换行符\n为结束标识符。日志服务在进行分隔符格式日志处理时,您需要为每个分开的字段定义唯一的 key。

假设您的一条日志原始数据为:
10.20.20.10 ::: [Tue Jan 22 14:49:45 CST 2019 +0800] ::: GET /online/sample HTTP/1.1 ::: 127.0.0.1 ::: 200 ::: 647 ::: 35 ::: http://127.0.0.1/

LogConfig配置的参考如下:

apiVersion: cls.cloud.tencent.com/v1
kind: LogConfig
spec:
  clsDetail:
    topicId: xxxxxx-xx-xx-xx-xxxxxxxx
    # 分隔符日志
    logType: delimiter_log
    extractRule:
      # 分隔符
      delimiter: ':::'
      # 提取的key列表,与被分割的字段一一对应
      keys: ['IP','time','request','host','status','length','bytes','referer']

采集到日志服务的数据为:

IP: 10.20.20.10
bytes: 35
host: 127.0.0.1
length: 647
referer: http://127.0.0.1/
request: GET /online/sample HTTP/1.1
status: 200
time: [Tue Jan 22 14:49:45 CST 2019 +0800]

日志源

CLS支持以下几种集群日志源:
  • 容器标准输出
  • 容器文件
  • 主机文件

容器标准输出

示例1:采集default命名空间中的所有容器的标准输出

apiVersion: cls.cloud.tencent.com/v1
kind: LogConfig
spec:
  inputDetail:
    type: container_stdout
    containerStdout:
      namespace: default
      allContainers: true
 ...

示例2: 采集production命名空间中属于ingress-gateway deployment的pod中的容器的标准输出

apiVersion: cls.cloud.tencent.com/v1
kind: LogConfig
spec:
  inputDetail:
    type: container_stdout
    containerStdout:
      allContainers: false
      workloads:
      - namespace: production
        name: ingress-gateway
        kind: deployment
  ...

示例3: 采集production命名空间中pod标签中包含“k8s-app=nginx”的pod中的容器的标准输出

apiVersion: cls.cloud.tencent.com/v1
kind: LogConfig
spec:
  inputDetail:
    type: container_stdout
    containerStdout:
      namespace: production
      allContainers: false
      includeLabels:
        k8s-app: nginx
  ...

容器文件

示例1: 采集production命名空间中属于ingress-gateway deployment的pod中的nginx容器中/data/nginx/log/路径下名为access.log的文件

apiVersion: cls.cloud.tencent.com/v1
kind: LogConfig
spec:
  topicId: xxxxxx-xx-xx-xx-xxxxxxxx
  inputDetail:
    type: container_file
    containerFile:
      namespace: production
      workload:
        name: ingress-gateway
        type: deployment
      container: nginx
      logPath: /data/nginx/log
      filePattern: access.log
  ...

示例2: 采集production命名空间中pod标签包含“k8s-app=ingress-gateway“的pod中的nginx容器中/data/nginx/log/路径下名为access.log的文件

apiVersion: cls.cloud.tencent.com/v1
kind: LogConfig
spec:
  inputDetail:
    type: container_file
    containerFile:
      namespace: production
      includeLabels:
        k8s-app: ingress-gateway
      container: nginx
      logPath: /data/nginx/log
      filePattern: access.log
  ...

主机文件

示例: 采集主机/data/路径下所有.log文件

apiVersion: cls.cloud.tencent.com/v1
kind: LogConfig
spec:
  inputDetail:
    type: host_file
    hostFile:
      logPath: /data
      filePattern: *.log
  ...

创建LogConfig对象

于您的需求,参见 步骤2 配置说明,定义LogConfig.yaml声明文件,并使用kubect创建LogConfig对象。操作命令如下:
# kubectl create -f /usr/local/LogConfig.yaml

配置CLS鉴权ConfigMap

日志从自建K8s集群上传至CLS侧涉及鉴权, 需要创建ConfigMap用于存储API密钥IDAPI密钥KEY

以Master节点路径/usr/local/为例:wget下载ConfigMap.yaml声明文件

# wget https://mirrors.tencent.com/install/cls/k8s/ConfigMap.yaml

注意:需将ConfigMap.yaml中的TmpSecretId以及TmpSecretKey的值配置为您的API密钥IDAPI密钥KEY。

使用kubect创建ConfigMap对象。操作命令如下:

# kubectl create -f /usr/local/ConfigMap.yaml

部署Log-Provinsioner

Log-Provisioner负责发现并监听LogConfig资源中日志主题ID, 日志采集规则,日志文件路径,并同步至CLS侧。

以Master节点路径/usr/local/为例:wget下载Log-Provisioner.yaml声明文件

# wget https://mirrors.tencent.com/install/cls/k8s/Log-Provisioner.yaml

注意:需将Log-Provisioner.yaml中环境变量env下的CLS_HOST字段配置为目标日志主题所在地域的域名。不同地域的域名可参考资料中 [3]可用地域

使用kubect以Deployment的方式部署Log-Provinsioner。操作命令如下:

# kubectl create -f /usr/local/Log-Provinsioner.yaml

部署Log-Agent和Loglistener

集群的日志采集主要分为两个部分, 一个是Log-Agent,一个是Loglistener:
  • Log-Agent负责拉取集群中LogConfig中的日志源信息,并计算容器日志在宿主机上映射的绝对路径。
  • Loglistener负责采集与解析宿主机日志文件路径下的日志文件,并上传至CLS侧

以Master节点路径/usr/local/为例:wget下载Log-Agent和Loglistener的声明文件

# wget https://mirrors.tencent.com/install/cls/k8s/Log—Agent.yaml

注意:如果宿主机的docker根目录不在/var/lib/docker(这是在宿主机的根目录)下,需要在Log—Agent.yaml声明文件中把docker的根目录映射到容器中,如下图,将/data/docker挂载到容器中。

使用kubect以DaemonSet的方式部署Log-Agent和Loglistener。操作命令如下:

# kubectl create -f /usr/local/Log—Agent.yaml

CLS控制台查看日志

至此, 就完成了集群日志采集的所有部署,您可以到 [5]CLS日志服务检索页查看采集上来的日志。


其他资料

[1] TKE开启日志采集:https://cloud.tencent.com/document/product/457/56751

[2] 创建日志主题:https://cloud.tencent.com/document/product/614/41035

[3] 可用地域:https://cloud.tencent.com/document/product/614/18940

[4] API密钥管理:https://console.cloud.tencent.com/cam/capi

[5] CLS 日志服务检索页:https://console.cloud.tencent.com/cls/search

[6] CLS 日志服务免费额度领取:https://cloud.tencent.com/document/product/614/47116

以上就是本期自建 K8s 集群部署 CLS 日志服务的分享,如果您有更多有意思的日志实践,欢迎投稿~

重 磅 来 袭


【云原生正发声】第十二期【10月26日 19:30】将和你一起深研腾讯云容器服务 K8s 日志一站式解决方案部署实践!
本期将由腾讯云高级开发工程师 “易洋” ,为大家介绍 Kubernetes 集群日志收集的方案原理与实现,以及日志解决方案部署的实践指南
只要用 Kubernetes 就会接触日志服务CLS,快快扫码进入直播预约吧!





  往期精选推荐  

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存