Nginx 灰度实现方式(支持纯灰度,纯生产,50度灰及更多比例配置)
前言
Nginx相关技术短信本篇幅不做详细介绍,所以学习本文之前要对Nginx有相关的了解。
生产环境即线上环境,在经历开发、测试再到上线,不可避免的会更新生产环境,但谁又能保证测试过的代码到线上运行就一定不会有问题?
相信大部人都有相似经历,测试环境好好的代码,上了生产却可能发生问题,为何呢?
因为环境不一样,最经常发生的情况可能是:新的迭代中数据库表结构发生了变化、数据初始化不一致、配置文件不一致。
要如何避免这种情况呢,那就引入灰度模式,简单的说来就是模拟线上环境,即准生产环境,配置一致、数据一致,然后再看有没有问题,如果一切正常则上线,此时能kill掉99%可能出现的BUG(另外1%可能是硬件或网络问题,情况极少)。
其实灰度模式不止这个好处,下面简单列举一下:
1、做为内测环境,先对内部开放,功能是否达到预期要求,而不是上线让用户当小白鼠。
2、做为备份环境,因为灰度的数据跟线上是一模一样,旦生产发生故障,可快速切换为灰度,保证有足够的反应时间来修复生产。
3、作为负载均衡环境,当流量高峰时,可适当引流入灰度,以减轻生产压力。
实现步骤
一、配置结构
Nginx的配置文件为:nginx.conf,这是主配置,我们另外新建二个配置文件:
这二个文件其实是对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写入
这个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." //可以是空,也可以是一个注释性文本
更多待续...