Nginx 支持 Lua:使用动态模块集成 LuaJIT 完整实践
本文记录一种在现有生产 Nginx 基础上增加 Lua 能力的实现方案。
核心思路:不重新编译、不替换生产环境的 Nginx,只编译
ngx_http_lua_module.so动态模块,并配套部署 LuaJIT 及 Lua 依赖库。
一、为什么需要让 Nginx 支持 Lua?
Nginx 本身非常擅长处理网络请求,例如:
- HTTP 反向代理
- HTTPS
- 负载均衡
- WebSocket
- URL Rewrite
- Header 操作
- 静态资源
- Stream/TCP 代理
但是,当网关逻辑逐渐复杂之后,仅仅依靠 Nginx 原生配置会比较困难。
例如下面这样的需求:
客户端
│
▼
Nginx
│
├── Token 校验
├── 动态路由
├── Header 处理
├── 调用 HTTP API
├── JSON 处理
└── 缓存
│
▼
后端服务
这类逻辑如果全部交给后端实现,可能需要增加大量接口调用。
而通过 Lua,可以直接在 Nginx 请求处理阶段执行代码。
例如:
location /api/ {
access_by_lua_block {
ngx.log(ngx.INFO, "Lua is running")
}
proxy_pass http://backend;
}
于是 Nginx 就从一个单纯的:
Web Server / Reverse Proxy
变成:
可编程的 API Gateway
二、Nginx + Lua 到底由哪些部分组成?
完整的 Nginx Lua 环境并不是只有一个组件。
整体结构如下:
Nginx
│
▼
ngx_http_lua_module.so
│
▼
LuaJIT
│
┌───────────┼───────────┐
▼ ▼ ▼
lua-resty-core resty-http cjson
│ │ │
└───────────┼───────────┘
▼
Lua Script
主要组件包括:
| 组件 | 作用 |
|---|---|
| Nginx | Web Server / Reverse Proxy |
| lua-nginx-module | 将 Lua 集成到 Nginx |
| LuaJIT | Lua 运行时 |
| lua-resty-core | OpenResty Lua 核心库 |
| lua-resty-http | Lua HTTP Client |
| lua-resty-string | 字符串及相关工具 |
| lua-resty-lrucache | LRU 缓存 |
| lua-cjson | JSON 编解码 |
其中最核心的是:
Nginx
+
ngx_http_lua_module.so
+
LuaJIT
三、安装 LuaJIT 并不等于 Nginx 支持 Lua
这是很多人在第一次接触 Nginx Lua 时容易产生的误解。
例如:
luajit test.lua
能够运行,只能说明:
系统已经安装 LuaJIT
但是 Nginx 并不能因此执行:
location /test {
content_by_lua_block {
ngx.say("Hello Lua")
}
}
因为 Nginx 还需要:
ngx_http_lua_module
因此:
LuaJIT
↓
负责执行 Lua
lua-nginx-module
↓
负责把 Lua 接入 Nginx
两者缺一不可。
四、为什么采用动态模块?
如果生产服务器已经部署了一个稳定运行的 Nginx,那么我们通常不希望为了增加 Lua 功能而重新替换整个 Nginx。
传统静态编译方式:
Nginx Source
│
▼
加入 Lua Module
│
▼
重新编译 Nginx
│
▼
新的 Nginx
这样意味着生产环境需要替换:
nginx
而本文采用的是动态模块方案:
生产 Nginx
│
│ load_module
▼
ngx_http_lua_module.so
│
▼
LuaJIT
│
▼
Lua Runtime
生产环境原来的:
nginx
保持不变。
只增加:
ngx_http_lua_module.so
以及 LuaJIT Runtime。
五、为什么 Dockerfile 还要下载 Nginx 源码?
这里是整个方案最容易让人产生疑问的地方。
既然生产环境已经存在 Nginx:
nginx/1.26.3
为什么 Dockerfile 还要下载:
nginx-1.26.3.tar.gz
原因是:
我们不是使用源码重新安装生产 Nginx,而是利用对应版本的 Nginx 源码编译动态模块。
整个过程:
Nginx 1.26.3 Source
│
▼
lua-nginx-module
│
▼
./configure
│
▼
make modules
│
▼
ngx_http_lua_module.so
最后真正需要部署到生产服务器的是:
ngx_http_lua_module.so
而不是重新生成一个 Nginx。
六、为什么 Nginx 版本必须保持一致?
假设生产服务器:
nginx -V
得到:
nginx/1.26.3
那么构建 Lua 动态模块时也使用:
nginx-1.26.3
而不是:
nginx-1.24.x
或者:
nginx-1.27.x
因为动态模块与 Nginx 的构建环境、模块 ABI 等存在关联。
因此本文:
ARG NGINX_VERSION=1.26.3
就是为了明确指定:
生产 Nginx = 1.26.3
构建模块 = 1.26.3
七、为什么 configure 参数也需要尽量一致?
除了版本之外,还应该关注生产环境的:
nginx -V
例如生产环境:
nginx/1.26.3
configure arguments:
--prefix=/home/opt/local/nginx
--with-http_stub_status_module
--with-http_ssl_module
--with-stream
--with-http_v2_module
那么编译动态模块时也使用:
./configure \
--prefix=/home/opt/local/nginx \
--with-http_stub_status_module \
--with-http_ssl_module \
--with-stream \
--with-http_v2_module \
--add-dynamic-module=/build/lua-nginx-module
核心原则就是:
生产 Nginx
≈
模块构建环境
越接近,动态模块加载时出现兼容性问题的概率越低。
八、--add-dynamic-module 到底是什么意思?
Dockerfile 中最重要的参数之一:
--add-dynamic-module=/build/lua-nginx-module
它的意思是:
将指定目录中的第三方 Nginx 模块作为动态模块进行编译。
这里:
/build/lua-nginx-module
就是 Lua Nginx Module 的源码。
执行:
make modules
之后,会生成:
objs/ngx_http_lua_module.so
然后:
RUN cp \
/build/nginx-${NGINX_VERSION}/objs/ngx_http_lua_module.so \
/output/nginx/modules/ngx_http_lua_module.so
最终得到:
/output/nginx/modules/ngx_http_lua_module.so
九、为什么不是 make install?
因为我们的目标不是构建新的 Nginx。
如果执行:
make
make install
主要目标是:
编译 Nginx
+
安装 Nginx
而本文的目标是:
只生成动态模块
所以执行:
make modules
即可。
可以简单理解为:
make
↓
构建 Nginx
make install
↓
安装 Nginx
make modules
↓
构建动态模块
因此整个 Docker Builder 实际上是:
Nginx Module Builder
而不是:
Nginx Installer
十、LuaJIT 的编译
Dockerfile 中:
ARG LUAJIT_BRANCH=v2.1
ARG LUAJIT_PREFIX=/home/opt/local/luajit
然后:
RUN git clone \
--depth=1 \
--branch=${LUAJIT_BRANCH} \
${GITHUB_PROXY}https://github.com/LuaJIT/LuaJIT.git \
/build/LuaJIT
编译:
RUN cd /build/LuaJIT && \
make -j$(nproc) && \
make install PREFIX=${LUAJIT_PREFIX}
最终:
/home/opt/local/luajit
里面会包含:
bin/
luajit
lib/
libluajit-5.1.so
include/
luajit-2.1
十一、为什么 LuaJIT 安装路径非常重要?
本文没有把 LuaJIT 安装到:
/usr/local
而是:
/home/opt/local/luajit
这是一个非常重要的设计。
因为后面编译 Nginx Lua Module 时使用:
--with-ld-opt="-Wl,-rpath,${LUAJIT_PREFIX}/lib"
实际就是:
-Wl,-rpath,/home/opt/local/luajit/lib
它会把 LuaJIT 的动态库搜索路径写入模块。
十二、RPATH 是什么?
ngx_http_lua_module.so 本身依赖 LuaJIT:
ngx_http_lua_module.so
│
▼
libluajit-5.1.so.2
系统加载动态模块时,需要找到:
libluajit-5.1.so.2
如果找不到,就可能出现:
error while loading shared libraries
或者:
libluajit-5.1.so.2: cannot open shared object file
因此通过:
-Wl,-rpath,/home/opt/local/luajit/lib
告诉动态链接器:
LuaJIT 动态库在:
/home/opt/local/luajit/lib
这样生产环境只要保持目录结构一致即可。
十三、为什么还需要 lua-resty-core?
安装 LuaJIT 后,只能执行基础 Lua。
而 Nginx Lua 开发通常需要大量 OpenResty API。
例如:
ngx.req.get_headers()
ngx.req.set_header()
ngx.var
ngx.shared
等等。
这些能力需要 OpenResty Lua 生态提供支持。
因此 Dockerfile 下载:
lua-resty-core
并复制:
lib/resty
lib/ngx
到:
/output/luajit/share/lua/5.1/
最终:
share/lua/5.1/
├── ngx/
└── resty/
十四、lua-resty-http
如果 Lua 需要访问其他 HTTP 服务,可以使用:
local http = require "resty.http"
例如:
local http = require "resty.http"
local client = http.new()
local res, err = client:request_uri(
"http://127.0.0.1:8080/test",
{
method = "GET"
}
)
if not res then
ngx.log(ngx.ERR, err)
return
end
ngx.say(res.body)
这样就可以实现:
Nginx
↓
Lua
↓
HTTP Client
↓
其他服务
对于 API Gateway 非常有用。
十五、lua-resty-lrucache
lua-resty-lrucache 可以提供 LRU 缓存。
例如:
第一次请求
↓
访问后端
↓
结果写入 Cache
第二次请求
↓
读取 Cache
↓
减少后端请求
可以用于:
- Token 缓存
- 配置缓存
- 用户信息缓存
- 临时数据缓存
十六、lua-resty-string
lua-resty-string 是 OpenResty Lua 生态中的常用基础库。
在实际项目中,经常会涉及:
Base64
SHA
HMAC
AES
随机数
字符串处理
因此将它一起打包到 Runtime 中。
十七、lua-cjson
网关开发几乎离不开 JSON。
例如:
local cjson = require "cjson.safe"
local data = {
code = 0,
message = "success"
}
ngx.say(cjson.encode(data))
Dockerfile 中:
RUN git clone \
--depth=1 \
--branch=${LUA_CJSON_BRANCH} \
${GITHUB_PROXY}https://github.com/openresty/lua-cjson.git \
/build/lua-cjson
然后编译:
RUN cd /build/lua-cjson && \
make \
LUA_INCLUDE_DIR=${LUAJIT_PREFIX}/include/luajit-2.1
最终得到:
cjson.so
放到:
/home/opt/local/luajit/lib/lua/5.1/
十八、Lua 模块搜索路径
Lua 加载模块的时候需要知道:
Lua 文件在哪里?
C 扩展在哪里?
因此设置:
ENV LUA_PATH="/output/luajit/share/lua/5.1/?.lua;/output/luajit/share/lua/5.1/?/init.lua;;"
ENV LUA_CPATH="/output/luajit/lib/lua/5.1/?.so;;"
对应关系:
LUA_PATH
↓
.lua 文件
LUA_CPATH
↓
.so 文件
例如:
require("resty.http")
最终寻找:
resty/http.lua
而:
require("cjson.safe")
则需要加载对应的 C 模块。
十九、为什么构建阶段必须做验证?
一个比较可靠的工程实践是:
能在 Docker Build 阶段发现的问题,不要留到生产环境。
因此 Dockerfile 中增加了:
RUN test -f /output/nginx/modules/ngx_http_lua_module.so && \
test -f /output/luajit/bin/luajit && \
test -f /output/luajit/lib/libluajit-5.1.so.2 && \
test -f /output/luajit/lib/lua/5.1/cjson.so && \
test -f /output/luajit/share/lua/5.1/resty/core.lua && \
test -f /output/luajit/share/lua/5.1/resty/http.lua && \
test -f /output/luajit/share/lua/5.1/resty/string.lua && \
test -f /output/luajit/share/lua/5.1/resty/lrucache.lua && \
test -f /output/luajit/share/lua/5.1/ngx/balancer.lua && \
echo "LUA OUTPUT FILES OK"
如果任何一个文件不存在:
Docker Build
↓
失败
这样可以避免:
构建成功
↓
发布生产
↓
Nginx 启动失败
二十、使用 file 检查动态模块
还可以检查:
RUN file /output/nginx/modules/ngx_http_lua_module.so
主要用于确认它是正确的 ELF Shared Object。
例如:
ELF 64-bit LSB shared object
二十一、使用 ldd 检查依赖
这是整个 Dockerfile 中非常重要的一项检查:
RUN ldd /output/nginx/modules/ngx_http_lua_module.so
重点关注:
libluajit-5.1.so.2
如果显示:
not found
就说明动态链接库无法找到。
这时候即使:
ngx_http_lua_module.so
文件存在,生产环境也可能无法正常加载。
二十二、最终 Runtime Package
Docker Build 完成后:
/output
├── VERSION.txt
│
├── nginx
│ └── modules
│ └── ngx_http_lua_module.so
│
└── luajit
├── bin
│ └── luajit
│
├── include
│ └── luajit-2.1
│
├── lib
│ ├── libluajit-5.1.so
│ ├── libluajit-5.1.so.2
│ └── lua
│ └── 5.1
│ └── cjson.so
│
└── share
└── lua
└── 5.1
├── ngx
└── resty
这就是完整的 Lua Runtime。
二十三、生产环境部署
假设生产环境:
/home/opt/local/
原本已经存在:
nginx/
现在只增加:
luajit/
并把动态模块放到:
nginx/modules/
最终:
/home/opt/local/
├── nginx/
│ ├── nginx
│ ├── conf/
│ └── modules/
│ └── ngx_http_lua_module.so
│
└── luajit/
├── bin/
│ └── luajit
├── lib/
│ ├── libluajit-5.1.so.2
│ └── lua/
│ └── 5.1/
│ └── cjson.so
└── share/
└── lua/
└── 5.1/
├── ngx/
└── resty/
二十四、Nginx 加载 Lua 模块
在 Nginx 配置文件顶部增加:
load_module modules/ngx_http_lua_module.so;
例如:
load_module modules/ngx_http_lua_module.so;
events {
}
http {
}
注意:
load_module 必须位于顶层配置区域。
不能写成:
server {
load_module modules/ngx_http_lua_module.so;
}
也不能放到:
location {
}
里面。
二十五、配置 Lua Package Path
在 http 中增加:
http {
lua_package_path "/home/opt/local/luajit/share/lua/5.1/?.lua;/home/opt/local/luajit/share/lua/5.1/?/init.lua;;";
lua_package_cpath "/home/opt/local/luajit/lib/lua/5.1/?.so;;";
}
这样:
require("resty.http")
就可以找到:
/home/opt/local/luajit/share/lua/5.1/resty/http.lua
而:
require("cjson.safe")
可以找到:
/home/opt/local/luajit/lib/lua/5.1/cjson.so
二十六、第一个 Lua 测试
配置:
location /lua-test {
content_by_lua_block {
ngx.say("Hello Lua!")
}
}
首先:
nginx -t
如果:
syntax is ok
test is successful
说明 Nginx 配置正常。
然后访问:
http://127.0.0.1/lua-test
返回:
Hello Lua!
说明整个调用链已经成功:
HTTP Request
↓
Nginx
↓
ngx_http_lua_module.so
↓
LuaJIT
↓
Lua
↓
ngx.say()
↓
HTTP Response
二十七、测试 HTTP Client
继续测试:
location /lua-http {
content_by_lua_block {
local http = require "resty.http"
local client = http.new()
local res, err = client:request_uri(
"http://127.0.0.1:8080/test",
{
method = "GET"
}
)
if not res then
ngx.status = 500
ngx.say(err)
return
end
ngx.say(res.body)
}
}
整个请求流程变成:
客户端
↓
Nginx
↓
Lua
↓
lua-resty-http
↓
后端服务
这也是 Nginx + Lua 作为 API Gateway 时非常典型的使用方式。
二十八、Nginx + Lua 可以实现什么?
1. 动态鉴权
请求
↓
Lua
↓
Token 校验
↓
通过 → Backend
失败 → 401 / 403
2. 动态路由
例如根据:
Header
Cookie
Query 参数
Token
用户信息
租户信息
选择不同后端。
请求
↓
Lua
↓
判断条件
↓
Backend A / Backend B / Backend C
3. API Gateway
可以将:
鉴权
限流
路由
缓存
Header
HTTP 调用
JSON
统一放在 Nginx + Lua 层处理。
4. 动态修改 Header
例如:
ngx.req.set_header("X-User-Id", user_id)
然后再转发给后端。
5. 缓存
使用:
lua-resty-lrucache
可以实现进程内的 LRU 缓存。
二十九、整个方案的架构
最终可以理解为:
Production Server
┌─────────────────────────────────────────────────────┐
│ │
│ Nginx 1.26.3 │
│ │
│ ┌─────────────────────────────────────────────┐ │
│ │ ngx_http_lua_module.so │ │
│ └──────────────────────┬──────────────────────┘ │
│ │ │
│ ▼ │
│ LuaJIT 2.1 │
│ │ │
│ ┌───────────┼───────────┐ │
│ ▼ ▼ ▼ │
│ resty.core resty.http cjson │
│ │ │ │ │
│ └───────────┼───────────┘ │
│ ▼ │
│ Lua Script │
│ │
└─────────────────────────────────────────────────────┘
三十、Dockerfile 的职责
这份 Dockerfile 并不是用来重新制作生产 Nginx。
它的职责实际上是:
编译 LuaJIT
↓
编译 ngx_http_lua_module.so
↓
准备 lua-resty-core
↓
准备 lua-resty-http
↓
准备 lua-resty-string
↓
准备 lua-resty-lrucache
↓
编译 lua-cjson
↓
检查动态库依赖
↓
生成 Runtime Package
因此它更准确的定位是:
Nginx Lua Runtime Builder
三十一、为什么这个方案适合已有生产环境?
如果生产环境已经有:
Nginx 1.26.3
不需要:
重新安装 Nginx
重新编译 Nginx
替换 Nginx 二进制
只需要:
部署 LuaJIT
↓
部署 Lua Libraries
↓
部署 ngx_http_lua_module.so
↓
load_module
↓
nginx -t
↓
reload
因此改造范围相对较小。
三十二、部署前检查清单
正式部署之前建议检查:
Nginx
nginx -V
确认:
版本一致
configure 参数尽量一致
LuaJIT
/home/opt/local/luajit/bin/luajit -v
动态模块
file /home/opt/local/nginx/modules/ngx_http_lua_module.so
动态依赖
ldd /home/opt/local/nginx/modules/ngx_http_lua_module.so
确认:
libluajit-5.1.so.2
没有:
not found
Nginx 配置
nginx -t
确认:
syntax is ok
test is successful
三十三、总结
为已有 Nginx 增加 Lua 能力,本质上需要建立下面这条运行链:
Nginx
↓
ngx_http_lua_module.so
↓
LuaJIT
↓
Lua Runtime
↓
lua-resty-* / cjson
本文采用的是:
动态模块
+
LuaJIT Runtime
+
OpenResty Lua Libraries
而不是重新编译并替换整个 Nginx。
整个方案的关键点有三个。
第一:Nginx 版本匹配
生产环境:
Nginx 1.26.3
构建动态模块:
Nginx 1.26.3
第二:只构建动态模块
核心参数:
--add-dynamic-module=/build/lua-nginx-module
然后:
make modules
最终得到:
ngx_http_lua_module.so
而不是重新安装 Nginx。
第三:保证 LuaJIT Runtime 完整
除了:
ngx_http_lua_module.so
还需要:
LuaJIT
lua-resty-core
lua-resty-http
lua-resty-string
lua-resty-lrucache
lua-cjson
同时需要保证:
RPATH
Lua Package Path
Lua C Package Path
配置正确。
最终,原有 Nginx 不需要被替换,只需要加载:
load_module modules/ngx_http_lua_module.so;
即可获得 Lua 执行能力。
这套方案特别适合已经运行在生产环境中的 Nginx:保留原有 Nginx 二进制和配置体系,通过独立的动态模块和 LuaJIT Runtime 增加可编程能力。
完整编译Dockerfile
FROM ubuntu:22.04 AS builder
ENV DEBIAN_FRONTEND=noninteractive
# ============================================================
# Build arguments
# ============================================================
ARG NGINX_VERSION=1.26.3
ARG LUAJIT_BRANCH=v2.1
ARG LUA_NGINX_MODULE_BRANCH=master
ARG LUA_RESTY_CORE_BRANCH=master
ARG LUA_RESTY_LRUCACHE_BRANCH=master
ARG LUA_RESTY_STRING_BRANCH=master
ARG LUA_RESTY_HTTP_BRANCH=master
ARG LUA_CJSON_BRANCH=master
# GitHub proxy prefix
#
# 最终:
# https://ghfast.top/https://github.com/xxx
#
ARG GITHUB_PROXY=https://ghfast.top/
# ============================================================
# Important:
#
# 这是生产服务器 LuaJIT 的最终安装路径。
#
# ngx_http_lua_module.so 会使用这个路径作为 RPATH,
# 所以不能使用 Docker 内部的 /opt/build/luajit。
# ============================================================
ARG LUAJIT_PREFIX=/home/opt/local/luajit
ENV LUAJIT_PREFIX=${LUAJIT_PREFIX}
WORKDIR /build
# ============================================================
# 1. Build dependencies
# ============================================================
RUN apt-get update && \
apt-get install -y \
build-essential \
gcc \
g++ \
make \
git \
wget \
curl \
ca-certificates \
perl \
pkg-config \
file \
binutils \
libpcre3 \
libpcre3-dev \
zlib1g-dev \
libssl-dev \
&& \
rm -rf /var/lib/apt/lists/*
# ============================================================
# 2. Build LuaJIT
# ============================================================
RUN git clone \
--depth=1 \
--branch=${LUAJIT_BRANCH} \
${GITHUB_PROXY}https://github.com/LuaJIT/LuaJIT.git \
/build/LuaJIT
RUN cd /build/LuaJIT && \
make -j$(nproc) && \
make install PREFIX=${LUAJIT_PREFIX}
# ============================================================
# 3. Verify LuaJIT
# ============================================================
RUN ${LUAJIT_PREFIX}/bin/luajit -v
# ============================================================
# 4. Download Nginx source
#
# 这里只是为了使用 nginx 1.26.3 的源码编译动态模块。
#
# 不会生成新的 nginx 二进制用于生产部署。
# ============================================================
RUN wget \
https://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz \
-O /build/nginx-${NGINX_VERSION}.tar.gz
RUN tar \
-xzf /build/nginx-${NGINX_VERSION}.tar.gz \
-C /build
# ============================================================
# 5. Download lua-nginx-module
# ============================================================
RUN git clone \
--depth=1 \
--branch=${LUA_NGINX_MODULE_BRANCH} \
${GITHUB_PROXY}https://github.com/openresty/lua-nginx-module.git \
/build/lua-nginx-module
# ============================================================
# 6. Configure Nginx dynamic module
#
# 必须与你生产环境的 nginx -V 保持一致。
#
# 你的生产环境:
#
# nginx/1.26.3
#
# configure:
#
# --prefix=/home/opt/local/nginx
# --with-http_stub_status_module
# --with-http_ssl_module
# --with-stream
# --with-http_v2_module
#
# 注意:
#
# 1. 不使用 --with-compat
# 2. 不执行 make install
# 3. 只执行 make modules
# 4. RPATH 指向生产环境 LuaJIT 路径
# ============================================================
WORKDIR /build/nginx-${NGINX_VERSION}
RUN export LUAJIT_LIB=${LUAJIT_PREFIX}/lib && \
export LUAJIT_INC=${LUAJIT_PREFIX}/include/luajit-2.1 && \
./configure \
--prefix=/home/opt/local/nginx \
--with-http_stub_status_module \
--with-http_ssl_module \
--with-stream \
--with-http_v2_module \
--with-ld-opt="-Wl,-rpath,${LUAJIT_PREFIX}/lib" \
--add-dynamic-module=/build/lua-nginx-module
# ============================================================
# 7. Compile ONLY dynamic modules
# ============================================================
RUN make -j$(nproc) modules
# ============================================================
# 8. Prepare output directories
# ============================================================
RUN mkdir -p \
/output/nginx/modules \
/output/luajit/bin \
/output/luajit/lib \
/output/luajit/include \
/output/luajit/share
# ============================================================
# 9. Copy ngx_http_lua_module.so
# ============================================================
RUN cp \
/build/nginx-${NGINX_VERSION}/objs/ngx_http_lua_module.so \
/output/nginx/modules/ngx_http_lua_module.so
# ============================================================
# 10. Download lua-resty-core
# ============================================================
RUN git clone \
--depth=1 \
--branch=${LUA_RESTY_CORE_BRANCH} \
${GITHUB_PROXY}https://github.com/openresty/lua-resty-core.git \
/build/lua-resty-core
RUN mkdir -p \
/output/luajit/share/lua/5.1
RUN cp -a \
/build/lua-resty-core/lib/resty \
/output/luajit/share/lua/5.1/
RUN cp -a \
/build/lua-resty-core/lib/ngx \
/output/luajit/share/lua/5.1/
# ============================================================
# 11. Download lua-resty-lrucache
# ============================================================
RUN git clone \
--depth=1 \
--branch=${LUA_RESTY_LRUCACHE_BRANCH} \
${GITHUB_PROXY}https://github.com/openresty/lua-resty-lrucache.git \
/build/lua-resty-lrucache
RUN cp -a \
/build/lua-resty-lrucache/lib/resty/. \
/output/luajit/share/lua/5.1/resty/
# ============================================================
# 12. Download lua-resty-string
# ============================================================
RUN git clone \
--depth=1 \
--branch=${LUA_RESTY_STRING_BRANCH} \
${GITHUB_PROXY}https://github.com/openresty/lua-resty-string.git \
/build/lua-resty-string
RUN cp -a \
/build/lua-resty-string/lib/resty/. \
/output/luajit/share/lua/5.1/resty/
# ============================================================
# 13. Download lua-resty-http
# ============================================================
RUN git clone \
--depth=1 \
--branch=${LUA_RESTY_HTTP_BRANCH} \
${GITHUB_PROXY}https://github.com/ledgetech/lua-resty-http.git \
/build/lua-resty-http
RUN cp -a \
/build/lua-resty-http/lib/resty/. \
/output/luajit/share/lua/5.1/resty/
# ============================================================
# 14. Build lua-cjson
# ============================================================
RUN git clone \
--depth=1 \
--branch=${LUA_CJSON_BRANCH} \
${GITHUB_PROXY}https://github.com/openresty/lua-cjson.git \
/build/lua-cjson
RUN cd /build/lua-cjson && \
make \
LUA_INCLUDE_DIR=${LUAJIT_PREFIX}/include/luajit-2.1
RUN mkdir -p \
/output/luajit/lib/lua/5.1
RUN cp \
/build/lua-cjson/cjson.so \
/output/luajit/lib/lua/5.1/cjson.so
# ============================================================
# 15. Copy LuaJIT runtime
# ============================================================
RUN cp \
${LUAJIT_PREFIX}/bin/luajit \
/output/luajit/bin/luajit
RUN cp -a \
${LUAJIT_PREFIX}/lib/libluajit-5.1.so* \
/output/luajit/lib/
# ============================================================
# 16. Copy LuaJIT headers
#
# 生产运行其实不需要 headers,
# 但保留它们方便以后继续编译 Lua 模块。
# ============================================================
RUN cp -a \
${LUAJIT_PREFIX}/include/luajit-2.1 \
/output/luajit/include/
# ============================================================
# 17. Verify Lua modules
#
# 这里非常重要。
#
# 在 Docker build 阶段直接验证:
#
# require("resty.core")
# require("resty.http")
# require("resty.string")
# require("resty.lrucache")
# require("cjson.safe")
# ============================================================
ENV LUA_PATH="/output/luajit/share/lua/5.1/?.lua;/output/luajit/share/lua/5.1/?/init.lua;;"
ENV LUA_CPATH="/output/luajit/lib/lua/5.1/?.so;;"
RUN test -f /output/nginx/modules/ngx_http_lua_module.so && \
test -f /output/luajit/bin/luajit && \
test -f /output/luajit/lib/libluajit-5.1.so.2 && \
test -f /output/luajit/lib/lua/5.1/cjson.so && \
test -f /output/luajit/share/lua/5.1/resty/core.lua && \
test -f /output/luajit/share/lua/5.1/resty/http.lua && \
test -f /output/luajit/share/lua/5.1/resty/string.lua && \
test -f /output/luajit/share/lua/5.1/resty/lrucache.lua && \
test -f /output/luajit/share/lua/5.1/ngx/balancer.lua && \
echo "LUA OUTPUT FILES OK"
# ============================================================
# 18. Verify nginx lua module
# ============================================================
RUN echo "===== file =====" && \
file /output/nginx/modules/ngx_http_lua_module.so
# ============================================================
# 19. Verify dynamic dependencies
# ============================================================
RUN echo "===== ldd ngx_http_lua_module.so =====" && \
ldd /output/nginx/modules/ngx_http_lua_module.so
# ============================================================
# 20. Verify LuaJIT dynamic library
# ============================================================
RUN echo "===== LuaJIT libraries =====" && \
ls -lah /output/luajit/lib/
# ============================================================
# 21. Verify cjson
# ============================================================
RUN echo "===== cjson =====" && \
ls -lah /output/luajit/lib/lua/5.1/cjson.so
# ============================================================
# 22. Verify resty libraries
# ============================================================
RUN echo "===== resty libraries =====" && \
find /output/luajit/share/lua/5.1/resty \
-type f \
| sort
# ============================================================
# 23. Generate VERSION.txt
# ============================================================
RUN { \
echo "=================================================="; \
echo "Nginx Lua Runtime Package"; \
echo "=================================================="; \
echo ""; \
echo "Nginx Version: ${NGINX_VERSION}"; \
echo "LuaJIT Branch: ${LUAJIT_BRANCH}"; \
echo ""; \
echo "Production Nginx Prefix:"; \
echo "/home/opt/local/nginx"; \
echo ""; \
echo "Production LuaJIT Prefix:"; \
echo "/home/opt/local/luajit"; \
echo ""; \
echo "Nginx Configure:"; \
echo "--prefix=/home/opt/local/nginx"; \
echo "--with-http_stub_status_module"; \
echo "--with-http_ssl_module"; \
echo "--with-stream"; \
echo "--with-http_v2_module"; \
echo "--add-dynamic-module=lua-nginx-module"; \
echo ""; \
echo "Lua Modules:"; \
find /output/luajit/share/lua/5.1/resty \
-type f | sort; \
echo ""; \
echo "C Modules:"; \
find /output/luajit/lib/lua/5.1 \
-type f | sort; \
} > /output/VERSION.txt
# ============================================================
# ============================================================
# Final image
# ============================================================
# ============================================================
FROM ubuntu:22.04
COPY --from=builder /output /output
CMD ["bash"]