发布

Nginx+Lua灰度发布

作者
  • avatar
    名称
    徐志毅
    Twitter

用户请求 nginx,nginx 根据灰度策略判断当前用户需要渲染的版本后返回对应版本的 html, 判断版本的逻辑由 nginx 处理,灰度策略存放在某个数据库或者 apollo(比如白名单 ip,白名单 id,百分比值)

优势:

  • 不改动前端业务代码,维护简单

劣势:

  • 改造成本稍大,需要后端或者运维支援、需要使用 lua 语言,可能需要修改构建流程。
location  /  {
    rewrite_by_lua_block {
        -- 请求接口返回版本号,请求失败不返回
        local function version(uid,my_uuid)
                local res = ngx.location.capture('/xxxx',
                        { args = { 唯一标识 } }
                )
                if res.body then
                    return string.match(res.body,"%d.%d.%d")
                end
        end
        -- 写入cookie
        local function writeUuid()
            if ngx.var.cookie_uuid == nli then
                local uuid = io.open("/proc/sys/kernel/random/uuid", "r"):read()
                ngx.header["Set-Cookie"] = string.format("uuid=%s; Expires=%s;path=%s", uuid, ngx.cookie_time(ngx.time() + 86400 * 1000),"/")
                return uuid
            end
        end
 
        -- main
        if ngx.var.cookie_uuid == nil then
            local sec,ver pcall(version,ngx.var.cookie_uid,writeUuid())
            if sec then
                    ngx.var.docroot = ver
            end
        else
            ver = version(ngx.var.cookie_uid,ngx.var.cookie_uuid)
            ngx.var.docroot = ver
        end
    }
}  

后续:lua的location.capture与http2有兼容问题,详情于 https://github.com/openresty/lua-nginx-module/issues/1195。

openstry服务器需要安装 https://github.com/ledgetech/lua-resty-http。

location / {
    rewrite_by_lua_block {
        -- 请求接口返回版本号,请求失败不返回
        local function version(uid,my_uuid)
            local httpc = require("resty.http").new()
            local res, err = httpc:request_uri("xxxxx", {
                method = "GET",
                query = { 唯一标识 },
            })
 
            if res and string.match(res.body,"%d.%d.%d") then
                ngx.var.docroot = string.match(res.body,"%d.%d.%d")
            end
        end
 
        -- 写入cookie
        local function writeUuid()
            if ngx.var.cookie_uuid == nli then
                local uuid = io.open("/proc/sys/kernel/random/uuid", "r"):read()
                ngx.header["Set-Cookie"] = string.format("uuid=%s; Expires=%s;path=%s", uuid, ngx.cookie_time(ngx.time() + 86400 * 1000),"/")
                return uuid
            end
        end
 
        -- main
        if ngx.var.cookie_uuid == nil then
            local sec,ver pcall(version,ngx.var.cookie_uid,writeUuid())
            if sec then
                    ngx.var.docroot = ver
            end
        else
            ver = version(ngx.var.cookie_uid,ngx.var.cookie_uuid)
            ngx.var.docroot = ver
        end
    }
     
    ...
}