2016 年 9 月 18 日
DevOps与阿里云容器服务(四)- 复杂拓扑应用的蓝绿发布
前言
在上一篇文章中,我们演示了如何使用蓝绿发布来实现热部署,但是在实际生产的场景中,应用的拓扑结构会复杂很多。在本篇文章中我们将会讨论下复杂应用拓扑中的蓝绿发布方案以及蓝绿发布适用的场景。
场景分析
大于大多数场景而言,对客户提供服务的软件的形态有三种。一种是前端类服务,用户可以直接或者间接通过网页、接口调用使用该服务提供的能力;一种是后端类服务,用户无法直接使用该服务提供的功能,该服务主要的使用者是其他服务,并通过其他服务最终将处理后的结果反馈给用户;第三种是调度任务类服务,即不被用户使用也不被其他服务调用,它的生命周期只存在在一个任务的执行生命周期中,通常任务的执行周期完毕,服务的生命周期就停止,通常为无状态资源密集性服务。
对于上述三种场景,以路由权重切换为主要实现方式的发布策略例如蓝绿发布、A/BTest等通常情况下比较适用于前端类服务与后端类服务。下面我们用一个简单的例子来拆解下如何使用阿里云容器服务来实现这两类服务的蓝绿发布。
在上一篇发布策略的文章中,有的开发者会问我的应用的拓扑关系不是单体的,不能通过单一容器级别的路由权重切换解决。在此要明确的一件事情,蓝绿发布是一种发布策略,部署的最小维度是容器,而发布的最小维度是应用。蓝绿发布的原理是老的应用的版本不变,新的应用版本进行部署,如果新版本与老版本之间应用的名字以及相关的配置没有改变,那么会认为这个应用是新老版本中共用的,无需变更;需要进行变更的应用通过名字的不同进行区分。简单的来讲,蓝绿发布是一个应用级别的更新操作,你可以对一个服务进行两个版本之间的切换,服务是一个逻辑的概念,而不是容器这样一个实体的概念,蓝绿发布可以做复杂拓扑的应用更新操作。
DEMO
下面我们通过一个拓扑结构复杂一点的例子来讲述蓝绿发布。应用的拓扑结构如下:
serviceB会调用serviceA,这两个都是python构建的,代码如下:
serviceA,通过接口返回数据 “world!”
from flask import Flask
app = Flask(__name__)
@app.route(\'/\')
def world():
return \'world!\'
if __name__ == \'__main__\':
app.run(port=5000,debug=True,host=\'0.0.0.0\')
serviceB,调用serviceA的接口,并将返回的数据字段前面添加 “Hello ”
from flask import Flask
import requests
import os
dep_endpoint = os.getenv(\'dep_endpoint\');
app = Flask(__name__)
@app.route(\'/\')
def hello():
msg = requests.get(dep_endpoint);
return \'Hello \' msg.text;
if __name__ == \'__main__\':
app.run(port=5001,debug=True,host=\'0.0.0.0\')
传统应用之间的调用方式可以是通过配置IP地址或者域名来调用,也可以通过服务注册中心中的地址的方式调用,但是对于一个无状态的多实例的服务,常见的做法是使用客户端的负载均衡器或者服务器的服务均衡器端点来实现。在容器服务中使用的方式是使用服务器端的负载均衡端点的方式,提供内部调用的路由端点,来实现后端服务的负载均衡。大致的调用方式如下。
serviceA-v1:
image: \'registry-internal.cn-hangzhou.aliyuncs.com/ringtail/demo-service-a:1.0\'
labels:
aliyun.scale: 3
aliyun.routing.port_5000: servicea.local
serviceB-v1:
image: \'registry-internal.cn-hangzhou.aliyuncs.com/ringtail/demo-service-b:1.0\'
environment:
- \'dep_endpoint=http://servicea.local\'
labels:
aliyun.scale: 2
aliyun.routing.port_5001: serviceb
external_links:
- "servicea.local"
serviceB通过external_links的方式将serviceA的内部路由端点引入,在环境变量中将dep_endpoint=http://servicea.local,使用servicea.local作为调用的地址。
访问serviceB的对外访问地址,可以得到:
我们最开始基本结构的应用就已经部署完毕了,下面开始进行不同服务的蓝绿发布。
前端服务的蓝绿发布
首先我们先看如果做前端服务的蓝绿发布,也就是说要对serviceB进行蓝绿发布的流程。大致的结构图如下。
在这个应用中前端服务的蓝绿发布,也就是对serviceB进行蓝绿发布,下面我们修改serviceB的代码
from flask import Flask
import requests
import os
dep_endpoint = os.getenv(\'dep_endpoint\');
app = Flask(__name__)
@app.route(\'/\')
def hello():
msg = requests.get(dep_endpoint);
return \'Welcome to the \' msg.text; //修改"Hello " => "Welcome to the "
if __name__ == \'__main__\':
app.run(port=5001,debug=True,host=\'0.0.0.0\')
修改编排模板,进行蓝绿发布
serviceA-v1:
image: \'registry-internal.cn-hangzhou.aliyuncs.com/ringtail/demo-service-a:1.0\'
labels:
aliyun.scale: 3
aliyun.routing.port_5000: servicea.local
serviceB-v2:
image: \'registry-internal.cn-hangzhou.aliyuncs.com/ringtail/demo-service-b:2.0\'
environment:
- \'dep_endpoint=http://servicea.local\'
labels:
aliyun.scale: 2
aliyun.routing.port_5001: serviceb
external_links:
- "servicea.local"
进行蓝绿发布更新后,可以看到更新后的服务列表,其中黄色的serviceA-v1表示当前的应用在蓝绿发布的过程中不会产生变化,serviceB-v1为老版本,serviceB-v2为新版本
通过路由列表选择相应的权重管理,进行权重的调整,将serviceA-v1的权重调整为0,将serviceA-v2的权重调整为100,此时访问serviceB的网址可以发现。
验证完毕,点击确认发布完成,完成蓝绿发布。
后端服务的蓝绿发布
我们再看下如何做后端服务的蓝绿发布,也就是说对serviceA进行蓝绿发布。大致的结构图如下。
修改编排模板
serviceA-v2:
image: \'registry-internal.cn-hangzhou.aliyuncs.com/ringtail/demo-service-a:2.0\'
labels:
aliyun.scale: 3
aliyun.routing.coexist: true
aliyun.routing.port_5000: servicea.local
serviceB-v2:
image: \'registry-internal.cn-hangzhou.aliyuncs.com/ringtail/demo-service-b:2.0\'
environment:
- \'dep_endpoint=http://servicea.local\'
labels:
aliyun.scale: 2
aliyun.routing.coexist: true
aliyun.routing.port_5001: serviceb
external_links:
- "servicea.local"
部署后的服务列表:
此时可以发现serviceB-v2在本次发布中不会进行变更,调整服务的权重
此时再访问serviceB的地址,可以得到如下的结果