DevOps设计之 Kubenetes 容器日志收集方案

Kubenetes 容器日志收集方案

Posted by 董江 on Tuesday, June 11, 2024

Kubenetes 容器日志收集方案

应用日志收集痛点

业务无要求:

日志格式无要求,可以是json序列化也可以是logback格式,对日志是否格式化/归一化格式无要求; 日志文件名无要求,可以是application.log、access.log; 只是需要规范认证后缀,以 .log/.txt/.lo

对业务要求:

1.业务日志轮转能力 -业务日志Node节点上 a.默认保留最近7份,最新2份明文保留,最久5份tar.gz压缩保留; b.如果node节点磁盘总可用度小于15%,将减少默认保留分数; -业务云端中心化保留N天 2.业务日志自定义输出 container 自定义日志写入到$(LOG_MOUNT_PATH)目录中

spec:
  containers:
  - env:
    - name: POD_NAME
      valueFrom:
        fieldRef:
          apiVersion: v1
          fieldPath: metadata.name
    - name: NAMESPACE
      valueFrom:
        fieldRef:
          apiVersion: v1
          fieldPath: metadata.namespace
    - name: LOG_MOUNT_PATH
      value: "/app/log/"
    image: tomcat
    name: tomcat
    volumeMounts:
    - mountPath: $(LOG_MOUNT_PATH)
      name: datalog
      subPathExpr: $(NAMESPACE)/$(POD_NAME)
  volumes:
  - hostPath:
      path: /data/log
      type: ""
    name: datalog

日志轮转

  • 无日志轮转应用:如python、php、C/C++,sae支持外部轮转;
  • 有日志轮转医用:如java(logback, log4j2)、golang;支持内部轮转;

日志采集

  • 自定义日志:可以根据日志文件路径挂载的Volume,找到相应在节点的文件进行采集。并同时自动将Pod上的Env/Annotation/Label加入到日志里作为元信息
  • 标准输出日志:自动自动发现并采集

容器CSI存储介质对比

存储介质

Kubernetes 从 1.12-1.30 被使用出差日志的存储介质,分为4类:

  • emptyDir/gitRepo
  • hostPath/Local
  • volume(cephfs、glusterfs、NFS、openebs)
  • 云厂商存储(awsEBS、NAS、AzureDisk)

日志落盘模式

Pod里不仅仅是输出stdout,还包括日志文件,就需要考虑到挂载日志文件到节点上,同时采用DaemonSet部署的Agent也需要挂载相同的目录,否则采用容器化部署的Agent无法查看到相应的文件,更无法采集。

emtpyDir

emtpyDir的生命周期跟随Pod,Pod销毁后其中存储的日志也会消失。

优点:使用简单,不同Pod都使用自己的emtpyDir,有一定的隔离性。 缺点:日志如果采集不及时,在Pod消耗后,存在丢失的可能性。 使用emptyDir挂载的日志文件,一般在节点的路径如下:

/var/lib/kubelet/pods/${pod.UID}/volumes/kubernetes.io~empty-dir/${volumeName}

hostPath

生命周期和Pod无关,Pod迁移或者销毁,日志文件还保留在现有磁盘上。

优点:生命周期和Pod无关,即使Pod销毁,日志文件依然在节点磁盘上,假设Agent没有采集日志,仍然可以找到日志文件 缺点:默认无隔离性,需要控制挂载的日志路径;另外,Pod迁移节点后,残留的日志文件长期积累容易占据磁盘,同时日志占据的磁盘无法控制使用的配额 为了解决隔离性,避免多个Pod打印日志到相同的路径和文件中,我们需要使用 subPathExpr 字段从 Downward API 环境变量构造 subPath 目录名。 该 VolumeSubpathEnvExpansion 功能从 Kubernetes1.15 开始默认开启,在1.17 GA。可参考 feature-gates 和 using-subpath-expanded-environment。

使用subPathExpr的示例如下所示:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx
  name: nginx
  namespace: default
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: metadata.name
        - name: NAMESPACE
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: metadata.namespace
        image: nginx
        name: nginx
        resources: {}
        volumeMounts:
        - mountPath: /data/log
          name: datalog
          subPathExpr: $(NAMESPACE)/$(POD_NAME)
      volumes:
      - hostPath:
          path: /data/log
          type: ""
        name: datalog

Pv

Pv的访问模式包括:

  • ReadWriteOnce(RWO):读写权限,并且只能被单个Node挂载。
  • ReadOnlyMany(ROX):只读权限,允许被多个Node挂载。
  • ReadWriteMany(RWX):读写权限,允许被多个Node挂载。

对于大部分的业务来说,都是Deployment无状态部署,需要挂载同一个Pv共享;对于一些中间件等有状态服务,一般会使用StatefulSet部署,每个Pod会使用独立的Pv。

优点:存储日志不容易丢失; 缺点:有一定的使用和运维复杂度;多个Pod共享同一个Pv时存在隔离性问题;很多的日志Agent对采集云盘上的日志文件支持不够成熟,可能存在一些隐患; 在Node上找到使用Pv挂载的对应日志文件,但是Pv根据不同的底层实现,在Node上的路径会有一定的区别。

总结

  1. 大部分主流的开源Agent,只对容器Stdout有部分支持,比如支持采集的时候统一加上一些K8s相关元信息;
  2. Agent采集 不能保证数据100%及时,通过落盘再采集方式 做IO缓冲;
  3. HostPath是上云节点业务感知最小方式;
  4. 最终态,是提高网络传输可靠性和稳定性,减少落盘空间大小(以网络IO换磁盘IO);

应用日志现状假设

应用日志:混在标准输出stdout 和 错误输出stderr中情况

  1. 非容器化:
  • 标准输出到系统回收站 2>&1 1>/dev/null
  • 标准输出到文件 2>&1 &>filename
  1. 容器化方式: 标准输出stdout和错误输出stderr,到 /var

kubelet每个节点上运行的进程来管理日志轮换。

  • containerLogMaxSize - 它定义容器日志文件在轮换之前的最大大小。例如:“5Mi”或“256Ki”。默认值:“10Mi”。
  • containerLogMaxFiles - 指定每个容器可以存在的最大容器日志文件数量。默认值:5 对于 docker ce 引擎:/etc/docker/daemon.json
{
  "log-driver": "local",
  "log-opts": {
    "max-size": "10m",
    "max-file": "10"
  }
}

对于非 docker ce引擎:只能通过kubelet 做日志轮转

为了轮转方式统一,统一使用 kubelet 做日志轮转

应用日志:输出local文件,无format和日志轮转能力

  1. 非容器化方式:
  • 写到规划好的/data盘下的一个目录中,再做日志轮转。核心在于日志保存和轮转;
  1. 容器化方式:通过外挂hostPath 或者 外挂卷实现日志本地化存储;
  • 通过DaemonSet/CrobJob对规范下的日志目录(/data/log//<pod.name>/xxxx)进行 日志轮转:
apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: app-logrotate
spec:
  schedule: "*/1 * * * *"
  concurrencyPolicy: Forbid
  jobTemplate:
    spec:
      completions: 5 # Number of nodes
      parallelism: 5 # Number of nodes
      template:
        metadata:
          labels:
            app.kubernetes.io/name: app-logrotate
        spec:
          affinity:
            podAntiAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
                - labelSelector:
                    matchExpressions:
                      - key: app.kubernetes.io/name
                        operator: In
                        values:
                          - app-logrotate
                  topologyKey: kubernetes.io/hostname
          containers:
          - name: logrotate
            image: docker.io/kicm/logrotate
            volumeMounts:
            - name: logrotate-conf
              mountPath: /etc/logrotate.d
              readOnly: true
            - name: app-logs
              mountPath: /var/log/app
          volumes:
          - name: logrotate-conf
            configMap:
              name: logrotate-config
          - name: app-logs
            hostpath:
              path: /data/log/
              type: Directory
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: logrotate
  namespace: kube-system
spec:
  template:
    metadata:
      labels:
        k8s-app: logrotate
    spec:
      dnsPolicy: Default
      containers:
      - name: logrotate
        image: docker.io/kicm/logrotate
        resources:
          limits:
            memory: 10Mi
          requests:
            cpu: 10m
            memory: 10Mi
        env:
        - name: LOGROTATE_ARGS
          value: -v
        volumeMounts:
        - name: logrotate-conf
          mountPath: /etc/logrotate.d
          readOnly: true
        - name: app-logs
          mountPath: /var/log/app
      terminationGracePeriodSeconds: 30
      volumes:
      - name: logrotate-conf
        configMap:
          name: logrotate-config
      - name: app-logs
        hostpath:
          path: /data/log/
          type: Directory
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: logrotate-config
data:
  my_logs.conf: |
    /var/log/app/*/*/*.log {
        daily
        maxsize 500M
        rotate 7
        missingok
        notifempty
        compress
        delaycompress
        dateformat -%Y%m%d_%H%M%S
        create
        su
    }    

为了解决隔离性,避免多个Pod打印日志到相同的路径和文件中,我们需要使用 subPathExpr 字段从 Downward API 环境变量构造 subPath 目录名。

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx
  name: nginx
  namespace: default
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: metadata.name
        - name: NAMESPACE
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: metadata.namespace
        image: nginx
        name: nginx
        resources: {}
        volumeMounts:
        - mountPath: /data/log
          name: datalog
          subPathExpr: $(NAMESPACE)/$(POD_NAME)
      volumes:
      - hostPath:
          path: /data/log
          type: ""
        name: datalog

总结

  • 应用日志轮转:使用DaemonSet/CrobJob对日志轮转;
  • 应用日志采集:/data/log/$(NAMESPACE)/$(POD_NAME)/xxx.log 进行收集,并将日志自动添加pod label

应用日志:输出local文件,并且分类型。包括不限于accesslog、application.log和crash.log, 并且也有日志轮转能力

和上面类似

日志采集架构设计方案

日志采集范畴

  • 业务容器日志:容器标准输出 和 容器日志采集
  • PaaS Addon组件日志: 包括 coredns、metrics-server、kube-state-metrics等
  • PaaS平台组件 日志采集 和 聚合: 包括: etcd、kube-controller-manager、kube-apiserver、kube-scheduler 的 标准输出和文件
  • Linux系统日志: /var/log/message 、/var/log/yum.log 等

日志采集架构

业务监控日志

  • 支持pod 自定义日志、标准输出日志采集,并主动管理上pod元数据
  • agent支持多住户采集,不同租户id,写入不同loki index
  • agent支持部分pipeline改写能力
  • 支持checkpoint机制,断点续传
  • 不依赖日志是否序列化

业务大数据日志

  • agent支持数据采集 多写出源;
  • 支持sink loki、外部es和外部kafka能力;

「如果这篇文章对你有用,请随意打赏」

Kubeservice博客

如果这篇文章对你有用,请随意打赏

使用微信扫描二维码完成支付