Nginx 灰度实现方式(支持纯灰度,纯生产,50度灰及更多比例配置)

前言

Nginx相关技术短信本篇幅不做详细介绍,所以学习本文之前要对Nginx有相关的了解。

生产环境即线上环境,在经历开发、测试再到上线,不可避免的会更新生产环境,但谁又能保证测试过的代码到线上运行就一定不会有问题?

相信大部人都有相似经历,测试环境好好的代码,上了生产却可能发生问题,为何呢?

因为环境不一样,最经常发生的情况可能是:新的迭代中数据库表结构发生了变化、数据初始化不一致、配置文件不一致。

要如何避免这种情况呢,那就引入灰度模式,简单的说来就是模拟线上环境,即准生产环境,配置一致、数据一致,然后再看有没有问题,如果一切正常则上线,此时能kill掉99%可能出现的BUG(另外1%可能是硬件或网络问题,情况极少)。

其实灰度模式不止这个好处,下面简单列举一下:

1、做为内测环境,先对内部开放,功能是否达到预期要求,而不是上线让用户当小白鼠。

2、做为备份环境,因为灰度的数据跟线上是一模一样,旦生产发生故障,可快速切换为灰度,保证有足够的反应时间来修复生产。

3、作为负载均衡环境,当流量高峰时,可适当引流入灰度,以减轻生产压力。

实现步骤

一、配置结构

Nginx的配置文件为:nginx.conf,这是主配置,我们另外新建二个配置文件:

Nginx 灰度实现方式(支持纯灰度,纯生产,50度灰及更多比例配置)

这二个文件其实是对nginx.conf的延伸,在nginx.conf中include进来的,放在与nginx.conf同级的目录下即可。

它们主要定义一些全局变量,就是规则定义,一般情况下用到grey_cond.rules就可以了,如果配合项目管理工具(如jenkins发布),需要手动控制灰度规则,那么grey_cond_set.rules就起作用了。

下面是这二个文件与nginx.conf的关系配置,我直接贴配置代码:

http {

sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;

include /etc/nginx/mime.types;
default_type application/octet-stream;

log_format access '$proxy_add_x_forwarded_for $remote_user [$time_local] "$request" '
'$status $body_bytes_sent $request_time "$http_referer" '
'"$http_user_agent" '
'upsteam: $upstream_addr';
access_log /data/log/nginx/access.log access;
error_log /data/log/nginx/error.log;

gzip on;
gzip_http_version 1.0;
gzip_disable "msie6";

gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;

include /etc/nginx/grey_cond.rules;
include /etc/nginx/grey_cond_set.rules;
include /etc/nginx/sites-enabled/*;
}

其中include /etc/nginx/sites-enabled/*;这是真正的业务规则,后面细说。

注意:这里的顺序要保证灰度规则文件在业务规则之前,而且grey_cond.rules要在grey_cond_set.rules之前(保证手动控制能生效)。

简单的描述为:全局规则--->手动规则--->业务规则。业务规则可覆盖手动规则,手动规则可覆盖全局规则,我们真正要实现的是业务规则,这样的顺序可以带来规则变化上的灵活性。

二、定制灰度规则

无外乎几种情况:

1、根据请求IP制定规则,如内部用户访问灰度,外部用户访问生产

2、根据特殊请求路径制定规则,如一个新功能上线,需要走灰度

3、根据客户端制定规则,如ios还是android

其他更多规则视具体情况而定。以下是本机示例:

root@m1:/etc/nginx# cat grey_cond.rules 
set $grey_cond  0;

if ($http_x_forwarded_for = 115.228.193.20){
        set $grey_cond  1;      
}
if ($http_user_agent ~ "AppVersion/5.0"){
        set $grey_cond  1;       
}

if ($http_x_forwarded_for = 124.95.61.78){
        set $grey_cond  1;       
}
if ($http_x_forwarded_for = 134.188.193.83){
        set $grey_cond  1;
        set $sns_grey_cond  1;
        set $rocket_grey_cond  1;
}

if ($http_x_forwarded_for = 221.76.138.142){
        set $grey_cond  1;
        set $sns_grey_cond  1;
        set $rocket_grey_cond  1;
}

if ($http_user_agent ~ "Android"){
   set $grey_cond  1;  
}
....

上面示例代码中给了部分规则,如按IP地址(一般是公司内部的公网IP地址,如有多个配置多行即可)。

三、定制动控制规则

grey_cond_set.rules初始化可以是空,你可以把它理解成为一个临时性的文件,在需要时往里面写入一段nginx文本。

根据上段介绍,这个文件中的变量是可以向上覆盖的,你可以手动编辑这个文件,或者通过jenkins执行命令写入,然后然后配合nginx reload来做文章。

  本机示例:

 1、初始状态 (可空)

root@m1:/etc/nginx# cat grey_cond_set.rules 
#Default beta testing policy.

2、通过jenkins写入

Nginx 灰度实现方式(支持纯灰度,纯生产,50度灰及更多比例配置)

         这个jenkins配置中,有一行脚本变量 为:-PpolicyFile=grey_cond_set.rules,正是我们说的那个临时文件,下面还有一行变量是要写入这个临时文件中的:-PpolicyString="set $grey_cond 1;",

         如果对jenkins不熟悉,可适当去了解一下,本文不做详情介绍。

         那么这个执行是怎么进行的呢,就是通过/var/lib/jenkins/buildscripts/build.gradle这个构建脚本来执行其中的deployPolicy方法,下面直接贴示例代码:

        

         

[root@jenkins buildscripts]# cat build.gradle 
buildscript {
  repositories {
    jcenter()
  }
  dependencies {
    classpath 'org.hidetake:gradle-ssh-plugin:1.1.4'
  }
}

apply plugin: 'org.hidetake.ssh'

ssh.settings {
  logging = 'stdout'
}

remotes {
  javaServer {
    host = remoteHost
    user = remoteUser
        password = remotePassword
        knownHosts = allowAnyHosts
  }
  
  nginxServer {
    host = remoteHost
    user = remoteUser
        password = remotePassword
        knownHosts = allowAnyHosts
  }
  
  nodeJsServer {
        host = remoteHost
        user = remoteUser
        password = remotePassword
        knownHosts = allowAnyHosts
  }
}

.....

task deployPolicy {
    def policyFile = project.properties['policyFile']
        doLast{
          ssh.run {
                session(remotes.nginxServer) {
                    if(project.hasProperty('policyString')) {
                                println "policy applied: '$policyString'"
                                executeSudo("echo '$policyString' > /etc/nginx/$policyFile", pty: true)
                                executeSudo("service nginx reload", pty: true)
                        }
                }
        }
  }
}

.....

这个脚本中标红的部分为核心,policyFile与policyString就是在jenkins中传进来的,下面二行executeSudo就是执行linux下的二个命令,一个是写入我们上面说到的临时文件,一个是重新加载nginx配置。

四、常用规则

10%的灰度:
-PpolicyString="if ($http_x_forwarded_for ~ 0$){set $grey_cond 1;}"   
50度灰:
-PpolicyString="if ($http_x_forwarded_for ~ [0,1,2,3,4]$){set $grey_cond 1;}"   //这是一个正则匹配,ip以0,1,2,3,4结尾的就跳灰度
纯灰:
-PpolicyString="set $grey_cond 1;"
纯生产:
-PpolicyString="#Defaultbeta testing policy." //可以是空,也可以是一个注释性文本

  

更多待续...