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

1356

积分

0

好友

175

主题
发表于 昨天 00:00 | 查看: 5| 回复: 0

在 Kubernetes 集群的运维中,由业务日志持续增长引发的节点磁盘空间告警,是一个频繁出现且消耗大量精力的“老大难”问题。传统的人工排查与清理方式,不仅效率低下,更伴随着资源成本攀升和合规风险。本文将结合云原生技术栈的特性,分享一套通过 Kubelet 配置优化、OpenKruise SidecarSet 注入、Filebeat 日志收集 等技术手段实现的自动化解决方案,旨在从根源上治理磁盘空间问题,同时满足日志长期保留的合规要求。

以下思路和方法具有普适性,不仅可以处理日志问题,也能灵活变通,应用于其他需要自动化运维的场景。

磁盘空间不足引发的四大痛点

在 K8s 集群中,许多业务日志采用 emptyDirhostPath 方式存储于本地。随着业务规模扩大与时间推移,磁盘空间不足通常会伴随一系列痛点:

  1. 运维效率低下:在值守过程中,磁盘不足告警占比极高。此类问题技术含量低却需要人工紧急干预,严重占用了 SRE 团队的宝贵时间,影响核心运维工作的推进。
  2. 资源成本攀升:节点服务器往往采用标准化机型,磁盘空间有限。面对日志的持续增长,不得不频繁扩容磁盘或增加节点,导致服务器资源成本持续上升。
  3. 合规风险突出:等保三级等合规要求明确规定,业务日志需保留 180 天以上。传统的本地存储方式难以满足长期留存需求,且日志分散存储极易造成丢失或篡改风险。
  4. 业务稳定性隐患:当节点磁盘使用率达到特定阈值时,Kubelet 会触发 Pod 被动驱逐,严重时可能导致业务中断,直接影响服务可用性。

接下来,我们将从三个主要维度拆解问题,并提供对应的自动化解决方案。

解决方案一:节点镜像导致的磁盘空间不足

K8s 集群节点的 nodefs(节点文件系统)和 imagefs(镜像文件系统)通常共享同一个磁盘分区。累积的未使用镜像是导致磁盘空间不足的首要元凶。通过优化 Kubelet 配置,我们可以建立自动化的镜像清理机制,从而避免 Pod 被意外驱逐。

核心思路:建立“镜像 GC 阈值 < 监控告警阈值 < Pod 驱逐阈值”的三层纵深防护机制。

  • 镜像 GC 阈值:当镜像磁盘使用率超过 80% 时触发自动垃圾回收(GC),低于 70% 时停止。
  • 监控告警阈值:磁盘使用率超过 85% 时触发监控告警(此阈值可根据业务敏感度调整)。
  • Pod 驱逐阈值:当镜像磁盘或节点磁盘剩余空间低于 10% 时,Kubelet 将最终触发 Pod 驱逐。

Kubelet 关键配置
以下配置片段展示了如何实现上述策略:

kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
# 镜像GC配置
imageMinimumGCAge: 2m0s         # 未使用镜像的最小保留时长
imageGCHighThresholdPercent: 80 # 镜像磁盘使用率高阈值(触发GC)
imageGCLowThresholdPercent: 70  # 镜像磁盘使用率低阈值(停止GC)
# Pod驱逐配置
evictionHard:
  imagefs.available: 10%  # imagefs剩余空间阈值
  nodefs.available: 10%   # nodefs剩余空间阈值
  nodefs.inodesFree: 5%   # nodefs inode剩余阈值
  memory.available: 5%    # 内存剩余阈值

整合到完整的 KubeletConfiguration 中,关键部分如下所示(仅展示部分配置):

kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
...
imageMinimumGCAge: 2m0s
imageGCHighThresholdPercent: 80
imageGCLowThresholdPercent: 70
...
evictionHard:
  imagefs.available: 10%
  memory.available: 5%
  nodefs.available: 10%
  nodefs.inodesFree: 5%
...

解决方案二:业务日志导致的磁盘空间不足

对于业务容器内产生的日志(通常通过 emptyDir 挂载),我们通过 SidecarSet 注入日志处理脚本和 Filebeat 采集器,实现日志的自动压缩、本地清理与远端同步,形成完整的生命周期管理。

1. 业务 Pod 标准化配置

首先,需要对业务 Pod 进行标准化配置,以确保 Sidecar 能够正常注入并共享日志卷。

template:
  metadata:
    labels:
      kruise.io/inject-log-deal:  "true"   # 用于匹配日志压缩清理Sidecar
      kruise.io/inject-log-remote: "true"  # 用于匹配日志远端同步Sidecar
  spec:
    volumes:
      - name: app-log-volume
        emptyDir: {}  # 日志存储卷
    containers:
      - name: [业务容器名]
        volumeMounts:
          - name: app-log-volume
            mountPath: /data/logs  # 统一的日志挂载目录

2. 统一管理:ConfigMap 配置

创建一个 ConfigMap 来集中管理日志处理脚本、定时任务以及 Filebeat 的配置。

apiVersion: v1
kind: ConfigMap
metadata:
  name: sidecarset-cm
  namespace: log
data:
  # 定时任务配置(每天凌晨2:02执行)
  crontab.txt: |
    2 2 * * * sh /root/compress-log.sh

  # 日志压缩与清理脚本
  compress-log.sh: |
    #!/bin/bash
    set -x
    # 压缩一天前的非压缩日志
    for i in `find /data/logs/ -mtime +0 -type f -name "*log*" ! -name "*.tar.gz"`
    do
            filename=`echo $i|awk -F'/' '{print $NF}'`
            bdir=`dirname $i`
            cd $bdir
            tar zcvf $filename".tar.gz" $filename
            rm $filename
    done

    # 本地日志保留策略(默认7天,特殊模块30天)
    keepDay=7
    hostname | grep "modulename" && keepDay=30
    find /data/logs/ -mtime +$keepDay -type f -name "*log*.tar.gz" | xargs -I {} rm -rf {} \; &> /dev/null

  # Filebeat日志采集配置(输出至Kafka)
  filebeat.yml: |
    filebeat.inputs:
    - type: log
      enabled: true
      paths:
      - /data/logs/**/*
      encoding: utf-8
      fields:
        module_name: "${MODULE_NAME}"
        module_namespace: "${POD_NAMESPACE}"
    # 多行日志合并配置
    multiline.pattern: '(^\S|^\s)'
    multiline.negate: true
    multiline.match: after
    multiline.max_lines: 1000
    multiline.timeout: 5s

    # Kafka输出配置
    output.kafka:
      enabled: true
      hosts: ["yourkafka.domain.local:9092"]
      partition.round_robin:
        reachable_only: true
      version: 2.0.0
      topic: 'module_logs'

3. SidecarSet 注入:日志压缩与本地清理

利用 OpenKruise 的 SidecarSet 能力,向匹配的业务 Pod 中注入一个负责日志定时压缩和清理的 Sidecar 容器。

apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
  name: log-deal
spec:
  selector:
    matchLabels:
      kruise.io/inject-log-deal: "true"  # 匹配业务Pod标签
  updateStrategy:
    type: RollingUpdate
    maxUnavailable: 1
  containers:
  - name: log-deal
    image: youharbor.domain.com/alpine/3.14.3/alpine  # 私有仓库镜像
    command: ["/bin/sh", "-c"]
    args: ["usr/bin/crontab /root/crontab.txt ; /usr/sbin/crond -f -l 8"]
    volumeMounts:
    - name: compress-log
      mountPath: /root/compress-log.sh
      subPath: compress-log.sh
    - name: crontab
      mountPath: /root/crontab.txt
      subPath: crontab.txt
    podInjectPolicy: AfterAppContainer  # 主容器启动后注入
    shareVolumePolicy: enabled          # 共享Pod日志卷
    resources:                          # 资源限制(按需调整)
      limits:
        memory: 1000Mi
        cpu: 1
      requests:
        memory: 10Mi
        cpu: 10m
  volumes:
  - name: compress-log
    configMap:
      name: sidecarset-cm
      key: compress-log.sh
  - name: crontab
    configMap:
      name: sidecarset-cm
      key: crontab.txt

4. SidecarSet 注入:日志远端同步

注入第二个 Sidecar,使用 Filebeat 实时将本地日志采集并同步到远端的 Kafka 消息队列,为后续写入 ES、对象存储等长期存储做准备。

apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
  name: log-remote
spec:
  selector:
    matchLabels:
      kruise.io/inject-log-remote: "true"  # 匹配业务Pod标签
  updateStrategy:
    type: RollingUpdate
    maxUnavailable: 1
  containers:
  - name: log-remote
    image: youharbor.domain.com/filebeat:8.5.0  # 私有仓库Filebeat镜像
    command: ["/bin/sh", "-c"]
    args: ["/usr/share/filebeat/filebeat -e -c /usr/share/filebeat/filebeat.yml"]
    volumeMounts:
    - name: filebeat-config
      mountPath: /usr/share/filebeat/filebeat.yml
      subPath: filebeat.yml
    podInjectPolicy: AfterAppContainer  # 主容器启动后注入
    shareVolumePolicy: enabled          # 共享Pod日志卷
    env:                               # 注入环境变量(用于日志字段标识)
      - name: POD_NAME
        valueFrom:
          fieldRef: {fieldPath: metadata.name}
      - name: POD_NAMESPACE
        valueFrom:
          fieldRef: {fieldPath: metadata.namespace}
      - name: MODULE_NAME
        valueFrom:
          fieldRef: {fieldPath: metadata.labels['app.kubernetes.io/name']}
      - name: TZ
        value: "Asia/Shanghai"
    resources:                         # 资源限制(按需调整)
      limits:
        memory: 1000Mi
        cpu: 1
      requests:
        memory: 10Mi
        cpu: 10m
  volumes:
  - name: filebeat-config
    configMap:
      name: sidecarset-cm
      key: filebeat.yml

解决方案三:节点非容器日志导致的磁盘空间不足

对于直接运行在节点上的服务(非容器化)产生的日志,我们通过 OpenKruise 的 AdvancedCronJob + BroadcastJob 组合,实现集群范围内所有节点的定时批量清理。

1. ConfigMap:清理脚本

存放用于节点日志清理的 Shell 脚本。

apiVersion: v1
kind: ConfigMap
metadata:
  name: compress-log-cm
  namespace: log
data:
  compress_logfile.sh: |
    #!/bin/bash
    set -x
    # 压缩一天前的非压缩日志
    for i in `find /data/logs/ -mtime +0 -type f -name "*log*" ! -name "*.tar.gz"`
    do
            filename=`echo $i|awk -F'/' '{print $NF}'`
            bdir=`dirname $i`
            cd $bdir
            tar zcvf $filename".tar.gz" $filename
            rm $filename
    done

    # 非容器日志保留30天
    find /data/logs/ -mtime +30 -type f -name "*log*.tar.gz" | xargs -I {} rm -rf {} \; &> /dev/null

2. AdvancedCronJob:全节点定时任务

定义一个定时任务,在每天指定时间创建一个 BroadcastJob。该 Job 会在集群所有节点上并行启动一个 Pod,执行日志清理脚本后自动退出。

apiVersion: apps.kruise.io/v1alpha1
kind: AdvancedCronJob
metadata:
  namespace: op
  name: compress-log
spec:
  timeZone: "Asia/Shanghai"   # 时区配置
  schedule: "1 1 * * *"       # 每天凌晨1:01执行
  template:
    broadcastJobTemplate:
      spec:
        template:
          spec:
            tolerations:       # 容忍节点污点(确保调度到所有节点)
              - key: "your-key"
                operator: "Equal"
                value: "your-value"
                effect: "NoSchedule"
            volumes:
              - name: compress-log-cm-conf
                configMap:
                  name: compress-log-cm
                  items:
                    - key: compress_logfile.sh
                      path: compress_logfile.sh
              - name: data-logs-hostpath
                hostPath: {path: /data/logs}  # 挂载节点日志目录
            imagePullSecrets:
              - name: your-docker-registry   # 私有仓库密钥
            containers:
              - name: compress-log
                image: yourharbor.domain.com/alpine/3.14.3/alpine
                command: ["/bin/sh"]
                args: ["/root/compress_logfile.sh"]
                volumeMounts:
                  - name: data-logs-hostpath
                    mountPath: /data/logs
                  - name: compress-log-cm-conf
                    mountPath: /root/compress_logfile.sh
                    subPath: compress_logfile.sh
            restartPolicy: Never  # 任务执行完毕后退出
        completionPolicy:
          type: Always
          ttlSecondsAfterFinished: 86400  # 任务记录保留24小时

方案收益总结

通过实施以上一套组合拳式的云原生自动化治理方案,我们获得了显著的收益:

  • 运维效率提升:日志压缩、清理、同步全流程自动化,无需人工干预,磁盘不足告警量大幅降低。
  • 资源成本优化:节点本地日志仅保留 7 天(特殊模块 30 天),磁盘使用率保持稳定,无需额外扩容磁盘或增加节点。
  • 合规达标:日志通过 Filebeat 同步至远端 Kafka,并可进一步持久化到 ES 或对象存储,轻松保留 180 天以上,满足等保三级要求。
  • 稳定性增强:通过预防性的镜像 GC 和日志管理,有效避免了因磁盘满导致的 Pod 驱逐,提升了业务服务的整体稳定性。

这套方案充分体现了云原生技术栈在自动化运维方面的强大能力。如果你在实践过程中有更好的想法或遇到了其他挑战,欢迎在云栈社区交流讨论。




上一篇:C语言不透明指针:实现极致封装与SDK级接口设计
下一篇:积分系统搭建指南:从核心原则到ROI计算的完整实践
您需要登录后才可以回帖 登录 | 立即注册

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

GMT+8, 2026-1-16 00:35 , Processed in 0.205267 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

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