捕鱼大亨网络版 有状态集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.