- 云原生
- 容器运行时
【免费下载链接】kata-containers
Kata Containers is an open source project and community working to build a standard implementation of lightweight Virtual Machines (VMs) that feel and perform like containers, but provide the workload isolation and security advantages of VMs. https://katacontainers.io/
kata-log-parser是 Kata Containers 项目中用于处理系统组件日志的专用命令行工具:它将 runtime、virtcontainers、agent 等组件产生的多个 logfmt 格式日志文件合并为一个按时间戳排序的序列,并在每条记录间标注时间差,同时支持日志记录合法性校验与多格式输出。阅读完本文,你将掌握 kata-log-parser 的日志格式要求、安装构建方法、完整的使用流程、全部命令行参数、6 种输出格式,以及如何结合 jq 从合并日志中精确提取 guest 串口输出、agent 日志与指定 Sandbox 的日志记录。
工具概述:它解决什么问题
在 Kata Containers 的运行链路中,一个 Pod 的完整生命周期会横跨多个系统组件:kata-runtime(OCI 运行时)、virtcontainers(沙箱与虚拟机管理层)、containerd-shim-kata-v2(容器运行时 shim),以及运行在 guest 内部的 agent。这些组件各自产生日志,且时间上相互交织,人工逐一翻阅既低效又容易遗漏关键线索。
kata-log-parser的核心职责(见 src/tools/log-parser/main.go 的文件头注释)正是:
- 合并:读取多个 logfmt 格式的日志文件;
- 排序:按时间戳(timestamp)对所有记录统一排序;
- 重放:重新展示日志条目,并在每条记录后附上与前一条记录之间的时间差(time delta),直观呈现组件间的耗时;
- 校验:检查所有日志记录的字段合法性(如是否存在必填字段、字段值是否符合约束);
- 重格式化:将日志以 text、json、csv、toml、xml、yaml 等多种格式输出,便于后续工具链消费。
在源码层面,排序与时间差逻辑定义于 src/tools/log-parser/logentry.go:LogEntries实现了sort.Sort接口,Less()基于entries[i].Time.Before(entries[j].Time)做时间排序;每条记录(从第二条起)的TimeDelta由this.Time.Sub(prev.Time)计算得出,TimeDelta.String()特意固定输出纳秒级整数格式,避免 Go 的time.Duration默认人性化格式在不同数值下展示不一致。
日志格式要求:logfmt 与必填字段
输入格式:logfmt 结构化日志
工具只读取logfmt结构化日志格式。logfmt 是一种key=value的紧凑文本格式,例如 Go 生态中 Logrus 日志库的输出即符合该格式。Kata Containers 的 runtime、agent、shim 组件普遍采用这种格式输出日志。
从源码看,解析过程使用github.com/go-logfmt/logfmt解码器(见 src/tools/log-parser/parse.go 的parseLogFmtData()):以行为单位,将每行拆分为若干 key/value 对。解析时还会做额外的健壮性处理——HexByteReader(见 src/tools/log-parser/hexbytes.go)会在送入 logfmt 解码器之前把字符串中的\x转义为\\x,因为 logfmt 无法直接处理字符串中的十六进制转义字节,这保证包含二进制字节的日志值(如 agent 的原始输出)也能被正确解析。
默认必填字段
默认情况下,每条日志记录必须包含以下字段,否则解析会报错:
| 字段 | 说明 | 约束示例 |
|---|---|---|
level | 日志级别 | 必须是 LogrusLogLevel的字符串形式,如debug、info、error |
name | 产生日志的应用名称 | 单个单词,如kata-runtime |
pid | 产生日志的进程 ID | 数值型 |
source | 系统中某个唯一组成部分的名称 | 单个单词,如runtime |
time | 时间戳 | RFC3339 格式且包含纳秒值 |
此外还期望一个非强制字段:
| 字段 | 说明 |
|---|---|
msg | 文本消息,用于区分不同的日志记录 |
这些默认要求可以通过--ignore-missing-fields标志忽略(对缺失pid、source、name、level的记录不报错)。
字段校验与时间格式的严格性
从源码可以确认校验远比文档描述更严格:
- 字段值校验(src/tools/log-parser/logentry.go 的
Check()):Level、Source、Name三个字段不允许出现多单词(即值中不能包含空格);Pid不能为负数;时间戳不可为零值。Container与Sandbox字段不做必填检查,因为并非所有记录都携带这两个 ID。 - 时间戳双重校验(src/tools/log-parser/parse.go 的
parseTime()):先用 Go 的time.Parse()解析,再用正则表达式做二次确认。正则dateFormatPattern匹配YYYY-MM-DDTHH:MM:SS.后接 1~9 位纳秒数字、最后是Z或±HH:MM时区。注意纳秒位数下限是 1,因为time.RFC3339Nano格式会截断尾部零。 - 非法字符检查(src/tools/log-parser/check.go 的
checkValid()):拒绝不可打印字符,还会检测 Gofmt包格式化出错时留下的%!(BADINDEX)、%!(EXTRA等特征错误串——这些串暗示日志产生方存在编程错误,工具会显式报错提示。
组件日志来源
kata-log-parser主要读取以下组件日志:
- runtime 日志:即
kata-runtime(src/runtime)产生的日志,它内部已经包含了 virtcontainers(src/runtime/virtcontainers)的日志条目; - agent 日志:runtime 日志中会以 best-effort 方式**解包(unpack)**内嵌的 agent(src/agent)日志条目,除非显式指定
--no-agent-unpack关闭该行为。
Agent 日志解包机制(v1 与 v2)
agent 的日志实际上是被代理(proxy)或 runtime 日志条目"封装"(encode)后传递出来的,因此工具需要解包还原。解包逻辑位于 src/tools/log-parser/agent.go:
- v1 格式:当某条记录的
source=agent且msg以time=开头时,unpackAgentLogEntry_v1()会把Msg当作一段内嵌的 logfmt 文本再次解析为独立的 agent 日志条目,并保留原文件名、行号与计数。 - v2 格式:当记录
Msg=="reading guest console"且Data["vmconsole"]非空时,unpackAgentLogEntry_v2()将vmconsole字段按 JSON 解包。该路径会把 agent 短日志级别(CRIT/DEBG/ERRO/TRCE/WARN)映射为通用级别(critical/debug/error/trace/warning),并提取container-id/cid作为 Container ID。特别地,v2 解包不采用 agent 自己的时间戳——因为 agent 日志传输存在约 1 秒延迟,若使用其时间戳会导致 guest 内日志与其它日志在合并排序时顺序错乱;agent 原始时间戳仍保留在Data字段中供参考。
解包失败时的行为由两个标志控制:默认仅告警(内核可能随时向控制台写入非结构化消息,导致 agent 日志条目"看起来损坏");--strict模式则直接报错;非 strict 模式下失败记录会被打上-agent-unpack-failed标签以便排查。
安装与构建
方式一:go get + make install(原文档方式)
$ go get -d github.com/kata-containers/kata-containers $ pushd $GOPATH/src/github.com/kata-containers/kata-containers/src/tools/log-parser && make install && popdmake install会将kata-log-parser二进制安装到$(GOPATH)/bin下。
方式二:直接在仓库中构建
从 src/tools/log-parser/Makefile 可以看到构建细节:
$ cd src/tools/log-parser $ make # 等价于 make install,先执行 go test . 再 go buildMakefile 通过-ldflags "-X main.name=${TARGET} -X main.commit=${COMMIT} -X main.version=${VERSION}"注入二进制名称、commit(含-dirty标记,若工作区有未提交改动)与版本号(来自 src/tools/log-parser/VERSION),这些信息会出现在输出的注释头中。常用目标:make check(静默运行go test .)、make test(详细输出测试)、make clean(删除构建产物)。
查看帮助:
$ kata-log-parser --help帮助输出中还会附加一段 NOTES:若文件参数指定为-则从标准输入读取;若开启--debug则必须同时指定--output-file=,否则输出会被 debug 信息污染。
完整使用流程
合并所有组件日志的推荐步骤如下:
- 开启完整调试:参考 docs/Developer-Guide.md 中 Enable full debug 一节,确保 runtime/agent 输出完整 debug 级别日志。
- (可选)清空 systemd journal,保证收集到的日志时间窗干净:
$ sudo systemctl stop systemd-journald $ sudo rm -f /var/log/journal/*/* /run/log/journal/*/* $ sudo systemctl start systemd-journald也可以不清 journal,改为在收集日志时用
--since=<容器创建时间>约束时间范围。 - 创建一个 Kata 容器,触发你希望排查的负载或操作。
- 收集日志,只取
kata标识(tag)的输出:$ sudo journalctl -q -o cat -a -t kata > ./kata.log - 确保日志可读:
$ sudo chown $USER *.log - 安装程序(见上文安装小节)。
- 运行解析器:
$ kata-log-parser kata.log
kata-log-parser支持同时传入多个日志文件,它们会被合并后统一按时间排序:
$ kata-log-parser runtime.log agent.log shim.log从标准输入读取
日志文件参数支持魔术值-,表示从标准输入读取(见 src/tools/log-parser/main.go 中的stdinFile常量)。这使得它可以与journalctl直接管道串联(见下文 jq 示例)。
命令行参数详解
以下参数定义均来自 src/tools/log-parser/main.go 中的 CLI 标志声明:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
--check-only | bool | false | 仅校验日志文件,只在出错时显示输出;内部会对所有格式运行一遍格式化器,以便发现解析本身发现不了的数据问题 |
--debug | bool | false | 显示调试信息(解析记录数、各文件记录数统计等);必须与--output-file搭配,否则报错,以免污染输出 |
--error-if-file-empty | bool | false | 任一输入文件为空则报错(默认跳过空文件并 debug 提示) |
--error-if-no-records | bool | false | 所有日志文件均为空(无记录可处理)时报错(默认仅输出 debug 消息) |
--ignore-missing-fields | bool | false | 对缺少pid、source、name、level的行不报错 |
--list-output-formats | bool | false | 列出所有可用的输出格式名称 |
--no-agent-unpack | bool | false | 不执行 agent 日志条目解包 |
--quiet | bool | false | 抑制警告消息(debug 模式下忽略) |
--strict | bool | false | 不容忍格式错误的 agent 消息(通常由内核写入控制台导致) |
--output-format | string | text | 输出格式,取值见--list-output-formats |
--output-file | string | stdout | 将输出写入指定文件(文件权限为0600) |
其中--debug模式在解析结束后会通过showSummary()输出统计:总共解析了多少条记录、来自多少个文件、每个文件各贡献多少条。
输出格式与数据结构
六种内置格式
--list-output-formats可列出全部可用格式。从 src/tools/log-parser/display.go 的 handlers 映射可见共 6 种:
text(默认):每条记录一行Record N: {LogEntry 全字段},并在文件头输出包含名称、版本、commit、字段列表、格式版本的注释头(见 src/tools/log-parser/display_text.go);json:带缩进的 JSON 数组(缩进 4 空格,见 src/tools/log-parser/display_json.go),这是与 jq 组合使用的基础;csv(display_csv.go)、toml(display_toml.go)、xml(display_xml.go)、yaml(display_yaml.go)。
$ kata-log-parser --output-format json kata.log $ kata-log-parser --list-output-formatsLogEntry 数据结构
每条记录在内部被建模为LogEntry结构(src/tools/log-parser/logentry.go),输出字段包括:
Time:解析后的时间戳;Level、Msg、Source、Name:日志级别、消息、组件源、应用名;Pid:进程 ID;Container、Sandbox:容器 ID 与沙箱 ID(多数记录有,部分记录无,如不针对具体容器的 CLI 命令、guest 内核启动输出、agent 早期启动日志);Filename、Line:记录来源文件与行号;Count:合并后的全局序号(1 起始);TimeDelta:与上一条记录的时间差(纳秒整数);Data:附加的非标准字段映射(如vmconsole、agent 的原始时间戳等)。
结构顶层另有FormatVersion(当前为0.0.2,每次修改 LogEntry 结构都需更新)。另外解析器会自动从 agent 的 gRPC 请求字段(req)中提取container_id并补充为container字段,方便按容器过滤。
高级用法:结合 jq 精确过滤
jq 是命令行 JSON 处理器,与kata-log-parser的 JSON 输出配合,可以精准筛选特定日志条目。以下为原文档给出的三个经典示例:
1. 只取 guest 的原始串口输出
$ kata-log-parser --ignore-missing-fields --output-format json --no-agent-unpack kata.log | jq '.Entries[] | select(.Msg=="reading guest console") | .Data.vmconsole'要点:--no-agent-unpack保持reading guest console记录原样,Data.vmconsole即 guest 控制台原始输出。
2. 只取 agent 解包后的日志条目(journal 直通管道)
$ journalctl -q -o cat -a -t kata | kata-log-parser --ignore-missing-fields --output-format json - | jq '.Entries[] | select(.Source=="agent")'这里文件参数-表示从标准输入读取,journalctl输出直接管道进入解析器;--ignore-missing-fields容忍 agent 日志中不完整字段。
3. 只取指定 Sandbox 的 shim 日志
$ kata-log-parser --ignore-missing-fields --output-format json kata.log | jq '.Entries[] | select(.Source=="containerd-kata-shim-v2" and .Sandbox=="2fa50251ccc3b9a85350e8fe6836d1875023714153b503b548360946fcec3829") | "\(.Msg) \(.Time) \(.Container)"'该示例打印来自containerd-kata-shim-v2、属于指定 Sandbox ID 的记录,并同时输出消息内容、时间戳与容器 ID。
校验模式:CI 友好
--check-only模式专为自动化场景设计:它不会把结果写到输出文件,但会对全部 6 种格式运行一遍格式化器(因为格式化过程可能发现解析校验发现不了的数据问题),任何失败都会以check failed for format %q: ...形式报错。结合--error-if-file-empty、--error-if-no-records,可以方便地嵌入 CI 脚本,对日志文件集做"是否合法"的断言式检查:
$ kata-log-parser --check-only --error-if-no-records kata.log使用建议与注意事项
- 先开启 debug 再收集:不开启全量 debug,许多细节(如 agent 的 gRPC 请求、guest 控制台输出)不会出现在日志中,合并分析的价值将大打折扣。
- 合理选择时间窗:清空 journal 最干净但影响面大,使用
journalctl --since=约束时间范围更轻量;从标准输入管道读取则完全不受 journal 留存策略影响。 - 区分两种 agent 日志形态:需要 guest 原始串口内容时用
--no-agent-unpack保留封装层;需要结构化 agent 条目时保持默认解包行为,必要时用--strict将异常提升为错误。 - 时间差解读:
TimeDelta是当前记录与前一条(按时间排序后)的纳秒差,可用于定位组件间耗时异常(如某两条记录间隔突增,往往对应阻塞或等待事件)。 - 字段缺失是常态:
Container/Sandbox并非每条记录都有,过滤时应使用select(.Sandbox==...)这类精确匹配而非假设所有记录都携带 ID。
通过上述流程,你可以把散落在 journald 中的多组件日志快速汇聚成一张按时间轴展开、带耗时标注、可机器过滤的全链路日志视图,这是排查 Kata Containers 沙箱创建失败、agent 通信异常、启动性能瓶颈等问题的第一利器。
- 云原生
- 容器运行时
【免费下载链接】kata-containers
Kata Containers is an open source project and community working to build a standard implementation of lightweight Virtual Machines (VMs) that feel and perform like containers, but provide the workload isolation and security advantages of VMs. https://katacontainers.io/
相关推荐
Kata Containers under Minikube:嵌套虚拟化环境搭建、kata-deploy 安装与 Kata Pod 验证实战
Kata Containers under Minikube:嵌套虚拟化环境搭建、kata deploy 安装与 Kata Pod 验证实战 本文基于 Kata
云原生容器运行时Kata Containers 日志接入 Fluentd 实战:systemd journal 与 JSON 日志导入 EFK/ELK 全流程
Kata Containers 日志接入 Fluentd 实战:systemd journal 与 JSON 日志导入 EFK/ELK 全流程 导读 本文基于
云原生容器运行时MongoDB 查询优化:$unwind + $group 到 DISTINCT_SCAN 改写与多计划竞争(Multiplanning)实战解析
MongoDB 查询优化:$unwind + $group 到 DISTINCT_SCAN 改写与多计划竞争(Multiplanning)实战解析 导读 本文以
云原生容器运行时
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考