openresty使用

docker.io/openresty/openresty:latest

sed -i 's/deb.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list
/etc/openresty/nginx.conf
/etc/nginx/conf.d/default.conf

docker cp ./default.conf opresty:/etc/nginx/conf.d/default.conf
access_by_lua_file:指定一个 Lua 文件,在 access 阶段执行该文件中的 Lua 代码。
content_by_lua_block:在 content 阶段执行一段 Lua 代码块。
content_by_lua_file:指定一个 Lua 文件,在 content 阶段执行该文件中的 Lua 代码。
header_filter_by_lua_block:在 header_filter 阶段执行一段 Lua 代码块,用于修改响应头。
header_filter_by_lua_file:指定一个 Lua 文件,在 header_filter 阶段执行该文件中的 Lua 代码。
body_filter_by_lua_block:在 body_filter 阶段执行一段 Lua 代码块,用于修改响应体。
body_filter_by_lua_file:指定一个 Lua 文件,在 body_filter 阶段执行该文件中的 Lua 代码。
log_by_lua_block:在 log 阶段执行一段 Lua 代码块,用于自定义日志记录。
log_by_lua_file:指定一个 Lua 文件,在 log 阶段执行该文件中的 Lua 代码
  • vscode扩展

# 适用于nginx语言环境, 可以格式化
hangxingliu.vscode-nginx-conf-hint
  • 限流demo https://opm.openresty.org/package/openresty/lua-resty-limit-traffic

# nginx.vh.default.conf  --  docker-openresty
#
# This file is installed to:
#   `/etc/nginx/conf.d/default.conf`
#
# It tracks the `server` section of the upstream OpenResty's `nginx.conf`.
#
# This config (and any other configs in `etc/nginx/conf.d/`) is loaded by
# default by the `include` directive in `/usr/local/openresty/nginx/conf/nginx.conf`.
#
# See https://github.com/openresty/docker-openresty/blob/master/README.md#nginx-config-files
#

# 设置限流空间
lua_shared_dict my_limit_req_store 100m;
server {
    listen       8099;
    #server_name  localhost;

    #charset koi8-r;
    access_log  /tmp/host.access2.log;
    error_log  /tmp/error2.log;

    location / {
        content_by_lua_block {
            error_log file [level];
        }
        access_by_lua_block {
            local limit_req = require "resty.limit.req"
            -- req/sec, and reject any requests exceeding 300 req/sec.
            local lim, err = limit_req.new("my_limit_req_store", 1, 5)
            if not lim then
                ngx.say("lim error")
                return ngx.exit(500)
            end

            -- the following call must be per-request.
            -- here we use the remote (IP) address as the limiting key
            local key = ngx.var.binary_remote_addr
            local delay, err = lim:incoming(key, true)
            if not delay then
                if err == "rejected" then
                    ngx.say("rejected 503")
                    return ngx.exit(503)
                end
                ngx.say(err)
                return ngx.exit(500)
            end

            -- 延迟
            -- if delay >= 0.001 then
                -- the 2nd return value holds the number of excess requests
                -- per second for the specified key. for example, number 31
                -- means the current request rate is at 231 req/sec for the
                -- specified key.
                -- local excess = err

                -- the request exceeding the 200 req/sec but below 300 req/sec,
                -- so we intentionally delay it here a bit to conform to the
                -- 200 req/sec rate.
            --     ngx.sleep(delay)
            -- end
        }
        root   /usr/local/openresty/nginx/html;
        index  index.html index.htm;
    }

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/local/openresty/nginx/html;
    }

    # proxy the PHP scripts to Apache listening on 127.0.0.1:80
    #
    #location ~ \.php$ {
    #    proxy_pass   http://127.0.0.1;
    #}

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    #location ~ \.php$ {
    #    root           /usr/local/openresty/nginx/html;
    #    fastcgi_pass   127.0.0.1:9000;
    #    fastcgi_index  index.php;
    #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
    #    include        fastcgi_params;
    #}

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    #location ~ /\.ht {
    #    deny  all;
    #}
}
  • 使用防火墙 registry.cn-hangzhou.aliyuncs.com/jcleng/openresty-waf:latest

# 防火墙: https://github.com/codiy1992/lua-resty-waf

### 获取配置
GET http://adhome.2011101.xyz:1122/waf/config
Authorization: Basic d2FmOlRUcHNYSHRJNW13cQ==

### 更新规则和配置
POST http://adhome.2011101.xyz:1122/waf/config
Authorization: Basic d2FmOlRUcHNYSHRJNW13cQ==
Content-Type: application/json
  • opm安装额外的依赖

# 下载到当前目录
wget https://raw.githubusercontent.com/openresty/opm/master/bin/opm
chmod +x ./opm
# 模块市场
https://opm.openresty.org/package/tokers/lua-resty-requests/
#
mkdir -p ./vendor
./opm --install-dir=./vendor get \
  ledgetech/lua-resty-http \
  fffonion/lua-resty-openssl

# 使用时指定到lualib目录
lua_package_path "/home/jcleng/work/mywork/podmanserver/lua/vendor/lualib/?.lua;;";
  • nginx.conf https://gitee.com/jcleng/openresty_docker_balancer.git

# 设置日志等级
error_log  logs/error.log  info;

events {
    worker_connections 1024;
}

http {
    lua_shared_dict podman_upstreams 10m;
    lua_package_path "/usr/local/openresty/lualib/?.lua;;/home/jcleng/work/mywork/podmanserver/lua/vendor/lualib/?.lua;;";
    default_type application/json;

    init_worker_by_lua_block {
        ngx.log(ngx.INFO, "✅init_worker_by_lua_block")
        local cjson = require("cjson")
        local http = require("resty.http")
        ngx.log(ngx.INFO, "✅")

        -- 每 5 秒拉取容器列表
        local ok, err = ngx.timer.every(5, function()
            local httpc = http.new()
            local ok, err = httpc:connect("unix:/var/run/docker.sock")
            if not ok then
                ngx.log(ngx.ERR, "连接失败: ", err)
                return
            end

            local path = "/version"
            -- Send an HTTP GET request to the Docker daemon via the socket
            local res, err = httpc:request({
                path = path,
                method = "GET",
                headers = {
                    ["Host"] = "localhost" -- Host header is required for HTTP 1.1
                }
            })

            local body, err = res:read_body()

            -- ngx.log(ngx.ERR, "❌请求失败: ", body)
            local res_version, err = cjson.decode(body)

            -- 获取容器列表
            local res_containers, err = httpc:request({
                path = "http://127.0.0.1/v" .. res_version.ApiVersion .. "/containers/json?filters=%7B%22label%22%3A%20%5B%22openresty.enable%3Dtrue%22%5D%7D",
                method = "GET",
                headers = {
                    ["Host"] = "localhost"
                }
            })
            local body_containers, err = res_containers:read_body()

            -- 解析容器列表
            local containers, err = cjson.decode(body_containers)
            if not containers then
                ngx.log(ngx.ERR, "JSON解析失败")
                return
            end


            -- 提取IP和端口,存入共享内存
            local upstreams = {}
            for _, c in ipairs(containers) do
                if c.NetworkSettings and c.NetworkSettings.Networks['openresty-proxy-net'].IPAddress then
                    table.insert(upstreams, c.NetworkSettings.Networks['openresty-proxy-net'].IPAddress .. ":80")
                end
            end

            -- 存入共享字典
            ngx.shared.podman_upstreams:set("api.example.com", cjson.encode(upstreams))
            ngx.log(ngx.INFO, "刷新上游节点成功: ", #upstreams, " 个")
        end)
    }

    server {
        listen 80;
        server_name api.example.com;

        location / {
            proxy_pass http://backend;
            proxy_set_header Host $host;
        }
    }

    upstream backend {
        server 127.0.0.1:8080;

        balancer_by_lua_block {
            ngx.log(ngx.INFO, "✅balancer_by_lua_block")
            local balancer = require("ngx.balancer")
            local cjson = require("cjson")
            local dict = ngx.shared.podman_upstreams

            -- 从共享字典获取节点列表
            local peers_str = dict:get("api.example.com")
            ngx.log(ngx.INFO, "✅peers_str", peers_str)

            local peers = cjson.decode(peers_str)

            -- 4. 【正确的轮询算法】跨 worker 共享状态
            local current, _ = dict:incr("balancer_round_robin_idx", 1, 0)
            local idx = (current % #peers) + 1 -- Lua 数组从 1 开始
            local peer = peers[idx]
            ngx.log(ngx.INFO, "✅ 选中节点[", idx, "]: ", peer)

            -- 5. 解析 IP:端口
            local host, port = peer:match("^([^:]+):(%d+)$")
            if not host or not port then
                ngx.log(ngx.ERR, "❌ 节点格式错误: ", peer)
                return ngx.exit(503)
            end

            -- 6. 设置当前转发的后端节点(核心!)
            local ok, set_err = balancer.set_current_peer(host, tonumber(port))
            if not ok then
                ngx.log(ngx.ERR, "❌ 设置后端节点失败: ", set_err)
                return ngx.exit(503)
            end
        }
    }
}