SidecarSet
这个控制器支持通过 admission webhook 来自动为集群中创建的符合条件的 Pod 注入 sidecar 容器。 这个注入过程和 istio 的自动注入方式很类似。 除了在 Pod 创建时候注入外,SidecarSet 还提供了为运行时 Pod 原地升级其中已经注入的 sidecar 容器镜像的能力。
简单来说,SidecarSet 将 sidecar 容器的定义和生命周期与业务容器解耦。 它主要用于管理无状态的 sidecar 容器,比如监控、日志等 agent。
范例
apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
name: test-sidecarset
spec:
containers:
- command:
- sleep
- 999d
image: centos:6.7
imagePullPolicy: Always
name: tools
podInjectPolicy: BeforeAppContainer
resources:
limits:
cpu: 50m
memory: 100Mi
requests:
cpu: 50m
memory: 100Mi
shareVolumePolicy:
type: enabled
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
upgradeStrategy:
upgradeType: ColdUpgrade
injectionStrategy: {}
namespace: default
revisionHistoryLimit: 10
selector:
matchExpressions:
- key: app
operator: Exists
updateStrategy:
maxUnavailable: 1
partition: 0%
paused: true
type: RollingUpdate
- spec.selector 通过label的方式选择需要注入、更新的pod,支持matchLabels、MatchExpressions两种方式,详情请参考:https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/
- spec.containers 定义需要注入、更新的pod.spec.containers容器,支持完整的k8s container字段,详情请参考:https://kubernetes.io/docs/concepts/containers/
- spec.initContainers 定义需要注入的pod.spec.initContainers容器,支持完整的k8s initContainer字段,详情请参考:https://kubernetes.io/docs/concepts/workloads/pods/init-containers/
- 注入initContainers容器默认基于container name升级排序
- initContainers只支持注入,不支持pod原地升级
- spec.updateStrategy sidecarSet更新策略,type表明升级方式:
- NotUpdate 不更新,此模式下只会包含注入能力
- RollingUpdate 注入+滚动更新,包含了丰富的滚动更新策略,后面会详细介绍
- spec.namespace sidecarset默认在k8s整个集群范围内生效,即对所有的命名空间生效(除了kube-system, kube-public),当设置该字段时,只对该namespace的pod生效
- podInjectPolicy 定义container注入到pod.spec.containers中的位置
- BeforeAppContainer(默认) 注入到pod原containers的前面
- AfterAppContainer 注入到pod原containers的后面
- 数据卷共享
- 共享指定卷:通过 spec.volumes 来定义 sidecar 自身需要的 volume,详情请参考:https://kubernetes.io/docs/concepts/storage/volumes/
- 共享所有卷:通过 spec.containers[i].shareVolumePolicy.type = enabled | disabled 来控制是否挂载pod应用容器的卷,常用于日志收集等 sidecar,配置为 enabled 后会把应用容器中所有挂载点注入 sidecar 同一路经下(sidecar中本身就有声明的数据卷和挂载点除外)
- 环境变量共享
可以通过 spec.containers[i].transferEnv 来从别的容器获取环境变量,会把名为 sourceContainerName 容器中名为 envName 的环境变量拷贝到本容器
创建pod测试注入是否生效
apiVersion: v1
kind: Pod
metadata:
labels:
app: nginx # matches the SidecarSet's selector
name: test-pod
spec:
containers:
- name: app
image: nginx:1.15.1
确认是否注入
kubectl get po | grep test-pod
test-pod 2/2 Running 0 82s
CloneSet
CloneSet 控制器提供了高效管理无状态应用的能力,它可以对标原生的 Deployment,但 CloneSet 提供了很多增强功能。
按照 Kruise 的命名规范,CloneSet 是一个直接管理 Pod 的 Set 类型 workload。
例如
apiVersion: apps.kruise.io/v1alpha1
kind: CloneSet
metadata:
name: sample
labels:
app: sample
spec:
replicas: 5
selector:
matchLabels:
app: sample
template:
metadata:
labels:
app: sample
spec:
containers:
- name: nginx
image: nginx:alpine
扩缩容功能
支持pvc模板
CloneSet
允许用户配置 PVC
模板 volumeClaimTemplates
,用来给每个 Pod
生成独享的 PVC
,这是 Deployment
所不支持的。如果用户没有指定这个模板,CloneSet
会创建不带 PVC
的 Pod
。
注意:
每个被自动创建的 PVC 会有一个 ownerReference 指向 CloneSet,因此 CloneSet 被删除时,它创建的所有 Pod 和 PVC 都会被删除
每个被 CloneSet 创建的 Pod 和 PVC,都会带一个 apps.kruise.io/cloneset-instance-id: xxx 的 label。关联的 Pod 和 PVC 会有相同的 instance-id,且它们的名字后缀都是这个 instance-id
如果一个 Pod 被 CloneSet Controller 缩容删除时,这个 Pod 关联的 PVC 都会被一起删掉
如果一个 Pod 被外部直接调用删除或驱逐时,这个 Pod 关联的 PVC 还都存在;并且 CloneSet Controller 发现数量不足重新扩容时,新扩出来的 Pod 会复用原 Pod 的 instance-id 并关联原来的 PVC
当 Pod 被 重建升级 时,关联的 PVC 会跟随 Pod 一起被删除、新建
当 Pod 被 原地升级 时,关联的 PVC 会持续使用
例如:
apiVersion: apps.kruise.io/v1alpha1
kind: CloneSet
metadata:
name: sample-data
namespace: ops
labels:
app: sample
spec:
replicas: 5
selector:
matchLabels:
app: sample
template:
metadata:
labels:
app: sample
spec:
containers:
- name: nginx
image: nginx:alpine
volumeMounts:
- name: cephfs-pvc-demo
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: cephfs-pvc-demo
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 1Gi
storageClassName: ceph-storageclass
创建后可以看到每个pod都有自己的pvc
kubectl get pvc -n ops
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
cephfs-pvc-demo-sample-data-4v6wb Bound pvc-ba67e462-deed-4153-803d-ad52b9e6d89f 1Gi RWO ceph-storageclass 4m3s
cephfs-pvc-demo-sample-data-6wz8m Bound pvc-be4c9711-3f18-4153-be65-2b0a495893b1 1Gi RWO ceph-storageclass 4m2s
cephfs-pvc-demo-sample-data-f7cxs Bound pvc-4c4fc404-88e6-4320-945e-914882375749 1Gi RWO ceph-storageclass 4m2s
cephfs-pvc-demo-sample-data-k2rd6 Bound pvc-ceb78d1f-99e5-4011-b87f-dd18ebebbc28 1Gi RWO ceph-storageclass 4m2s
cephfs-pvc-demo-sample-data-ql7bn Bound pvc-cedf91b0-eac9-437e-a193-7b53c9dd437c 1Gi RWO ceph-storageclass 4m2s
指定pod缩容
当一个 CloneSet
被缩容时,有时候用户需要指定一些 Pod
来删除。这对于 StatefulSet
或者 Deployment
来说是无法实现的,因为 StatefulSet
要根据序号来删除 Pod
,而 Deployment/ReplicaSet
目前只能根据控制器里定义的排序来删除。
CloneSet
允许用户在缩小 replicas
数量的同时,指定想要删除的 Pod
名字。参考下面这个例子:
apiVersion: apps.kruise.io/v1alpha1
kind: CloneSet
spec:
#...
replicas: 3
revisionHistoryLimit: 10
scaleStrategy:
podsToDelete:
- sample-data-6wz8m
当控制器收到上面这个 CloneSet
更新之后,会确保 replicas
数量为 4。如果 podsToDelete
列表里写了一些 Pod
名字,控制器会优先删除这些 Pod
。对于已经被删除的 Pod
,控制器会自动从 podsToDelete
列表中清理掉。
注意:如果你只把 Pod 名字加到 podsToDelete,但没有修改 replicas 数量,那么控制器会先把指定的 Pod 删掉,然后再扩一个新的 Pod。
如果不指定 podsToDelete,控制器会按照默认顺序来选择 Pod 删除:not-ready < ready, unscheduled < scheduled, pending < running。
升级功能
原地升级
CloneSet
提供了和 Advanced StatefulSet
相同的 3 个升级方式,默认为 ReCreate
:
ReCreate: 控制器会删除旧 Pod 和它的 PVC,然后用新版本重新创建出来
InPlaceIfPossible: 控制器会优先尝试原地升级 Pod,如果不行再采用重建升级。目前,只有修改 spec.template.metadata.* 和 spec.template.spec.containers[x].image 这些字段才可以走原地升级
InPlaceOnly: 控制器只允许采用原地升级。因此,用户只能修改上一条中的限制字段,如果尝试修改其他字段会被 Kruise 拒绝
当一个 Pod 被原地升级时,控制器会先利用 readinessGates 把 Pod status 中修改为 not-ready 状态,然后再更新 Pod spec 中的 image 字段来触发 Kubelet 重建对应的容器。不过这样可能存在的一个风险:有时候 Kubelet 重建容器太快,还没等到其他控制器如 endpoints-controller 感知到 Pod not-ready,可能会导致流量受损。
因此又在原地升级中提供了 graceful period 选项,作为优雅原地升级的策略。用户如果配置了 gracePeriodSeconds 这个字段,控制器在原地升级的过程中会先把 Pod status 改为 not-ready,然后等一段时间(gracePeriodSeconds),最后再去修改 Pod spec 中的镜像版本。这样,就为 endpoints-controller 这些控制器留出了充足的时间来将 Pod 从 endpoints 端点列表中去除。
apiVersion: apps.kruise.io/v1alpha1
kind: CloneSet
spec:
# ...
updateStrategy:
inPlaceUpdateStrategy:
gracePeriodSeconds: 10
maxSurge: 0
maxUnavailable: 20%
partition: 0
type: InPlaceIfPossible
配置完成后修改镜像测试
Normal Killing 2m35s kubelet, dev-k8s Container nginx definition changed, will be restarted
Normal Pulling 2m35s kubelet, dev-k8s Pulling image "nginx:1.17"
Normal Created 2m17s (x2 over 49m) kubelet, dev-k8s Created container nginx
Normal Started 2m17s (x2 over 49m) kubelet, dev-k8s Started container nginx
Normal Pulled 2m17s kubelet, dev-k8s Successfully pulled image "nginx:1.17" in 18.297840376s
查看pod发现镜像更新 但是pod没有重新创建而是重启了
kubectl get po -n ops
sample-data-f7cxs 1/1 Running 1 50m
sample-data-k2rd6 1/1 Running 1 50m
Partition 分批灰度
Partition
的语义是 保留旧版本 Pod
的数量或百分比,默认为 0。这里的 partition
不表示任何 order
序号。
如果在发布过程中设置了 partition
:
如果是数字,控制器会将 (replicas - partition) 数量的 Pod 更新到最新版本;
如果是百分比,控制器会将 (replicas * (100% - partition)) 数量的 Pod 更新到最新版本。
比如,将 CloneSet
例子的 image
更新为 nginx:mainline
并且设置 partition=3
。过了一会,查到的 CloneSet
如下:
apiVersion: apps.kruise.io/v1alpha1
kind: CloneSet
metadata:
# ...
generation: 2
spec:
replicas: 5
template:
metadata:
labels:
app: sample
spec:
containers:
- image: nginx:mainline
imagePullPolicy: Always
name: nginx
updateStrategy:
maxSurge: 0
maxUnavailable: 20%
partition: 2
type: ReCreate
因为partition: 2
所以会更新三个pod
sample-data-8p6gx 1/1 Running 1 106m sample-data-7656fc4998
sample-data-8rzcr 1/1 Running 0 3m1s sample-data-78479f6458
sample-data-9qq4j 1/1 Running 0 103m sample-data-7656fc4998
sample-data-dc7qz 1/1 Running 0 2m25s sample-data-78479f6458
sample-data-ql7bn 1/1 Running 1 104m sample-data-7656fc4998