不想凌晨上线的你,不考虑徒手撸一个灰度发布系统?
现在的你,每天还是等到凌晨上线吗?反正最近的我不在凌晨上线,我也不区分业务的低谷和高峰,一律直接上线,我靠的不是运气,也不是胆量,而是有一套成熟的机制再给我们做后盾,看到这里,你可能认为我在吹牛皮,没事,多点耐心,且继续阅读下去。
1、安装 openresty,如下所示:
centos:
yum -y install readline-devel pcre-devel openssl-devel
ubuntu:
apt-get install libreadline-dev libpcre3-dev libssl-dev perl
http://openresty.org/cn/download.html # 下载
tar xzvf openresty-1.15.8.3.tar.gz # 解压
cd openresty-1.15.8.3/
./configure --prefix=/home/nginx/openresty --with-stream
make
make install
2、启动 nginx
/home/nginx/openresty/nginx/sbin/nginx
3、编写 nginx lua 负载均衡模块
在没有编写 lua 模块之前,且先看下图,如下图所示,是大部分 web 项目的部署方式,静态页面放在 nginx 中,后端服务放在 web 动态容器中。nginx 在其过程中对内承担动静分离和反向代理的角色,对外可以做负载均衡,出现压力可以横向扩容,通过 这种方式可以满足绝大多数 web 项目使用场景。
这种部署方式本身没有任何问题,但是面对互联网高速发展的今天,用户越来越挑剔,任何系统讲究 [高速度、低故障],这个高速度不仅说的是产品更新迭代速度快,更是部署速度快,每完成一部分功能,都能快速上线,提升用户体验。其实从某种程度上来说,这并不是什么坏事,其一,经常发布新功能,可以快速抓住用户,降低成本;其二,发布速度快,说明更新功能少,功能少了,自然出现的问题也就少,故障自然降低。
但为了保证发布速度快,不分时段的上线部署,必然需要一套成熟的发布机制做保证,如下图所示:
在了解了上述的灰度发布机制之后,你会看到有一个新版本和老版本,一套服务变成了两套,这时如果你的服务已经使用 nginx 部署,那么可以直接在该 nginx 进行改造,如果你的服务没有使用 nginx,你可以按照上述介绍的方式进行安装部署 nginx。nginx 作为 LB 对外提供服务。如下图所示:
看到这里,你大概已经明白,所谓的灰度发布,就是部署两套服务,最新版本的服务在没有测试通过之前,不对外开放,用户仍然访问已经通过测试的老版本服务,自然也不会出现什么问题。那么现在最简单的一步来了,我如何控制不同的用户路由到不同的服务呢?其实方式有很多种,最简单的我们可以通过nginx 的 if、map 指令获取特定字段信息,不同字段信息路由到不同底层服务,但是 nginx 语法不够灵活,不过 nginx 有 lua 扩展模块做补充,绝大数 nginx 无法实现的功能,都可以考虑通过 lua 模块来实现,下面就简单介绍下如何通过 nginx lua 模块把包含不同头信息的 url 请求路由到不同的服务。
如下代码所示,启动了两个 nginx server 块,第一个wap-web-new 是新服务,wap-web-old 是老服务。
upstream wap-web-new{
server 171.2.10.3:8080 max_fails=5;
}
upstream wap-web-old{
server 171.2.10.4:8080 max_fails=5;
}
server {
listen 80;
server_name localhost;
# ssl on;
ssl_certificate /home/nginx/ssl/prd.crt;
ssl_certificate_key /home/nginx/ssl/prd.key;
ssl_session_timeout 5m;
ssl_protocols SSLv2 SSLv3 TLSv1;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
#charset koi8-r;
underscores_in_headers on;
access_log logs/host.access.log main;
error_log logs/nginx.error.log error;
lua_need_request_body on;
location / {
root html;
index index.html index.htm;
}
location /wap-web {
proxy_pass https://wap-web-new;
}
}
server {
listen 81;
server_name localhost;
# ssl on;
ssl_certificate /home/nginx/ssl/prd.crt;
ssl_certificate_key /home/nginx/ssl/prd.key;
ssl_session_timeout 5m;
ssl_protocols SSLv2 SSLv3 TLSv1;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
#charset koi8-r;
underscores_in_headers on;
access_log logs/host.access.log main;
error_log logs/nginx.error.log error;
lua_need_request_body on;
location / {
root html;
index index.html index.htm;
}
location /wap-web {
proxy_pass https://wap-web-old;
}
}
如下代码所示是最外层 nginx server,其中 backend upstream 块中的意思是说,默认情况下,所有的 url 请求路由到 81 端口,也就是老服务,只有匹配到特定标识的 url 请求路由到新服务,通过此段代码解决了灰度发布的问题。当然在此过程中,可以使用一个 nginx 或者多个 nginx,其不影响其实现过程,另外如果你要做的更完善,比如通过 UI 界面进行控制整个发布过程,更需要借助 lua 和 MySQL 等数据库相结合实现更完善的业务流程。
upstream backend{
server 0.0.0.0;
balancer_by_lua_block {
local balancer = require "ngx.balancer"
local backend = 81
local val = ngx.req.get_headers()["id"]
local str = "1111,2222,33333,44444"
if val ~= nil then
if string.find(str, match) then
backend = 80
end
end
local ok, err = balancer.set_current_peer("127.0.0.1", backend)
}
}
server {
listen 443;
server_name localhost;
ssl on;
ssl_certificate /home/nginx/ssl/prd.crt;
ssl_certificate_key /home/nginx/ssl/prd.key;
ssl_session_timeout 5m;
ssl_protocols SSLv2 SSLv3 TLSv1;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
#charset koi8-r;
underscores_in_headers on;
access_log logs/host.access.log main;
error_log logs/nginx.error.log error;
lua_need_request_body on;
location / {
root html;
index index.html index.htm;
}
location /wap-web {
proxy_pass https://backend;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
4、总结
通过此文,让你浅显易懂的了解了灰度发布的机制和过程。但是我仍然承认我是标题党,在你看的过程中可能也会产生很多疑问,只是把服务部署了两份,我不仅仅有服务,还有数据库 MySQL、甚至 habse .....这些我也需要考虑进去的.......
在此之前,你应该听说过蓝绿部署、影子部署、金丝雀部署/灰度发布,这几种部署方式目的都是为了降低故障的波及范围,在线上用户在没有接入进来之前,专业测试人员根据特定标识先进行测试,测试完成后,运营人员根据自己定义的维度进行切分用户接入新发布功能,比如根据用户所在的区域、用户的年龄、甚至通过用户 id 奇偶性来路由到不同的服务,稳定运行一段时间后,所有用户都切换到最新环境。但是它们内部实现机制有一定的区别,比如说影子部署就是一种使用完全独立的环境,通过流量复制、对比和回放验证服务的正确性;但是这种方案需要一套冗余的资源,而且服务依赖越多越复杂。但是灰度发布可以变得简单很多,只要把服务部署两份,至于数据库以及依赖服务,自己在设计之初就要考虑前后兼容性。所以说,至于选择何种方式,具体要跟自己业务使用场景相结合。
推荐阅读:
docker bridge 到 k8s pod 跨节点网络通信
原创不易,随手关注或者”在看“,诚挚感谢!