不想凌晨上线的你,不考虑徒手撸一个灰度发布系统?

2020-05-12 14:41:13 浏览数 (1)

现在的你,每天还是等到凌晨上线吗?反正最近的我不在凌晨上线,我也不区分业务的低谷和高峰,一律直接上线,我靠的不是运气,也不是胆量,而是有一套成熟的机制再给我们做后盾,看到这里,你可能认为我在吹牛皮,没事,多点耐心,且继续阅读下去。


1、安装 openresty,如下所示:

代码语言:javascript复制
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

代码语言:javascript复制
/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 是老服务。

代码语言:javascript复制
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 等数据库相结合实现更完善的业务流程。

代码语言:javascript复制
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 奇偶性来路由到不同的服务,稳定运行一段时间后,所有用户都切换到最新环境。但是它们内部实现机制有一定的区别,比如说影子部署就是一种使用完全独立的环境,通过流量复制、对比和回放验证服务的正确性;但是这种方案需要一套冗余的资源,而且服务依赖越多越复杂。但是灰度发布可以变得简单很多,只要把服务部署两份,至于数据库以及依赖服务,自己在设计之初就要考虑前后兼容性。所以说,至于选择何种方式,具体要跟自己业务使用场景相结合。

0 人点赞