3. Kubernetes基础与Pod日志:K8s核心组件、kubectl logs命令详解、Pod日志的生命周期、Sidecar模式收集日志、实战:使用kubectl排查Pod日志

3.1 K8s核心组件:日志的“交通枢纽”

聊Pod日志之前,得先搞清楚K8s的架构。说白了,K8s就是一个“管家”,帮你管理一堆容器。

核心组件就这几个:

  • API Server:整个集群的“门面”。你敲的kubectl命令,最终都打到它这里。日志请求也不例外。
  • kubelet:每个节点上的“代理人”。它负责拉取容器日志,并暴露给API Server。我排查问题时,经常直接SSH到节点上,用journalctl -u kubelet看kubelet自己的日志,往往能找到线索。
  • Scheduler & Controller Manager:这俩负责调度和维持期望状态。它们不直接处理Pod日志,但Pod跑在哪个节点上,是它们决定的。
  • etcd:集群的“数据库”。Pod的元数据存在这里,但日志内容不存。

你想想看,日志从容器里产生,到被你用kubectl logs看到,中间经过了kubelet和API Server。理解这个链路,对后面排查问题很有帮助。

3.2 kubectl logs命令详解:不只是“看日志”

kubectl logs是我日常使用频率最高的命令之一。但很多人只用了它的皮毛。

基础用法:

# 查看单个容器的日志
kubectl logs <pod-name>

# 实时跟踪,类似 tail -f
kubectl logs -f <pod-name>

# 查看前N行
kubectl logs --tail=100 <pod-name>

# 查看特定时间范围的日志
kubectl logs --since=1h <pod-name>
kubectl logs --since-time="2024-01-01T00:00:00Z" <pod-name>

多容器Pod的情况:

# 指定容器名
kubectl logs <pod-name> -c <container-name>

# 查看所有容器的日志(K8s 1.14+)
kubectl logs <pod-name> --all-containers=true
我的小技巧: 我习惯用 kubectl logs --tail=50 -f 来调试。既能看到最近的上下文,又能实时跟踪。比直接 -f 从头刷屏舒服多了。

3.3 Pod日志的生命周期:日志去哪儿了?

Pod日志的生命周期,其实挺脆弱的。我遇到过不少新手,以为日志会永久保存。

实际情况是这样的:

  • 日志文件位置:kubelet把容器日志写到宿主机的 /var/log/pods/ 目录下。每个Pod一个子目录,里面按容器名分文件。
  • 日志轮转:kubelet默认会做日志轮转。默认配置是每个日志文件最大10MB,最多保留5个文件。超过就删旧的。
  • Pod删除后:Pod一旦被删除,对应的日志目录也会被清理。默认情况下,日志保留时间取决于kubelet的配置,通常是几天。
  • 容器重启:容器重启后,之前的日志还在。但如果你用 kubectl logs --previous,可以看到上一次容器的日志。这个参数我经常用来排查“容器CrashLoopBackOff”的问题。
注意: 默认情况下,Pod日志不会持久化。节点挂了,日志就丢了。我曾经在生产环境遇到过节点磁盘写满,导致kubelet日志轮转失败,最终整个节点的Pod都受影响。所以,生产环境一定要配日志收集。

3.4 Sidecar模式收集日志:优雅的解决方案

Sidecar模式,说白了就是在同一个Pod里,多跑一个容器专门干日志收集的活。主容器只管写日志到标准输出或文件,Sidecar容器负责读取、处理、发送。

为什么用Sidecar?

  • 解耦:业务容器不需要关心日志怎么发、发到哪。
  • 灵活性:可以给不同的Pod配不同的日志处理逻辑。
  • 资源隔离:日志收集的CPU、内存消耗,不会影响业务容器。

一个典型的Sidecar配置示例:

apiVersion: v1
kind: Pod
metadata:
  name: my-app
spec:
  containers:
  - name: app
    image: my-app:latest
    volumeMounts:
    - name: log-volume
      mountPath: /var/log/app
  - name: log-collector
    image: fluentd:latest
    volumeMounts:
    - name: log-volume
      mountPath: /var/log/app
      readOnly: true
  volumes:
  - name: log-volume
    emptyDir: {}

这里的关键是 emptyDir 卷。两个容器共享这个目录,主容器写日志,Sidecar容器读日志。Pod删除时,emptyDir也会被清理,不会留下垃圾。

避坑指南: 我曾经遇到一个案例,Sidecar容器启动比主容器慢,导致主容器启动时的日志没被收集到。解决方案是在Sidecar容器里加一个启动延迟,或者用 initContainers 先初始化日志目录。

3.5 实战:使用kubectl排查Pod日志

光说不练假把式。咱们来一个真实的排查场景。

场景: 一个Web服务Pod,用户反馈接口返回500错误。我们需要通过日志定位问题。

步骤1:查看Pod状态

kubectl get pods -n production
# 输出:my-web-app-7d4f8b9c6-x2k3l   Running   0/1   CrashLoopBackOff

嗯,Pod在CrashLoopBackOff状态。说明容器一直在重启。

步骤2:查看当前日志

kubectl logs -n production my-web-app-7d4f8b9c6-x2k3l
# 输出:Error: failed to connect to database: connection refused

数据库连接失败。但这是当前容器的日志,可能不完整。

步骤3:查看上一次容器的日志

kubectl logs -n production my-web-app-7d4f8b9c6-x2k3l --previous
# 输出:Starting server on port 8080...
#       2024-01-15 10:30:45 [INFO] Received request GET /api/users
#       2024-01-15 10:30:46 [ERROR] Database timeout after 30s

看到了!上一次容器是正常启动的,但处理请求时数据库超时了。结合当前容器的“连接被拒绝”,基本可以判断数据库服务挂了。

步骤4:查看事件

kubectl describe pod -n production my-web-app-7d4f8b9c6-x2k3l
# 在Events部分看到:
# Warning  BackOff  2m  kubelet  Back-off restarting failed container

事件也确认了容器在反复重启。

步骤5:查看节点日志(如果需要)

如果问题更复杂,比如Pod一直Pending,我会去节点上看kubelet日志:

# SSH到节点
journalctl -u kubelet -n 100 --no-pager
我的排查习惯: 先看Pod事件(describe),再看当前日志(logs),最后看上一次日志(logs --previous)。这个顺序能覆盖90%的问题。如果还不行,才去看节点日志。

总结一下,kubectl logs 是排查Pod问题的第一把钥匙。配合 describeget events,基本能搞定日常大部分问题。但记住,这只是临时手段。生产环境一定要上集中的日志收集系统,不然节点一挂,啥都查不到。