向虚拟节点Pod注入Sidecar容器

通过OpenKruise SidecarSet功能,可以为调度到虚拟节点的Pod自动注入Sidecar容器,从而解耦业务容器和辅助功能容器。本文介绍如何创建SidecarSet定义Sidecar容器,并自动将Sidecar容器注入到虚拟节点的Pod中。

基本概念

  • Sidecar:一种将应用辅助功能从应用本身剥离出来作为单独进程或容器的设计方式。该模式允许您向应用无侵入添加多种功能,避免为满足第三方组件需求而向应用添加额外的配置代码。

  • Sidecar容器:在Pod中添加附加容器,扩展和增强主容器,无需改变主容器。

  • SidecarSet:是阿里云开源的云原生应用自动化引擎OpenKruise的核心功能之一。SidecarSet可以为符合条件的Pod自动注入Sidecar容器,实现Sidecar容器(如监控、日志等agent)的定义和生命周期与业务容器解耦。

前提条件

  • 集群版本为1.22及以上,集群类型为ACK Serverless集群Pro

  • 已安装ack-virtual-node组件,且版本为v2.10.0及以上。更多信息,请参见ACK Virtual Node

  • 已安装ack-kruise组件,且版本为v1.3.0及以上。更多信息,请参见OpenKruise

    重要

    虚拟节点场景下,支持OpenKruise v1.3.0及以下版本的SidecarSet全部功能,但不支持1.3.0以上版本中新增的SidecarSet功能。

  • 已自定义Kube API Server组件的参数配置,在featureGates中设置SidecarSetServerlessPod=true以开启SidecarSet功能的特性门控。更多信息,请参见自定义控制面组件参数

功能介绍

SidecarSet

您可以使用与默认SidecarSet完全一致的方式来匹配所有调度到虚拟节点的Pod,即通过标签serverless.alibabacloud.com/virtual-node: "true"指定。该标签会在Pod确定调度到虚拟节点后添加。关于默认SidecarSet的使用方法,请参见SidecarSet

SidecarSet另一个常用的功能是跨命名空间引用ConfigMapSecret。DaemonSet核心容器运行经常依赖ConfigMap(如配置参数),将DaemonSet核心容器注入到业务Pod中时,业务PodConfigMap通常在不同的命名空间。由于虚拟节点场景不支持DaemonSet,需要采用Sidecar容器替代。在Sidecar容器的Volume中通过Namespace/Name方式,可以跨命名空间引用ConfigMapSecret。

说明

跨命名空间访问ConfigMapSecret需要授权。具体方式,请参见SidecarSetResourceBinding

展开查看SidecarSet挂载ConfigMapYAML示例

apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
  name: filebeat-sidecarset
spec:
  selector:
    matchLabels:      
      serverless.alibabacloud.com/virtual-node: "true" # 表示匹配所有调度到虚拟节点的Pod。
  updateStrategy:
    type: NotUpdate
  containers:
  - name: filebeat
    image: busybox
    imagePullPolicy: IfNotPresent    
    args: [
      "/bin/sh",
      "-c",
      "cat /etc/filebeat.yml && sleep 36000", # 此示例仅打印filebeat配置内容。
    ]    
    volumeMounts:
    - name: config
      mountPath: /etc/filebeat.yml
      readOnly: true
      subPath: filebeat.yml    
  volumes:
  - name: config
    configMap:
      name: kube-system/filebeat-config # 使用namespace/name方式指定引用其他namespace下ConfigMap。

SidecarSetResourceBinding

出于安全考虑,在Sidecar容器Volume中引入其他命名空间的ConfigMapSecret时,需要通过SidecarSetResourceBinding显式授权。

说明

该授权仅授予对ConfigMapSecret的只读权限(Get,List,Watch)。

展开查看SidecarSetResourceBindingYAML示例

# 授权filebeat-sidecarset,SidecarSet匹配的Pod能够访问kube-system命名空间下filebeat-config ConfigMap。
apiVersion: sidecarset.alibabacloud.com/v1alpha1
kind: SidecarSetResourceBinding
metadata:
  name: filebeat-sidecarset-resourcebinding
  namespace: kube-system # 此SidecarSetResourceBinding只能对kube-system命名空间下的资源做授权。
  labels:
spec:
  subjects:
    - kind: SidecarSet
      name: filebeat-sidecarset
  resourceRefs:
    - kind: ConfigMap # 仅授权只读权限(Get, List, Watch)。
      name: filebeat-config

容器启动和退出顺序

Sidecar容器经常需要在业务容器前启动,在业务容器后退出,您可以通过设置容器启动和退出顺序实现。

自动结束Sidecar容器

对于JobPod,Sidecar容器可能会导致业务容器完成后Job无法退出的情况,您可以通过强制终止Sidecar容器并忽略容器退出码来解决问题。

升级Sidecar容器

使用Sidecar模式后,您可能会有Sidecar容器升级等运维需求。您可以使用OpenKruise已有的Sidecar热升级功能,该方式能在不影响Pod可用性情况下无缝升级Sidecar容器,且与当前虚拟节点方式完全兼容。

采集标准输出日志

通过将虚拟节点Pod的标准输出日志(以stdlog卷的形式)挂载到Sidecar容器的指定目录,可以采集业务容器的标准输出日志。更多信息,请参见挂载stdlog实现挂载容器标准输出日志

展开查看SidecarSet挂载stdlogYAML示例

apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
  name: filebeat-sidecarset
spec:
  selector:
    matchLabels:
      serverless.alibabacloud.com/virtual-node: "true" # 表示匹配所有调度到虚拟节点的Pod。
  updateStrategy:
    type: NotUpdate
  containers:
  # 此示例仅打印Sidecar容器日志内容。
  - name: filebeat
    image: busybox
    imagePullPolicy: IfNotPresent
    args: [
      "/bin/sh",
      "-c",
      "cat /var/log/std/filebeat/0.log && sleep 36000",
    ]    
    volumeMounts:    
    - name: stdlog # 挂载Pod标准输出日志卷/var/log/std目录以便sidecar容器读取。
      mountPath: /var/log/std
      readOnly: true
  volumes:  
  - name: stdlog
    csi:
      driver: stdlogplugin.csi.alibabacloud.com

操作示例

下文演示如何向业务Pod(echo-server)注入Sidecar容器(filebeat容器),您可以参考操作示例设计您的实际业务应用。

  1. 部署SidecarSet,用于后续为虚拟节点上的业务Pod自动注入Sidecar容器。

    1. 部署ConfigMap,用于后续挂载到Sidecar容器。

      kubectl apply -f filebeat-config.yaml

      filebeat-config.yaml的内容示例如下。本示例仅将配置文件(filebeat.yml)挂载到Sidecar容器并打印,相关变量不生效,无需替换。

      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: filebeat-config
        namespace: kube-system
        labels:
          k8s-app: filebeat
      data:
        filebeat.yml: |-
          filebeat.inputs:
          - type: log
            paths:
              - /var/log/std/*.log
            processors:
              - add_kubernetes_metadata:
                  host: ${NODE_NAME} # 不生效,无需修改,请直接使用。
                  matchers:
                  - logs_path:
                      logs_path: "/var/log/std/"
      
          # To enable hints based autodiscover, remove `filebeat.inputs` configuration and uncomment this:
          #filebeat.autodiscover:
          #  providers:
          #    - type: kubernetes
          #      node: ${NODE_NAME}
          #      hints.enabled: true
          #      hints.default_config:
          #        type: container
          #        paths:
          #          - /var/log/containers/*${data.kubernetes.container.id}.log
      
          processors:
            - add_cloud_metadata:
            - add_host_metadata:
      
          cloud.id: ${ELASTIC_CLOUD_ID}  # 不生效,无需修改,请直接使用。
          cloud.auth: ${ELASTIC_CLOUD_AUTH}  # 不生效,无需修改,请直接使用。
      
          output.elasticsearch:
            hosts: ['${ELASTICSEARCH_HOST:elasticsearch}:${ELASTICSEARCH_PORT:9200}']
            username: ${ELASTICSEARCH_USERNAME}  # 不生效,无需修改,请直接使用。
            password: ${ELASTICSEARCH_PASSWORD}  # 不生效,无需修改,请直接使用。
    2. 部署SidecarSet,用于描述Sidecar容器。

      kubectl apply -f sidecarset.yaml

      sidecarset.yaml的内容示例如下。本示例中,作为Sidecar容器的filebeat容器将打印配置文件的内容,并且挂载了stdlog,可以收集业务Pod的标准输出日志。

      apiVersion: apps.kruise.io/v1alpha1
      kind: SidecarSet
      metadata:
        name: filebeat-sidecarset
      spec:
        selector:
          matchLabels:
            serverless.alibabacloud.com/virtual-node: "true" # 表示匹配所有调度到虚拟节点的Pod。
        updateStrategy:
          type: NotUpdate
        containers:
        # 此示例未真正运行filebeat,替换为bosybox cat。
        #- name: filebeat
        #  image: docker.elastic.co/beats/filebeat:8.6.1
        #  args: [
        #    "-c", "/etc/filebeat.yml",
        #    "-e",
        #  ]
        - name: filebeat
          image: busybox
          imagePullPolicy: IfNotPresent
          args: [
            "/bin/sh",
            "-c",
            "cat /etc/filebeat.yml && sleep 36000",
          ]
          env:
          - name: ECI_SIDECAR_CONTAINER         # 表示Sidecar容器在业务容器后退出。
            value: "true"
          volumeMounts:
          - name: config
            mountPath: /etc/filebeat.yml
            readOnly: true
            subPath: filebeat.yml
          - name: stdlog                        # 挂载Pod标准输出日志以便Sidecar容器读取。
            mountPath: /var/log/std
            readOnly: true
        volumes:
        - name: config
          configMap:
            name: kube-system/filebeat-config  # 使用Namespace/Name方式指定引用其他Namespace下的ConfigMap。
        - name: stdlog
          csi:
            driver: stdlogplugin.csi.alibabacloud.com
    3. 授权Sidecar容器能够访问ConfigMap。

      如果业务PodConfigMap处于不同的命名空间,业务Pod中注入的Sidecar容器访问ConfigMap时,需要通过SidecarSetResourceBinding显式授权。

      kubectl apply -f sidecarset-resourcebinding.yaml

      sidecarset-resourcebinding.yaml的内容示例如下。本示例计划部署业务Poddefault命名空间,而ConfigMap处于kube-system命名空间,因此需要授权。

      # 授权filebeat-sidecarset,SidecarSet匹配的Pod能够访问kube-system命名空间下filebeat-config ConfigMap。
      apiVersion: sidecarset.alibabacloud.com/v1alpha1
      kind: SidecarSetResourceBinding
      metadata:
        name: filebeat-sidecarset-resourcebinding
        namespace: kube-system # 此SidecarSetResourceBinding仅对kube-system命名空间下的资源做授权。
        labels:
      spec:
        subjects:
          - kind: SidecarSet
            name: filebeat-sidecarset
        resourceRefs:
          - kind: ConfigMap
            name: filebeat-config
  2. 部署调度到虚拟节点的业务Pod。

    kubectl apply -f echo-server.yaml

    echo-server.yaml的内容示例如下。该Deployment包含一个副本,Pod包含一个容器,添加了alibabacloud.com/eci: "true"Label后,Pod会被调度到虚拟节点。

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: echo-server
      labels:
        app: echo-server
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: echo-server
      template:
        metadata:
          labels:
            app: echo-server        
            alibabacloud.com/eci: "true"
        spec:
          containers:
            - name: echo-server
              image: hashicorp/http-echo
              imagePullPolicy: IfNotPresent
              args:
                - -listen=:8080
                - -text="hello world"
  3. 确认业务Pod已自动注入Sidecar容器,并验证挂载结果。

    1. 查看业务Pod。

      kubectl get pod

      预期返回如下,可以看到业务Pod包含2个容器,说明Sidecar容器注入成功。

      NAME                          READY   STATUS    RESTARTS   AGE
      echo-server-f8bdc5844-r44nj   2/2     Running   0          14m
    2. 验证Sidecar容器已挂载业务Pod的标准输出日志。

      kubectl exec echo-server-f8bdc5844-r44nj -c filebeat -- cat /var/log/std/echo-server/0.log

      预期返回如下,可以看到业务Pod的标准输出日志。

      2025-04-29T11:26:06.783205694+08:00 stderr F 2025/04/29 03:26:06 Server is listening on :8080
    3. 验证Sidecar容器已挂载跨命名空间的配置文件。

      kubectl exec echo-server-f8bdc5844-r44nj -c filebeat -- cat /etc/filebeat.yml

      预期返回如下,表示挂载正常。

      展开查看返回示例

      filebeat.inputs:
      - type: log
        paths:
          - /var/log/std/*.log
        processors:
          - add_kubernetes_metadata:
              host: ${NODE_NAME} # 不生效,无需修改,请直接使用。
              matchers:
              - logs_path:
                  logs_path: "/var/log/std/"
      
      # To enable hints based autodiscover, remove `filebeat.inputs` configuration and uncomment this:
      #filebeat.autodiscover:
      #  providers:
      #    - type: kubernetes
      #      node: ${NODE_NAME}
      #      hints.enabled: true
      #      hints.default_config:
      #        type: container
      #        paths:
      #          - /var/log/containers/*${data.kubernetes.container.id}.log
      
      processors:
        - add_cloud_metadata:
        - add_host_metadata:
      
      cloud.id: ${ELASTIC_CLOUD_ID}  # 不生效,无需修改,请直接使用。
      cloud.auth: ${ELASTIC_CLOUD_AUTH}  # 不生效,无需修改,请直接使用。
      
      output.elasticsearch:
        hosts: ['${ELASTICSEARCH_HOST:elasticsearch}:${ELASTICSEARCH_PORT:9200}']
        username: ${ELASTICSEARCH_USERNAME}  # 不生效,无需修改,请直接使用。
        password: ${ELASTICSEARCH_PASSWORD}  # 不生效,无需修改,请直接使用。