先给结论
如果只看 Kubernetes 节点本地日志,结论可以这样记:
| 场景 | Pod 对象还在吗 | 本地日志是否还在 | kubectl logs 能否查看 |
|---|---|---|---|
| 容器正常运行 | 在 | 在 | 可以,默认看当前容器日志 |
| 容器重启,Pod 还在 | 在 | 通常保留当前和上一次容器实例日志 | 当前日志用 kubectl logs,上一次用 kubectl logs --previous |
| Pod 被删除 | 不在 | 通常会被 kubelet 清理 | 通常不能再通过 kubectl logs 查看 |
| Pod 被重建,同名但新 UID | 新 Pod 在 | 旧 Pod 日志不会自动回来 | 只能看新 Pod 日志 |
| 已接入 Loki、ELK、云日志等外部系统 | 不一定 | 节点本地可能没了 | 可以去外部日志系统查 |
所以问题的直接答案是:
- Pod 删除后,本地日志通常会消失。
- 只是容器重启、Pod 还在时,上一轮容器实例的日志通常还能通过
kubectl logs --previous查看。
但这两个结论背后有几个容易混淆的点:Pod 名字不等于 Pod 身份,容器重启不等于 Pod 删除,0.log / 1.log 也不完全等同于“永久历史日志”。
Kubernetes 里的日志是谁在管?
大多数业务容器写日志,推荐写到标准输出和标准错误:
容器运行时会把这些输出落到节点文件系统里,kubelet 再负责管理这些容器日志文件,并通过 Kubernetes API 暴露给 kubectl logs。
也就是说,当你执行:
| |
大致链路是:
Kubernetes 官方文档也明确说明,kubelet 默认会让容器运行时把容器日志写到 /var/log/pods 目录下,同时 kubelet 负责容器日志轮转和日志目录结构管理。
典型路径长这样:
| |
同时你经常还会看到:
| |
这个路径通常是指向 /var/log/pods/... 下实际日志文件的软链接,方便日志采集器按容器维度扫描。
Pod 删除后日志为什么会没?
Pod 的真实身份不是名字,而是 UID。
例如你看到的 Pod 叫:
| |
它在节点上的日志目录并不是只按名字组织,而是带上 Pod UID:
| |
这个 UID 很关键。即使 Deployment 后面又拉起一个同名风格的新 Pod,新 Pod 也会有新的 UID,对应新的日志目录。
当 Pod 被删除后,kubelet 会逐步清理这个 Pod 在节点上的资源,包括容器、挂载目录、日志目录等。清理完成后,本地节点上的这份 Pod 日志就不再可靠可查。
所以:
| |
之后再执行:
| |
通常会得到类似结果:
| |
即使 Deployment 很快创建了一个新的 Pod,也不是原来的 Pod:
新 Pod 的日志目录是新的,旧 Pod 的日志不会自动迁移过去。
容器重启时日志会保留吗?
会,但要加上限定条件:Pod 还在,且 kubelet / 容器运行时还没有清理对应的上一轮容器实例日志。
当 Pod 没删,只是里面的容器因为异常退出、探针失败、OOM、进程崩溃等原因重启时,Pod 还是同一个 Pod:
这时:
| |
看的是当前正在运行的容器实例日志。
而:
| |
看的是上一次已经终止的容器实例日志。
多容器 Pod 要指定容器名:
| |
这就是排查 CrashLoopBackOff 时最常用的命令。因为当前容器可能刚启动,还没打印出关键错误;真正的错误通常在上一轮崩溃前的日志里。
0.log、1.log 到底怎么理解?
在节点上你可能会看到类似目录:
这很容易让人理解成:
这个理解在很多场景下接近现象,但不要把它当成稳定的应用层契约。
更准确的说法是:
- kubelet 和容器运行时会按 CRI 日志机制维护容器日志文件。
- 容器重启会产生新的容器实例,上一轮实例的日志可能以另一个文件保留。
kubectl logs --previous面向的是“上一次终止的容器实例”,不是任意历史版本。- 日志文件还会受到 kubelet 日志轮转配置影响。
也就是说,你可以在节点上观察 0.log、1.log 这类文件,但日常排障不要依赖“手动猜编号”。优先使用:
如果要查更久以前的日志,不应该指望节点本地文件,而应该依赖外部日志系统。
还有一种“轮转”:不是重启,而是文件太大
容器一直不重启,日志文件也不可能无限增长。kubelet 会对容器日志做轮转。
常见默认值是:
含义是:
- 单个容器日志文件达到一定大小后会触发轮转
- 每个容器最多保留一定数量的日志文件
这和“容器重启产生上一轮日志”不是同一个概念。
可以把它们分开理解:
| 类型 | 触发条件 | 目的 |
|---|---|---|
| 容器实例切换 | 容器退出后被 kubelet 重新拉起 | 让当前实例和上一轮实例日志可区分 |
| 日志大小轮转 | 单个日志文件达到上限 | 防止节点磁盘被日志打满 |
官方文档也提醒:通过 kubectl logs 通常只能拿到最新日志文件的内容。如果容器写了很多日志,旧的轮转文件不一定会通过普通 kubectl logs 全量返回。
所以不要误以为:
| |
一定能拿到容器从启动到现在的所有日志。它拿到的是 kubelet 当前能通过日志接口返回的内容。
三个典型场景
场景一:Deployment 滚动发布
Deployment 发布新版本时,旧 ReplicaSet 下的 Pod 会被逐步删除,新版本 Pod 会被创建。
这个过程不是“容器重启”,而是:
结果是:
- 旧 Pod 本地日志会随 Pod 清理而消失
- 新 Pod 是新 UID,只能看到新日志
- 想查旧版本发布前后的日志,必须依赖外部日志系统
如果线上问题发生在发布瞬间,只靠 kubectl logs 很容易错过现场。
场景二:CrashLoopBackOff
容器反复崩溃,但 Pod 对象还在。
这时不要只看:
| |
还要看:
| |
如果是多容器:
| |
排查顺序建议是:
describe 看事件和退出原因,logs --previous 看上一轮进程退出前的业务日志。
场景三:节点磁盘被日志打满
如果业务疯狂输出日志,Pod 不删除、不重启,节点磁盘也可能被打满。
这时要关注:
- kubelet 的
containerLogMaxSize - kubelet 的
containerLogMaxFiles - 节点
/var/log/pods和/var/log/containers使用量 - 日志采集器是否及时采集并上报
- 是否有应用把日志写进容器文件系统或
emptyDir
生产环境里,容器 stdout/stderr 只适合作为采集入口,不适合作为长期存储。
为什么“Pod 删除日志就没了”是合理设计?
Kubernetes 的设计重点是调度和编排,不是日志长期存储。
Pod 是临时对象,可能因为很多原因消失:
- Deployment 滚动发布
- 节点驱逐
- HPA 缩容
- Job 执行完成
- 手动删除
- 节点故障
- 镜像升级
如果 Kubernetes 默认把每个已经删除的 Pod 日志都长期保存在节点上,会带来几个问题:
- 节点磁盘不可控
- Pod 被调度到不同节点后日志分散
- 节点故障时日志仍然可能丢
- 多租户环境下清理和隔离更复杂
所以 Kubernetes 只提供基本日志访问能力。真正的“日志持久化”和“跨 Pod 查询”应该交给集群级日志系统。
生产环境应该怎么做?
如果日志有排障、审计、交易链路追踪等价值,不要依赖节点本地日志。
推荐做法是接入集群级日志采集:
常见方案:
- Fluent Bit / Fluentd
- Vector
- Filebeat
- Promtail + Loki
- OpenTelemetry Collector
- 云厂商托管日志服务
关键原则:
- 应用日志写 stdout/stderr
- 节点上部署 DaemonSet 采集器
- 日志进入远端存储后再做查询和告警
- 本地日志只当短期缓冲,不当长期存储
常用命令整理
查看当前容器日志:
| |
查看指定容器日志:
| |
查看上一次终止的容器实例日志:
| |
查看 Pod 事件和容器退出原因:
| |
查看重启次数:
| |
查看 Pod UID:
| |
在节点上观察日志目录:
查看 kubelet 日志轮转配置,具体路径取决于集群安装方式:
| |
最后总结
这两个问题可以这样回答:
Pod 删除,日志通常就没了。
因为 Pod 删除后 kubelet 会清理该 Pod 在节点上的相关资源,本地 /var/log/pods/<namespace>_<pod>_<uid>/ 不应被当成持久化日志存储。后续即使出现同名 Pod,也已经是新 UID、新日志目录。
容器只是重启,上一轮日志通常还能看。
因为 Pod 还在,kubelet 通常会保留上一轮终止容器实例的日志,可以通过:
| |
查看。但它不是完整历史日志系统,也不是无限保留。它受容器实例生命周期、kubelet 清理策略和日志轮转配置影响。
真正可靠的结论是:kubectl logs 适合临时排障,生产环境日志留存必须依赖外部日志系统。
参考资料
- Kubernetes 官方文档:Logging Architecture
- Kubernetes 官方文档:kubectl logs
- Kubernetes 官方文档:kubelet 参数