OpenKruise x iLogtail:管理可观测数据采集Sidecar容器最佳实践

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 在Kubernetes集群中使用Sidecar容器采集业务容器的可观测性数据是一种常见的数据采集部署方式,然而Sidecar容器对业务部署的侵入性和生命周期管理的复杂性使得这种部署模式的管理代价不仅高昂而且容易出错。本文对Sidecar采集容器管理的难点进行分析,使用OpenKruise提供的管理能力逐一解决,并以iLogtail为例给出基于OpenKruise管理可观测数据采集Sidecar容器的最佳实践。

Sidecar部署方式的使用场景

可观测系统是IT系统的眼睛,在K8S的 官方文档中介绍了多种的可观测数据采集形式,总结起来主要有下述3种:原生方式、DaemonSet方式和Sidecar方式。 三种方式都有利有弊,没有哪种方式能够完美的解决100%问题的,所以要根据场景来贴合。其中Sidecar方式为每个Pod单独部署采集agent,相对资源占用较多,但稳定性、灵活性以及多租户隔离性较强,建议在Job类任务采集场景或作为PaaS平台为多个业务方服务的集群使用该方式。

  • 稳定性:Sidecar采集方式利用K8s同一个Pod内的容器可以共享存储和网络的特性,无容器发现过程。同时只要采集容器没有退出,共享存储卷上的文件就不会被删除,因此无论是业务容器的生命周期非常短或是日志采集出现延时的情况都可以保证采集数据的完整性。
  • 灵活性:作为在每个Pod单独部署的Agent,为其分配多少资源、采集哪些数据都可以根据业务需求定制,甚至不同业务可以因不同采集需求而部署不同种类和版本的Agent。
  • 多租户隔离性:各个Sidecar容器内部署的Agent仅能观测到各自Pod内的数据,无法获取其他租户的信息。Agent之间互相独立运行,当某个Agent崩溃或无法正常采集数据时,同节点上的其他Agent仍然可以正常工作。

K8s Sidecar模式的痛点

使用K8s Sidecar模式采集业务可观测性数据需要修改业务的部署声明,将采集Agent容器与业务App容器部署在同一个Pod内,并通过共享Volume和网络使Agent可以采集数据并上传。如果要做到数据采集在任何情况下都不发生丢失,那么还需要确保Agent进程在业务App进程启动前启动、Agent进程在App进程结束后退出。对于Job类型的工作负载还需要考虑如何使Agent在App容器完成后主动退出的问题。因此,下面以iLogtail作为采集Agent为例,给出通用的Sidecar采集部署配置。

iLogtail是阿里云开源的一款高性能的轻量级可观测性数据采集Agent,支持多种Logs、Traces、Metrics可观测数据采集到Kafka、ElasicSearch、ClickHouse等多种下游中,其稳定性已在阿里巴巴以及数万阿里云客户生产中使用验证。

apiVersion: batch/v1
kind: Job
metadata:
  name: nginx-mock
  namespace: default
spec:
  template:
    metadata:
      name: nginx-mock
    spec:
      restartPolicy: Never
      containers:
        - name: nginx
          image: registry.cn-hangzhou.aliyuncs.com/log-service/docker-log-test:latest
          command: ["/bin/sh", "-c"]
          args:
            - until [[ -f /tasksite/cornerstone ]]; do sleep 1; done;
              /bin/mock_log --log-type=nginx --path=/var/log/nginx/access.log --total-count=100;
              retcode=$?;
              touch /tasksite/tombstone;
              exit $retcode
              # 通知Sidecar容器业务完成,否则Sidecar不会随业务容器退出
          volumeMounts:
            # 通过volumeMounts与iLogtail sidecar容器共享 log 目录
            - mountPath: /var/log/nginx
              name: log
            # 通过volumeMounts与iLogtail sidecar容器通信进程状态
            - mountPath: /tasksite
              name: tasksite
        # iLogtail Sidecar
        - name: ilogtail
          image: sls-opensource-registry.cn-shanghai.cr.aliyuncs.com/ilogtail-community-edition/ilogtail:latest
          command: ["/bin/sh", "-c"]
          # 第一个`sleep 10`的目的是等待iLogtail开始采集。iLogtail可能需要等从远端成功拉取配置后才开始采集。
          # 第二个`sleep 10`的目的是等待iLogtail完成采集。直到iLogtail将所有收集数据发送给下游才算完成采集。
          args:
            # 通知业务容器Sidecar就绪
            - /usr/local/ilogtail/ilogtail_control.sh start;
              sleep 10;
              touch /tasksite/cornerstone;
              until [[ -f /tasksite/tombstone ]]; do sleep 1; done;
              sleep 10;
              /usr/local/ilogtail/ilogtail_control.sh stop;
          volumeMounts:
            - name: log
              mountPath: /var/log/nginx
            - name: tasksite
              mountPath: /tasksite
            - mountPath: /usr/local/ilogtail/ilogtail_config.json
              name: ilogtail-config
              subPath: ilogtail_config.json
      volumes:
        - name: log
          emptyDir: {
   
   }
        - name: tasksite
          emptyDir:
            medium: Memory
        - name: ilogtail-config
          secret:
            defaultMode: 420
            secretName: ilogtail-secret

配置中的volumeMounts部分声明了共享存储,其中name为log的用于共享数据,name为tasksite的用于进程协调。args部分大量代码用于控制Sidecar和业务容器间进程的启动及退出顺序。通过以上配置代码可以看到,Sidecar模式存在如下一些弊端:

  • 业务Pod耦合(运维、代理)多种Sidecar容器,增加业务开发人员的学习成本。
  • Pod中的多个Sidecar容器和业务需要考虑运行依赖关系,增加了配置的复杂性。
  • Sidecar容器升级将导致业务Pod重建,由于Sidecar容器一般是独立的中间件团队负责,如果升级会存在极大的业务方阻力。

OpenKruise管理Sidecar容器的利器

SidecarSet

SidecarSet是OpenKruise中针对Sidecar容器管理抽象出来的概念,负责注入和升级K8s集群中的Sidecar容器,是OpenKruise的核心workload之一,详细可参考:SidecarSet文档

  • 自动注入Sidecar:将Sidecar容器配置与业务Workload(Deployment、CloneSet等)配置解耦,简化用户使用成本。
  • 独立升级Sidecar容器:不重建Pod,单独升级Sidecar容器,对业务无感。

Container Launch Priority

Container Launch Priority 提供了控制一个 Pod 中容器启动顺序的方法,详细可参考:Container Launch Priority文档

  • 控制一个 Pod 中容器启动顺序:按声明中的顺序启动或根据自定义顺序启动,简化配置。

Job Sidecar Terminator

对 Job 类型的 Workload 提供业务主容器完成任务并退出后,通知日志收集等 Sidecar 容器退出的能力,详细可参考:Job Sidecar Terminator文档

  • 提供 Job Sidecar 主动退出能力:主容器退出后通知 Sidecar容器退出,从而使得 Job Controller 能够正确完成,简化配置。

iLogtail Sidecar部署实践

社区中有很多应用iLogtail部署可观测方案的案例,参考Kubernetes使用iLogtail社区版使用案例。本文聚焦如何使用OpenKruise的上述能力简化iLogtail Sidecar的管理,如果遇到iLogtail方面的问题请到GitHub Discussions提问。

OpenKruise Feature Gate

在kruise-controller-manager Deployment中启用SidecarTerminator特性,另外两项特性默认打开。

    spec:
      containers:
        - args:
            - '--enable-leader-election'
            - '--metrics-addr=:8080'
            - '--health-probe-addr=:8000'
            - '--logtostderr=true'
            - '--leader-election-namespace=kruise-system'
            - '--v=4'
            - '--feature-gates=SidecarTerminator=true'
            - '--sync-period=0'

iLogtail SidecarSet

ConfigServer开源版中创建iLogtail采集配置,如下:

enable: true
inputs:
  - Type: file_log
    LogPath: /var/log/nginx
    FilePattern: access.log
flushers:
  - Type: flusher_kafka
    Brokers:
      - <kafka_host>:<kafka_port>
      - <kafka_host>:<kafka_port>
    Topic: nginx-access-log

关联到机器组

定义iLogtail SidecarSet配置,如下:

apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
  name: ilogtail-sidecarset
spec:
  selector:
    # 需要注入 sidecar 容器的 pod labels
    matchLabels:
      kruise.io/inject-ilogtail: "true"
  # SidecarSet默认是对整个集群生效,可以通过namespace字段指定生效的范围
  namespace: default
  containers:
    - command:
        - /bin/sh
        - '-c'
        - '/usr/local/ilogtail/ilogtail_control.sh start_and_block 10'
        # 参数10的目的是等待10秒数据发送完毕后退出
      image: sls-opensource-registry.cn-shanghai.cr.aliyuncs.com/ilogtail-community-edition/ilogtail:edge
      livenessProbe:
        exec:
          command:
            - /usr/local/ilogtail/ilogtail_control.sh
            - status
      name: ilogtail
      env:
        - name: KRUISE_CONTAINER_PRIORITY
          value: '100'
        - name: KRUISE_TERMINATE_SIDECAR_WHEN_JOB_EXIT
          value: 'true'
        - name: ALIYUN_LOGTAIL_USER_DEFINED_ID
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: 'metadata.labels[''ilogtail-tag'']'
      volumeMounts:
        - mountPath: /var/log/nginx
          name: log
        - mountPath: /usr/local/ilogtail/checkpoint
          name: checkpoint
        - mountPath: /usr/local/ilogtail/ilogtail_config.json
          name: ilogtail-config
          subPath: ilogtail_config.json
  volumes:
    - name: log
      emptyDir: {
   
   }
    - name: checkpoint
      emptyDir: {
   
   }
    - name: ilogtail-config
      secret:
        defaultMode: 420
        secretName: ilogtail-secret

注意,此处仅写了livenessProbe,请勿使用readinessProbe,否则后续Sidecar容器升级时会导致Pod状态先变成Not Ready。

针对机器资源不太充足的场景,为减少Pod资源的申请,可以将Sidecar container request.cpu=0,此种情况下Pod的QoS将会是 Burstable

自动注入iLogtail Sidecar容器

定义Nginx Mock Job,只包含 nginx 相关配置,如下:

apiVersion: batch/v1
kind: Job
metadata:
  name: nginx-mock
  namespace: default
spec:
  template:
    metadata:
      name: nginx-mock
      labels:
        # 注入 ilogtail sidecar 容器的label
        kruise.io/inject-ilogtail: "true"
    spec:
      restartPolicy: Never
      containers:
        - name: nginx
          image: registry.cn-hangzhou.aliyuncs.com/log-service/docker-log-test:latest
          command: ["/bin/sh", "-c"]
          args:
            - /bin/mock_log --log-type=nginx --path=/var/log/nginx/access.log --total-count=100
          volumeMounts:
          # 通过volumeMounts与iLogtail sidecar容器共享 log 目录
          - mountPath: /var/log/nginx
            name: log
      volumes:
        - name: log
          emptyDir: {
   
   }
        - name: tasksite
          emptyDir:
            medium: Memory

将Nginx Mock Job Apply到K8s集群后,发现创建的Pod都被注入了 iLogtail Sidecar 容器,说明kruise.io/inject-ilogtail: "true"起到了效果,如下:

若没有此项配置,将只有nginx container而没有ilogtail container。

通过查阅K8s事件可知,ilogtail container优先于nginx container创建,说明KRUISE_CONTAINER_PRIORITY起到了效果,如下:

若没有此项配置,ilogtail container可能晚于nginx container启动,导致采集数据不完整。

通过查阅K8s事件还可知,ilogtail container在nginx container完成后退出,说明KRUISE_TERMINATE_SIDECAR_WHEN_JOB_EXIT起到了效果,如下:

若没有此项配置,Pod将一直处于Running状态,因为Sidecar不会随业务容器退出,除非手动Delete Job。

以上两项Sidecar容器启动和退出的正确顺序保证了iLogtail采集数据的完整性。查询采集到Kafka的数据,从点位计算可知,数据采集完整,共100条(92-40+106-58=100),如下:

独立升级iLogtail Sidecar容器(edge -> latest)

我们将上面iLogtail SidecarSet的配置更新为latest并apply。

      image: sls-opensource-registry.cn-shanghai.cr.aliyuncs.com/ilogtail-community-edition/ilogtail:latest

通过K8s事件可以发现Pod并没有重建,ilogtail容器由于声明发生了变化进行了重建,而同时nginx容器并没有受影响中断。如下:

该特性依赖Kruise原地升级的能力实现,详情参考文档:Kruise原地升级。 不过独立升级sidecar容器也存在一定的风险性,如果sidecar容器升级过程中失败,则将导致Pod Not Ready,进而影响业务,因此SidecarSet本身提供了非常丰富的灰度发布能力来尽量规避该风险, 详情参考文档:Kruise SidecarSet,如下:

apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
  name: sidecarset
spec:
  # ...
  updateStrategy:
    type: RollingUpdate
    # 最大不可用数量
    maxUnavailable: 20%
    # 分批发布
    partition: 90
    # 金丝雀发布,通过pod labels
    selector:
      matchLabels:
        # Some Pods contain canary labels,
        # or any other labels where a small number of pods can be selected
        deploy-env: canary

另外,如果是类似于ServiceMesh Envoy Mesh类容器则需要借助于SidecarSet热升级特性,详情请参考:SidecarSet热升级

Argo-cd部署SidecarSet(Optional)

如果使用Argo-cd发布Kruise SidecarSet,则需要配置 SidecarSet Custom CRD Health Checks。 Argo-cd根据该配置能够实现SidecarSet自定义资源的检查,如SidecarSet是否发布完成,以及Pod是否ready等,如下:

apiVersion: v1
kind: ConfigMap
metadata:
  labels:
    app.kubernetes.io/name: argocd-cm
    app.kubernetes.io/part-of: argocd
  name: argocd-cm
  namespace: argocd
data:
  resource.customizations.health.apps.kruise.io_SidecarSet: |
    hs = {}
    -- if paused
    if obj.spec.updateStrategy.paused then
      hs.status = "Suspended"
      hs.message = "SidecarSet is Suspended"
      return hs
    end

    -- check sidecarSet status
    if obj.status ~= nil then
      if obj.status.observedGeneration < obj.metadata.generation then
        hs.status = "Progressing"
        hs.message = "Waiting for rollout to finish: observed sidecarSet generation less then desired generation"
        return hs
      end

      if obj.status.updatedPods < obj.spec.matchedPods then
        hs.status = "Progressing"
        hs.message = "Waiting for rollout to finish: replicas hasn't finished updating..."
        return hs
      end

      if obj.status.updatedReadyPods < obj.status.updatedPods then
        hs.status = "Progressing"
        hs.message = "Waiting for rollout to finish: replicas hasn't finished updating..."
        return hs
      end

      hs.status = "Healthy"
      return hs
    end

    -- if status == nil
    hs.status = "Progressing"
    hs.message = "Waiting for sidecarSet"
    return hs

总结

Pod包含多个container的方式将越来越被更多的开发者接受,进而K8S生态里面急需一种能够有效管理Sidecar容器的方式。 Kruise SidecarSet、Container Launch Priority、Job Sidecar Terminator等功能是在Sidecar容器管理上面的一些探索,

使用OpenKruise管理iLogtail日志采集大幅降低了管理Sidecar的难度,解耦了Sidecar和业务容器的配置,使得容器间的启动顺序维护变得清晰,并且支持不重建Pod更新Sidecar容器。但是我们也看到比如Sidecar容器应该分配多少资源、日志路径挂载如何规划、不同业务Pod中的Sidecar的配置如何区分等问题并没有完全解决。 所以,我们也希望能够与社区的更多开发者一起探索,同时也欢迎大家都能提供一些思路,共同繁荣K8S生态。

加入钉钉群聊OpenKruise 社区:https://qr.dingtalk.com/action/joingroup?code=v1,k1,24NpKJPrv75JraA8vb/T8W8aTXpc97wboFP2cOjSL4U=&_dt_no_comment=1&origin=1


加入钉钉群聊iLogtail社区:https://qr.dingtalk.com/action/joingroup?code=v1,k1,A7uxZF3XMTsiAL3GGd18DZFGmAHD1QVijwkYzxCl8Lc=&_dt_no_comment=1&origin=11

相关文章
|
2月前
|
消息中间件 监控 NoSQL
容器化应用系统上生产的最佳实践
容器化应用系统上生产的最佳实践
|
2月前
|
Kubernetes Java 应用服务中间件
制作容器镜像的最佳实践
制作容器镜像的最佳实践
|
3月前
|
缓存 监控 网络性能优化
从内核的视角观测容器——SysOM 容器监控
从内核的视角观测容器——SysOM 容器监控
|
6月前
|
XML Java 编译器
如何使用IOC容器进行对象的管理和创建?
如何使用IOC容器进行对象的管理和创建?
如何使用IOC容器进行对象的管理和创建?
|
6月前
|
Cloud Native 测试技术 持续交付
Docker Compose 解析:定义和管理多容器应用,从多角度探索其优势和应用场景
Docker Compose 解析:定义和管理多容器应用,从多角度探索其优势和应用场景
206 0
|
1月前
|
测试技术 Go 云计算
Go语言优化云计算与容器化环境的策略与最佳实践
【2月更文挑战第15天】在云计算和容器化环境中,Go语言的应用和优化策略对于提高系统的性能和效率至关重要。本文深入探讨了如何使用Go语言优化云计算和容器化环境,包括内存管理、并发编程、代码优化等方面的最佳实践,旨在帮助开发者充分利用Go语言的优势,提升系统的整体性能。
|
4月前
|
负载均衡 持续交付 Docker
Docker容器编排与微服务架构的整合:最佳实践
微服务架构和Docker容器编排工具的结合为现代应用程序的构建和部署提供了巨大的便利性。本文将深入探讨如何将这两者整合在一起,提供丰富的示例代码和详细的最佳实践,以帮助大家构建可伸缩、可维护和高可用的微服务应用。
|
5月前
|
监控 Cloud Native 应用服务中间件
带你读《Apache Tomcat的云原生演进》——Web容器可观测最佳实践(1)
带你读《Apache Tomcat的云原生演进》——Web容器可观测最佳实践(1)
带你读《Apache Tomcat的云原生演进》——Web容器可观测最佳实践(1)
|
5月前
|
Prometheus Cloud Native 应用服务中间件
带你读《Apache Tomcat的云原生演进》——Web容器可观测最佳实践(2)
带你读《Apache Tomcat的云原生演进》——Web容器可观测最佳实践(2)
带你读《Apache Tomcat的云原生演进》——Web容器可观测最佳实践(2)
|
5月前
|
Cloud Native Java 应用服务中间件
带你读《Apache Tomcat的云原生演进》——Web容器可观测最佳实践(3)
带你读《Apache Tomcat的云原生演进》——Web容器可观测最佳实践(3)
带你读《Apache Tomcat的云原生演进》——Web容器可观测最佳实践(3)