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容器。
sidecarsets的状态也进行了更新。
更新sidecar container Image
更新sidecarSet中sidecar container的image=busybox:stable
,可以看到test-pod中的sidecar容器镜像已经更新为了busybox:stable
,并且app容器没有重启,说明是原地升级。
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,支持
matchLabels
、MatchExpressions
两种方式。 - 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容器的区别
- Sidecar容器与主容器同时运行,扩展其功能并提供附加服务,而init容器在主容器启动之前停止。
- Sidecar容器在整个Pod的生命周期中都处于活动状态,并且可以独立于主容器启动和停止。与init容器不同,Sidecar容器支持探针来控制其生命周期。
- 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。
- 权重高的容器,会保证在权重低的容器之前启动。
- 相同权重的容器不保证启动顺序。