0%

OpenResty的部署与使用

OpenResty是什么

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

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

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

OpenResty的配置

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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配置文件做路径映射

一般反向代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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/${user_id}/containers/${container_id} 映射到 ${host}:${port}

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

OpenResty的安装部署

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

Docker Compose的安装

1
2
3
4
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服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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

参考