Kong Gateway自定义插件部署报错求助及插件优化咨询
一、容器崩溃错误解决
容器启动时出现module 'kong.plugins.oauth2.secret' not found错误,原因及解决方案如下:
错误原因
- 使用了Kong Gateway(企业版)镜像,该镜像默认启用RBAC组件,但未授权状态下部分依赖模块缺失;
- 声明式配置或环境变量触发了oauth2插件加载,但对应模块未完整安装。
解决方案
- 替换为社区版镜像
修改docker-compose.yml中的镜像为社区版(无需授权,组件完整):image: kong/kong:latest - 清理旧资源后重启
执行命令彻底清理旧容器与卷,避免缓存干扰:docker-compose down -v docker-compose up -d - 锁定插件加载范围
确保KONG_PLUGINS环境变量仅包含需要的插件:KONG_PLUGINS=bundled, lambda_s3_handler
二、自定义插件lambda_s3_handler的潜在问题
1. AWS签名不完整
当前invoke_lambda函数中手动构造的Authorization头仅包含Access Key ID,缺少AWS4-HMAC-SHA256签名的核心参数(如签名日期、签名值、算法等),Lambda会直接拒绝请求。
修复建议:使用resty.aws或Kong官方AWS SDK自动生成合法签名,避免手动拼接。
2. 大文件内存溢出风险
处理超过6MB的Base64 PDF时,ngx.decode_base64会将整个文件解码后加载到内存,可能导致Kong Worker进程内存溢出(OOM),直接影响服务稳定性。
优化建议:
- 通过
KONG_CLIENT_MAX_BODY_SIZE环境变量设置合理的请求体上限; - 实现流式解码与上传,避免一次性加载整个文件到内存。
3. 错误处理存在漏洞
- Lambda返回的响应可能不是合法JSON,直接调用
cjson.decode会抛出未捕获的错误,导致请求中断; - S3上传仅检查请求是否发送成功,未校验响应状态码(如403权限错误、500服务错误等),无法准确判断上传结果。
修复建议:
- 用
pcall包裹JSON解码逻辑,捕获解码错误:local success, lambda_result = pcall(cjson.decode, res.body) if not success then return nil, "Lambda response decode failed: " .. lambda_result end - 校验S3响应状态码:
if res.status ~= 200 and res.status ~= 201 then return false, "S3 upload failed with status: " .. res.status .. ", body: " .. res.body end
4. 配置硬编码,灵活性不足
代码中直接写死AWS区域、Lambda函数名、S3桶名的默认值,不符合Kong插件的配置规范,无法通过Admin API或声明式配置动态修改。
优化建议:通过插件conf参数传递配置,在handler.lua中读取:
function LambdaS3Handler:access(conf) local AWS_REGION = conf.aws_region local LAMBDA_FUNCTION = conf.lambda_function local S3_BUCKET = conf.s3_bucket -- 后续逻辑 end
同时在kong.yml中配置插件参数:
- name: lambda_s3_handler route: my-lambda-route_2 config: aws_region: "regionname" lambda_function: "lambdafunctionname" s3_bucket: "bucketname"
5. 同步请求导致响应延迟
Lambda调用与S3上传均为同步阻塞操作,会显著增加用户请求的响应时间,高并发场景下可能导致Kong Worker线程耗尽。
优化建议:将S3上传逻辑放到log阶段异步执行,提前返回UUID给用户,降低延迟。
6. 凭证管理不安全
直接通过os.getenv获取AWS密钥,存在环境变量泄露风险;未使用Kong Secrets管理或IAM角色(若部署在AWS环境)。
优化建议:
- 若部署在ECS/EKS,为容器绑定IAM角色,无需手动配置密钥;
- 使用Kong Secrets插件存储AWS密钥,通过
kong.secrets.get()读取。
7. 请求体大小限制未适配
Kong默认client_max_body_size为10MB,若PDF解码后超过该值,会直接拒绝请求。
修复建议:在docker-compose.yml中添加环境变量:
- KONG_CLIENT_MAX_BODY_SIZE=20M
附:用户提供的配置与代码
kong.yml
_format_version: "3.0" _transform: true services: - name: online-test # <just to check if connection is working url: https://httpstat.us/200 - name: aws-ping-test # <just to check if connection is working url: https://lambda.eu-central-1.amazonaws.com - name: lambda-s3-service url: https://lambda.eu-central-1.amazonaws.com routes: - name: my-online-test service: online-test paths: - /online - name: my-aws-ping-test service: aws-ping-test paths: - /aws-ping - name: my-lambda-route paths: - /lambda - name: my-lambda-route_2 service: lambda-s3-service paths: - /lambda-2 plugins: - name: aws-lambda route: my-lambda-route config: aws_region: "regionname" function_name: "lambdafunctionname" invocation_type: "RequestResponse" log_type: "Tail" forward_request_body: true forward_request_headers: true forward_request_uri: true - name: lambda_s3_handler route: my-lambda-route_2
docker-compose.yml
version: '3.3' services: kong: image: kong/kong-gateway:latest container_name: kong volumes: - "./config:/usr/local/kong/declarative" #- "./.aws:/usr/local/kong/declarative" - "./plugins:/usr/local/share/lua/5.1/kong/plugins" environment: - KONG_DATABASE=off - KONG_DECLARATIVE_CONFIG=/usr/local/kong/declarative/kong.yml - KONG_PROXY_ACCESS_LOG=/dev/stdout - KONG_ADMIN_ACCESS_LOG=/dev/stdout - KONG_PROXY_ERROR_LOG=/dev/stderr - KONG_ADMIN_ERROR_LOG=/dev/stderr - KONG_ADMIN_LISTEN=0.0.0.0:8001, 0.0.0.0:8444 ssl - KONG_LOG_LEVEL=debug - KONG_PLUGINS=bundled, lambda_s3_handler - KONG_ADMIN_GUI_URL=http://localhost:8002 - AWS_DEFAULT_REGION=regionname - AWS_REGION=regionname - AWS_SHARED_CREDENTIALS_FILE=/usr/local/kong/declarative/credentials - AWS_PROFILE=profilename ports: - "8000:8000/tcp" - "127.0.0.1:7990:8001/tcp" - "8001:8001/tcp" - "8443:8443/tcp" - "127.0.0.1:8444:8444/tcp" #- "127.0.0.1:8001:8001/tcp" networks: - learn-networking networks: learn-networking: external: true driver: bridge
handler.lua
local cjson = require "cjson" local http = require "resty.http" local base64 = require "ngx.base64" local LambdaS3Handler = { PRIORITY = 1000, VERSION = "1.0.0" } -- AWS credentials (ensure they are set in environment variables) local AWS_REGION = os.getenv("AWS_REGION") or "regionname" local LAMBDA_FUNCTION = os.getenv("AWS_LAMBDA_FUNCTION") or "lambdafunctionname" local S3_BUCKET = os.getenv("AWS_S3_BUCKET") or "bucketname" -- Function to invoke AWS Lambda local function invoke_lambda(filename, email_text) local function_name = LAMBDA_FUNCTION local payload = { FileName = filename, EmailText = email_text} local httpc = http.new() local res, err = httpc:request_uri("https://lambda." .. AWS_REGION .. ".amazonaws.com/2015-03-31/functions/" .. function_name .. "/invocations", { method = "POST", body = cjson.encode(payload), headers = { ["Content-Type"] = "application/json", ["Authorization"] = "AWS4-HMAC-SHA256 Credential=" .. os.getenv("AWS_ACCESS_KEY_ID"), }, }) if not res then return nil, "Failed to invoke Lambda: " .. (err or "Unknown error") end local lambda_result = cjson.decode(res.body) return lambda_result.uuid end -- Function to upload file to S3 local function upload_to_s3(uuid, file_data) local httpc = http.new() local s3_url = "https://" .. S3_BUCKET .. ".s3." .. AWS_REGION .. ".amazonaws.com/" .. uuid .. ".pdf" local res, err = httpc:request_uri(s3_url, { method = "PUT", body = file_data, headers = { ["Content-Type"] = "application/pdf", ["x-amz-acl"] = "bucket-owner-full-control", } }) if not res then return false, "Failed to upload file to S3: " .. (err or "Unknown error") end return true end -- Main Kong request handler function LambdaS3Handler:access(conf) -- Read JSON body from the request ngx.req.read_body() local body = ngx.req.get_body_data() if not body then return kong.response.exit(400, { message = "Invalid request, no body provided" }) end local data = cjson.decode(body) if not data.FileName or not data.EmailText or not data.Base64File then return kong.response.exit(400, { message = "Missing required fields" }) end -- Step 1: Invoke Lambda local uuid, err = invoke_lambda(data.FileName, data.EmailText) if not uuid then return kong.response.exit(500, { message = "Lambda invocation failed: " .. err }) end -- Step 2: Decode Base64 and upload to S3 local file_bytes = ngx.decode_base64(data.Base64File) local success, upload_err = upload_to_s3(uuid, file_bytes) if not success then return kong.response.exit(500, { message = "S3 upload failed: " .. upload_err }) end -- Step 3: Return UUID to user return kong.response.exit(200, { uuid = uuid }) end return LambdaS3Handler
内容的提问来源于stack exchange,提问作者MaxS.

