捕鱼大亨网络版 有状态集s和DaemonSets更新
编者注:今天的帖子由Google软件工程师Janet Kuo和Kenneth Owens撰写。
这篇文章讨论了对 守护程序集 和 有状态集 捕鱼大亨网络版的API对象。我们使用以下方法探索这些功能 Apache ZooKeeper 和 阿帕奇·卡夫卡 有状态集和 Prometheus节点导出器 守护程序集。
在捕鱼大亨网络版 1.6中,我们添加了 滚动更新 守护程序集 API对象的更新策略。使用RollingUpdate策略配置DaemonSet会使DaemonSet控制器在更新其spec.template时对DaemonSet中的Pod执行自动滚动更新。
在捕鱼大亨网络版 1.7中,我们增强了DaemonSet控制器以跟踪DaemonSets的PodTemplateSpecs的修订历史。这允许DaemonSet控制器回滚更新。我们还添加了 滚动更新 战略 有状态集 API对象,并为StatefulSet控制器实现了修订历史记录跟踪。此外,我们添加了 平行 Pod管理策略,用于支持需要Pod具有唯一标识但不按顺序进行Pod创建和终止的有状态应用程序。
有状态集滚动更新和Pod管理策略
首先,我们将通过部署ZooKeeper集成和Kafka集群来演示如何使用StatefulSet滚动更新和Pod管理策略。
先决条件
为此,您需要设置一个捕鱼大亨网络版 1.7集群,其中至少包含3个可调度的节点。每个节点需要1个CPU和2 GiB的可用内存。您还需要一个动态供应商,以允许StatefulSet控制器以每个10 GiB的价格供应6个永久卷(PV),或者您需要在部署ZooKeeper集成或部署Kafka集群之前手动供应PV。
部署ZooKeeper合奏
Apache ZooKeeper是一个高度一致的分布式系统,其他分布式系统将其用于集群协调和配置管理。
注意:您可以使用此方法创建ZooKeeper合奏 zookeeper_mini.yaml 表现。您可以了解有关在捕鱼大亨网络版上运行ZooKeeper集成的更多信息 在这里,以及 更深入的解释 清单及其内容。
应用清单时,您将看到类似以下的输出。
$ kubectl apply -f zookeeper\_mini.yaml
service "zk-hs" created
service "zk-cs" created
poddisruptionbudget "zk-pdb" created
statefulset "zk" created
清单使用StatefulSet zk创建了三个ZooKeeper服务器的集合;无头服务zk-hs,用于控制集成域;客户端可以用来连接到准备好的ZooKeeper实例的服务zk-cs;还有一个PodDisruptionBugdet zk-pdb,它允许一个计划的中断。 (请注意,虽然此合奏适合用于演示目的,但其尺寸大小不适合生产使用。)
如果您使用kubectl get在另一个终端上观看Pod的创建,则与 订购就绪 策略(实现StatefulSet保证的完整版本的默认策略),zk 有状态集中的所有Pod都是并行创建的。
$ kubectl get po -lapp=zk -w
NAME READY STATUS RESTARTS AGE
zk-0 0/1 Pending 0 0s
zk-0 0/1 Pending 0 0s
zk-1 0/1 Pending 0 0s
zk-1 0/1 Pending 0 0s
zk-0 0/1 ContainerCreating 0 0s
zk-2 0/1 Pending 0 0s
zk-1 0/1 ContainerCreating 0 0s
zk-2 0/1 Pending 0 0s
zk-2 0/1 ContainerCreating 0 0s
zk-0 0/1 Running 0 10s
zk-2 0/1 Running 0 11s
zk-1 0/1 Running 0 19s
zk-0 1/1 Running 0 20s
zk-1 1/1 Running 0 30s
zk-2 1/1 Running 0 30s
这是因为zookeeper_mini.yaml清单将StatefulSet的podManagementPolicy设置为Parallel。
apiVersion: apps/v1beta1
kind: 有状态集
metadata:
name: zk
spec:
serviceName: zk-hs
replicas: 3
updateStrategy:
type: 滚动更新
podManagementPolicy: 平行
...
许多分布式系统,例如ZooKeeper,不需要为其过程进行有序的创建和终止。您可以使用Parallel Pod管理策略来加速创建和删除管理这些系统的StatefulSet。请注意,使用并行Pod管理时,StatefulSet控制器在创建Pod失败时将不会阻塞。当StatefulSet的podManagementPolicy设置为OrderedReady时,将执行有序的顺序Pod创建和终止。
部署Kafka集群
阿帕奇·卡夫卡是一种流行的分布式流平台。 Kafka生产者将数据写入分区的主题,该主题以可配置的复制因子存储在代理群集上。消费者从存储在代理上的分区中消费产生的数据。
注意:清单内容的详细信息可以找到 这里。您可以了解有关在捕鱼大亨网络版上运行Kafka集群的更多信息 这里.
要创建集群,您只需下载并应用 kafka_mini.yaml 表现。应用清单时,您将看到如下输出:
$ kubectl apply -f kafka\_mini.yaml
service "kafka-hs" created
poddisruptionbudget "kafka-pdb" created
statefulset "kafka" created
清单使用kafka 有状态集(无头服务,kafka-hs)创建三个代理群集,以控制代理的域;还有一个PodDisruptionBudget,kafka-pdb,它允许一个计划的中断。通过通过zk-cs服务进行连接,将代理配置为使用我们在上面创建的ZooKeeper集成。与上面部署的ZooKeeper合奏一样,此Kafka集群适合演示用途,但其大小可能不适合生产使用。
如果您观看Pod的创建,您会注意到,就像上面创建的ZooKeeper集成一样,Kafka集群使用Parallel podManagementPolicy。
$ kubectl get po -lapp=kafka -w
NAME READY STATUS RESTARTS AGE
kafka-0 0/1 Pending 0 0s
kafka-0 0/1 Pending 0 0s
kafka-1 0/1 Pending 0 0s
kafka-1 0/1 Pending 0 0s
kafka-2 0/1 Pending 0 0s
kafka-0 0/1 ContainerCreating 0 0s
kafka-2 0/1 Pending 0 0s
kafka-1 0/1 ContainerCreating 0 0s
kafka-1 0/1 Running 0 11s
kafka-0 0/1 Running 0 19s
kafka-1 1/1 Running 0 23s
kafka-0 1/1 Running 0 32s
生产和消费数据
您可以使用kubectl run执行kafka-topics.sh脚本来创建名为test的主题。
$ kubectl run -ti --image=gcr.io/google\_containers/kubernetes-kafka:1.0-10.2.1 createtopic --restart=Never --rm -- kafka-topics.sh --create \
\> --topic test \
\> --zookeeper zk-cs.default.svc.cluster.local:2181 \
\> --partitions 1 \
\> --replication-factor 3
现在,您可以使用kubectl run执行kafka-console-consumer.sh命令来侦听消息。
$ kubectl run -ti --image=gcr.io/google\_containers/kubnetes-kafka:1.0-10.2.1 consume --restart=Never --rm -- kafka-console-consumer.sh --topic test --bootstrap-server kafka-0.kafka-hs.default.svc.cluster.local:9093
在另一个终端中,您可以运行kafka-console-producer.sh命令。
$kubectl run -ti --image=gcr.io/google\_containers/kubernetes-kafka:1.0-10.2.1 produce --restart=Never --rm \
\> -- kafka-console-producer.sh --topic test --broker-list kafka-0.kafka-hs.default.svc.cluster.local:9093,kafka-1.kafka-hs.default.svc.cluster.local:9093,kafka-2.kafka-hs.default.svc.cluster.local:9093
第二个端子的输出出现在第一个端子中。如果在更新集群时继续产生和使用消息,则会注意到没有消息丢失。您可能会看到错误消息,因为当更新各个代理程序时,分区的领导者会发生更改,但是客户端将重试直到该消息提交为止。这是由于StatefulSet滚动更新的顺序性和顺序性,我们将在下一部分中进一步探讨。
更新Kafka集群
有状态集更新类似于DaemonSet更新,因为它们都通过设置相应API对象的spec.updateStrategy进行配置。当更新策略设置为OnDelete时,仅当StatefulSet或DaemonSet中的Pod被删除时,各个控制器才会创建新Pod。当更新策略设置为RollingUpdate时,对DaemonSet或StatefulSet的spec.template字段进行修改时,控制器将删除并重新创建Pod。您可以使用滚动更新来更改配置(通过环境变量或命令行参数),StatefulSet或DaemonSet中的Pod的配置,资源请求,资源限制,容器图像,标签和/或注释。请注意,所有更新都是破坏性的,始终要求销毁并重新创建DaemonSet或StatefulSet中的每个Pod。 有状态集滚动更新与DaemonSet滚动更新的不同之处在于Pod的终止和创建是有序的和顺序的。
您可以修补kafka 有状态集,以将CPU资源请求减少到250m。
$ kubectl patch sts kafka --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/resources/requests/cpu", "value":"250m"}]'
statefulset "kafka" patched
如果您在StatefulSet中观察Pod的状态,您会看到每个Pod都被删除并以相反的顺序重新创建(从具有最大序号的Pod开始,到最小的Pod)。控制器在更新后续Pod之前,等待每个更新的Pod处于运行状态并准备就绪。
$kubectl get po -lapp=kafka -w
NAME READY STATUS RESTARTS AGE
kafka-0 1/1 Running 0 13m
kafka-1 1/1 Running 0 13m
kafka-2 1/1 Running 0 13m
kafka-2 1/1 Terminating 0 14m
kafka-2 0/1 Terminating 0 14m
kafka-2 0/1 Terminating 0 14m
kafka-2 0/1 Terminating 0 14m
kafka-2 0/1 Pending 0 0s
kafka-2 0/1 Pending 0 0s
kafka-2 0/1 ContainerCreating 0 0s
kafka-2 0/1 Running 0 10s
kafka-2 1/1 Running 0 21s
kafka-1 1/1 Terminating 0 14m
kafka-1 0/1 Terminating 0 14m
kafka-1 0/1 Terminating 0 14m
kafka-1 0/1 Terminating 0 14m
kafka-1 0/1 Pending 0 0s
kafka-1 0/1 Pending 0 0s
kafka-1 0/1 ContainerCreating 0 0s
kafka-1 0/1 Running 0 11s
kafka-1 1/1 Running 0 21s
kafka-0 1/1 Terminating 0 14m
kafka-0 0/1 Terminating 0 14m
kafka-0 0/1 Terminating 0 14m
kafka-0 0/1 Terminating 0 14m
kafka-0 0/1 Pending 0 0s
kafka-0 0/1 Pending 0 0s
kafka-0 0/1 ContainerCreating 0 0s
kafka-0 0/1 Running 0 10s
kafka-0 1/1 Running 0 22s
请注意,计划外的中断不会在更新过程中导致意外更新。也就是说,StatefulSet控制器将始终以正确的版本重新创建Pod,以确保保留更新的顺序。如果Pod已删除,并且已经更新,则会从StatefulSet的spec.template的更新版本中创建。如果Pod尚未更新,则将从StatefulSet的spec.template的先前版本中创建。我们将在以下各节中进一步探讨。
进行更新
根据组织处理部署和配置修改的方式,您可能希望或需要先进行对StatefulSet的更新,然后再进行部署。您可以通过为RollingUpdate设置分区来实现。当StatefulSet控制器在StatefulSet的updateStrategy中检测到分区时,它将仅将StatefulSet的spec.template的更新版本应用于序数大于或等于该分区值的Pod。
您可以修补kafka 有状态集,以将分区添加到RollingUpdate更新策略。如果您将分区设置为大于或等于StatefulSet的spec.replicas的数字(如下所示),则您对StatefulSet的spec.template执行的任何后续更新都将分阶段发布,但StatefulSet控制器将不会开始滚动更新。
$ kubectl patch sts kafka -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":3}}}}'
statefulset "kafka" patched
如果对StatefulSet进行修补以将请求的CPU设置为0.3,则会注意到没有Pod进行更新。
$ kubectl patch sts kafka --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/resources/requests/cpu", "value":"0.3"}]'
statefulset "kafka" patched
即使删除Pod并等待StatefulSet控制器重新创建它,您也会注意到该Pod是使用当前CPU请求重新创建的。
$ kubectl delete po kafka-1
pod "kafka-1" deleted
$ kubectl get po kafka-1 -w
NAME READY STATUS RESTARTS AGE
kafka-1 0/1 ContainerCreating 0 10s
kafka-1 0/1 Running 0 19s
kafka-1 1/1 Running 0 21s
$ kubectl get po kafka-1 -o yaml
apiVersion: v1
kind: Pod
metadata:
...
resources:
requests:
cpu: 250m
memory: 1Gi
推出金丝雀
通常,我们要在全球范围内推广之前,先在应用程序的单个实例上验证映像更新或配置更改。如果您将上面创建的分区修改为2,则StatefulSet控制器将推出一个 金丝雀 可用于验证更新是否按预期运行。
$ kubectl patch sts kafka -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":2}}}}'
statefulset "kafka" patched
您可以观看StatefulSet控制器更新kafka-2 Pod,并在更新完成后暂停。
$ kubectl get po -lapp=kafka -w
NAME READY STATUS RESTARTS AGE
kafka-0 1/1 Running 0 50m
kafka-1 1/1 Running 0 10m
kafka-2 1/1 Running 0 29s
kafka-2 1/1 Terminating 0 34s
kafka-2 0/1 Terminating 0 38s
kafka-2 0/1 Terminating 0 39s
kafka-2 0/1 Terminating 0 39s
kafka-2 0/1 Pending 0 0s
kafka-2 0/1 Pending 0 0s
kafka-2 0/1 Terminating 0 20s
kafka-2 0/1 Terminating 0 20s
kafka-2 0/1 Pending 0 0s
kafka-2 0/1 Pending 0 0s
kafka-2 0/1 ContainerCreating 0 0s
kafka-2 0/1 Running 0 19s
kafka-2 1/1 Running 0 22s
分阶段推出
与推出金丝雀类似,您可以根据阶段性进度(例如线性,几何或指数推出)推出更新。
如果修补kafka 有状态集以将分区设置为1,则StatefulSet控制器将再更新一个代理。
$ kubectl patch sts kafka -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":1}}}}'
statefulset "kafka" patched
如果将其设置为0,则StatefulSet控制器将更新最终代理并完成更新。
$ kubectl patch sts kafka -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":0}}}}'
statefulset "kafka" patched
请注意,您不必将分区减一。对于较大的StatefulSet(例如,一个具有100个副本的StatefulSet),您可以使用更像100、99、90、50、0的进度。在这种情况下,您将分阶段进行更新,部署金丝雀,然后部署到10实例,更新百分之五十的Pod,然后完成更新。
打扫干净
要删除上面创建的API对象,可以在用于创建ZooKeeper集成和Kafka集群的两个清单上使用kubectl delete。
$ kubectl delete -f kafka\_mini.yaml
service "kafka-hs" deleted
poddisruptionbudget "kafka-pdb" deleted
Statefulset “kafka” deleted
$ kubectl delete -f zookeeper\_mini.yaml
service "zk-hs" deleted
service "zk-cs" deleted
poddisruptionbudget "zk-pdb" deleted
statefulset "zk" deleted
根据设计,StatefulSet控制器不会删除任何持久卷声明(PVC):必须手动删除为ZooKeeper集成和Kafka群集创建的PVC。根据集群的存储回收策略,许多人还需要手动删除后备PV。
守护程序集滚动更新,历史记录和回滚
在本节中,我们将向您展示如何在DaemonSet上进行滚动更新,查看其历史记录,然后在失败的发布后执行回滚。我们将使用DaemonSet来部署 Prometheus节点导出器 在集群中的每个捕鱼大亨网络版节点上。这些节点导出器将节点度量导出到Prometheus监视系统。为了简单起见,我们省略了 Prometheus服务器 和服务 与DaemonSet容器通信 来自此博客文章。
先决条件
要遵循博客的这一部分,您需要一个有效的捕鱼大亨网络版 1.7集群和kubectl版本1.7或更高版本。如果按照第一部分进行操作,则可以使用相同的集群。
守护程序集汇总首先,准备节点导出器DaemonSet清单以在集群中的每个节点上运行v0.13 Prometheus节点导出器:
$ cat \>\> node-exporter-v0.13.yaml \<\<EOF
apiVersion: extensions/v1beta1
kind: 守护程序集
metadata:
name: node-exporter
spec:
updateStrategy:
type: 滚动更新
template:
metadata:
labels:
app: node-exporter
name: node-exporter
spec:
containers:
- image: prom/node-exporter:v0.13.0
name: node-exporter
ports:
- containerPort: 9100
hostPort: 9100
name: scrape
hostNetwork: true
hostPID: true
EOF
请注意,您需要通过将DaemonSet .spec.updateStrategy.type显式设置为RollingUpdate来启用DaemonSet滚动更新功能。
应用清单创建节点导出器DaemonSet:
$ kubectl apply -f node-exporter-v0.13.yaml --record
daemonset "node-exporter" created
等待第一个DaemonSet卷展完成:
$ kubectl rollout status ds node-exporter
daemon set "node-exporter" successfully rolled out
您应该看到每个节点都运行一个节点导出器容器的副本:
$ kubectl get pods -l app=node-exporter -o wide
要在节点导出器DaemonSet上执行滚动更新,请准备包含v0.14 Prometheus节点导出器的清单:
$ cat node-exporter-v0.13.yaml ``` sed "s/v0.13.0/v0.14.0/g" \> node-exporter-v0.14.yaml
然后应用v0.14节点导出器DaemonSet:
$ kubectl apply -f node-exporter-v0.14.yaml --record
daemonset "node-exporter" configured
等待DaemonSet滚动更新完成:
$ kubectl rollout status ds node-exporter
...
Waiting for rollout to finish: 3 out of 4 new pods have been updated...
Waiting for rollout to finish: 3 of 4 updated pods are available...
daemon set "node-exporter" successfully rolled out
我们只是通过更新DaemonSet模板触发了DaemonSet滚动更新。默认情况下,将杀死一个旧的DaemonSet吊舱,并一次创建一个新的DaemonSet吊舱。
现在,通过将图片更新为无效值,将导致首次发布失败:
$ cat node-exporter-v0.13.yaml | sed "s/v0.13.0/bad/g" \> node-exporter-bad.yaml
$ kubectl apply -f node-exporter-bad.yaml --record
daemonset "node-exporter" configured
请注意,推出永远不会结束:
$ kubectl rollout status ds node-exporter
Waiting for rollout to finish: 0 out of 4 new pods have been updated...
Waiting for rollout to finish: 1 out of 4 new pods have been updated…
# Use ^C to exit
此行为是预期的。前面我们提到过,DaemonSet滚动更新会杀死并一次创建一个pod。由于新的Pod永远都无法使用,因此暂停了发布,防止了无效的规范传播到多个节点。关于失败的部署,StatefulSet滚动更新实现了相同的行为。不成功的更新将被阻止,直到通过回滚或使用规范前滚对其进行纠正为止。
$ kubectl get pods -l app=node-exporter
NAME READY STATUS RESTARTS AGE
node-exporter-f2n14 0/1 ErrImagePull 0 3m
...
# N = number of nodes
$ kubectl get ds node-exporter
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
node-exporter N N N-1 1 N \<none\> 46m
守护程序集的历史记录,回滚和前滚
接下来,执行回滚。看一下节点导出器DaemonSet的推出历史记录:
$ kubectl rollout history ds node-exporter
daemonsets "node-exporter"
REVISION CHANGE-CAUSE
1 kubectl apply --filename=node-exporter-v0.13.yaml --record=true
2 kubectl apply --filename=node-exporter-v0.14.yaml --record=true
3 kubectl apply --filename=node-exporter-bad.yaml --record=true
检查要回滚到的修订的详细信息:
$ kubectl rollout history ds node-exporter --revision=2
daemonsets "node-exporter" with revision #2
Pod Template:
Labels: app=node-exporter
Containers:
node-exporter:
Image: prom/node-exporter:v0.14.0
Port: 9100/TCP
Environment: \<none\>
Mounts: \<none\>
Volumes: \<none\>
您可以快速回滚到通过kubectl推出历史记录找到的任何DaemonSet修订版:
# Roll back to the last revision
$ kubectl rollout undo ds node-exporter
daemonset "node-exporter" rolled back
# Or use --to-revision to roll back to a specific revision
$ kubectl rollout undo ds node-exporter --to-revision=2
daemonset "node-exporter" rolled back
守护程序集回滚是通过前滚完成的。因此,回滚后,DaemonSet修订版2变为修订版4(当前修订版):
$ kubectl rollout history ds node-exporter
daemonsets "node-exporter"
REVISION CHANGE-CAUSE
1 kubectl apply --filename=node-exporter-v0.13.yaml --record=true
3 kubectl apply --filename=node-exporter-bad.yaml --record=true
4 kubectl apply --filename=node-exporter-v0.14.yaml --record=true
节点导出器DaemonSet现在又恢复正常:
$ kubectl rollout status ds node-exporter
daemon set "node-exporter" successfully rolled out
# N = number of nodes
$ kubectl get ds node-exporter
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
node-exporter N N N N N \<none\> 46m
如果执行回滚时指定了当前的DaemonSet修订版,则回滚将被跳过:
$ kubectl rollout undo ds node-exporter --to-revision=4
daemonset "node-exporter" skipped rollback (current template already matches revision 4)
如果找不到DaemonSet修订版,您将看到来自kubectl的投诉:
$ kubectl rollout undo ds node-exporter --to-revision=10
error: unable to find specified revision 10 in history
请注意,kubectl推出历史记录和kubectl推出状态也支持StatefulSets!
打扫干净
$ kubectl delete ds node-exporter
守护程序集和StatefulSet的下一步是什么
滚动更新和回滚弥补了DaemonSet和StatefulSet的重要功能差距。在计划捕鱼大亨网络版 1.8的同时,我们希望继续专注于将核心控制器升级到GA。这可能意味着某些高级功能请求(例如自动回滚,婴儿死亡率检测)将被推迟,以确保核心控制器的一致性,可用性和稳定性。我们欢迎您提供反馈和意见,因此请随时与我们联系 松弛,在 堆栈溢出,或者打开问题或请求 的GitHub.
- 发表问题(或回答问题) 堆栈溢出
- 加入社区门户以倡导 K8端口
- 在推特上关注我们 @捕鱼大亨网络版io 有关最新更新
- 与社区联系 松弛
- 参与捕鱼大亨网络版项目 的GitHub