
# 使用Service实现简单的灰度发布和蓝绿发布
CCE实现灰度发布通常需要向集群额外部署其他开源工具，例如Nginx Ingress，或将业务部署至服务网格，利用服务网格的能力实现。这些方案均有一些难度，如果您的灰度发布需求比较简单，且不希望引入过多的插件或复杂的用法，则可以参考本文利用Kubernetes原生的特性实现简单的灰度发布和蓝绿发布。
#### 原理介绍
用户通常使用无状态负载 Deployment、有状态负载 StatefulSet等Kubernetes对象来部署业务，每个工作负载管理一组Pod。以Deployment为例，示意图如下：
![](https://support.huaweicloud.com/bestpractice-cce/zh-cn_image_0000001169475948.png)
通常还会为每个工作负载创建对应的Service，Service使用selector来匹配后端Pod，其他服务或者集群外部通过访问Service即可访问到后端Pod提供的服务。如需对外暴露可直接设置Service类型为LoadBalancer，弹性负载均衡ELB将作为流量入口。
- **灰度发布原理**
  以Deployment为例，用户通常会为每个Deployment创建一个Service，但Kubernetes并未限制Service需与Deployment一一对应。Service通过selector匹配后端Pod，若不同Deployment的Pod被同一selector选中，即可实现一个Service对应多个版本Deployment。调整不同版本Deployment的副本数，即可调整不同版本服务的权重，实现灰度发布。示意图如下：
  ![](https://support.huaweicloud.com/bestpractice-cce/zh-cn_image_0000001168997466.png "点击放大")
  
- **蓝绿发布原理**
  以Deployment为例，集群中已部署两个不同版本的Deployment，其Pod拥有共同的label。但有一个label值不同，用于区分不同的版本。Service使用selector选中了其中一个版本的Deployment的Pod，此时通过修改Service的selector中决定服务版本的label的值来改变Service后端对应的Pod，即可实现让服务从一个版本直接切换到另一个版本。示意图如下：
  ![](https://support.huaweicloud.com/bestpractice-cce/zh-cn_image_0000001214635805.png "点击放大")
  
 
#### 前提条件
已上传Nginx镜像至容器镜像服务。为方便观测流量切分效果，Nginx镜像包含v1和v2两个版本，欢迎页分别为"Nginx-v1"和"Nginx-v2"。
#### 资源创建方式
本文提供以下两种方式使用YAML部署Deployment和Service：
- 方式1：在创建无状态工作负载向导页面，单击右侧"YAML创建"，再将本文示例的YAML文件内容输入编辑窗中。
- 方式2：将本文的示例YAML保存为文件，再使用kubectl指定YAML文件进行创建。例如：**kubectl create -f xxx.yaml**。
 
#### 步骤1：部署两个版本的服务
在集群中部署两个版本的Nginx服务，通过ELB对外提供访问。
1. 创建第一个版本的Deployment，本文以nginx-v1为例。YAML示例如下： 
   ```
   apiVersion: apps/v1
   kind: Deployment
   metadata:
     name: nginx-v1
   spec:
     replicas: 2               # Deployment的副本数，即Pod的数量
     selector:                 # Label Selector（标签选择器）
       matchLabels:
         app: nginx
         version: v1
     template:
       metadata:
         labels:               # Pod的标签
           app: nginx
           version: v1
       spec:
         containers:
         - image: {your_repository}/nginx:v1 # 容器使用的镜像为：nginx:v1
           name: container-0
           resources:
             limits:
               cpu: 100m
               memory: 200Mi
             requests:
               cpu: 100m
               memory: 200Mi
         imagePullSecrets:
         - name: default-secret
   ```
   
   
2. 创建第二个版本的Deployment，本文以nginx-v2为例。YAML示例如下： 
   ```
   apiVersion: apps/v1
   kind: Deployment
   metadata:
     name: nginx-v2
   spec:
     replicas: 2               # Deployment的副本数，即Pod的数量
     selector:                 # Label Selector（标签选择器）
       matchLabels:
         app: nginx
         version: v2
     template:
       metadata:
         labels:               # Pod的标签
           app: nginx
           version: v2
       spec:
         containers:
         - image: {your_repository}/nginx:v2   # 容器使用的镜像为：nginx:v2
           name: container-0
           resources:
             limits:
               cpu: 100m
               memory: 200Mi
             requests:
               cpu: 100m
               memory: 200Mi
         imagePullSecrets:
         - name: default-secret
   ```
   您可以登录云容器引擎控制台查看部署情况。
   
   
 
#### 步骤2：实现灰度发布
1. 为部署的Deployment创建LoadBalancer类型的Service对外暴露服务，selector中不指定版本，让Service同时选中两个版本的Deployment的Pod。YAML示例如下： 
   ```
   apiVersion: v1
   kind: Service
   metadata:
     annotations:
       kubernetes.io/elb.id: 586c97da-a47c-467c-a615-bd25a20de39c    # ELB实例的ID，请替换为实际取值
     name: nginx
   spec: 
     ports:
     - name: service0
       port: 80
       protocol: TCP
       targetPort: 80
     selector:             # selector中不包含version信息
       app: nginx
     type: LoadBalancer    # 类型为LoadBalancer
   ```
   
   
2. 执行以下命令，测试访问。 
   **for i in {1..10}; do curl** \<EXTERNAL_IP\>**; done;**
   其中，\<EXTERNAL_IP\>为ELB实例的IP地址。
   返回结果如下，一半为v1版本的响应，一半为v2版本的响应。
   ```
   Nginx-v2
   Nginx-v1
   Nginx-v1
   Nginx-v1
   Nginx-v2
   Nginx-v1
   Nginx-v2
   Nginx-v1
   Nginx-v2
   Nginx-v2
   ```
   
   
3. 通过控制台或kubectl方式调整Deployment的副本数，将v1版本调至4个副本，v2版本调至1个副本。 
   **kubectl scale deployment/nginx-v1 --replicas=4**
   **kubectl scale deployment/nginx-v2 --replicas=1**
   
   
4. 执行以下命令，再次测试访问。 
   **for i in {1..10}; do curl** \<EXTERNAL_IP\>**; done;**
   其中，\<EXTERNAL_IP\>为ELB实例的IP地址。
   返回结果如下，可以看到10次访问中仅2次为v2版本的响应，v1与v2版本的响应比例与其副本数比例一致，为4:1。通过控制不同版本服务的副本数就实现了灰度发布。
   ```
   Nginx-v1
   Nginx-v1
   Nginx-v1
   Nginx-v1
   Nginx-v2
   Nginx-v1
   Nginx-v2
   Nginx-v1
   Nginx-v1
   Nginx-v1
   ```
   ![](https://support.huaweicloud.com/bestpractice-cce/public_sys-resources/note_3.0-zh-cn.png)
   如果10次访问中v1和v2版本比例并非4:1，可以将访问次数调整至更大，比如20。理论上来说，次数越多，v1与v2版本的响应比例将越接近于4:1。
   
   
 
#### 步骤3：实现蓝绿发布
1. 为部署的Deployment创建LoadBalancer类型的Service对外暴露服务，指定使用v1版本的服务。YAML示例如下： 
   ```
   apiVersion: v1
   kind: Service
   metadata:
     annotations:
       kubernetes.io/elb.id: 586c97da-a47c-467c-a615-bd25a20de39c    # ELB实例的ID，请替换为实际取值
     name: nginx
   spec:
     ports:
     - name: service0
       port: 80
       protocol: TCP
       targetPort: 80
     selector:             # selector中指定version为v1
       app: nginx
       version: v1
     type: LoadBalancer    # 类型为LoadBalancer
   ```
   
   
2. 执行以下命令，测试访问。 
   **for i in {1..10}; do curl** \<EXTERNAL_IP\>**; done;**
   其中，\<EXTERNAL_IP\>为ELB实例的IP地址。
   返回结果如下，均为v1版本的响应。
   ```
   Nginx-v1
   Nginx-v1
   Nginx-v1
   Nginx-v1
   Nginx-v1
   Nginx-v1
   Nginx-v1
   Nginx-v1
   Nginx-v1
   Nginx-v1
   ```
   
   
3. 通过控制台或kubectl方式修改Service的selector，使其选中v2版本的服务。 
   **kubectl patch service nginx -p '{"spec":{"selector":{"version":"v2"}}}'**
   
   
4. 执行以下命令，再次测试访问。 
   **for i in {1..10}; do curl** \<EXTERNAL_IP\>**; done;**
   其中，\<EXTERNAL_IP\>为ELB实例的IP地址。
   返回结果如下，均为v2版本的响应，成功实现了蓝绿发布。
   ```
   Nginx-v2
   Nginx-v2
   Nginx-v2
   Nginx-v2
   Nginx-v2
   Nginx-v2
   Nginx-v2
   Nginx-v2
   Nginx-v2
   Nginx-v2
   ```
   
   
 
