欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-05-12 22:28:43
...

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

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


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 跨节点网络通信

深入探究 K8S ConfigMap 和 Secret

Kubernetes入门培训(内含PPT)


原创不易,随手关注或者”在看“,诚挚感谢!