找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

5391

积分

0

好友

744

主题
发表于 4 小时前 | 查看: 8| 回复: 0

背景与问题

在容器化环境中,日志收集和查看是运维工程师日常工作中最频繁的操作之一。很多运维工程师习惯性地进入容器内部使用 tail -f 命令查看日志,这种方式在小规模环境下勉强可用,但当容器数量增加、工作负载上升时,效率就会变得低下,且存在诸多隐患。

试想一下,当你有几十个甚至上百个 Pod 在运行,每个 Pod 又可能包含多个容器,你还能轻松地逐个 exec 进去吗?这不仅繁琐,还可能因为操作失误影响业务运行。本文基于 containerd 1.7.xKubernetes 1.29 环境,系统讲解容器日志的正确查看方式、分布式收集架构以及常见排障方法,所有命令均经过实际验证。

一、容器日志的基础机制

1.1 容器日志的存储位置

在 containerd 环境中,容器日志默认存储在节点上的 /var/log/pods/<namespace>/<pod_name>/<container_name>/ 目录下。

查看容器日志文件:

# 查看节点的容器日志目录
ls -la /var/log/pods/

# 查看特定 Pod 的日志
ls -la /var/log/pods/default_nginx-deployment-6d9f4c8b7-x2m9n_abc123/

# 查看特定容器的日志文件
ls -la /var/log/pods/default_nginx-deployment-6d9f4c8b7-x2m9n_abc123/nginx/

# 查看日志内容
cat /var/log/pods/default_nginx-deployment-6d9f4c8b7-x2m9n_abc123/nginx/0.log

容器日志文件的命名规则为:<container_name>/<restart_count>.log。其中 restart_count 从 0 开始,每次容器重启后递增。

1.2 kubelet 的日志处理

kubelet 负责将容器主进程的 stdoutstderr 重定向到日志文件。当容器主进程写入这些标准输出时,containerd 会捕获并写入日志文件。

同时,kubelet 还负责日志的轮转,当日志文件达到一定大小后会进行切割:

# 查看日志轮转配置
cat /var/lib/kubelet/config.yaml | grep -A 5 "logging"

# 默认配置
# containerLogMaxSize: 10Mi
# containerLogMaxFiles: 5

1.3 containerd-shim 的作用

containerd-shim 是容器运行时的一个关键中间进程,它的主要作用包括:

  • 作为容器的父进程,处理容器的退出信号。
  • 保持容器在 kubelet 重启后仍然运行。
  • 管理容器的 stdin/stdout 流。

查看 containerd-shim 进程:

# 查看所有 containerd-shim 进程
ps aux | grep containerd-shim

# 查看 containerd-shim 管理的容器
sudo ctr -n k8s.io containers list

二、kubectl logs 的正确使用方式

2.1 基本用法

kubectl logs 是查看容器日志的标准姿势,足以应对大多数场景:

# 查看当前最新的日志
kubectl logs nginx-deployment-6d9f4c8b7-x2m9n

# 查看上一个 terminated 容器的日志(容器重启后)
kubectl logs nginx-deployment-6d9f4c8b7-x2m9n --previous

# 持续跟踪日志(类似 tail -f)
kubectl logs -f nginx-deployment-6d9f4c8b7-x2m9n

# 查看最近 100 行日志
kubectl logs nginx-deployment-6d9f4c8b7-x2m9n --tail=100

# 查看指定时间范围内的日志
kubectl logs nginx-deployment-6d9f4c8b7-x2m9n --since=1h
kubectl logs nginx-deployment-6d9f4c8b7-x2m9n --since-time="2026-01-01T00:00:00Z"

2.2 多容器 Pod 的日志查看

当一个 Pod 包含多个容器时,必须明确指定容器名称:

# 查看多容器 Pod 中特定容器的日志
kubectl logs <pod-name> -c <container-name>

# 持续跟踪特定容器的日志
kubectl logs -f <pod-name> -c <container-name>

# 同时查看所有容器的日志
kubectl logs <pod-name> --all-containers=true

# 使用标签选择器查看多个 Pod 的日志
kubectl logs -l app=nginx --tail=100

2.3 常见错误与解决方案

错误一:Pod 未找到

kubectl logs nginx
Error from server (NotFound): pods "nginx" not found

# 解决:指定正确的命名空间
kubectl logs nginx -n production

错误二:容器未找到

kubectl logs multi-container-pod
Error from server (BadRequest): a container name must be specified for pod multi-container-pod, choose one of: [nginx sidecar]

# 解决:使用 -c 参数指定容器名
kubectl logs multi-container-pod -c nginx

错误三:Pod 处于 Terminating 状态

kubectl logs nginx-deployment-6d9f4c8b7-x2m9n
Error from server (NotFound): pods "nginx-deployment-6d9f4c8b7-x2m9n" not found

# 解决:使用 --previous 查看上一个容器的日志
kubectl logs nginx-deployment-6d9f4c8b7-x2m9n --previous

# 如果 Pod 已经彻底删除,查看节点上的日志文件
ssh <node-name> "cat /var/log/pods/<pod-id>/<container-name>/0.log"

三、日志过滤与格式化

3.1 日志级别过滤

大多数应用使用结构化日志,支持不同的日志级别(DEBUG、INFO、WARN、ERROR)。可以结合 grep 进行快速筛选:

# 查看 ERROR 级别的日志
kubectl logs nginx-deployment-6d9f4c8b7-x2m9n | grep ERROR

# 查看最近 1 小时的严重错误日志
kubectl logs nginx-deployment-6d9f4c8b7-x2m9n --since=1h | grep -E "ERROR|FATAL|CRITICAL"

# 统计各级别日志数量
kubectl logs nginx-deployment-6d9f4c8b7-x2m9n --since=24h | awk '{print $5}' | sort | uniq -c

3.2 时间戳过滤

# 使用 sed 进行时间范围过滤
kubectl logs nginx-deployment-6d9f4c8b7-x2m9n | \
  sed -n '/2026-01-15 10:00:00/,/2026-01-15 11:00:00/p'

3.3 JSON 格式日志解析

如果应用输出 JSON 格式的日志,jq 是非常强大的解析工具:

# 安装 jq(如果尚未安装)
kubectl exec -it <pod-name> -- apk add jq

# 解析 JSON 日志的特定字段
kubectl logs <pod-name> | jq -r '.level, .message'

# 统计各错误类型的数量
kubectl logs <pod-name> --since=24h | jq -r '.error // "no-error"' | sort | uniq -c | sort -rn

# 提取特定错误的详细信息
kubectl logs <pod-name> | jq -r 'select(.level == "error") | .timestamp, .message'

四、节点级别的日志访问

4.1 直接访问日志文件

kubectl 无法使用时,可以直接登录节点访问文件系统:

# 连接到节点
ssh <node-name>

# 查看 Pod 日志目录
ls -la /var/log/pods/

# 使用 journalctl 查看容器日志
sudo journalctl -u kubelet --since "1 hour ago" | grep <pod-name>

# 使用 ctr 查看容器信息
sudo ctr -n k8s.io containers list | grep <container-name>
sudo ctr -n k8s.io tasks logs <container-id>

4.2 containerd 日志

containerd 自身的日志通常由 systemd 管理:

# 查看 containerd 日志
sudo journalctl -u containerd --since "1 hour ago" | grep -E "error|warn" --color=never

# 查看 containerd 服务状态
sudo systemctl status containerd

# 查看 containerd 配置文件
cat /etc/containerd/config.toml | grep -E "sandbox|log_level"

4.3 kubelet 日志

kubelet 日志对于诊断节点级别的问题至关重要:

# 查看 kubelet 日志
sudo journalctl -u kubelet --since "30 minutes ago" --no-pager

# 查看 kubelet 日志中的错误
sudo journalctl -u kubelet --since "1 hour ago" | grep -i error

# 实时跟踪 kubelet 日志
sudo journalctl -u kubelet -f

五、分布式日志收集架构

5.1 EFK 日志架构

EFK(Elasticsearch + Fluentd + Kibana)是 Kubernetes 环境中最经典的日志解决方案之一。

架构组件:

  • Fluentd/Fluent Bit:作为 DaemonSet 运行在每个节点上,负责收集容器日志并转发。
  • Elasticsearch:分布式搜索引擎,用于存储和索引海量日志数据。
  • Kibana:提供可视化界面,用于查询和分析日志。

5.1.1 Fluentd/Fluent Bit 部署

下面是一个 Fluent Bit 的 DaemonSet 部署示例:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluent-bit
  namespace: kube-system
spec:
  selector:
    matchLabels:
      k8s-app: fluent-bit
  template:
    metadata:
      labels:
        k8s-app: fluent-bit
    spec:
      containers:
      - name: fluent-bit
        image: fluent/fluent-bit:3.0.0
        ports:
        - name: http
          containerPort: 2020
        volumeMounts:
        - name: varlog
          mountPath: /var/log
          readOnly: true
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
        - name: fluent-bit-config
          mountPath: /fluent-bit/etc
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers
      - name: fluent-bit-config
        configMap:
          name: fluent-bit-config
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: fluent-bit-config
  namespace: kube-system
data:
  fluent-bit.conf: |
    [SERVICE]
        Flush         5
        Log_Level     info
        Daemon        off
        Parsers_File  parsers.conf

    [INPUT]
        Name          tail
        Path          /var/log/containers/*.log
        Parser        docker
        Tag           kube.containers.*
        Refresh_Interval 5

    [OUTPUT]
        Name          es
        Match         kube.containers.*
        Host          elasticsearch.logging.svc
        Port          9200
        Index         kubernetescontainers
        Type          flb_type
        Retry_Limit   2

  parsers.conf: |
    [PARSER]
        Name   docker
        Format json
        Time_Key time
        Time_Format %Y-%m-%dT%H:%M:%S.%L

5.1.2 Fluent Bit 配置详解

Fluent Bit 的核心配置围绕 Input、Filter 和 Output 展开:

  • INPUT:定义日志来源,这里使用 tail 插件读取容器日志文件。
  • FILTER:处理和转换日志数据,例如添加 Kubernetes 元信息。
  • OUTPUT:定义日志输出目标,这里发送到 Elasticsearch。
# 添加 Kubernetes 元信息的 Filter
[FILTER]
    Name                kubernetes
    Match               kube.containers.*
    Kube_URL            https://kubernetes.default.svc:443
    Kube_CA_File        /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
    Kube_Token_File     /var/run/secrets/kubernetes.io/serviceaccount/token
    Kube_Tag_Prefix     kube.containers.var.log.containers.
    Merge_Log           On
    Keep_Log            Off
    K8S-Logging.Parser  On
    K8S-Logging.Exclude On

5.2 Loki 日志架构

Loki 是 Grafana 实验室开发的日志聚合系统,与 Prometheus 架构类似,具有资源占用低、易于部署的优点。它通常配合 Promtail 进行日志收集。

# Loki 部署示例
apiVersion: apps/v1
kind: Deployment
metadata:
  name: loki
  namespace: monitoring
spec:
  replicas: 2
  selector:
    matchLabels:
      app: loki
  template:
    metadata:
      labels:
        app: loki
    spec:
      containers:
      - name: loki
        image: grafana/loki:3.0.0
        ports:
        - name: http
          containerPort: 3100
        args:
        - -config.file=/etc/loki/local-config.yaml
        - -target=all
        resources:
          limits:
            cpu: "2"
            memory: 4Gi
          requests:
            cpu: "500m"
            memory: 1Gi
---
# Promtail DaemonSet 配置(用于收集 Kubernetes 日志)
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: promtail
  namespace: monitoring
spec:
  selector:
    matchLabels:
      app: promtail
  template:
    metadata:
      labels:
        app: promtail
    spec:
      containers:
      - name: promtail
        image: grafana/promtail:3.0.0
        args:
        - -config.file=/etc/promtail/promtail.yaml
        volumeMounts:
        - name: config
          mountPath: /etc/promtail
        - name: varlog
          mountPath: /var/log
        - name: pods
          mountPath: /var/log/pods
        - name: dockercontainers
          mountPath: /var/lib/docker/containers
      volumes:
      - name: config
        configMap:
          name: promtail-config
      - name: varlog
        hostPath:
          path: /var/log
      - name: pods
        hostPath:
          path: /var/log/pods
      - name: dockercontainers
        hostPath:
          path: /var/lib/docker/containers

5.3 日志收集的最佳实践

5.3.1 结构化日志

应用应主动输出结构化日志,最理想的格式是 JSON,这能为后续的自动化解析和分析提供极大便利:

{
  "timestamp": "2026-01-15T10:30:00.123Z",
  "level": "error",
  "message": "Database connection failed",
  "service": "user-api",
  "request_id": "abc123",
  "error_code": "DB_CONN_001",
  "stack_trace": "..."
}

5.3.2 日志级别规范

  • ERROR:影响核心功能的问题,需要立即介入处理。
  • WARN:潜在问题或配置不当,可能在未来引发错误。
  • INFO:重要的业务事件和状态变更,用于回溯问题上下文。
  • DEBUG:开发调试细节,生产环境应默认关闭。

5.3.3 日志保留策略

根据合规要求和存储成本,制定多级保留策略是个好习惯:

  • 热存储(Elasticsearch/Loki):7-30 天
  • 温存储(对象存储):30-90 天
  • 冷存储(归档):90 天以上

六、应用日志的最佳实践

6.1 应用层日志配置

6.1.1 日志输出到 stdout/stderr

容器环境的标准做法是将日志直接输出到 stdout/stderr,由容器运行时收集,切勿在容器内编写复杂的文件日志持久化逻辑。

# Python 示例
import logging
import sys

logging.basicConfig(
    level=logging.INFO,
    format='{"timestamp": "%(asctime)s", "level": "%(levelname)s", "message": "%(message)s"}',
    handlers=[logging.StreamHandler(sys.stdout)]
)

6.1.2 日志轮转配置

即使平台层有日志轮转,应用层也应配置,避免单文件过大导致性能问题:

<!-- Logback 配置示例(Java) -->
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>/var/log/app/application.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>/var/log/app/application.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory>
            <totalSizeCap>10GB</totalSizeCap>
        </rollingPolicy>
        <encoder>
            <pattern>{"timestamp": "%d{ISO8601}", "level": "%level", "logger": "%logger", "message": "%msg"}%n</pattern>
        </encoder>
    </appender>
    <root level="INFO">
        <appender-ref ref="FILE"/>
    </root>
</configuration>

6.2 Sidecar 日志收集模式

对于需要特殊日志处理逻辑的应用,可以使用 Sidecar 模式。让主容器将日志写入共享存储,由一个 Sidecar 容器专门负责收集和处理。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-with-sidecar
spec:
  template:
    spec:
      containers:
      - name: app
        image: my-app:latest
        volumeMounts:
        - name: log-volume
          mountPath: /var/log/myapp
      - name: log-collector
        image: fluent/fluent-bit:3.0.0
        volumeMounts:
        - name: log-volume
          mountPath: /var/log/myapp
        - name: fluent-bit-config
          mountPath: /fluent-bit/etc
      volumes:
      - name: log-volume
        emptyDir: {}
      - name: fluent-bit-config
        configMap:
          name: fluent-bit-config

七、日志相关故障排查

7.1 kubectl logs 无响应

# 检查 API Server 是否正常
kubectl get componentstatuses

# 检查 API Server 日志
kubectl logs -n kube-system kube-apiserver-<node-name> --tail=100

# 检查 kubelet 是否正常
ssh <node-name> "systemctl status kubelet"

# 绕过 API Server 直接查看节点日志文件
ssh <node-name> "cat /var/log/pods/<namespace>_<pod-name>_<uid>/<container-name>/0.log | tail -100"

7.2 日志丢失问题

7.2.1 容器重启导致日志丢失

默认情况下,容器重启后旧日志会被清理。解决方案是配置日志持久化、使用远程收集系统,或增加 kubelet 的日志保留策略。

# 修改 kubelet 日志配置,增加保留文件数和大小限制
cat /var/lib/kubelet/config.yaml
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
containerLogMaxFiles: 10
containerLogMaxSize: 50Mi

7.2.2 Fluent Bit/Fluentd 无法收集日志

# 检查 Fluent Bit Pod 状态
kubectl get pod -n kube-system -l k8s-app=fluent-bit

# 查看 Fluent Bit 自身日志
kubectl logs -n kube-system -l k8s-app=fluent-bit

# 检查 ConfigMap 配置是否正确
kubectl describe configmap fluent-bit-config -n kube-system

# 测试配置是否能正确解析日志
kubectl exec -it -n kube-system fluent-bit-xxxx -- fluent-bit --test --parser=parsers.conf < /var/log/containers/*.log

7.3 日志文件过大的处理

# 查找大于 100M 的日志文件
find /var/log/pods -name "*.log" -size +100M -exec ls -lh {} \;

# 在节点上手动压缩日志
sudo gzip /var/log/pods/<path>/0.log

# 清理 7 天前的旧日志
sudo find /var/log/pods -name "*.log.*" -mtime +7 -delete

# 监控日志目录大小
du -sh /var/log/pods/*

7.4 Pod 日志与容器日志不一致

某些情况下,kubectl logs 输出的日志与实际容器日志文件可能存在差异。

# 比较两者的差异
kubectl logs <pod-name> > /tmp/kubectl.log
ssh <node-name> "cat /var/log/pods/<pod-id>/<container>/0.log" > /tmp/file.log
diff /tmp/kubectl.log /tmp/file.log

# 可能的原因:
# 1. 日志轮转导致文件变化
# 2. 容器重启,--previous 参数指向了不同的日志文件
# 3. 日志编码问题

八、日志监控与告警

8.1 错误日志监控

配置 Prometheus 规则来监控错误日志的数量是一种主动防御手段。

apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: log-error-monitoring
  namespace: monitoring
spec:
  groups:
  - name: application-logs
    rules:
    - alert: HighErrorRate
      expr: |
        sum(rate(container_log_messages_total{level="error"}[5m])) by (namespace, pod)
        > 0.1
      for: 5m
      labels:
        severity: warning
      annotations:
        summary: "High error rate in {{ $labels.namespace }}/{{ $labels.pod }}"
        description: "Error rate exceeds 10% over 5 minutes"

8.2 日志存储容量监控

- alert: LogStorageUsageHigh
  expr: |
    (node_filesystem_avail_bytes{mountpoint="/var/log"} / node_filesystem_size_bytes{mountpoint="/var/log"}) < 0.2
  for: 10m
  labels:
    severity: warning
  annotations:
    summary: "Log storage usage is above 80%"

九、实用命令参考

9.1 快速查看多个 Pod 日志

# 查看标签为 app=nginx 的所有 Pod 的最近 50 行日志
kubectl logs -l app=nginx --tail=50

# 查看最近 1 小时内的严重错误日志
kubectl logs -l app=nginx --since=1h | grep -E "ERROR|FATAL"

# 统计近 24 小时错误日志总数
kubectl logs -l app=nginx --since=24h | grep -c "ERROR"

9.2 实时查看多个 Pod 日志

使用 stern 工具可以非常方便地同时追踪多个 Pod 的日志流。

# 使用 stern 同时查看多个 Pod 的日志
stern -n production app=nginx --since=10m

# 只显示包含 ERROR 的行
stern -n production app=nginx --filter=ERROR

# 显示带有时间戳的日志
stern -n production app=nginx --timestamp

9.3 日志分析常用命令

# 查看日志级别分布
kubectl logs <pod-name> | awk '{print $5}' | sort | uniq -c | sort -rn

# 查看 Top 10 常见错误
kubectl logs <pod-name> --since=24h | jq -r '.error // "no-error"' | sort | uniq -c | sort -rn | head -10

# 分析请求平均延迟
kubectl logs <pod-name> | jq -r 'select(.type=="request") | .duration' | awk '{sum+=$1; count++} END {print "Average:", sum/count, "ms"}'

# 查看特定时间段内的日志
kubectl logs <pod-name> | awk -F'[] []' '$2 >= "2026-01-15T10:00:00" && $2 <= "2026-01-15T11:00:00"'

总结

处理容器日志的正确方式可以分为三个层次来理解:

  1. 基础层:熟练运用 kubectl logs 命令及其各项参数(--tail--since-f--previous 等)。尤其注意,多容器 Pod 必须指定容器名称。
  2. 架构层:深入理解容器日志的存储机制(/var/log/pods 目录结构)、kubelet 的日志轮转逻辑。
  3. 平台层:构建起分布式的日志收集架构(如 EFK、Loki),实现日志的集中存储、搜索和可视化分析。

运维工程师应逐步摒弃频繁进入容器内部查看日志的习惯。这不仅效率堪忧,还可能因容器重启或进入调试状态而意外影响业务。正确做法是,通过 kubectl logs 或统一的日志平台来完成所有日志相关工作。

此外,日志本身的规范性也同样重要:坚持结构化输出(如 JSON)、合理设置日志级别、统一字段命名。这些看似微小的细节,都将为日后的日志分析、链路追踪和故障定位提供坚实的数据基础。在云栈社区,我们一直强调这种从点工具到平台化思维的转变,是运维工程师进阶的必经之路。




上一篇:Token涨价破圈:从Claude Code移除入门套餐看前沿AI模型成本普遍上涨
下一篇:AI编程效率革命:为什么输入法自带的语音输入才是目前最优解
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-4-25 12:07 , Processed in 0.630606 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表