使用Nginx+Lua实现Web项目的灰度发布
使用Nginx+Lua实现Web项目的灰度发布 Nginx编译安装Lua模块 一、安装LUA环境及相关库 官方网站:https://github.com/openresty/lua-nginx-module 1、LuaJIT wget http://luajit.org/download/LuaJIT-2.0.2.tar.gz make && make install PREFIX=/usr/local/LuaJIT # vim /etc/profile export LUAJIT_LIB=/usr/local/LuaJIT/lib export LUAJIT_INC=/usr/local/LuaJIT/include/luajit-2.0 # source /etc/profile 2、下载解压ngx_devel_kit和lua-nginx-module cd /usr/local/src wget https://github.com/simpl/ngx_devel_kit/archive/v0.3.0.tar.gz wget https://github.com/openresty/lua-nginx-module/archive/v0.10.9rc7.tar.gz 分别解压 tar xf v0.10.9rc7.tar.gz tar xf v0.3.0.tar.gz 3、重新编译编译Nginx cd /usr/local/src/ wget http://nginx.org/download/nginx-1.12.1.tar.gz [root@node1 src]# tar xf nginx-1.12.1.tar.gz [root@node1 src]# cd nginx-1.12.1 预编译 ./configure --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -fPIC' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -pie' --add-module=/usr/local/src/ngx_devel_kit-0.3.0 --add-module=/usr/local/src/lua-nginx-module-0.10.9rc7 # 并行编译 make -j 4 && make install 4、加载lua库,加入到ld.so.conf文件 echo "/usr/local/LuaJIT/lib"/etc/ld.so.conf ldconfig 报错: [root@node1 nginx-1.12.1]# nginx -v nginx: error while loading shared libraries: libluajit-5.1.so.2: cannot open shared object file: No such file or directory 解决: # ln -s /usr/local/LuaJIT/lib/libluajit-5.1.so /usr/lib64/libluajit-5.1.so.2 二、使用Nginx+Lua实现Web项目的灰度发布 灰度发布概念: 按照一定的关系区别,分部分的代码进行上线,使代码的发布能平滑过渡上线 1.使用用户的信息cookie等信息区别 2.根据用户的ip地址区分 灰度发布在百度百科中解释: 灰度发布(又名金丝雀发布)是指在黑与白之间,能够平滑过渡的一种发布方式。在其上可以进行A/B testing,即让一部分用户继续用产品特性A,一部分用户开始用产品特性B,如果用户对B没有什么反对意见,那么逐步扩大范围,把所有用户都迁移到B上面来。灰度发布可以保证整体系统的稳定,在初始灰度的时候就可以发现、调整问题,以保证其影响度。 灰度期:灰度发布开始到结束期间的这一段时间,称为灰度期。 这里用于WEB系统新代码的测试发布,让一部分(IP)用户访问新版本,一部分用户仍然访问正常版本,其原理如图: 具体实现步骤: 1.部署两个tomcat实例: 分别监听 8080和9090 端口 tomcat8080作为灰度环境(升级以后的环境) tomcat9090作为生产环境(升级之前的环境) tomcat8080的测试页面代码: [root@node1 tomcat8080]# cat /data/tomcat8080/webapps/ROOT/java_test.jsp <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <HTML> <HEAD> <TITLE>jsp Test Page</TITLE> </HEAD> <BODY> <% Random rand = new Random(); out.println("<h1>Random number:</h1>"); out.println("<h1>gray server:</h1>"); out.println(rand.nextInt(99)+100); %> </BODY> </HTML> [root@node1 tomcat8080]# cat /data/tomcat9090/webapps/ROOT/java_test.jsp <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <HTML> <HEAD> <TITLE>jsp Test Page</TITLE> </HEAD> <BODY> <% Random rand = new Random(); out.println("<h1>shengchang xianshang server</h1>"); out.println("<h1>Random number:</h1>"); out.println(rand.nextInt(99)+100); %> </BODY> </HTML> 测试tomcat是否部署正常
2.安装memcached [root@node1 ~]# yum install -y memcached [root@node1 conf.d]# memcached -p11211 -u nobody -d [root@node1 conf.d]# ps -ef|grep memcached nobody 17177 1 0 18:12 ? 00:00:00 memcached -p11211 -u nobody -d root 17213 17150 0 18:12 pts/1 00:00:00 grep --color=auto memcached [root@node1 conf.d]# netstat -tnlp|grep 11211 tcp 0 0 0.0.0.0:11211 0.0.0.0:* LISTEN 17177/memcached tcp6 0 0 :::11211 :::* LISTEN 17177/memcached 修改nginx配置文件 [root@node1 conf.d]# pwd /etc/nginx/conf.d 修改nginx配置,引入lua脚本 经过测试,通过命令引入脚本文件的方式测试无法通过content_by_lua_file /opt/app/dep.lua; ①合并nginx写法,都写入nginx主配置文件: [root@node1 nginx]# cat nginx.conf worker_processes 1; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; proxy_next_upstream error timeout; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $http_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; client_max_body_size 100m; client_body_buffer_size 256k; proxy_connect_timeout 180; proxy_send_timeout 180; proxy_read_timeout 180; proxy_buffer_size 8k; proxy_buffers 8 64k; proxy_busy_buffers_size 128k; proxy_temp_file_write_size 128k; upstream sc_server { server 192.168.3.177:9090; } upstream gray_server { server 192.168.3.177:8080; } lua_package_path "/usr/local/share/lua/5.1/memcached.lua"; server { listen 80; server_name localhost; location / { content_by_lua ' clientIP = ngx.req.get_headers()["X-Real-IP"] if clientIP == nil then clientIP = ngx.req.get_headers()["x_forwarded_for"] end if clientIP == nil then clientIP = ngx.var.remote_addr end local memcached = require "resty.memcached" local memc, err = memcached:new() if not memc then ngx.say("failed to instantiate memc: ", err) return end local ok, err = memc:connect("127.0.0.1", 11211) if not ok then ngx.say("failed to connect: ", err) return end local res, flags, err = memc:get(clientIP) if err then ngx.say("failed to get clientIP ", err) return end if res == "1" then ngx.exec("@gray_server") return end ngx.exec("@sc_server") '; } location @sc_server{ proxy_pass http://sc_server; } location @gray_server{ proxy_pass http://gray_server; } location /hello { default_type 'text/plain'; content_by_lua 'ngx.say("hello, lua")'; } location /myip { default_type 'text/palin'; content_by_lua ' clientIP = ngx.req.get_headers()["x_forwarded_for"] if clientIP == nil then clientIP = ngx.var.remote_addr end ngx.say("IP:",clientIP) '; } location = /50x.html { root html; } } } ②主配置和lua配置文件分开写 主配置文件 [root@node1 conf.d]# cat /etc/nginx/nginx.conf user nginx; worker_processes 2; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for" "$request_uri"'; access_log /var/log/nginx/access.log main; sendfile on; #tcp_nopush on; keepalive_timeout 65; #gzip on; include /etc/nginx/conf.d/*.conf; } lua的配置文件 [root@node1 conf.d]# cat /etc/nginx/conf.d/gray_lua.conf upstream sc_server { server 192.168.3.177:9090; } upstream gray_server{ server 192.168.3.177:8080; } lua_package_path "/usr/local/share/lua/5.1/memcached.lua"; server { listen 80; server_name localhost; location / { content_by_lua ' clientIP = ngx.req.get_headers()["X-Real-IP"] if clientIP == nil then clientIP = ngx.req.get_headers()["x_forwarded_for"] end if clientIP == nil then clientIP = ngx.var.remote_addr end local memcached = require "resty.memcached" local memc, err = memcached:new() if not memc then ngx.say("failed to instantiate memc: ", err) return end local ok, err = memc:connect("127.0.0.1", 11211) if not ok then ngx.say("failed to connect: ", err) return end local res, flags, err = memc:get(clientIP) if err then ngx.say("failed to get clientIP ", err) return end if res == "1" then ngx.exec("@gray_server") return end ngx.exec("@sc_server") '; } location @sc_server{ proxy_pass http://sc_server; } location @gray_server{ proxy_pass http://gray_server; } location /hello { default_type 'text/plain'; content_by_lua 'ngx.say("hello, lua")'; } location /myip { default_type 'text/palin'; content_by_lua ' clientIP = ngx.req.get_headers()["x_forwarded_for"] if clientIP == nil then clientIP = ngx.var.remote_addr end ngx.say("IP:",clientIP) '; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } } 3.安装lua_memcached插件 wget https://github.com/agentzh/lua-resty-memcached/archive/v0.11.tar.gz tar xf v0.11.tar.gz cp -r lua-resty-memcached-0.11/lib/resty /usr/share/lua/5.1/ 向memcached中插入本机ip数据,作为可以访问灰度环境的IP [root@node1 conf.d]# telnet 127.0.0.1 11211 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. set 192.168.3.84 0 0 1 1 STORED get 192.168.3.84 VALUE 192.168.3.84 0 1 1 END 测试是否可以分离: 不在memcached中的IP访问java_test.jsp [root@node1 tomcat8080]# ifconfig eth0 eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.3.177 netmask 255.255.255.0 broadcast 192.168.3.255 inet6 fe80::3c78:50fd:7203:2f0e prefixlen 64 scopeid 0x20<link> ether 00:50:56:3b:dc:7e txqueuelen 1000 (Ethernet) RX packets 495041 bytes 239594322 (228.4 MiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 54942 bytes 10493549 (10.0 MiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 [root@node1 tomcat8080]# curl http://192.168.3.177/java_test.jsp <HTML> <HEAD> <TITLE>jsp Test Page</TITLE> </HEAD> <BODY> <h1>shengchang xianshang server</h1> <h1>Random number:</h1> 114 </BODY> </HTML> 在memcached环境中的IP访问: lua脚本的基础知识 [root@node1 ~]# yum install -y lua 基本语法: 1运行方式 交互模式运行: [root@node1 ~]# lua Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio > print("hello lua") hello lua 写入脚本运行: [root@node1 ~]# cat test.lua #!/usr/bin/lua print("hello lua") [root@node1 ~]# lua test.lua hello lua 2.注释 --行注释 --[[ 块注释 --]] 3.变量 > a = 'aio\n123"' > print(a) aio 123" > a="aio\n123\"" > print(a) aio 123" > a='\97io\10\04923"' > print(a) aio 123" > a=[[aio >> 123"]] > print(a) aio 123" 布尔类型只有nil和false是false,数字0 空字符串(' \0' )都是true lua中的变量如果没有特殊说明,全是全局变量 如果是局部变量前面加Local 4.循环 while循环语句 [root@node1 ~]# cat add.lua #!/usr/bin/lua sum = 0 num = 1 while num <= 100 do sum = sum + num num = num + 1 end print("sum=",sum) [root@node1 ~]# lua add.lua sum= 5050 注意: lua没有++ 或者 += 这样的操作 for循环 [root@node1 ~]# cat for.lua #!/usr/bin/lua sum = 0 for i = 1,100 do sum = sum + i end print("sum=",sum) [root@node1 ~]# lua for.lua sum= 5050 if-else判断语句(脚本测试不通过) if age == 40 and sex == "Male" then print("大于40男人") elseif age > 60 and sex ~= "Female" then print("非女人且大于60") else local age = io.read() print('your age is'..age) end