Kruise 是 OpenKruise 中的核心项目之一,它提供一套在Kubernetes核心控制器之外的扩展 workload 管理和实现。
目前,Kruise 提供了以下 5 个 workload 控制器:
- CloneSet: 提供了更加高效、确定可控的应用管理和部署能力,支持优雅原地升级、指定删除、发布顺序可配置、并行/灰度发布等丰富的策略,可以满足更多样化的应用场景。
- Advanced StatefulSet: 基于原生 StatefulSet 之上的增强版本,默认行为与原生完全一致,在此之外提供了原地升级、并行发布(最大不可用)、发布暂停等功能。
- SidecarSet: 对 sidecar 容器做统一管理,在满足 selector 条件的 Pod 中注入指定的 sidecar 容器。
- UnitedDeployment: 通过多个 subset workload 将应用部署到多个可用区。
- BroadcastJob: 配置一个 job,在集群中所有满足条件的 Node 上都跑一个 Pod 任务。
项目的 roadmap 参考这里。 Video by Lachlan Evenson 是一个对于新人很友好的 demo。
安装前检查
使用 Kruise 需要在 kube-apiserver
启用一些 feature-gate 比如 MutatingAdmissionWebhook
、ValidatingAdmissionWebhook
(K8s 1.12以上默认开启)。 如果你的 K8s 版本低于 1.12,需要先执行以下命令来验证是否支持:
sh -c "$(curl -fsSL https://raw.githubusercontent.com/openkruise/kruise/master/scripts/check_for_installation.sh)"
使用 helm charts 安装 [推荐]
推荐使用 helm v3 安装 Kruise,helm 是一个简单的命令行工具可以从这里 获取。
helm install kruise https://github.com/openkruise/kruise/releases/download/v0.5.0/kruise-chart.tgz
注意直接安装 chart 会使用默认的 template values,你也可以根据你的集群情况指定一些特殊配置,比如修改 resources 限制或者只启用某些特定的控制器能力。
使用 YAML files 安装 [不推荐]
# Install CRDs
kubectl apply -f https://raw.githubusercontent.com/kruiseio/kruise/master/config/crds/apps_v1alpha1_broadcastjob.yaml
kubectl apply -f https://raw.githubusercontent.com/kruiseio/kruise/master/config/crds/apps_v1alpha1_sidecarset.yaml
kubectl apply -f https://raw.githubusercontent.com/kruiseio/kruise/master/config/crds/apps_v1alpha1_statefulset.yaml
kubectl apply -f https://raw.githubusercontent.com/kruiseio/kruise/master/config/crds/apps_v1alpha1_uniteddeployment.yaml
kubectl apply -f https://raw.githubusercontent.com/kruiseio/kruise/master/config/crds/apps_v1alpha1_cloneset.yaml
# Install kruise-controller-manager
kubectl apply -f https://raw.githubusercontent.com/kruiseio/kruise/master/config/manager/all_in_one.yaml
注意 all_in_one.yaml
中包含的 Kruise-manager 镜像是每天周期性从 master 分支打出来的,无法保证功能的稳定性。 所以你可以通过 YAML 部署到测试集群做验证,但不推荐在生产环境使用。
官方的 kruise-manager 镜像维护在 docker hub 。
可选: 启用部分特定控制器
如果你只需要使用某些 Kruise 中的控制器并关闭其他的控制器,你可以做以下两个方式或同时做:
- 只安装你需要使用的 CRD。
- 在 kruise-manager 容器中设置
CUSTOM_RESOURCE_ENABLE
环境变量,配置需要启用的功能,比如CUSTOM_RESOURCE_ENABLE=CloneSet,StatefulSet
。
如果使用 helm chart 安装,可以通过以下参数来生效这个配置:
helm install kruise https://github.com/openkruise/kruise/releases/download/v0.5.0/kruise-chart.tgz --set manager.custom_resource_
上面部分来自官网。有兴趣的自己的了解
安装完成后可以看到集群中已经存在其CRD了。
# kubectl get crd | grep kruise
broadcastjobs.apps.kruise.io 2020-05-31T00:43:29Z
clonesets.apps.kruise.io 2020-05-31T00:43:28Z
sidecarsets.apps.kruise.io 2020-05-31T00:43:28Z
statefulsets.apps.kruise.io 2020-05-31T00:43:28Z
uniteddeployments.apps.kruise.io 2020-05-31T00:43:28Z
Advanced StatefulSet 顾名思义,是原生 StatefulSet 的增强版,默认行为与原生完全一致,在此之外提供了原地升级、并行发布(最大不可用)、发布暂停等功能。而 CloneSet 则对标原生 Deployment,主要服务于无状态应用,提供了最为全面丰富的部署发布策略。
原地升级
CloneSet、Advanced StatefulSet 均支持指定 Pod 升级方式:
- ReCreate:重建 Pod 升级,和原生 Deployment/StatefulSet 一致;
- InPlaceIfPossible:如果只修改 image 和 metadata 中的 labels/annotations 等字段,则触发 Pod 原地升级;如果修改了其他 template spec 中的字段,则退化到 Pod 重建升级;
- InPlaceOnly:只允许修改 image 和 metadata 中的 labels/annotations 等字段,只会使用原地升级。
所谓原地升级,就是在升级 template 模板的时候,workload 不会把原 Pod 删除、新建,而是直接在原 Pod 对象上更新对应的 image 等数据。
如上图所示,在原地升级的时候 CloneSet 只会更新 Pod spec 中对应容器的 image,而后 kubelet 看到 Pod 中这个容器的定义发生了变化,则会把对应的容器停掉、拉取新的镜像、并使用新镜像创建启动容器。另外可以看到在过程中,这个 Pod 的 sandbox 容器以及其他本次未升级的容器还一直处于正常运行状态,只有需要升级的容器会受到影响。
原地升级给我们带来的好处实在太多了:
- 首先就是发布效率大大提升了,根据非完全统计数据,在阿里环境下原地升级至少比完全重建升级提升了 80% 以上的发布速度:不仅省去了调度、分配网络、分配远程盘的耗时,连拉取新镜像的时候都得益于 node 上已有旧镜像、只需要拉取较少的增量 layer);
- IP 不变、升级过程 Pod 网络不断,除本次升级外的其他容器保持正常运行;
- Volume 不变,完全复用原容器的挂载设备;
- 保障了集群确定性,使排布拓扑能通过大促验证。
流式+分批发布
目前 Deployment 支持 maxUnavailable/maxSurge 的流式升级,而 StatefulSet 支持 partition 的分批升级。但问题在于,Deployment 无法灰度分批,而 StatefulSet 则只能一个一个 Pod 串行发布,没办法并行的流式升级。
Advanced StatefulSet引入了maxUnavailable 。原生 StatefulSet 的 one by one 发布,大家其实可以理解为一个强制 maxUnavailable=1 的过程,而 Advanced StatefulSet 中如果我们配置了更大的 maxUnavailable,那么就支持并行发布更多的 Pod 了。
再来看一下 CloneSet,它支持原生 Deployment 和 StatefulSet 的全部发布策略,包括 maxUnavailable、maxSurge、partition。那么 CloneSet 是如何把它们结合在一起的呢?我们来看一个例子:
apiVersion: apps.kruise.io/v1alpha1
kind: CloneSet
# ...
spec:
replicas: 5 # Pod 总数为 5
updateStrategy:
type: InPlaceIfPossible
maxSurge: 20% # 多扩出来 5 * 20% = 1 个 Pod (rounding up)
maxUnavailable: 0 # 保证发布过程 5 - 0 = 5 个 Pod 可用
partition: 3 # 保留 3 个旧版本 Pod (只发布 5 - 3 = 2 个 Pod)
针对这个副本数为 5 的 CloneSet,如果我们修改了 template 中的 image,同时配置:maxSurge=20% maxUnavailable=0 partition=3。当开始发布后:
- 先扩出来 1 个新版本的 Pod,5 个存量 Pod 保持不动;
- 新 Pod ready 后,逐步把旧版本 Pod 做原地升级;
- 直到剩余 3 个旧版本 Pod 时,因为满足了 partition 终态,会把新版本 Pod 再删除 1 个;
- 此时 Pod 总数仍然为 5,其中 3 个旧版本、1 个新版本。
如果我们接下来把 partition 调整为 0,则 CloneSet 还是会先扩出 1 个额外的新版 Pod,随后逐渐将所有 Pod 升级到新版,最终再次删除一个 Pod,达到 5 个副本全量升级的终态。
发布顺序可配置
对于原生的 Deployment 和 StatefulSet,用户是无法配置发布顺序的。Deployment 下的 Pod 发布顺序完全依赖于它修改 ReplicaSet 后的扩缩顺序,而 StatefulSet 则严格按照 order 的反序来做一一升级。
但在 CloneSet 和 Advanced StatefulSet 中,我们增加了发布顺序的可配置能力,使用户可以定制自己的发布顺序。目前可以通过以下两种发布优先级和一种发布打散策略来定义顺序:
优先级(1):按给定 label key,在发布时根据 Pod labels 中这个 key 对应的 value 值作为权重:
apiVersion: apps.kruise.io/v1alpha1
kind: CloneSet
spec:
# ...
updateStrategy:
priorityStrategy:
orderPriority:
- orderedKey: some-label-key
优先级(2):按 selector 匹配计算权重,发布时根据 Pod 对多个 weight selector 的匹配情况计算权重总和:
apiVersion: apps.kruise.io/v1alpha1
kind: CloneSet
spec:
# ...
updateStrategy:
priorityStrategy:
weightPriority:
- weight: 50
matchSelector:
matchLabels:
test-key: foo
- weight: 30
matchSelector:
matchLabels:
test-key: bar
打散:将匹配 key-value 的 Pod 打散到不同批次中发布:
apiVersion: apps.kruise.io/v1alpha1
kind: CloneSet
spec:
# ...
updateStrategy:
scatterStrategy:
- key: some-label-key
value: foo
cloneSets
cloneSets的功能比较丰富,更多资料可以去这里了解。
- 支持动态pvc,deployment没有这个功能。
- 选择性pod删除,就是用户可以选择删除哪个pod
- 支持原地升级
- ….
一个简单的demo如下:
apiVersion: apps.kruise.io/v1alpha1
kind: CloneSet
metadata:
labels:
app: sample
name: sample
spec:
replicas: 5
selector:
matchLabels:
app: sample
template:
metadata:
labels:
app: sample
spec:
containers:
- name: nginx
image: nginx:alpine
和咱们正常创建deploy一样。创建完成后则可以生成5个Pod。
# kubectl get pod
NAME READY STATUS RESTARTS AGE
sample-5pc64 1/1 Running 0 5m52s
sample-g5w28 1/1 Running 0 5m52s
sample-mthv7 1/1 Running 0 5m52s
sample-qn8p5 1/1 Running 0 5m52s
sample-xhbjd 1/1 Running 0 5m52s
支持动态pvc的yaml如下:
apiVersion: apps.kruise.io/v1alpha1
kind: CloneSet
metadata:
labels:
app: sample
name: sample-data
spec:
replicas: 5
selector:
matchLabels:
app: sample
template:
metadata:
labels:
app: sample
spec:
containers:
- name: nginx
image: nginx:alpine
volumeMounts:
- name: data-vol
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: data-vol
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 20Gi
可以看到其yaml的定义和我们熟悉的statefulset一样。
选择性删除pod,比如我们这里要删除名为sample-xhbjd
的pod,我们修改yaml文件如下:
apiVersion: apps.kruise.io/v1alpha1
kind: CloneSet
metadata:
labels:
app: sample
name: sample
spec:
replicas: 4
scaleStrategy:
podsToDelete:
- sample-xhbjd
selector:
matchLabels:
app: sample
template:
metadata:
labels:
app: sample
spec:
containers:
- name: nginx
image: nginx:alpine
我们可以看到执行完yaml,信息如下:
# kubectl apply -f cloneset-demo.yaml
cloneset.apps.kruise.io/sample configured
[root@k8s-master ali-workload]# kubectl get pod
NAME READY STATUS RESTARTS AGE
sample-5pc64 1/1 Running 0 14m
sample-g5w28 1/1 Running 0 14m
sample-mthv7 1/1 Running 0 14m
sample-qn8p5 1/1 Running 0 14m
sample-xhbjd 0/1 Terminating 0 14m
是不是我们定义的这个Pod在被删除?
注意:要删除的pod必须存在,不然就会报错哦。如果定义了要删除了pod而没有修改replicas,那么会删除存在的Pod,然后生成新的Pod。
更多功能慢慢来测试,不过我觉得很强大,而且还实用。
参考连接: