当前位置: 首页 > news >正文

Docker手动重构Nginx镜像,融入Lua、Redis功能

核心内容:Docker重构Nginx镜像,融入Lua、Redis功能

文章目录

  • 前言
  • 一、准备工作
    • 1、说明
    • 2、下载模块
    • 3、Nginx配置文件
    • 3、Dockerfile配置文件
    • 3、准备工作全部结束
  • 二、构建镜像
  • 三、基于镜像创建容器
  • 三、lua脚本的redis功能使用
  • 总结


前言



⁣⁣⁣⁣ ⁣⁣⁣⁣ 哈喽,各位it同学们好,今天内容主要是:去重构nginx镜像,融入lua、Redis功能,能防范CC攻击,搭建合理有效的WAF 。我知道可能有不少人说OpenResty 已经做了这个事情,他也能去运行lua脚本防范CC攻击,除此之外还有一些流量监测、流量黑洞、蜜罐技术等等,但是今天我没有借用这些方式,就是单纯的想去加强一下nginx,让他去融入这些lua功能,然后我自己去写代码,进行流量的颗粒度控制。 我在网上也看了不少的文章去增强Nginx,但是好多都不全,而且复刻不成功,今天的话,我就记录一下我的做法。在开始之前需要强调一下,生成的镜像他本身还是Nginx,只是给他增强融入lua!!!


提示:以下是本篇文章正文内容,下面案例可供参考。以下教程,本人在4个服务器上面完美复刻,都是经过测试的,跟着我的笔记,你也可以的。



一、准备工作



 ⁣⁣⁣⁣ ⁣⁣⁣⁣ 准备工作其实就是下载东西,还有准备一些初始配置文件。

1、说明

⁣⁣⁣⁣ ⁣⁣⁣⁣ Nginx 执行 Lua 脚本的核心依赖是:
 ⁣⁣⁣⁣ ⁣⁣⁣⁣ - LuaJIT
 ⁣⁣⁣⁣ ⁣⁣⁣⁣ - ngx_devel_kit
 ⁣⁣⁣⁣ ⁣⁣⁣⁣ - lua-nginx-module

⁣⁣⁣⁣ ⁣⁣⁣⁣ 这3个非常重要!!!残缺、版本不一致都会使重构镜像失败!很多重构Nginx镜像都是失败在版本不一致的问题!下载模块里面的版本,都是个人搜集资料和多次尝试之后,搭配出来最优解的版本!这些版本在一起不会出错
 
 ⁣⁣⁣⁣ ⁣⁣⁣⁣ 准备的东西一共是10个,8个下载文件、1个nginx配置文件、1个Dockerfile。


2、下载模块


⁣⁣⁣⁣ ⁣⁣⁣⁣ 其实很多的教程,他都是要让你去在构筑镜像中去下载的,少的文件,没有问题,但是如果非常多的话,而且访问的链接、版本不一致的话,就会出错误,好多下载的东西都是国外的,很慢的。所以为了避免这些问题,这里我是全部都下载好,然后去放在Dockerfile同级的目录下


⁣⁣⁣⁣ ⁣⁣⁣⁣ 下面所有的链接截止(2025年.5月.6日时)全部能正常下载!!! 下载好之后就可以去和dockerfile放在同一个目录,而且要注意文件名字!!!链接有些是在国外,网络延迟大,可能需要多试一试,才能下载!


下面是下载的8个链接

下载的OpenSSL模块,1.1.1u版本 :

https://www.openssl.org/source/openssl-1.1.1u.tar.gz

下载的ngx_devel_kit模块,v0.3.0版本 :

https://github.com/simpl/ngx_devel_kit/archive/v0.3.0.tar.gz

下载的lua-nginx-module模块,v0.10.21版本 :

https://github.com/openresty/lua-nginx-module/archive/refs/tags/v0.10.21.tar.gz

下载的nginx源码:
Nginx 1.20.2 是非常稳定的 LTS(长期支持)版本

nginx 1.20.2版本   http://nginx.org/download/nginx-1.20.2.tar.gz

下载luajit2的源码,luajit2版本:

 git clone https://github.com/openresty/luajit2.git

下载lua-resty-core模块:

手动去如下地址下载源代码,下载之后将lua-resty-core-v0.1.17改为:lua-resty-core

v0.1.17版本   https://github.com/openresty/lua-resty-core/releases/tag/v0.1.17

下载lua-resty-lrucache模块:

git clone https://github.com/openresty/lua-resty-lrucache.git

下载luarocks-3.9.2的命令模块,版本3.9.2 :

 https://luarocks.org/releases/luarocks-3.9.2.tar.gz

链接有些是在国外,网络延迟大,可能需要多试一试,才能下载!!!
链接有些是在国外,网络延迟大,可能需要多试一试,才能下载!!!
链接有些是在国外,网络延迟大,可能需要多试一试,才能下载!!!


3、Nginx配置文件


⁣⁣⁣⁣ ⁣⁣⁣⁣ 这个是要用到的默认Nginx配置文件!

nginx.conf 如下:

worker_processes  1;events {worker_connections  1024;
}http {################################# 下面是核心内容,必须包含,否则会报各种错!# Nginx 的日志直接输出到标准输出,方便 docker logs 查看,同时也要注意-v挂载的日志路径问题!!access_log /var/log/nginx/access.log;error_log /var/log/nginx/error.log warn;# 显式添加 lua_package_path 路径lua_package_path "/opt/luarocks-3.9.2/lua_modules/share/lua/5.1/?.lua;;";# 确保 resty.core 启动时被加载,避免运行时缺模块init_by_lua_block {require "resty.core"}#################################include       mime.types;default_type  application/octet-stream;sendfile        on;keepalive_timeout  65;#gzip  on;server {listen       80;server_name  localhost;location / {root   /usr/share/nginx/html; #必须修改为:/usr/share/nginx/htmlindex  index.html index.htm;}}
}

这个nginx.conf我是进行精简过的!!!核心的内容一定不可少!构筑镜像的过程中需要这些!!!同时挂载也需要!



3、Dockerfile配置文件


⁣⁣⁣⁣ ⁣⁣⁣⁣ 所有点路径问题、报错问题,都已经解决了,可以直接抄!而且也可以作为通用模板!下面代码我都加上注释了!


# 指定基础镜像
FROM centos:7# 替换 yum 源为阿里云镜像,解决 DNS 和网络问题
RUN curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo# 安装Nginx和 LuaJIT 所需的依赖
RUN yum update -y && yum install -y \gcc make pcre-devel zlib-devel openssl-devel wget curl unzip git# 编译和安装 LuaJIT
# 编译后的 LuaJIT 会被安装到 Docker 容器的文件系统中,具体路径为 /usr/local/,这是默认的安装目录。
# 系统环境变量会指向 LuaJIT 的库和头文件所在的位置,这样 Nginx 在编译时能够找到并链接 LuaJIT。
WORKDIR /opt
COPY luajit2 /opt/luajit2
# 进入目录并编译安装
WORKDIR /opt/luajit2
RUN make && make install# 设置环境变量 。这里的文件路径和上面是自动保持一致的,不需要改!
# 在编译 Nginx 时用到了 LuaJIT,那么这里就是显式告诉他 LuaJIT 的头文件和库在哪里等等
ENV LUAJIT_LIB=/usr/local/lib
ENV LUAJIT_INC=/usr/local/include/luajit-2.1#下载并解压 OpenSSL 1.1.1u 源码 ,准备好 OpenSSL 源码,为后续 Nginx 编译链接使用。
COPY openssl-1.1.1u.tar.gz /opt/
WORKDIR /opt
RUN tar -zxvf openssl-1.1.1u.tar.gz#  lua-nginx-module 的底层依赖模块,提供宏和功能(如请求上下文、共享内存等)支持 Lua 脚本在 Nginx 中运行。
#  简单说:没它,lua-nginx-module 编译就失败,运行也不行。
COPY ngx_devel_kit-0.3.0.tar.gz /opt/
WORKDIR /opt
RUN tar -zxvf ngx_devel_kit-0.3.0.tar.gz# 这段代码的作用下载 Nginx 模块源码,为编译做准备。
# 这两部分源码是后续编译支持 Lua 的 Nginx 所必须的。
COPY nginx-1.20.2.tar.gz /opt/
WORKDIR /opt
RUN tar -zxvf nginx-1.20.2.tar.gz# 这段代码的作用下载 lua-nginx-module模块源码,为编译做准备。 
COPY lua-nginx-module-0.10.21.tar.gz /opt/
WORKDIR /opt
RUN tar -zxvf lua-nginx-module-0.10.21.tar.gz# -------------------------------
# 安装 Luarocks
COPY luarocks-3.9.2 /opt/luarocks-3.9.2
WORKDIR /opt/luarocks-3.9.2
# 给 configure 文件增加执行权限
RUN chmod +x ./configure
RUN ./configure --with-lua-include=/usr/local/include/luajit-2.1 && make && make install
#  检查是否安装成功
RUN luarocks --version 
# -------------------------------# -------------------------------
# 安装 lua依赖的redis模块
RUN yum install -y gcc make unzip wget readline-devel
RUN yum install -y lua-devel
RUN luarocks install lua-resty-redis
#  检查是否安装成功
RUN find /usr/local -name redis.lua
# -------------------------------# 编译 Nginx
# Nginx 编译 Nginx + Lua 模块时,只需要 lua-nginx-module 和 LuaJIT。
# 这部分代码的作用是编译 Nginx,使其支持 LuaJIT 和 lua-nginx-module 模块。手动编译 Nginx 的过程。
# WORKDIR /opt/nginx-1.24.0:进入 Nginx 源码目录。
# ./configure:配置 Nginx 编译参数:
# --prefix=/etc/nginx:指定自定义编译好的Nginx安装目录。
# --with-cc-opt=... 和 --with-ld-opt=...:让编译器正确找到 LuaJIT 的头文件和库。
# --add-module=...:添加 lua-nginx-module 模块,支持 Lua 脚本。
# --with-openssl=/opt/openssl-1.1.1u 告诉Nginx编译器:使用我指定的源码重新编译并集成进 Nginx,这样能让 Nginx 支持新版本的 TLS/SSL 加密功能。
# --with-http_ssl_module:启用 HTTPS 支持。
# --with-http_stub_status_module:启用 Nginx 状态监控模块。
# make && make install:编译并安装 Nginx。
WORKDIR /opt/nginx-1.20.2
RUN ./configure --prefix=/etc/nginx \--with-cc-opt="-O2 -Wno-error -I/usr/local/include/luajit-2.1" \--with-ld-opt="-L/usr/local/lib -Wl,-rpath,/usr/local/lib" \--add-module=/opt/lua-nginx-module-0.10.21\--add-module=/opt/ngx_devel_kit-0.3.0\--with-openssl=/opt/openssl-1.1.1u \--with-http_ssl_module \--with-http_stub_status_module \
&& make && make install# 下面两个 lua-resty-core 和 lua-resty-lrucache 是 运行时 Lua 模块,不影响Nginx编译。
# 安装 lua-resty-core 和 lua-resty-lrucache 模块(解决 'resty.core' 报错)WORKDIR /opt
COPY lua-resty-core /opt/lua-resty-core
# 将 lua-resty-core 中的 lib/resty 目录复制到容器的 /usr/local/share/lua/5.1 目录下
RUN cp -r /opt/lua-resty-core/lib/resty /usr/local/share/lua/5.1/WORKDIR /opt
COPY lua-resty-lrucache /opt/lua-resty-lrucache
# 将 lua-resty-lrucache 中的 lib/resty 目录复制到容器的 /usr/local/share/lua/5.1 目录下
RUN cp -r /opt/lua-resty-lrucache/lib/resty /usr/local/share/lua/5.1/# 复制自定义的 Nginx 配置文件
# 这一步只是将默认的配置文件复制到容器中,它本身并不会执行任何操作,只是保证 Nginx 在容器中启动时会使用你自定义的配置文件。
# 到时候我们在启动容器时,通过 -v 参数将宿主机上的配置文件挂载到容器中,这样 Nginx 就会使用我们自定义的配置文件了。
# 这一步非常重要!!!
COPY nginx.conf /etc/nginx/nginx.conf  # 添加配置文件和启动脚本
# 声明容器对外暴露 80 端口(HTTP)。
EXPOSE 80
CMD ["/etc/nginx/sbin/nginx", "-g", "daemon off;"]# /etc/nginx/sbin/nginx含义:来启动 Nginx,注意哦!这里的路径是编译后的Nginx的路径,上面我们已经指定的!!!
# 
# daemon off含义:设置容器启动时运行的命令,让 Nginx 以前台模式启动并保持运行(不进入守护进程),这样 Docker 容器才不会退出,Nginx 会持续运行。

⁣⁣⁣⁣ ⁣⁣⁣⁣ dockerfile文件都是写好的,直接复制就行了!!!问题都解决了!!!


3、准备工作全部结束


⁣⁣⁣⁣ ⁣⁣⁣⁣ OK!到这里基本上所有的东西全部准备齐了!!!一定要确保其名字、文件层级关系如下

在这里插入图片描述

一共10个文件!!! 下面就是重构镜像!!!





二、构建镜像


⁣⁣⁣⁣ ⁣⁣⁣⁣ 如果之前的构建过程中出现了错误并留下了不完整的文件,可能会导致后续构建失败。可以尝试清理之前的构建缓存,并重新构建,不会对你的容器、镜像、数据卷或系统本身造成危害。清理 Docker 缓存的命令

docker builder prune

⁣⁣⁣⁣ ⁣⁣⁣⁣ 我的建议是在重构镜像前面执行一次,根据我的实操经验,他会影响构建镜像!

⁣⁣⁣⁣ ⁣⁣⁣⁣ OK!!!正式开始!

⁣⁣⁣⁣ ⁣⁣⁣⁣ --no-cache :禁用缓存

docker build --no-cache -t nginx-lua:1.20.2 .

如下:
在这里插入图片描述
在这里插入图片描述


⁣⁣⁣⁣ ⁣⁣⁣⁣ 我的重构花费300多秒!!!如果他还要去下载东西,估计要更多的时间!!!这个重构,可能会受网络失败!你再去执行一下就可以了!!!



三、基于镜像创建容器


⁣⁣⁣⁣ ⁣⁣⁣⁣  注意!挂载的目录必须先创建,而且注意权限问题! 还要注意一点!!那就是把之前的dockerfile文件同级目录下的nginx.conf文件,要去放到/mydata/nginx_lua/conf/nginx.conf目录下!!必须的!!!不然报错的!!!


然后执行下面的命令:

docker run --name  nginx-lua -p 80:80 \-e TZ=Asia/Shanghai \-v /mydata/nginx_lua/html:/usr/share/nginx/html \-v /mydata/nginx_lua/conf/nginx.conf:/etc/nginx/conf/nginx.conf \-v /mydata/nginx_lua/page:/mydata/nginx_lua/page \-v /mydata/nginx_lua/lua/waf_limit.lua:/mydata/nginx_lua/lua/waf_limit.lua \-v /mydata/nginx_lua/logs:/var/log/nginx \-d  nginx-lua:1.20.2

这个时候就容器就创建好了!!!


下面可以去测试一下!!!

        #在server模块里面,随便找个地方添加下面的代码location /lua {default_type 'text/plain';content_by_lua 'ngx.say("hello, lua")';}

访问如下:
在这里插入图片描述

到这里只能说明你的功能等等各方面没有问题的!!!!


OK,下面我们去测试lua的redis模块!!!





三、lua脚本的redis功能使用


我给我的模块加上(粒度控制,加在谁的上面,谁起作用,贼好玩):

        location /ithe {access_by_lua_file /mydata/nginx_lua/lua/waf_limit.lua;alias  /mydata/nginx_lua/page; }

目录如下:
在这里插入图片描述




waf_limit.lua内容如下

这个脚本主要的作用就是:基于 IP + User-Agent 组合做访问频率限制(限流防护),防范 CC 攻击。直接复制就可以了(要改一下redis的账号密码)!!!!


-- 工具函数:获取真实 IP(考虑了反向代理)
local function get_client_ip()local headers = ngx.req.get_headers()local ip = headers["X-Real-IP"] or headers["X-Forwarded-For"] or ngx.var.remote_addrif ip and type(ip) == "string" then-- 处理多级代理下的多个IP地址(取第一个)local first_ip = ip:match("([^,]+)")return first_ipendreturn "unknown"
end-- 获取请求相关信息
local client_ip = get_client_ip()                  -- 访问者真实 IP
local user_agent = ngx.var.http_user_agent or ""   -- User-Agent-- 打印日志(方便调试)
ngx.log(ngx.ERR, "-----------------------------------[WAF] start:-------------------------------- ")
ngx.log(ngx.ERR, "############# [WAF] IP: ", client_ip," #############")
ngx.log(ngx.ERR, "############# [WAF] User-Agent: ", user_agent," #############")
ngx.log(ngx.ERR, "############# [WAF] URI: ", ngx.var.uri," #############")------------------------------------------------------------------------- 连接 Redis(默认连接 127.0.0.1:6379)
local redis = require "resty.redis"
local red = redis:new()-- 尝试连接远程 Redis(请替换成你的实际 IP 和端口)
local ok, err = red:connect("192.168.180.129", 6329)
if not ok thenngx.log(ngx.ERR, "[WAF] Redis 连接失败: ", err)return ngx.exit(500)
elsengx.log(ngx.ERR, "[WAF] Redis 连接成功")
end-- 使用密码进行 AUTH 验证(Redis 6.0+ 支持用户名)
local res, err = red:auth("XXXXXXXXX")
if not res thenngx.log(ngx.ERR, "[WAF] Redis 用户名密码认证失败: ", err)return ngx.exit(500)
elsengx.log(ngx.ERR, "[WAF] Redis 用户名密码认证成功")
end-- 选择指定数据库(0-15)
local ok, err = red:select(6)
if not ok thenngx.log(ERR, "[WAF] Redis 选择数据库失败: ", err)return ngx.exit(500)
elsengx.log(ngx.ERR, "[WAF] Redis 已选择数据库 5")
end------------------------------------------------------------------------- 统计整体访问次数(无论限流是否触发)
local total_key = "Visit:total"  -- 你也可以用 ip 直接当 key(比如:"total:" .. ip)-- INCR 总访问量(永久保存,不设置 expire)
local total_count, err = red:incr(total_key)
if not total_count thenngx.log(ngx.ERR, "[WAF] Redis 总访问计数累加失败: ", err)-- 不终止请求流程,统计失败可以忽略
elsengx.log(ngx.ERR, "[WAF] 当前总访问次数(永久累加): ", total_count)
end------------------------------------------------------------------------- -- 将 client_ip 和 user_agent 结合起来,生成一个唯一标识设备的 keylocal ip = client_ip or "unknown"
local ua = user_agent or "unknown"
local raw = ip .. "|" .. ualocal function to_hex(n)return string.format("%08x", n)  -- 8位十六进制,不足补0
end-- 多次哈希叠加
local part1 = to_hex(ngx.crc32_short(raw))
local part2 = to_hex(ngx.crc32_short(raw .. "1"))
local part3 = to_hex(ngx.crc32_short(raw .. "2"))
local part4 = to_hex(ngx.crc32_short(raw .. "3"))
local key = "limit:" .. part1 .. part2 .. part3 .. part4ngx.log(ngx.ERR, "############# [WAF] redis key = ", key,"#############")
-- so:limit:5d8e5c6f5d8e5c6f5d8e5c6f5d8e5c6f------------------------------------------------------------------------- 理解如下:
-- 第一次访问?
-- └─ 是 → Redis: set(key, 1) + expire(key, 60s)
-- └─ 否 → Redis: get(key) 变数字
--          └─ 超过10?→ 拦截返回429
--          └─ 没超限 → incr + 放行-- 设置最大允许次数和过期时间(单位:秒)
local max_count = 500      -- 限制访问次数:比如最多允许访问 10 次
local expire_time = 60   -- 规定的设计内容:统计 60 秒内的访问次数(限流时间段)
-- 比如设置的限流max_count = 10,expire_time = 60,规则就是「60 秒内最多只能访问 10 次」。-- 从 Redis 中读取你这个 IP + UA + 其他信息生成的 key 当前的访问次数。
local count, err = red:get(key)if not count thenngx.log(ngx.ERR, "[WAF] 获取 Redis 计数失败: ", err)return ngx.exit(500)
endif count == ngx.null thenlocal ok, err = red:set(key, 1)if not ok thenngx.log(ngx.ERR, "[WAF] Redis 设置初始值失败: ", err)return ngx.exit(500) endlocal ok, err = red:expire(key, expire_time)if not ok thenngx.log(ngx.ERR, "[WAF] Redis 设置过期时间失败: ", err)return ngx.exit(500)endngx.log(ngx.ERR, "[WAF] 初次访问,key 初始化完成")else local num = tonumber(count)if num >= max_count thenngx.log(ngx.ERR, "[WAF] 超过访问限制,直接拦截")--return ngx.exit(429)  -- HTTP 429 Too Many Requests--ngx.say('{"code":429, "msg":"请求过于频繁,请稍后再试!"}')return ngx.exit(429)elselocal new_count, err = red:incr(key)if not new_count thenngx.log(ngx.ERR, "[WAF] Redis 累加失败: ", err)return ngx.exit(500)endngx.log(ngx.ERR, "[WAF] 当前访问次数: ", new_count)end
end-- 关闭Redis 连接
red:close()
ngx.log(ngx.ERR, "-----------------------------------[WAF] over:-------------------------------- ")

OKOK!!!重启nginx,然后访问/ithe

在这里插入图片描述



然后看一下redis:

在这里插入图片描述



再看一下nginx的日志:

在这里插入图片描述



⁣⁣⁣⁣ ⁣⁣⁣⁣ okok!!!到这里就说明你已经成功让nginx融入lua、redis功能,并且成功运行!!!





总结

⁣⁣⁣⁣ ⁣⁣⁣⁣ 看到这里,不点个赞、收藏下,支持一下?(坏笑)

相关文章:

  • AI量化解析:从暴跌5%到飙涨3%—非线性动力学模型重构黄金极端波动预测框架
  • ApplicationRunner执行顺序问题
  • 深度解读 ARM 全新白皮书——《重塑硅基:AI 时代的新基石》
  • unordered_map和unordered_set的设计
  • 学习alpha,第2个alpha
  • Acrel-EIoT 能源物联网云平台在能耗监测系统中的创新设计
  • Pandas 的透视与逆透视
  • 雅思阅读--句子结构
  • 奇瑞依托汽车产业链,实现服务机器人万台下线
  • 【LeetCode Hot100 | 每日刷题】二叉树的层序遍历
  • 二叉树与堆排序(概念|遍历|实现)
  • [人机交互]理解用户
  • LightGBM算法原理及Python实现
  • 提示词工程:通向AGI时代的人机交互艺术
  • 从零实现基于Transformer的英译汉任务
  • 高并发PHP部署演进:从虚拟机到K8S的DevOps实践优化
  • 机器学习 day6 -线性回归练习
  • 【Part 2安卓原生360°VR播放器开发实战】第三节|实现VR视频播放与时间轴同步控制
  • CentOS虚拟机固定ip以及出现的问题
  • 引用第三方自定义组件——微信小程序学习笔记
  • 发表“男性患子宫肌瘤”论文的杂志一年发文三千余篇,中介称可提供代写
  • 路遇交通事故镇干部冲进火海救人,已申报见义勇为
  • 张求会谈陈寅恪的生前身后事
  • 视频丨英伟达总裁黄仁勋:美勿幻想AI领域速胜中国
  • 拍摄《我们这一代》的肖全开展“江浙沪叙事”
  • 美国第一季度经济环比萎缩0.3%