OpenResty的部署与使用

2,205 阅读2分钟

OpenResty是什么

  • OpenResty是什么,官网是这样介绍的:

    通过 Lua 扩展 NGINX 实现的可伸缩的 Web 平台

    的确,OpenResty可以简单的理解为Nginx + Lua,通过Lua库引入数据库访问能力,真正的让Nginx向搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关这一目标迈出了重要的一步

OpenResty的配置

  • OpenResty的配置可以分为2类
    • lua脚本
    • Nginx配置文件
  • 下面列举几个常见场景的Nginx配置

静态文件(页面)服务器配置

server {
    listen 80;
    # 以dvclab.com作为主网站域名,完全匹配
    server_name ${hostname};
    rewrite ^(.*)$  https://${hostname}$1 permanent;
}

server {
    listen       443;
    server_name  ${hostname};

    # ssl证书文件位置(常见证书文件格式为:crt/pem)
    ssl_certificate      /etc/nginx/ssl/${hostname}.pem;
       
    # ssl证书key位置
    ssl_certificate_key  /etc/nginx/ssl/${hostname}.key;
  
    ssl_session_timeout  10m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
    ssl_prefer_server_ciphers  on;

    root        /etc/nginx/dist;
    index index.html index.htm;
}
  • 将静态页面文件放到OpenResty容器内的/etc/nginx/dist内即可,后续会使用Docker Compose的yaml配置文件做路径映射

一般反向代理

server {
    listen 80;
    server_name ${hostname};
    rewrite ^(.*)$  https://${hostname}$1 permanent;
}

server {
    listen          443 ssl;
    server_name     ${hostname};

    # ssl证书文件位置(常见证书文件格式为:crt/pem)
    ssl_certificate      /etc/nginx/ssl/auth-cert.pem;
    
    # ssl证书key位置
    ssl_certificate_key  /etc/nginx/ssl/auth-cert.key;
    ssl_session_timeout  10m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
    ssl_prefer_server_ciphers  on;

    location / {
    
         proxy_set_header  Host  $host;
         proxy_set_header  X-Forwarded-Proto $scheme;
         proxy_set_header  X-Forwarded-For $host;
         proxy_set_header  Upgrade $http_upgrade;
         proxy_set_header  Connection 'Upgrade';
    		 proxy_http_version 1.1;
         proxy_set_header  X-Real-IP $remote_addr;
         
         proxy_pass    http://${target}/;
    }
}
  • ${target}就是反向代理的目标服务器地址或域名,注意不要丢掉后边的/

动态路由设置

  • 大致的请求-相应流程如下

image-20210314234123950

  • 需求说明:根据请求参数动态转发到不同的服务器、端口,比如hostname/users/1/info/2 转发到hosname1:9200hostname/users/3/info/4转发到hostname2:8080

在/opt/openresty/lua/目录下创建 split.lua

echo '
--[[ 
拆分字符串 
e.g. /a/b/c  
table[1] a
table[2] b
table[3] c
--]]
function split(str, pat)
    local t = {}
    local fpat = "(.-)" .. pat
    local last_end = 1
    local s, e, cap = str:find(fpat, 1)
    while s do
        if s ~= 1 or cap ~= "" then
            table.insert(t, cap)
        end
        last_end = e + 1
        s, e, cap = str:find(fpat, last_end)
    end
    if last_end <= #str then
        cap = str:sub(last_end)
        table.insert(t, cap)
    end
    return t
end

function split_path(str)
    return split(str, '[\\/]+')
end
' > /opt/openresty/lua/split.lua

在/opt/openresty/lua/目录下创建 query_redis.lua

echo ' 
-- redis结果解析,导入redis.parser脚本
local parser = require "redis.parser"

-- ngx.var.uri只包含路径参数,不包含主机与端口
-- 调用worker启动时引入的lua脚本中提供的函数
local parameters = split_path(ngx.var.uri)

-- 访问的是根路径
if(#parameters == 0) then
   ngx.exit(ngx.HTTP_FORBIDDEN)
end

-- 拆分出查询参数
user_id = parameters[2]
container_id = parameters[4]

ngx.log(ngx.EMERG, "user_id--->", user_id)
ngx.log(ngx.EMERG, "container_id--->", container_id)

-- 组合参数
key = "DYNA"
id = user_id .. "_" .. container_id

-- 向redis查询
res = ngx.location.capture(
           "/redis", { args = { key = key, id = id } }
)

-- 查询失败
if res.status ~= 200 then
           ngx.log(ngx.ERR, "redis server returned bad status: ",
               res.status)
           ngx.exit(res.status)
end

-- 结果为空
if not res.body then
           ngx.log(ngx.ERR, "redis returned empty body")
           ngx.exit(500)
end

-- raw tcp response from redis server
-- 共2条返回所以应该使用parse_replies(res.body, 2)
-- OK
-- 172.17.144.4:8080
ngx.log(ngx.EMERG, "raw response ----->", res.body)

local results = parser.parse_replies(res.body, 2)
for i, result in ipairs(results) do
if i == 2 then
      server = result[1]
      typ = result[2]
  end
end

-- 检查结果类型
if typ ~= parser.BULK_REPLY or not server then
           ngx.exit(500)
end

-- 返回value为空
if server == "" then
           server = "default.com"
end

ngx.var.target = server
ngx.log(ngx.EMERG, "key--->", key)
ngx.log(ngx.EMERG, "id--->", id)
ngx.log(ngx.EMERG, "service--->", server)
'   > /opt/openresty/lua/query_redis.lua
  • 上述的lua脚本中,假设Redis存储着以DYNA为key的hash表,hash表的key是由用户请求中解析出的user_id和container_id使用_组合而成,对应的value就是要转发到的目标target

在/opt/openresty/conf.d/目录下创建dynamicRouter.conf

echo '
# 启用主进程后,在每次Nginx工作进程启动时运行指定的Lua代码
init_worker_by_lua_file /usr/local/openresty/nginx/lua/split.lua;
server {
   listen       443;
   server_name  ${hostname};
   # redis交互库是openresty的内置的库
   location = /redis {
       # Specifies that a given location can only be used for internal requests
       internal;
       redis2_query auth ${redis_password};
       # 解析请求参数
       set_unescape_uri $id $arg_id;
       set_unescape_uri $key $arg_key;
       # 执行redis查询请求
       redis2_query hget $key $id;
       # 查询请求转发到指定的redis_server
       redis2_pass redis:6379;
   }

   location / {
      # 设置一个内嵌脚本的共享变量
      set $target '';  
      # 引入内嵌脚本
      access_by_lua_file /usr/local/openresty/nginx/lua/query_redis.lua;
      resolver 8.8.8.8;
      # 进行请求转发(反向代理)
      proxy_set_header  Host  $host;
      proxy_set_header  X-Forwarded-For $host;
      # 如果客户端请求升级,将代理WebSocket
      proxy_set_header  Upgrade $http_upgrade;
      proxy_set_header  Connection 'Upgrade';
      proxy_set_header  X-Forwarded-Proto $scheme;
      proxy_set_header  X-Real-IP $remote_addr;
      proxy_http_version 1.1;
      # 最后的斜杠勿丢 
      proxy_pass http://$target/;
   }
}
 > /opt/openresty/conf.d/dynamicRouter.conf

  • ${redis_password}是redis访问的密码

动态路由的使用

  • 在部署OpenResty服务后,就可以通过读写Redis的方式来实现动态路由转发了

  • 在shell命令行使用 docker exec命令结合redis-cli即可完成动态配置,举例如下:

    • 目的:将 /users/userid/containers/{user_id}**/containers/**{container_id} 映射到 host:{host}**:**{port}

      docker exec -it or-redis /bin/bash
      redis-cli --askpass
      # 输入redis密码
      hset DYNA ${user_id}_${container_id} ${host}:${port}
      
      • 注意:host:{host}**:**{port} 不加最后的/;不用加协议头,默认是HTTP,同样也支持WebSocket的协议升级
  • 或者使用Redis-Java API 接口完成动态路由的设置

OpenResty的安装部署

  • 本文章使用docker-compose进行OpneResty的安装部署

Docker Compose的安装

sudo curl -L "https://github.com/docker/compose/releases/download/1.27.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

# 增加可执行权限
sudo chmod +x /usr/local/bin/docker-compose
  • 具体的最新的版本,可以去Docker官网查看

Docker Compose 部署OpenResty服务

echo 'version: "3"
services:
  redis:
    image: redis
    restart: always
    volumes:
      - /opt/redis/redis.conf:/etc/redis/redis.conf
    command: redis-server /etc/redis/redis.conf
    ports:
      - "61379:6379"
    container_name: or-redis
  openresty:
    image: openresty/openresty
    restart: always
    depends_on:
      - redis
    container_name: openresty
    volumes:
      - /opt/openresty/ssl/:/etc/nginx/ssl/
      - /opt/openresty/conf.d/:/etc/nginx/conf.d/
      - /opt/openresty/lua/:/usr/local/openresty/nginx/lua/
      - /opt/static/:/etc/nginx/dist/
    ports:
      - "443:443"
      - "80:80"
' > /etc/openresty/openresty.yaml

docker-compose -f /opt/openresty.yaml up -d
  • /opt/openresty/ssl/ 目录是用来放域名的HTTPS证书的,当然也可以使用更方便的Let's Encrypt服务,可参考使用lua-resty-auto-ssl

参考