简介
采集方式
SideCar 模式 VS Node 模式
-
sidecar 模式,每个 Pod 中都附带一个 logging 容器来进行本 Pod 内部容器的日志采集,一般采用共享卷的方式
- 在集群规模比较大的情况下,或者说单个节点上容器特别多的情况下,很明显的一个问题就是占用的资源比较多
- 对日志存储后端占用过多的连接数。
-
node 模式,在每个 Node 节点上仅需布署一个 logging 容器来进行本 Node 所有容器的日志采集。最明显的优势就是占用资源比较少,同样在集群规模比较大的情况下表现出的优势越明显。但对于这种模式来说我们就需要一个更加智能的日志采集工具来配合
采集组件
- fluentd,CNCF 社区
- filebeat,来自 Elastic
- flume
- 阿里千万实例可观测采集器-iLogtail 正式开源
Stdout VS 文件
容器提供标准输出和文件两种方式,
-
在容器中,标准输出将日志直接输出到 stdout 或 stderr,实际的业务场景中建议大家尽可能使用文件的方式
- Stdout 性能问题,从应用输出 stdout 到服务端,中间会经过好几个流程(例如普遍使用的 JSON LogDriver):应用 stdout -> DockerEngine -> LogDriver -> 序列化成 JSON -> 保存到文件 -> Agent 采集文件 -> 解析 JSON -> 上传服务端。整个流程相比文件的额外开销要多很多,在压测时,每秒 10 万行日志输出就会额外占用 DockerEngine 1 个 CPU 核;
- Stdout 不支持分类,即所有的输出都混在一个流中,无法像文件一样分类输出,通常一个应用中有 AccessLog、ErrorLog、InterfaceLog(调用外部接口的日志)、TraceLog 等,而这些日志的格式、用途不一,如果混在同一个流中将很难采集和分析;
- Stdout 只支持容器的主程序输出,如果是 daemon/fork 方式运行的程序将无法使用 stdout;
- 文件的 Dump 方式支持各种策略,例如同步/异步写入、缓存大小、文件轮转策略、压缩策略、清除策略等,相对更加灵活。
-
日志打印到文件的方式和虚拟机/物理机基本类似,只是日志可以使用不同的存储方式,例如默认存储、EmptyDir、HostVolume、NFS 等。
「Allen 谈 Docker 系列」之 docker logs 实现剖析对于应用的标准输出(stdout)日志,Docker Daemon 在运行这个容器时就会创建一个协程(goroutine),负责标准输出日志。由于此 goroutine 绑定了整个容器内所有进程的标准输出文件描述符,因此容器内应用的所有标准输出日志,都会被 goroutine 接收。goroutine 接收到容器的标准输出内容时,立即将这部分内容,写入与此容器—对应的日志文件中,日志文件位于/var/lib/docker/containers/<container_id>
,文件名为-json.log。
Docker 则通过 docker logs 命令向用户提供日志接口。docker logs
实现原理的本质均基于与容器一一对应的 <container-id>-json.log
,kubectl logs
类似
containerd 场景:containerd is lower level than Docker, and does not itself provide log plugins or container output log rotation functionality. containerd simply provides plumbing for the orchestrator to receive the container output, so it would be up to the orchestrator (e.g. kubelet) to do the right thing. PS: 使用 containerd + stdout 时,需要通过 kubelet 来控制日志滚动文件的大小及个数。
腾讯云容器服务日志采集最佳实践 从公有云或私有云来说,不应对实现加以限制,采集什么(stdout/容器文件/host 文件);文件吐到哪里;如何解析日志格式;日志过滤/时间戳自定义;日志查询等 等都可以支持可配。
采集什么
- 容器文件,比如容器运行了 Tomcat,则 Tomcat 的启动日志也在采集范围之内
- 容器 Stdout
- 宿主机文件
- Journal
- Event
- 支持多种采集部署方式,包括 DaemonSet、Sidecar、DockerEngine LogDriver 等;
- 支持对日志数据进行富化,包括附加 Namespace、Pod、Container、Image、Node 等信息;
- 稳定、高可靠,基于阿里自研的 Logtail 采集 Agent 实现,目前全网已有几百万的部署实例;
- 基于 CRD 进行扩展,可使用 Kubernetes 部署发布的方式来部署日志采集规则,与 CICD 完美集成。
log-pilot
启动
容器(以 DaemonSet 方式运行)启动时,执行 entrypoint /pilot/entrypoint
entrypoint 是一个可执行脚本,根据 ENV_PILOT_TYPE 判断使用哪个采集插件,以 filebeat 为例,并执行启动命令/pilot/pilot -template /pilot/filebeat.tpl -base /host -log-level debug
容器内的一些关键文件
|
|
- log-pilot 比较喜欢用环境变量,比如采集插件/组件 使用 fluentd 还是 filebeat 都是由环境变量指定
PILOT_TYPE=filebeat
后来镜像专门区分开registry.cn-hangzhou.aliyuncs.com/acs/log-pilot:0.9.7-filebeat
和registry.cn-hangzhou.aliyuncs.com/acs/log-pilot:0.9.7-fluentd
- fluentd/filebeat 就像 nginx 一样,根据配置文件运行,本身不具备动态发现容器日志文件的能力,log-pilot 对其封装了下(exec.Command 启动
/usr/bin/filebeat -c /etc/filebeat/filebeat.yml
)。就像 istio pilot-agent 对 envoy 所做的那样。 - log-pilot 监听 docker 拿到 container 数据(比如 container 的 label),如果 container 是新的, 并为 container 生成一个 filebeat yml 文件,reload filebeat(filebeat 本身会动态 发现
/prospectors.d
下的配置文件,reload fluentd 则需要向 fluentd 进程发送 syscall.SIGHUP 信号 ),filebeat 便可以搜集容器日志发往后端存储了。
源码分析
log-pilot 源码目录
|
|
![[image-20240813154218750.png]]
pilot 和 piloter 有着明确的分工
- pilot 负责解析解析、监听容器事件、根据 piloter 对应的 tpl 文件创建 容器实例对应的 yml 文件,start/reload piloter
- piloter 相对简单一些,就是根据 配置文件采集 log 发往存储后端
启动过程
![[image-20240813154248436.png]]
通过 docker client 监听 event 数据
|
|
为每一个容器生成 filebeat yml 文件
Log-Pilot 支持声明式日志配置,可以依据容器的 Label 或者 ENV 来动态地生成日志采集配置文件,或者说采集哪些容器的哪些日志。采集带有 PILOT_LOG_PREFIX.logs.$name=$path
容器标签(注意不是 pod 标签) 或PILOT_LOG_PREFIX_logs_$name=$path
容器 env 的容器。
name 和 path 的含义
-
name:我们自定义的一个字符串,它在不同的场景下指代不同的含义。当我们将日志采集到 ElasticSearch 的时候, name 表示的是 Index;当我们将日志采集到 Kafka 的时候, name 表示的是 Topic;当我们将日志采集到阿里云日志服务的时候,name 表示的是 LogstoreName。
-
path:它本身支持两种,
- 一种是约定关键字 stdout,表示的是采集容器的标准输出日志,比如我们要采集 tomcat 容器日志,那么我们通过配置标签
log.catalina=stdout
来采集 tomcat 标准输出日志 - 第二种是容器内部的具体文件日志路径,可以支持通配符的方式。通过配置标签
log.access=/usr/local/tomcat/logs/*.log
来采集 tomcat 容器内部文件日志。
- 一种是约定关键字 stdout,表示的是采集容器的标准输出日志,比如我们要采集 tomcat 容器日志,那么我们通过配置标签
filebeat.tpl 内容
|
|
某个容器对应的 filebeat yml 文件示例
|
|
本地日志清理
从物理机角度,有一个方案是执行docker system df -v
可以列出每个容器占用的 磁盘空间,当期大小超过一定阈值时,可以根据 container id 将其删除。
|
|
使用定时任务每天执行docker system prune -af