k8s

OpenKruise核心能力之Sidecar容器管理

SidecarSet

这个控制器支持通过 admission webhook 来自动为集群中创建的符合条件的 Pod 注入 sidecar 容器。 这个注入过程和 istio 的自动注入方式很类似。 除了在 Pod 创建时候注入外,SidecarSet 还提供了为运行时 Pod 原地升级其中已经注入的 sidecar 容器镜像的能力。

简单来说,SidecarSet 将 sidecar 容器的定义和生命周期与业务容器解耦。 它主要用于管理无状态的 sidecar 容器,比如监控、日志等 agent。

例子

创建SidecarSet

下面定义了一个 SidecarSet,其中包括了一个名为 sidecar1 的 sidecar 容器:

apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
  name: test-sidecarset
spec:
  selector:
    matchLabels:
      app: nginx
  updateStrategy:
    type: RollingUpdate
    maxUnavailable: 1
  containers:
  - name: sidecar1
    image: busybox:latest
    imagePullPolicy: IfNotPresent
    command: ["sleep", "999d"] # do nothing at all
    volumeMounts:
    - name: log-volume
      mountPath: /var/log
  volumes: # this field will be merged into pod.spec.volumes
  - name: log-volume
    emptyDir: {}

创建pod

定义一个匹配 SidecarSet selector 的 Pod:

apiVersion: v1
kind: Pod
metadata:
  labels:
    app: nginx # matches the SidecarSet's selector
  name: test-pod
spec:
  containers:
  - name: app
    image: nginx:latest
    imagePullPolicy: IfNotPresent

可以发现test-pod已经被注入了sidecar1容器。

file

file

sidecarsets的状态也进行了更新。

file

更新sidecar container Image

更新sidecarSet中sidecar container的image=busybox:stable,可以看到test-pod中的sidecar容器镜像已经更新为了busybox:stable,并且app容器没有重启,说明是原地升级。

file

SidecarSet功能说明

apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
  name: sidecarset
spec:
  selector:
    matchLabels:
      app: sample
  containers:
  - name: nginx
    image: nginx:alpine
  initContainers:
  - name: init-container
    image: busybox:latest
    command: [ "/bin/sh", "-c", "sleep 5 && echo 'init container success'" ]
  updateStrategy:
    type: RollingUpdate
  namespace: ns-1
  • spec.selector:通过label的方式选择需要注入、更新的pod,支持matchLabelsMatchExpressions两种方式。
  • spec.containers:定义需要注入、更新的pod.spec.containers容器,支持完整的k8s container字段。
  • spec.initContainers:定义需要注入的pod.spec.initContainers容器,支持完整的k8s initContainer字段。注入initContainers容器默认基于container name升级排序,initContainers只支持注入,不支持pod原地升级。
  • spec.updateStrategy:sidecarSet更新策略。NotUpdate 不更新,此模式下只会包含注入能力,RollingUpdate 注入+滚动更新。
  • spec.namespace:sidecarset默认在k8s整个集群范围内生效,即对所有的命名空间生效(除了kube-system, kube-public),当设置该字段时,只对该namespace的pod生效。

namespace selector

spec.namespace 字段只能指定一个Namespace,并且不能排除的一些特定的Namespace。如果面对一些复杂的 namespace selector 场景,推荐使用 namespaceSelector 字段:

apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
  name: sidecarset
spec:
  ...
  namespaceSelector:
    matchLabels:
      environment: production
  # matchExpressions:
  # - {key: tier, operator: In, values: [frontend, middleware]}

sidecar container注入

sidecar 的注入只会发生在 Pod 创建阶段,并且只有 Pod spec 会被更新,不会影响 Pod 所属的 workload template 模板。

apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
  name: sidecarset
spec:
  selector:
    matchLabels:
      app: sample
  containers:
    # default K8s Container fields
  - name: nginx
    image: nginx:alpine
    volumeMounts:
    - mountPath: /nginx/conf
      name: nginx.conf
    # extended sidecar container fields
    podInjectPolicy: BeforeAppContainer
    shareVolumePolicy:
      type: disabled | enabled
    transferEnv:
    - sourceContainerName: main
      envName: PROXY_IP
    - sourceContainerNameFrom:
        fieldRef:
          apiVersion: "v1"
          fieldPath: "metadata.labels['cName']"
        # fieldPath: "metadata.annotations['cName']"
      envName: TC
  volumes:
  - name: nginx.conf
    hostPath:
      path: /data/nginx/conf
  • podInjectPolicy 定义container注入到pod.spec.containers中的位置
    • BeforeAppContainer(默认) 注入到pod原containers的前面
    • AfterAppContainer 注入到pod原containers的后面
  • 数据卷共享
    • 共享指定卷:通过 spec.volumes 来定义 sidecar 自身需要的 volume
    • 共享所有卷:通过 spec.containers[i].shareVolumePolicy.type = enabled | disabled 来控制是否挂载pod应用容器的卷,常用于日志收集等 sidecar,配置为 enabled 后会把应用容器中所有挂载点注入 sidecar 同一路经下(sidecar中本身就有声明的数据卷和挂载点除外)
  • 环境变量共享
    • 可以通过 spec.containers[i].transferEnv 来从别的容器获取环境变量,会把名为 sourceContainerName 容器中名为 envName 的环境变量拷贝到本容器
    • sourceContainerNameFrom 支持 downwardAPI 来获取容器name,比如:metadata.name, metadata.labels['<KEY>'], metadata.annotations['<KEY>']

imagePullSecrets

SidecarSet 可以通过配置 spec.imagePullSecrets,来配合 Secret 拉取私有 sidecar 镜像。其实现原理为: 当sidecar注入时,SidecarSet 会将其 spec.imagePullSecrets 注入到 Pod 的 spec.imagePullSecrets。用户必需确保这些 Pod 所在的命名空间中已存在对应的 Secret。

apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
  name: test-sidecarset
spec:
  selector:
    matchLabels:
      app: sample
  updateStrategy:
    type: NotUpdate
  initContainers:
  - name: sidecar
    image: nginx:alpine
    restartPolicy: Always
    lifecycle:
      postStart:
        exec:
          command: ["/bin/sh", "-c", "sleep 10"]

支持 k8s 1.28 Sidecar 容器

k8s从1.28版本通过 initContainers[x].restartPolicy=Always 的方式支持Sidecar Containers。k8s 1.29 是默认开启的。如果 K8S 版本低于 1.28,你可以使用 Kruise 提供的 Job Sidecar Terminator 和 Container Launch Priority 来解决上述问题。官方文档:https://kubernetes.io/zh-cn/docs/concepts/workloads/pods/sidecar-containers/

  • Sidecar容器保证在业务主容器启动之前Ready,比如:日志采集容器已经准备就绪,业务主容器启动Crash的日志也能够及时采集上来。
  • Job类型的Pod,主容器执行完成之后,Sidecar容器也会自行退出不会阻塞Job的完成(之前的模式Sidecar容器没办法自主推出的,会导致Job一直没法结束)。
apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
  name: test-sidecarset
spec:
  selector:
    matchLabels:
      app: sample
  updateStrategy:
    type: NotUpdate
  initContainers:
  - name: sidecar
    image: nginx:alpine
    restartPolicy: Always
    lifecycle:
      postStart:
        exec:
          command: ["/bin/sh", "-c", "sleep 10"]

和init容器的区别

  1. Sidecar容器与主容器同时运行,扩展其功能并提供附加服务,而init容器在主容器启动之前停止。
  2. Sidecar容器在整个Pod的生命周期中都处于活动状态,并且可以独立于主容器启动和停止。与init容器不同,Sidecar容器支持探针来控制其生命周期。
  3. Sidecar容器和init容器与主容器共享资源(CPU、内存、网络),但Sidecar容器可以直接与主应用容器交互,init容器不直接与主容器进行交互。不过init容器可以使用emptyDir进行数据交换。

Job Sidecar Terminator

对于 Job 类型的 Workload,我们通常希望当这些 Pod 中执行业务逻辑的主容器完成任务并退出后,日志收集等 sidecar 容器也能够主动退出,从而使得这些 Job Controller 能够正确判断 Pod 所处的完成状态,避免一些错误的信息上报和流程异常。

为了解决这个问题,我们在 Kruise 中加入了一个名为 SidecarTerminator 的控制器,专门用于在此类场景下,监听主容器的完成状态,并选择合适的时机终止掉 Pod 中的 sidecar 容器,而且无需对业务进行侵入式改造。

前提

  • 在安装或升级 Kruise 时启用 SidecarTerminator Feature-Gate(默认关闭)
  • 在安装或升级 Kruise 时启用 KruiseDaemon Feature-Gate(默认开启)。

使用方式

对于运行于普通节点的 Pod,使用该特性非常简单,用户只需要在要在目标 sidecar 容器中添加一个特殊的 env 对其进行标识,控制器会在恰当的时机利用 Kruise Daemon 提供的 CRR 的能力,将这些 sidecar 容器终止:

kind: Job
spec:
  template:
    spec:
      containers:
        - name: sidecar
          env:
            - name: KRUISE_TERMINATE_SIDECAR_WHEN_JOB_EXIT
              value: "true"
        - name: main
... ...

忽略 Sidecar 容器退出码非0

从 Kruise 1.6.0 版本开始,将忽略 Sidecar 容器退出码非0的情况,Pod Phase 状态只依赖于 Main 容器成功与否。在之前的版本,要求 Sidecar 容器能够接受、处理 SIGTERM 信号,并且退出码为0。否则,将会导致 Pod Phase=Failed。

Container Launch Priority

提供了控制一个 Pod 中容器启动顺序的方法。注意,这个功能作用在 Pod 对象上,不管它的 owner 是什么类型的,因此可以适用于 Deployment、CloneSet 以及其他的 workload 种类。需要打开 PodWebhook feature-gate(默认就是打开的,除非显式关闭)。

按照 container 顺序启动

只需要在 Pod 中定义一个 annotation 即可,Kruise 会保证前面的容器(sidecar)会在后面容器(main)之前启动。

apiVersion: v1
kind: Pod
  annotations:
    apps.kruise.io/container-launch-priority: Ordered
spec:
  containers:
  - name: sidecar
    # ...
  - name: main
    # ...

按自定义顺序启动

需要在 Pod container 中添加 KRUISE_CONTAINER_PRIORITY 环境变量:

apiVersion: v1
kind: Pod
spec:
  containers:
  - name: main
    # ...
  - name: sidecar
    env:
    - name: KRUISE_CONTAINER_PRIORITY
      value: "1"
    # ...
  • 值的范围在 [-2147483647, 2147483647],不写默认是 0。
  • 权重高的容器,会保证在权重低的容器之前启动。
  • 相同权重的容器不保证启动顺序。
分类: k8s
0 0 投票数
文章评分
订阅评论
提醒
guest

0 评论
最旧
最新 最多投票
内联反馈
查看所有评论

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部
0
希望看到您的想法,请您发表评论x