Kubernetes中的部署策略

112 阅读16分钟

**TL;DR:**在这篇文章中,我们将学习在使用Kubernetes容器-编排系统部署容器时有哪些部署策略。在本文的最后,我们将学习如何在Kubernetes集群中使用不同的方式进行部署。如果你觉得这个话题很有趣,就继续阅读吧本教程的代码可以在Github上找到

Kubernetes的快速介绍

随着容器化的普及,彻底改变了应用程序的构建、运输和维护过程,有必要有效地管理这些容器。许多容器协调工具被引入,以管理大规模系统中这些容器的生命周期。

Kubernetes就是这样一个协调工具,它负责供应和部署、资源分配、负载平衡、服务发现、提供高可用性以及任何系统的其他重要方面。有了这个平台,我们可以在开发时将我们的应用程序分解成更小的系统(称为微服务);然后,我们可以在部署时将这些系统组合(或协调)起来。

云原生方法的采用增加了基于微服务架构的应用程序的开发。对于这样的应用,组织面临的最大挑战之一是部署。在部署方面,有一个适当的策略是必要的。在Kubernetes中,有不同的方式来发布应用程序;有必要选择正确的策略,以使你的基础设施在应用程序的部署或更新期间是可靠的。例如,在生产环境中,总是需要确保终端用户不应该经历任何停机时间。在Kubernetes协调中,正确的策略可以确保正确管理不同版本的容器镜像。总而言之,本文将主要围绕Kubernetes中的不同部署策略展开。

前提条件

要跟上这篇文章,我们需要一些以前对Kubernetes的经验。如果你是这个平台的新手,请查看《Kubernetes基本概念分步介绍》教程。在那里,你可以学到你所需要的一切,以遵循这里的指示。如果需要的话,我们也会建议你浏览一下Kubernetes的文档

除此之外,我们将需要kubectl,这是一个命令行界面(CLI)工具,将使我们能够从终端控制你的集群。如果你没有这个工具,请查看安装Kube控制(kubectl)的说明。我们还需要对Linux和YAML有基本了解。

什么是Kubernetes中的部署?

部署是Kubernetes中的一个资源对象,它定义了我们程序所需的状态。部署是声明性的,也就是说,我们并没有规定如何实现这个状态。相反,我们声明所需的状态,并允许部署控制器以最有效的方式自动达到该最终目标。部署允许我们描述一个应用程序的生命周期,如应用程序使用哪种镜像,应该有多少个pod,以及它们应该如何更新。

使用Kubernetes部署的好处

手动更新容器化应用程序的过程可能是耗时和乏味的。Kubernetes部署使这个过程自动化和可重复。部署完全由Kubernetes后台管理,整个更新过程在服务器端进行,无需客户端交互。

此外,Kubernetes部署控制器一直在监控吊舱和节点的健康状况。它可以替换故障的pod或旁路停机的节点,确保关键应用程序的连续性。

部署策略

滚动更新部署

滚动部署是Kubernetes的默认部署策略。它用新版本的吊舱逐一替换我们应用程序的上一版本的吊舱,而没有任何集群停机。滚动部署会慢慢地用新版本的应用程序的实例替换上一版本的应用程序的实例。

Alt text

当使用RollingUpdate策略时,还有两个选项让我们对更新过程进行微调。

  1. maxSurge。在更新过程中,可以创建的pods数量超过所需的pods数量。这可以是一个绝对数字,也可以是复制数量的百分比。默认是25%。

  2. maxUnavailable。在更新过程中不可用的pod的数量。这可以是一个绝对数,也可以是副本数的一个百分比;默认是25%。

首先,我们创建我们的_rollingupdate.yaml_部署模板。在下面的模板中,我们将_maxSurge_设置为2,_maxUnavailable_设置为1。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: rollingupdate-strategy
  version: nanoserver-1709
spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 2
      maxUnavailable: 1
  selector:
    matchLabels:
      app: web-app-rollingupdate-strategy
      version: nanoserver-1709
  replicas: 3
  template:
    metadata:
      labels:
        app: web-app-rollingupdate-strategy
        version: nanoserver-1709
    spec:
      containers:
        - name: web-app-rollingupdate-strategy
          image: hello-world:nanoserver-1709

然后我们可以使用kubectl命令创建部署。

$ kubectl apply -f rollingupdate.yaml

一旦我们有了部署模板,我们就可以通过创建服务来提供一种访问部署实例的方法。注意,我们正在部署版本为_nanoserver-1709_的图像_hello-world_。所以在这种情况下,我们有两个标签,'name=web-app-rollingupdate-strategy'和'version=nanoserver-1709'。我们将把这些设置为下面服务的标签选择器。将此保存到_'service.yaml_'文件。

apiVersion: v1
kind: Service
metadata: 
  name: web-app-rollingupdate-strategy
  labels: 
    name: web-app-rollingupdate-strategy
    version: nanoserver-1709
spec:
  ports:
    - name: http
      port: 80
      targetPort: 80
  selector: 
    name: web-app-rollingupdate-strategy
    version: nanoserver-1709
  type: LoadBalancer

现在,创建服务将创建一个可在集群外访问的负载均衡器。

$ kubectl apply -f service.yaml

运行_kubectl get deployments_来检查部署是否已经创建。如果部署仍在创建中,输出结果应该与下面类似。

$ kubectl get deployments

NAME                             READY   UP-TO-DATE   AVAILABLE   AGE
rollingupdate-strategy   0/3     0            0           1s

如果我们在几秒钟后再次运行_kubectl get deployments_。输出应该与此类似。

$ kubectl get deployments

NAME                             READY   UP-TO-DATE   AVAILABLE   AGE
rollingupdate-strategy   3/3     0            0           7s

要查看由部署创建的ReplicaSet(rs),请运行_kubectl get rs_。输出应该与此类似。

$ kubectl get rs

NAME                                    DESIRED   CURRENT   READY   AGE
rollingupdate-strategy-87875f5897   3         3         3       18s

要查看正在运行的3个pod的部署,运行_kubectl get pods_。创建的ReplicaSet确保有三个Pod在运行。输出应该类似于下面的内容。

$ kubectl get pods

NAME                                      READY     STATUS    RESTARTS   AGE       
rollingupdate-strategy-87875f5897-55i7o   1/1       Running   0          12s       
rollingupdate-strategy-87875f5897-abszs   1/1       Running   0          12s       
rollingupdate-strategy-87875f5897-qazrt   1/1       Running   0          12s

让我们更新_rollingupdate.yaml_部署模板,使用_hello-world:nanoserver-1809_镜像而不是_hello-world:nanoserver-1709_镜像。然后使用kubectl命令更新现有运行中的部署的镜像。

$ kubectl set image deployment/rollingupdate-strategy web-app-rollingupdate-strategy=hello-world:nanoserver-1809 --record

输出与下面类似。

deployment.apps/rollingupdate-strategy image updated

我们现在部署的是版本为_nanoserver-1809_的_hello-world_镜像。因此,在这种情况下,我们将不得不更新_"service.yaml_"中的标签。标签将被更新为'version=nanoserver-1809'。我们将再次运行下面的kubectl命令来更新服务,这样它就可以挑选运行在较新镜像上的新pod。

$ kubectl apply -f service.yaml

运行下面的kubectl命令来查看推出的状态。

$ kubectl rollout status deployment/rollingupdate-strategy

Waiting for rollout to finish: 2 out of 3 new replicas have been updated...

再次运行以验证推广成功。

$ kubectl rollout status deployment/rollingupdate-strategy

deployment "rollingupdate-strategy" successfully rolled out

展开成功后,我们可以通过运行_kubectl get deployments_命令来查看部署情况。输出与此类似。

$ kubectl get deployments

NAME                             READY   UP-TO-DATE   AVAILABLE   AGE
rollingupdate-strategy   3/3     0            0           7s

运行_kubectl get rs_可以看到部署已经更新。新的Pod是在一个新的ReplicaSet中创建的,并被扩展到3个副本。旧的ReplicaSet被缩减到0个副本。

$ kubectl get rs

NAME                                    DESIRED   CURRENT   READY   AGE
rollingupdate-strategy-87875f5897   3         3         3       55s
rollingupdate-strategy-89999f7895   0         0         0       12s

运行_kubectl get pods_,现在应该只显示新ReplicaSet中的新Pod。

$ kubectl get pods

NAME                                      READY     STATUS    RESTARTS   AGE       
rollingupdate-strategy-89999f7895-55i7o   1/1       Running   0          12s       
rollingupdate-strategy-89999f7895-abszs   1/1       Running   0          12s       
rollingupdate-strategy-89999f7895-qazrt   1/1       Running   0          12s

kubectl的rollout命令在这里非常有用。我们可以用它来检查我们的部署是如何进行的。默认情况下,该命令会等到部署中的所有Pod都被成功启动。当部署成功时,该命令退出,返回代码为0,表示成功。如果部署失败,该命令将以非零代码退出。

$ kubectl rollout status deployment rollingupdate-strategy

Waiting for deployment "rollingupdate-strategy" rollout to finish: 0 of 3 updated replicas are available…
Waiting for deployment "rollingupdate-strategy" rollout to finish: 1 of 3 updated replicas are available…
Waiting for deployment "rollingupdate-strategy" rollout to finish: 2 of 3 updated replicas are available…

deployment "rollingupdate-strategy" successfully rolled out

如果在Kubernetes中部署失败,部署过程就会停止,但失败的部署中的pods会被保留下来。在部署失败时,我们的环境可能同时包含来自新旧部署的pod。为了回到一个稳定的工作状态,我们可以使用rollout undo命令,把工作的pod带回来,并清理失败的部署。

$ kubectl rollout undo deployment rollingupdate-strategy

deployment.extensions/rollingupdate-strategy

然后,我们将再次验证部署的状态。

$ kubectl rollout status deployment rollingupdate-strategy

deployment "rollingupdate-strategy" successfully rolled out

为了让Kubernetes知道一个应用程序何时准备就绪,它需要来自应用程序的一些帮助。Kubernetes使用准备度探针来检查应用程序的情况。一旦一个应用实例开始对准备度探针作出积极响应,该实例就被认为准备好了。准备度探针告诉Kubernetes一个应用程序何时准备好,但不知道该应用程序是否会成为准备好。如果应用程序一直失败,它可能永远不会对Kubernetes作出积极响应。

滚动部署通常会等待新的pods通过就绪检查准备好,然后再缩减旧的组件。如果出现了重大问题,可以中止滚动部署。如果有问题,可以中止滚动更新或部署,而不会使整个集群崩溃。

重现部署

在重新创建部署中,我们在扩大新的应用程序版本之前完全缩小了现有的应用程序版本。在下图中,版本1代表当前的应用程序版本,而版本2代表新的应用程序版本。当更新当前应用版本时,我们首先将版本1的现有副本缩减为零,然后同时部署新版本的副本。

Alt text

下面的模板显示了使用recreate策略的部署。首先,我们通过将以下yaml保存到文件recreate.yaml来创建我们的_recreate_部署

apiVersion: apps/v1
kind: Deployment
metadata:
  name: recreate-strategy
spec:
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: web-app-recreate-strategy
      version: nanoserver-1809
  replicas: 3
  template:
    metadata:
      labels:
        app: web-app-recreate-strategy
    spec:
      containers:
        - name: web-app-recreate-strategy
          image: hello-world:nanoserver-1809

然后我们可以使用kubectl命令创建部署。

$ kubectl apply -f recreate.yaml

一旦我们有了一个部署模板,我们就可以通过创建一个服务来提供一种访问部署实例的方法。注意,我们正在部署版本为_nanoserver-1809_的_hello-world_图像。所以在这种情况下,我们有两个标签,'name=web-app-recreat-strategy'和'version=nanoserver-1809'。我们将把这些设置为下面服务的标签选择器。将此保存到_service.yaml_文件中。

apiVersion: v1
kind: Service
metadata: 
  name: web-app-recreate-strategy
  labels: 
    name: web-app-recreate-strategy
    version: nanoserver-1809
spec:
  ports:
    - name: http
      port: 80
      targetPort: 80
  selector: 
    name: web-app-recreate-strategy
    version: nanoserver-1809
  type: LoadBalancer

现在,创建服务将创建一个可以在集群外访问的负载均衡器。

$ kubectl apply -f service.yaml

重新创建的方法在更新过程中涉及一些停机时间。对于能够处理维护窗口或中断的应用程序来说,停机时间不是问题。但是,如果有一个关键任务的应用程序,有很高的服务水平协议(SLA)和可用性要求,选择一个不同的部署策略将是正确的方法。重现部署一般由开发人员在开发阶段使用,因为它很容易设置,而且应用程序的状态完全随着新的版本更新。更重要的是,我们不需要平行管理一个以上的应用程序版本,因此我们避免了数据和应用程序的向后兼容性挑战。

蓝绿部署

在蓝/绿部署策略中(有时也被称为红/黑),蓝色代表当前的应用程序版本,而绿色代表新的应用程序版本。在这种情况下,每次只有一个版本是活的。流量被路由到蓝色部署,同时绿色部署被创建和测试。在我们完成测试后,我们再将流量路由到新版本。

部署成功后,我们可以保留蓝色部署以备回滚,或将其退役。另外,也可以在这些实例上部署较新版本的应用程序。在这种情况下,当前的(蓝色)环境可以作为下一个版本的暂存区。

这种技术可以消除我们在重新创建部署策略中面临的停机时间。此外,蓝-绿部署降低了风险:如果我们的新版本在绿色环境中发生意外,我们可以通过切换到蓝色环境立即回滚到最后一个版本。有即时滚出/滚回。我们还可以避免版本问题;整个应用程序的状态在一次部署中被改变。

Alt text

蓝-绿部署是昂贵的,因为它需要双倍的资源。在将其发布到生产之前,应该对整个平台进行适当的测试。此外,处理有状态的应用程序是困难的。

首先,我们通过将以下yaml保存到'blue.yaml'文件来创建我们的_蓝色_部署。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: blue-deployment
spec:
  selector:
    matchLabels:
      app: blue-deployment
      version: nanoserver-1709
  replicas: 3
  template:
    metadata:
      labels:
        app: blue-deployment
        version: nanoserver-1709
    spec:
      containers:
        - name: blue-deployment
          image: hello-world:nanoserver-1709

然后我们可以使用kubectl命令创建部署。

$ kubectl apply -f blue.yaml

一旦我们有了一个部署模板,我们就可以通过创建一个服务来提供一种访问部署实例的方法。注意,我们正在部署版本为_nanoserver-1809_的图像_hello-world_。所以在这种情况下,我们有两个标签,'name=blue-deployment'和'version=nanoserver-1709'。我们将把这些设置为下面服务的标签选择器。将此保存到_service.yaml_文件。

apiVersion: v1
kind: Service
metadata: 
  name: blue-green-service
  labels: 
    name: blue-deployment
    version: nanoserver-1709
spec:
  ports:
    - name: http
      port: 80
      targetPort: 80
  selector: 
    name: blue-deployment
    version: nanoserver-1709
  type: LoadBalancer

现在,创建服务将创建一个可在集群外访问的负载平衡器。

$ kubectl apply -f service.yaml

我们现在已经有了下面的设置。

Alt text

对于_绿色_部署,我们将在_蓝色_部署的同时部署一个新的部署。下面的模板是一个内容 green.yaml文件的内容。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: green-deployment
spec:
  selector:
    matchLabels:
      app: green-deployment
      version: nanoserver-1809
  replicas: 3
  template:
    metadata:
      labels:
        app: green-deployment
        version: nanoserver-1809
    spec:
      containers:
        - name: green-deployment
          image: hello-world:nanoserver-1809

注意,图像_hello-world:nanoserver-1809_的标签名称已经变成了2。 所以我们已经做了一个单独的部署,有两个标签,名称=_绿色部署_和版本=nanoserver-1809

$ kubectl apply -f green.yaml

为了切换到_绿色_部署,我们将更新现有服务的选择器。编辑service.yaml,将选择器的版本改为_2_,名称改为_green-deployemnt_。这将使它与 "绿色"部署中的pods相匹配。

apiVersion: v1
kind: Service
metadata: 
  name: blue-green-service
  labels: 
    name: green-deployment
    version: nanoserver-1809
spec:
  ports:
    - name: http
      port: 80
      targetPort: 80
  selector: 
    name: green-deployment
    version: nanoserver-1809
  type: LoadBalancer

我们使用kubectl命令再次创建该服务。

$ kubectl apply -f service.yaml

Alt text

因此,我们可以看到蓝绿部署是全有或全无的,不像滚动更新部署那样,我们不能逐步推出新的版本。所有用户将同时收到更新,尽管现有的会话将被允许在旧的实例上完成他们的工作。因此,赌注有点大,一旦我们启动了这个变化,一切都应该工作。它还需要分配更多的服务器资源,因为我们将需要运行每个pod的两个副本。

幸运的是,回滚程序也同样简单:我们只需再次翻转开关,以前的版本就会被换回原位。这是因为旧版本仍然在旧的吊舱上运行。只是流量不再被路由到它们。当我们确信新的版本将在这里停留时,我们应该退出这些pod。

金丝雀部署

金丝雀更新策略是一个部分更新过程,它允许我们在真实的用户群中测试我们的新程序版本,而不承诺全面推广。类似于蓝/绿部署,但它们更受控制,它们使用更多的渐进式交付,部署是分阶段进行的。有许多策略属于金丝雀的范畴,包括暗中推出或A/B测试。

在金丝雀部署中,应用程序的新版本被逐步部署到Kubernetes集群,同时获得非常小的实时流量(即,一个实时用户子集连接到新版本,而其余的仍在使用以前的版本)。在这种方法中,我们有两个几乎相同的服务器:一个用于所有当前的活跃用户,另一个具有新功能,被推广到一个用户子集,然后进行比较。当没有错误报告和信心增加时,新版本可以逐渐推广到基础设施的其他部分。最后,所有实时流量都进入金丝雀,使金丝雀版本成为新的_生产版本_。

下图显示了做金丝雀部署的最直接和简单的方法。一个新的版本被部署到一个子集的服务器上。

Alt text

当这种情况发生时,我们观察升级后的机器是如何做的。我们检查错误和性能问题,并听取用户的反馈。当我们对金丝雀越来越有信心时,我们继续在其余的机器上安装它,直到它们都在运行最新版本。

Alt text

在计划金丝雀的部署时,我们必须考虑到各种事情。

  1. 阶段:我们一开始要把多少用户送到金丝雀,分多少个阶段。
  2. 持续时间:我们将计划在多长时间内运行金丝雀?金丝雀的发布是不同的,因为我们必须等待足够的客户端被更新,然后才能评估结果。这可能发生在几天甚至几周内。
  3. 指标:要记录哪些指标来分析进展,包括应用性能和错误报告?精心选择的参数对于一个成功的金丝雀部署是至关重要的。例如,衡量部署的一个非常简单的方法是通过HTTP状态代码。我们可以有一个简单的ping服务,当部署成功时返回200。如果部署中出现问题,它将返回服务器端错误(5xx)。
  4. 评估:我们将使用什么标准来确定金丝雀是否成功

金丝雀用于我们必须测试一个新功能的场景,通常是在我们的应用程序的后端。当我们对新版本没有100%的信心时,应该使用金丝雀部署;我们预测我们可能有很低的失败机会。这种策略通常在我们有重大更新时使用,比如增加一个新的功能或实验性特征。

总结K8s的部署策略

总而言之,有不同的方法来部署一个应用程序;当发布到开发/暂存环境时,重新创建或斜坡式部署通常是一个不错的选择。当涉及到生产时,斜坡式或蓝色/绿色部署通常是一个很好的选择,但对新平台的适当测试是必要的。如果我们对平台的稳定性没有信心,对发布新的软件版本可能产生的影响没有信心,那么,金丝雀的发布应该是一种方式。通过这样做,我们让消费者测试应用程序和它与平台的整合。在这篇文章中,我们只是触及了Kubernetes部署的能力的表面。通过将部署与所有其他Kubernetes功能相结合,用户可以创建更强大的容器化应用,以满足任何需求。

  • Twitter icon
  • LinkedIn icon
  • Faceboook icon

Akhil Chawla

软件工程师

我是一个热情的软件专家,在将业务需求转化为创新的技术解决方案方面有6年以上的经验。在使用Java、Spring Boot/Spring Cloud、CI/CD、Azure、AWS、Docker、Kubernetes、Jenkins等构建端到端可扩展应用程序方面,拥有丰富的全栈开发者经验。我一直对学习算法、数据结构、架构设计和其他新的技术技能感兴趣。除了我的工程背景,我最近还完成了MDI古尔冈分校的行政工商管理硕士,以提高我的管理和团队领导能力。我目前在古尔冈的Optum(联合健康集团旗下公司)工作。

查看资料

阿克希尔-查瓦拉

软件工程师

我是一名热情的软件专家,在将业务需求转化为创新的技术解决方案方面有6年以上的经验。在使用Java、Spring Boot/Spring Cloud、CI/CD、Azure、AWS、Docker、Kubernetes、Jenkins等构建端到端可扩展应用程序方面,拥有丰富的全栈开发者经验。我一直对学习算法、数据结构、架构设计和其他新的技术技能感兴趣。除了我的工程背景,我最近还完成了MDI古尔冈分校的行政工商管理硕士,以提高我的管理和团队领导能力。我目前在古尔冈的Optum(联合健康集团旗下公司)工作。

查看资料


© 2013-2021 Auth0公司。保留所有权利。