K8S部署策略


Kubernetes 部署策略

随着市场的快速发展,产研团队需要更快迭代需求、更高频发布变更。但由于无法完全模拟线上流量和真实场景,频繁发布可能导致整个系统风险。当出现一个小问题,就可能导致服务的可用性受损、用户体验甚至客户服务受到影响。而随着云技术和基础架构的成熟,发布过程中可以通过引入相应的发布策略,能让我们在早期实验阶段就可以发现、调整问题,来保证整体系统的稳定性。

目前常见的发布策略有蓝绿发布红黑发布金丝雀(灰度)发布滚动发布等。

不同的发布策略及特点介绍

蓝绿发布

概念定义:蓝绿发布是一种以最小的停机时间做服务升级的策略。需要维护的两个版本的环境分别称为 “蓝环境” 和 “绿环境”。一般当前生产流量指向环境为绿环境,而在蓝环境上部署新版本,短时间内作为测试环境。

发布流程:首先将一半的服务流量从负载均衡列表中移除,并且更新服务版本,验证新版本没有问题后,将生产流量指向蓝环境,然后对于老版本的绿环境进行版本升级,最后将所有服务流量加回负载均衡。

图片

特点:

  • 升级过程无需停机,用户感知小
  • 升级过程一半资源提供服务
  • 升级/回滚速度快
  • 如果出了问题,影响面较广

红黑发布

概念定义:与蓝绿发布类似,红黑发布也是通过两个环境完成软件版本的升级,将当前生产流量指向的环境称为红环境,新版本环境称为黑环境。

发布流程:需申请新资源用于部署黑环境,在黑环境部署新版本的服务;黑环境部署完成后,一次性将生产流量指向黑环境;释放红环境的资源。

图片

特点:

  • 升级过程无需停机,用户感知小
  • 短时间内需要使用双倍资源

与蓝绿发布相比,红黑发布充分利用了云计算的弹性伸缩的优势,实现:

  • 简化发布流程
  • 避免在升级的过程中,由于只有一半资源提供服务,而导致的系统过载问题

灰度发布

概念定义:灰度发布属于增量发布,新老版本同时为用户提供服务。灰度发布的主要目的是保证系统的可用性。每一次的线上变更都无法保证系统 100% 的无 bug,所以变更后要在线上小范围验证,等没问题再全面放开。而金丝雀发布是灰度发布的一种实现。

金丝雀发布由来:以前矿工开矿,在下矿洞前需要检查下方是否有毒气,矿工们先会放一只金丝雀进去探是否有毒气体,看金丝雀能否活下来。

发布流程:在现有环境中部署少量服务的新版本(金丝雀),部署完成后,对线上流量进行监测,如果没有问题就对老版本服务进行全量升级。

图片

特点:

  • 用户体验影响小,灰度发布过程出现问题影响范围较小
  • 新版本功能逐步发布,可以逐步评估新版服务性能、稳定性和健康状态
  • 发布自动化程度不够,发布期间可能引发服务中断

滚动发布

在金丝雀发布基础上的进一步优化改进,是一种自动化程度较高的发布方式,用户体验比较平滑,是目前成熟型技术组织所采用的主流发布方式。

图片

滚动更新部署Rolling Update

滚动部署是 Kubernetes 中的默认部署策略。它用新版本的 pod 一个一个地替换我们应用程序的先前版本的 pod,而没有任何集群停机时间。滚动部署缓慢地用新版本应用程序的实例替换之前版本的应用程序实例。

图片

使用 RollingUpdate 策略时,还有两个选项可以让我们微调更新过程:

  1. maxSurge:更新期间可以创建的 pod 数量超过所需的 pod 数量。这可以是副本计数的绝对数量或百分比。默认值为 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  

一旦我们有了deployments模板,我们就可以通过创建服务来提供一种访问部署实例的方法。请注意,我们正在使用版本nanoserver-1709部署映像hello-world。因此,在这种情况下,我们有两个label,name= web-app-rollingupdate-strategyversion= 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”检查是否创建了 Deployment。如果 Deployment 仍在创建中,则输出应类似于以下内容:

$ kubectl get deployments  

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

如果我们几秒钟后再次运行" kubectl get 部署 "。输出应与此类似:

$ kubectl get deployments  

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

要查看 Deployment 创建的 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

要查看deployment的状态,请运行下面的 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来查看Deployment。输出类似于:

$ kubectl get deployments  

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

运行kubectl get rs以查看Deployment是否已更新。新的 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 成功启动。当部署成功时,命令退出并返回代码为零以表示成功。如果部署失败,该命令将以非零代码退出。

$ 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 中部署失败,部署过程会停止,但失败部署中的 pod 会保留下来。在部署失败时,我们的环境可能包含来自旧部署和新部署的 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 应用程序是否准备就绪。如果应用程序不断失败,它可能永远不会对 Kubernetes 做出积极响应。

滚动部署通常会在缩小旧组件之前通过就绪检查等待新 Pod 准备就绪。如果发生重大问题,可以中止滚动部署。如果出现问题,可以中止滚动更新或部署,而无需关闭整个集群。

重新创建部署

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

图片

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

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-recreate-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) 和可用性要求的关键任务应用程序,则选择不同的部署策略将是正确的方法。Recreate 部署一般用于开发者的开发阶段,因为它易于设置,并且应用程序状态会随着新版本完全更新。此外,我们不必并行管理多个应用程序版本,因此我们避免了数据和应用程序的向后兼容性挑战。

蓝绿部署

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

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

这种技术可以消除我们在重新创建部署策略中遇到的停机时间。此外,蓝绿部署降低了风险:如果我们在 Green 上的新版本发生意外,我们可以通过切换回 Blue 立即回滚到上一个版本。我们还可以避免版本问题;整个应用程序状态在一次部署中更改。

图片

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

首先,我们通过将以下 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  

我们现在有以下设置。

图片

对于绿色部署,我们将在_蓝色_部署的同时部署一个新部署。下面的模板是文件的内容: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。因此我们使用两个标签进行了单独部署,名称= green-deployment和 version= nanoserver-1809。

$ kubectl apply -f green.yaml  

为了切换到_绿色_部署,我们将更新现有服务的选择器。编辑 service.yaml 并将选择器版本更改为_2_并将名称更改为green-deployemnt。这将使它与绿色“部署”上的 pod 相匹配。

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  

图片

因此得出结论,我们可以看到蓝绿部署是全有或全无,不像滚动更新部署,我们无法逐步推出新版本。所有用户将同时收到更新,但允许现有会话在旧实例上完成他们的工作。因此,一旦我们启动更改,风险就比一切都应该工作的要高一些。它还需要分配更多的服务器资源,因为我们需要运行每个 Pod 的两个副本。

幸运的是,回滚过程同样简单:我们只需再次拨动开关,先前的版本就被换回原位。那是因为旧版本仍在旧 Pod 上运行。只是流量不再被路由到他们。当我们确信新版本会继续存在时,我们应该停用这些 pod。

金丝雀部署

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

在金丝雀部署中,新版本的应用程序逐渐部署到Kubernetes集群,同时获得极少量的实时流量(即,一部分实时用户正在连接到新版本,而其余的仍在使用以前的版本) .在这种方法中,我们有两个几乎相同的服务器:一个用于所有当前活跃用户,另一个带有新功能,用于向一部分用户推出然后进行比较。当没有错误报告并且信心增加时,新版本可以逐渐推广到基础架构的其余部分。最后,所有实时流量都流向金丝雀,使金丝雀版本成为新的生产版本。

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

图片

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

图片

在规划金丝雀部署时,我们必须考虑各种因素:

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

Canary 用于我们必须在应用程序后端测试新功能的场景。当我们对新版本不是 100% 有信心时,应该使用 Canary 部署;我们预测我们失败的可能性很小。当我们进行重大更新时,通常会使用此策略,例如添加新功能或实验性功能。

部署策略总结

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