基于 Nginx+lua+Memcache 實現(xiàn)灰度發(fā)布

作者:文彪
鏈接:https://www.cnblogs.com/wenbiao/p/3227998.html

一、灰度發(fā)布原理說明

灰度發(fā)布在百度百科中解釋:

灰度發(fā)布是指在黑與白之間,能夠平滑過渡的一種發(fā)布方式。AB test就是一種灰度發(fā)布方式,讓一部分用戶繼續(xù)用A,一部分用戶開始用B,如果用戶對B沒有什么反對意見,那么逐步擴大范圍,把所有用戶都遷移到B上面 來。灰度發(fā)布可以保證整體系統(tǒng)的穩(wěn)定,在初始灰度的時候就可以發(fā)現(xiàn)、調(diào)整問題,以保證其影響度。

這里的用于WEB系統(tǒng)新代碼的測試發(fā)布,讓一部分(IP)用戶訪問新版本,一部分用戶仍然訪問正常版本,其原理如圖:

image

執(zhí)行過程:

  • 當(dāng)用戶請求到達前端代理服務(wù)Nginx,內(nèi)嵌的lua模塊解析Nginx配置文件中的lua腳本代碼;

  • Lua變量獲得客戶端IP地址,去查詢memcached緩存內(nèi)是否有該鍵值,如果有返回值執(zhí)行@client_test,否則執(zhí)行@client。

  • Location @client_test把請求轉(zhuǎn)發(fā)給部署了new版代碼的服務(wù)器,location @client把請求轉(zhuǎn)發(fā)給部署了normal版代碼的服務(wù)器,服務(wù)器返回結(jié)果。整個過程完成。

下面把安裝配置過程詳細說明。

二、安裝配置過程詳解

1、安裝nginx

安裝依賴包

yum -y install gcc gcc-c++ autoconf libjpeg libjpeg-devel libpng libpng-devel freetype freetype-devel libxml2 libxml2-devel zlib zlib-devel glibc glibc-devel glib2 glib2-devel bzip2 bzip2-devel ncurses ncurses-devel curl curl-devel e2fsprogs e2fsprogs-devel krb5 krb5-devel libidn libidn-devel openssl openssl-devel openldap openldap-devel nss_ldap openldap-clients openldap-servers make pcre-devel

yum -y install gd gd2 gd-devel gd2-devel lua lua-devel

yum –y install memcached

下載lua模塊、lua-memcache操作庫文件和nginx包

wget https://github.com/simpl/ngx_devel_kit/archive/v0.2.18.tar.gz
wget https://github.com/chaoslawful/lua-nginx-module/archive/v0.8.5.tar.gz
wget https://github.com/agentzh/lua-resty-memcached/archive/v0.11.tar.gz
wget http://nginx.org/download/nginx-1.4.2.tar.gz

#解壓編譯安裝
tar xvf nginx-1.4.2.tar.gz
cd nginx-1.4.2/
./configure \
--prefix=/soft/nginx/ \
--with-http_gzip_static_module \
--add-module=/root/ngx_devel_kit-0.2.18/ \
--add-module=/root/lua-nginx-module-0.8.5/

make&&make install

拷貝lua的memcached操作庫文件

tar xvf v0.11.tar.gz

cp -r lua-resty-memcached-0.11/lib/resty/ /usr/lib64/lua/5.1/

配置nginx

#vim /soft/nginx/conf/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 client {
        server   192.168.200.29:80;
    }
    upstream client_test {
       server   192.168.200.29:81;
    }

    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("@client_test")
                    return
                end
                 ngx.exec("@client")             
               ';
       }
       location @client{
           proxy_pass http://client;
       }
      location @client_test{
           proxy_pass http://client_test;
       }

    location /hello {
       default_type 'text/plain';
      content_by_lua 'ngx.say("hello, lua")';
    }

    location = /50x.html {
        root   html;
    }
   }
}

檢測配置文件。

#/soft/nginx/sbin/nginx -t

nginx: the configuration file /soft/nginx/conf/nginx.conf syntax is ok

nginx: configuration file /soft/nginx/conf/nginx.conf test is successful

啟動nginx

/soft/nginx/sbin/nginx

啟動memcached服務(wù)

memcached -u nobody -m 1024 -c 2048 -p 11211 –d

三、測試驗證

測試lua模塊是否運行正常

訪問http://測試服務(wù)器ip地址/hello。如果顯示:hello,lua 表示安裝成功。

image

在另一臺測試機(這里是192.168.200.29)設(shè)置兩個虛擬主機,一個用80端口是執(zhí)行正常代碼,一個是81端口執(zhí)行灰度測試代碼。

在memcached中以你的客戶機IP地址為key,value值為1。這里我的IP是192.168.68.211.

telnet localhost 11211
Trying ::1...
Connected to localhost.
Escape character is '^]'.
set 192.168.68.211 0 3600 1
1
STORED
get 192.168.68.211
VALUE 192.168.68.211 9 1
1
END
quit

注意:

set后第一個值為key值。

192.168.68.211這是key值是需要灰度測試的IP地址;

0 表示一個跟該key有關(guān)的自定義數(shù)據(jù);

3600 表示該key值的有效時間;

1 表示key所對應(yīng)的value值的字節(jié)數(shù)。

下面訪問Nginx,效果符合預(yù)期,我的IP已經(jīng)在memcached中存儲值,所以請求轉(zhuǎn)發(fā)給執(zhí)行灰度測試代碼的主機。

image

從memcached刪除我的主機IP值。

image

再次請求Nginx,請求轉(zhuǎn)發(fā)給執(zhí)行正常代碼內(nèi)容的主機。

image

整個配置并不復(fù)雜,整個判斷過程對服務(wù)的影響非常小。如果需要使用這個系統(tǒng)最好自己看看lua腳本。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容