通过阿里云容器服务K8S Ingress Controller实现应用服务的灰度发布

本文涉及的产品
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
容器镜像服务 ACR,镜像仓库100个 不限时长
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
简介: 日常工作中我们经常需要对服务进行版本更新升级,为此我们经常使用到的发布方式有滚动升级、分批暂停发布、蓝绿发布以及灰度发布,今天主要跟大家分享下在阿里云容器服务Kubernetes集群中如何通过Ingress Controller来实现应用服务的灰度发布及AB测试。

简介

日常工作中我们经常需要对服务进行版本更新升级,为此我们经常使用到的发布方式有滚动升级、分批暂停发布、蓝绿发布以及灰度发布,今天主要跟大家分享下在阿里云容器服务Kubernetes集群中如何通过Ingress Controller来实现应用服务的灰度发布及AB测试。

发布场景

场景一

假设当前线上环境我们已经有一套服务Service A对外提供7层服务,此时我们新开发了一些新的特性,需要灰度发布上线一个新的版本Service A',但是我们暂时又不希望简单地直接替换掉Service A服务,而是希望将请求头中包含foo=bar或者cookie中包含foo=bar的客户端请求转发到Service A'服务中,待运行一段时间稳定,将所有的流量切换到Service A'服务中后,再平滑地下线掉Service A服务:

image

场景二

假设当前线上环境我们已经有一套服务Service B对外提供7层服务,此时我们修复了一些问题,需要灰度发布上线一个新的版本Service B',但是我们又不希望简单直接地将所有客户端流量切换到新版本Service B'中,而是希望仅仅切换20%的流量到新版本Service B'中,待运行一段时间稳定,将所有的流量切换到Service B'服务中后,再平滑地下线掉Service B服务:

image

针对以上多种不同的应用发布需求,阿里云容器服务K8S Ingress Controller支持了多种流量切分方式:

  1. 基于Request Header的流量切分,适用于灰度发布以及AB测试场景
  2. 基于Cookie的流量切分,适用于灰度发布以及AB测试场景
  3. 基于Query Param的流量切分,适用于灰度发布以及AB测试场景
  4. 基于服务权重的流量切分,适用于蓝绿发布场景

注解说明

阿里云容器服务K8S Ingress Controller通过下列Annotation来支持应用服务的灰度发布机制:

1. nginx.ingress.kubernetes.io/service-match

该注解用来配置新版本服务的路由匹配规则,格式如下:

nginx.ingress.kubernetes.io/service-match: | 
    <service-name>: <match-rule>
# 参数说明:
# service-name:服务名称,满足match-rule的请求会被路由到该服务中
# match-rule:路由匹配规则
#
# 路由匹配规则:
# 1. 支持的匹配类型
# - header:基于请求头,支持正则匹配和完整匹配
# - cookie:基于cookie,支持正则匹配和完整匹配
# - query:基于请求参数,支持正则匹配和完整匹配
#
# 2. 匹配方式
# - 正则匹配格式:/{regular expression}/,//表明采用正则方式匹配
# - 完整匹配格式:"{exact expression}",""表明采用完整方式匹配

路由匹配规则配置示例:

# 请求头中满足foo正则匹配^bar$的请求被转发到新版本服务new-nginx中
new-nginx: header("foo", /^bar$/)

# 请求头中满足foo完整匹配bar的请求被转发到新版本服务new-nginx中
new-nginx: header("foo", "bar")

# cookie中满足foo正则匹配^sticky-.+$的请求被转发到新版本服务new-nginx中
new-nginx: cookie("foo", /^sticky-.+$/)

# query param中满足foo完整匹配bar的请求被转发到新版本服务new-nginx中
new-nginx: query("foo", "bar")
2. nginx.ingress.kubernetes.io/service-weight

该注解用来配置新旧版本服务的流量权重,格式如下:

nginx.ingress.kubernetes.io/service-weight: | 
    <new-svc-name>:<new-svc-weight>, <old-svc-name>:<old-svc-weight>
参数说明:
new-svc-name:新版本服务名称
new-svc-weight:新版本服务权重
old-svc-name:旧版本服务名称
old-svc-weight:旧版本服务权重

服务权重配置示例:

nginx.ingress.kubernetes.io/service-weight: | 
    new-nginx: 20, old-nginx: 60

注意

  1. 服务权重采用相对值计算方式。如示例中的服务权重设置,new-nginx服务的权重百分比为25%,old-nginx服务的权重百分比为75%。
  2. 一个服务组(同一个ingress yaml中具有相同Host和Path的服务)中未明确设置权重的服务默认权重值为100。

版本更新

目前阿里云容器服务K8S Ingress Controller需要0.12.0-5及其以上版本才支持流量切分特性,可以通过下面命令来查看Ingress Controller的当前版本号:

  1. 采用Deployment部署的情况
 kubectl -n kube-system get deploy nginx-ingress-controller -o yaml | grep -v 'apiVersion' | grep 'aliyun-ingress-controller'
  1. 采用DaemonSet部署的情况
 kubectl -n kube-system get ds nginx-ingress-controller -o yaml | grep -v 'apiVersion' | grep 'aliyun-ingress-controller'

若您的集群内Ingress Controller当前版本号小于0.12.0-5,那么您可以通过下面方式来升级Ingress Controller到新版本:

  1. 采用Deployment部署的情况
 kubectl -n kube-system set image deploy/nginx-ingress-controller nginx-ingress-controller=registry.cn-hangzhou.aliyuncs.com/acs/aliyun-ingress-controller:0.12.0-5
  1. 采用DaemonSet部署的情况
 kubectl -n kube-system set image ds/nginx-ingress-controller nginx-ingress-controller=registry.cn-hangzhou.aliyuncs.com/acs/aliyun-ingress-controller:0.12.0-5

至此,您集群内的Ingress Controller已经支持灰度发布功能。

部署服务

在应用灰度发布前,我们必须事先已经存在一套现有的应用对外提供服务。这里我们部署一套nginx服务并通过Ingress Controller对外提供7层域名访问:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: old-nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      run: old-nginx
  template:
    metadata:
      labels:
        run: old-nginx
    spec:
      containers:
      - image: registry.cn-hangzhou.aliyuncs.com/xianlu/old-nginx
        imagePullPolicy: Always
        name: old-nginx
        ports:
        - containerPort: 80
          protocol: TCP
      restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
  name: old-nginx
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    run: old-nginx
  sessionAffinity: None
  type: NodePort

部署ingress资源:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: gray-release
spec:
  rules:
  - host: www.example.com
    http:
      paths:
      # 老版本服务
      - path: /
        backend:
          serviceName: old-nginx
          servicePort: 80

部署并测试访问情况:

 kubectl get ing
NAME           HOSTS             ADDRESS        PORTS     AGE
gray-release   www.example.com   47.107.20.35   80        1m
 curl -H "Host: www.example.com"  http://47.107.20.35
old
 curl -H "Host: www.example.com"  http://47.107.20.35
old
 curl -H "Host: www.example.com"  http://47.107.20.35
old

灰度发布新版本服务

此时我们需要发布一个新版本的nginx服务并配置仅允许部分客户端才能访问到这个新版本服务:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: new-nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      run: new-nginx
  template:
    metadata:
      labels:
        run: new-nginx
    spec:
      containers:
      - image: registry.cn-hangzhou.aliyuncs.com/xianlu/new-nginx
        imagePullPolicy: Always
        name: new-nginx
        ports:
        - containerPort: 80
          protocol: TCP
      restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
  name: new-nginx
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    run: new-nginx
  sessionAffinity: None
  type: NodePort
场景一:设置满足特定规则的客户端才能访问新版本服务

假若我们希望请求头中满足foo=bar的客户端请求才能路由到新版本服务中,那么我们可以如下修改配置ingress规则:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: gray-release
  annotations:
    # 请求头中满足正则匹配foo=bar的请求才会被路由到新版本服务new-nginx中
    nginx.ingress.kubernetes.io/service-match: | 
      new-nginx: header("foo", /^bar$/)
spec:
  rules:
  - host: www.example.com
    http:
      paths:
      # 老版本服务
      - path: /
        backend:
          serviceName: old-nginx
          servicePort: 80
      # 新版本服务
      - path: /
        backend:
          serviceName: new-nginx
          servicePort: 80

测试客户端请求:

 curl -H "Host: www.example.com"  http://47.107.20.35
old
 curl -H "Host: www.example.com"  http://47.107.20.35
old
 curl -H "Host: www.example.com"  http://47.107.20.35
old
 curl -H "Host: www.example.com" -H "foo: bar" http://47.107.20.35
new
 curl -H "Host: www.example.com" -H "foo: bar" http://47.107.20.35
new
 curl -H "Host: www.example.com" -H "foo: bar" http://47.107.20.35
new
场景二:在满足特定规则的基础上设置一定比例的请求被路由到新版本服务中

假若我们希望请求头中满足foo=bar的客户端请求仅允许50%的流量被路由到新版本服务中,那么我们可以如下修改配置ingress规则:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: gray-release
  annotations:
    # 请求头中满足正则匹配foo=bar的请求才会被路由到新版本服务new-nginx中
    nginx.ingress.kubernetes.io/service-match: |
        new-nginx: header("foo", /^bar$/)
    # 在满足上述匹配规则的基础上仅允许50%的流量会被路由到新版本服务new-nginx中
    nginx.ingress.kubernetes.io/service-weight: |
        new-nginx: 50, old-nginx: 50
spec:
  rules:
  - host: www.example.com
    http:
      paths:
      # 老版本服务
      - path: /
        backend:
          serviceName: old-nginx
          servicePort: 80
      # 新版本服务
      - path: /
        backend:
          serviceName: new-nginx
          servicePort: 80

测试客户端请求:

 curl -H "Host: www.example.com" http://47.107.20.35
old
 curl -H "Host: www.example.com" http://47.107.20.35
old
 curl -H "Host: www.example.com" http://47.107.20.35
old
 curl -H "Host: www.example.com" -H "foo: bar" http://47.107.20.35
new
 curl -H "Host: www.example.com" -H "foo: bar" http://47.107.20.35
old
 curl -H "Host: www.example.com" -H "foo: bar" http://47.107.20.35
old
 curl -H "Host: www.example.com" -H "foo: bar" http://47.107.20.35
new
场景三:仅设置一定比例的请求被路由到新版本服务中

假若我们仅仅希望50%的流量被路由到新版本服务中,那么我们可以如下修改配置ingress规则:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: gray-release
  annotations:
      # 允许50%的流量被路由到新版本服务new-nginx中
      nginx.ingress.kubernetes.io/service-weight: |
          new-nginx: 50, old-nginx: 50
spec:
  rules:
  - host: www.example.com
    http:
      paths:
      # 老版本服务
      - path: /
        backend:
          serviceName: old-nginx
          servicePort: 80
      # 新版本服务
      - path: /
        backend:
          serviceName: new-nginx
          servicePort: 80

测试客户端访问:

 curl -H "Host: www.example.com" http://47.107.20.35
new
 curl -H "Host: www.example.com" http://47.107.20.35
new
 curl -H "Host: www.example.com" http://47.107.20.35
new
 curl -H "Host: www.example.com" http://47.107.20.35
old
 curl -H "Host: www.example.com" http://47.107.20.35
old
 curl -H "Host: www.example.com" http://47.107.20.35
new
 curl -H "Host: www.example.com" http://47.107.20.35
old

特别说明:当我们既没有设置service-match也没有设置service-weight注解时,Ingress Controller在转发请求时默认会将请求均衡地随机转发到新老版本服务中。

灰度发布完成

系统运行一段时间后,当新版本服务已经稳定并且符合预期后,我们需要将老版本的服务下线掉,仅仅保留新版本服务在线上运行,此时我们需要修改ingress规则删除相关的annotation以及老版本服务,最终规则如下:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: gray-release
spec:
  rules:
  - host: www.example.com
    http:
      paths:
      # 新版本服务
      - path: /
        backend:
          serviceName: new-nginx
          servicePort: 80

测试客户端访问:

 curl -H "Host: www.example.com" http://47.107.20.35
new
 curl -H "Host: www.example.com" http://47.107.20.35
new
 curl -H "Host: www.example.com" http://47.107.20.35
new

可以看到,现在的请求是全部被路由到了新版本的服务中,这就完成了灰度发布的整个周期。最后,您可以删除老版本的service和deployment。

相关实践学习
巧用云服务器ECS制作节日贺卡
本场景带您体验如何在一台CentOS 7操作系统的ECS实例上,通过搭建web服务器,上传源码到web容器,制作节日贺卡网页。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
目录
相关文章
|
22天前
|
Kubernetes 应用服务中间件 nginx
Kubernetes 入门指南:快速上手容器编排
【8月更文第29天】Kubernetes(简称 K8s)是一个开源平台,用于自动化容器化应用程序的部署、扩展和管理。它提供了一种便捷的方式来部署和运行应用程序,而无需关心底层基础设施的细节。本指南将带你从零开始学习 Kubernetes 的基础知识,并帮助你部署第一个应用。
112 0
|
9天前
|
人工智能 Kubernetes Cloud Native
阿里云容器服务,全面助力云上体育盛会
本文讲述了阿里云容器服务,通过安全稳定的产品能力和成熟的稳定性保障体系,全面助力云上体育赛场,促进科技之光与五环之光交相辉映。
阿里云容器服务,全面助力云上体育盛会
|
13天前
|
人工智能 Prometheus 监控
使用 NVIDIA NIM 在阿里云容器服务(ACK)中加速 LLM 推理
本文介绍了在阿里云容器服务 ACK 上部署 NVIDIA NIM,结合云原生 AI 套件和 KServe 快速构建高性能模型推理服务的方法。通过阿里云 Prometheus 和 Grafana 实现实时监控,并基于排队请求数配置弹性扩缩容策略,提升服务稳定性和效率。文章提供了详细的部署步骤和示例,帮助读者快速搭建和优化模型推理服务。
73 7
使用 NVIDIA NIM 在阿里云容器服务(ACK)中加速 LLM 推理
|
6天前
|
存储 Kubernetes 持续交付
深入浅出 Kubernetes:掌握容器编排的艺术
Kubernetes作为容器编排领域的领头羊,提供了运行分布式系统的强大框架,支持自动化部署、扩展和管理容器化应用。本文深入浅出地介绍了Kubernetes的核心概念与关键组件,包括服务发现、存储编排及自动部署等特性。通过Minikube、kubeadm及云服务商等多种方式部署集群,并使用`kubectl`、YAML配置文件和Helm进行资源管理。掌握Kubernetes将成为软件开发者的宝贵技能。
|
20天前
|
存储 Kubernetes Cloud Native
探索Python编程的奥秘云原生时代的容器编排:Kubernetes入门与实践
【8月更文挑战第30天】本文以浅显易懂的方式,探讨了Python编程的核心概念和技巧。从基础语法到高级特性,再到实际应用案例,逐步引导读者深入理解Python编程的精髓。通过本文的学习,读者将能够掌握Python编程的基本技能,并激发进一步探索的兴趣。
29 13
|
20天前
|
Kubernetes Cloud Native Docker
云原生入门:从容器到Kubernetes的旅程
【8月更文挑战第31天】云原生技术正改变着应用的开发、部署和运维方式。本文将带你走进云原生的世界,从容器的基础开始,探索Docker和Kubernetes如何助力现代软件开发与运维。你将学会如何使用Docker创建和管理容器,以及如何通过Kubernetes进行集群管理,实现服务的自动化部署、扩展和管理。准备好让你的应用在云端自由翱翔了吗?让我们启航!
|
21天前
|
Kubernetes Shell 容器
k8s临时容器
k8s临时容器
|
19天前
|
Kubernetes Cloud Native 应用服务中间件
云原生入门:Kubernetes 和容器化技术的实践之旅
【8月更文挑战第31天】 在这篇文章中,我们将踏上一场探索云原生世界的旅程。我们将从基础的容器化技术讲起,逐步深入到Kubernetes这个强大的容器编排工具。文章会通过一个实际的例子,带领大家了解如何将一个简单的应用容器化并在Kubernetes集群上运行起来。无论你是云原生领域的新手,还是希望巩固知识的开发者,这篇文章都会为你提供一次实操的机会,让你对云原生有一个更加直观的认识。
|
21天前
|
运维 Kubernetes Cloud Native
云原生技术入门:从容器到Kubernetes的探索之旅
【8月更文挑战第30天】在数字时代的浪潮中,云计算已成为推动创新的重要力量。本文旨在通过浅显易懂的语言,为初学者揭开云原生技术的神秘面纱,从容器化技术的基础出发,逐步深入到Kubernetes集群管理的实际应用。我们将一起见证代码如何转化为可在云端无缝运行的服务,体验技术变革带来的无限可能。
|
21天前
|
Kubernetes Cloud Native 网络安全
云原生入门指南:Kubernetes和容器化技术云计算与网络安全:技术融合的新篇章
【8月更文挑战第30天】在云计算的浪潮中,云原生技术如Kubernetes已成为现代软件部署的核心。本文将引导读者理解云原生的基本概念,探索Kubernetes如何管理容器化应用,并展示如何通过实践加深理解。

相关产品

  • 容器计算服务
  • 容器服务Kubernetes版