精华内容
下载资源
问答
  • 使用 Docker 和 Nginx NJS 实现 API 聚合服务(前篇) 两个月前,我曾写过一篇名为《从封装 Nginx NJS 工具镜像聊起》的文章,简单介绍了 Nginx 官方团队推出的 NJS 以及我为他定制的 Docker 镜像。 这篇文章,我将...

    使用 Docker 和 Nginx NJS 实现 API 聚合服务(前篇)

    两个月前,我曾写过一篇名为《从封装 Nginx NJS 工具镜像聊起》的文章,简单介绍了 Nginx 官方团队推出的 NJS 以及我为他定制的 Docker 镜像。

    这篇文章,我将介绍如何使用 Nginx NJS 用精简的代码行数编写一套 API 聚合工具,并如何使用 Docker 将其封装为可用服务。

    写在前面

    本篇内容涉及到几块内容,如果你不熟悉,可以翻阅我之前的相关文章加深理解和掌握:

    为了能够模拟和演示接近真实的聚合服务功能,我在经常使用的开源软件的官网随便找了两个接口:

    • MySQL: https://www.mysql.com/common/chat/chat-translation-data.json
    • Redis: https://redislabs.com/wp-content/themes/wpx/proxy/signup_proxy.php

    好了,万事俱备,我们开始进行实践。

    编写 Nginx NJS 脚本

    万丈高楼平地起,先从最简单的部分开始。

    使用 NJS 编写 Nginx 基础接口

    在我们尝试聚合接口前,先试着写一个最基础的版本,让 Nginx 能够模拟输出一个类似 { code: 200, desc: "这是描述内容"} 的接口。

    如果你熟悉 Node 或者其他后端语言,下面代码要做的事情,就一目了然了:首先定义了一个名为 simple 的函数 ,接着定义了我们要展示的接口数据,然后设置 Nginx 响应内容类型为 UTF8 编码的 JSON,以及接口 HTTP Code 为 200,最后声明模块中的 simple 是可被公开调用的。

    function simple(req) {
      var result = { code: 200, desc: "这是描述内容" };
    
      req.headersOut["Content-Type"] = "application/json;charset=UTF-8";
      req.return(200, JSON.stringify(result));
    }
    
    export default { simple };
    

    将上面的内容保存为 app.js,并放置于一个名为 script 目录中,我们稍后使用。接着我们声明一份可以让 Nginx 调用 NJS 的配置文件:

    load_module modules/ngx_http_js_module.so;
    
    user nginx;
    worker_processes auto;
    
    error_log /var/log/nginx/error.log warn;
    pid /var/run/nginx.pid;
    
    events {
        worker_connections 1024;
    }
    
    http {
        include /etc/nginx/mime.types;
        default_type application/octet-stream;
    
        js_import app from script/app.js;
    
        log_format main '$remote_addr - $remote_user [$time_local] "$request" '
        '$status $body_bytes_sent "$http_referer" '
        '"$http_user_agent" "$http_x_forwarded_for"';
    
        access_log /var/log/nginx/access.log main;
        sendfile on;
        keepalive_timeout 65;
    
        server {
            listen 80;
            server_name localhost;
    
            charset utf-8;
            gzip on;
    
            location / {
                js_content app.simple;
            }
        }
    }
    

    将上述内容保存为 nginx.conf,我们同样稍后使用。

    可以看到这份配置文件和以往的配置文件看起来差别不大,但是确实又有一些“不同”,将所有和 NJS 无关的内容去掉,就可以清晰的看到 NJS 是如何和 Nginx 联动的。

    load_module modules/ngx_http_js_module.so;
    ...
    
    http {
    ...
        js_import app from script/app.js;
    
        server {
    ...
            location / {
                js_content app.simple;
            }
        }
    }
    

    首先是全局显式声明加载 ngx_http_js_module.so 模块,然后是将我们编写的脚本引入 Nginx HTTP 块作用域内,最后则是调用脚本具体的方法提供服务。

    为了方便的验证服务,我们还需要编写一个简单的 compose 编排文件:

    version: '3'
    
    services:
    
      nginx-api-demo:
        image: nginx:1.19.8-alpine
        restart: always
        ports:
          - 8080:80
        volumes:
          - ./nginx.conf:/etc/nginx/nginx.conf
          - ./script:/etc/nginx/script
    

    上一篇文章提过,目前 NJS 已经是 Nginx 官方模块,并默认附带在官方 Docker 镜像中,所以我们这里直接使用最新的官方镜像 nginx:1.19.8-alpine 就可以了。

    将上面的文件保存为 docker-compose.yml ,适当调整下上面文件的目录结构,并使用 docker-compose up 启动服务,访问 localhost:8080,可以看到我们得到了我们想要的结果,浏览器中出现了接口内容。

    浏览器中展示接口结果

    和我们使用 Nginx 调用 CGI 程序不同,可以看到接口处理时间只花费了 1ms ,虽然这和我们实现的代码复杂度非常低有关系,但是通常网络开销导致我们得到的结果会远大于这个数值。从某个角度说明不需要“外部程序”计算参与时, Nginx 直接参与结果计算在性能方面是有潜力的。

    尝试编写获取远端数据的接口

    接着我们来编写一个能够获取远端数据的接口,和之前编写的方式类似,只需要将我们定义的接口返回数据替换为使用 subrequest 方法请求的数据接口结果即可。

    function fetchRemote(req) {
      req.subrequest("https://www.mysql.com/common/chat/chat-translation-data.json").then((response) => {
        req.headersOut["Content-Type"] = "application/json;charset=UTF-8";
        req.return(200, JSON.stringify(response));  
      })
    }
    
    export default { fetchRemote };
    

    为了便于区分,我们这里将函数名改为更贴切的“fetchRemote”,接着将 nginx.conf 文件中的调用方法也进行更新:

    ...
    location / {
        js_content app.fetchRemote;
    }
    ...
    

    随后使用 docker-compose up 重新启动服务,再次访问 localhost:8080 来验证程序的结果是否符合预期。

    然而页面返回了类似下面的结果:

    {"status":404,"args":{},"httpVersion":"1.1","remoteAddress":"172.21.0.1","headersOut":{"Content-Type":"text/html","Content-Length":"555"},"method":"GET","uri":"https://www.mysql.com/common/chat/chat-translation-data.json","responseText":"<html>\r\n<head><title>404 Not Found</title></head>\r\n<body>\r\n<center><h1>404 Not Found</h1></center>\r\n<hr><center>nginx/1.19.8</center>\r\n</body>\r\n</html>\r\n<!-- a padding to disable MSIE and Chrome friendly error page -->\r\n<!-- a padding to disable MSIE and Chrome friendly error page -->\r\n<!-- a padding to disable MSIE and Chrome friendly error page -->\r\n<!-- a padding to disable MSIE and Chrome friendly error page -->\r\n<!-- a padding to disable MSIE and Chrome friendly error page -->\r\n<!-- a padding to disable MSIE and Chrome friendly error page -->\r\n","headersIn":{"Host":"localhost:8080","Connection":"keep-alive","Cache-Control":"max-age=0","sec-ch-ua":"\"Google Chrome\";v=\"89\", \"Chromium\";v=\"89\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","DNT":"1","Upgrade-Insecure-Requests":"1","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36","Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9","Sec-Fetch-Site":"none","Sec-Fetch-Mode":"navigate","Sec-Fetch-User":"?1","Sec-Fetch-Dest":"document","Accept-Encoding":"gzip, deflate, br","Accept-Language":"zh-CN,zh;q=0.9,en;q=0.8,ja;q=0.7"}}
    

    页面虽然返回了数据,但是显然不是我们想要的结果。

    检查 Nginx 日志,可以进一步了解这个错误发生的原因。

    [error] 33#33: *1 open() "/etc/nginx/htmlhttps://www.mysql.com/common/chat/chat-translation-data.json" failed (2: No such file or directory), client: 172.21.0.1, server: localhost, request: "GET / HTTP/1.1", subrequest: "https://www.mysql.com/common/chat/chat-translation-data.json", host: "localhost:8080"
    ...
    

    不卖关子了,来聊聊“正确答案”。

    正确的获取远程数据

    这里会发生错误因为 NJS 的 subrequest 方法仅支持将请求使用异步方式发送给反向代理。

    将要请求地址改为由 Nginx 反向代理,这里因为这个接口我们仅用作 NJS 调用,不需要提供开放访问,所以可以添加 internal 指令,来进行外部访问限制处理,避免 NJS 之外调用过程访问我们的远端接口:

    location /proxy/api-mysql {
        internal;
        proxy_pass https://www.mysql.com/;
        proxy_set_header Host www.mysql.com;
    }
    

    接着修改之前代码中的请求地址:

    function fetchRemote(req) {
      req.subrequest("/proxy/api-mysql/common/chat/chat-translation-data.json").then((response) => {
        req.headersOut["Content-Type"] = "application/json;charset=UTF-8";
        req.return(200, JSON.stringify(response));  
      })
    }
    
    export default { fetchRemote };
    

    再次启动服务,可以看到我们已经能够获取远端数据,但是结果看起来有一些问题:

    {"status":200,"args":{},"httpVersion":"1.1","remoteAddress":"172.27.0.1","headersOut":{"Content-Type":"application/json","Content-Length":"1863","X-Frame-Options":"SAMEORIGIN","Strict-Transport-Security":"max-age=15768000","Last-Modified":"Tue, 27 Nov 2018 20:34:52 GMT","Accept-Ranges":"bytes","Vary":"Accept-Encoding","Content-Encoding":"gzip","X-XSS-Protection":"1; mode=block","X-Content-Type-Options":"nosniff"},"method":"GET","uri":"/proxy/api-mysql/common/chat/chat-translation-data.json","responseText":"\u001f�\b\u0000\u0000\u0000\u0000\u0000\u0000\u0003�Z[o\u0013G\u0014~G�?��W(\u0002�J�R�\u0014���Bk�JT}\u0018{��$�]3��4��|!j�i�4��&$��P(��;qA��}�\u001b\u0016\u0007'1�_�\u0019�\u001d��c�(�M\"9^9����sf��\u0006\u0019+!\u0003���p\u0016}�\b����\u0017B\rD���?ᄆ�e�98�B�D�\u0010�o�q\u0003�؂��c[lh@U\u00022�xk��\u0004
    

    出现这个问题的原因是因为远端服务器给我们返回了 GZip 后的数据,所以这里我们有两个选择,告诉服务器我们不支持 GZip,或者让 Nginx 对取回的数据进行解压缩。

    因为存在即是我们告诉远程服务器,我们不支持 GZip,远程服务器还是会发送压缩后的数据(常见于CDN),所以这里建议使用方案二,再次修改 Nginx 配置,让 Nginx 能够自动解压缩远端数据。

    location /proxy/api-mysql {
        internal;
        gunzip on;
        proxy_pass https://www.mysql.com/;
        proxy_set_header Host www.mysql.com;
    }
    

    但是当我们重新启动服务进行测试的时候会发生另外一个问题:

    距离成功很近的时的错误

    [error] 33#33: *4 pending events while closing request, client: 172.28.0.1, server: 0.0.0.0:80
    [error] 33#33: *8 too big subrequest response while sending to client, client: 172.28.0.1, server: localhost, request: "GET / HTTP/1.1", subrequest: "/proxy/api-mysql/common/chat/chat-translation-data.json", upstream: "https://137.254.60.6:443//common/chat/chat-translation-data.json", host: "localhost:8080"
    

    检查日志可以看到上面的错误提示,这是因为 GZip 解压缩之后,数据量远大于 Nginx 默认处理临时数据的 Buffer 容量,所以我们要进一步对此进行调整:

    subrequest_output_buffer_size 200k;
    
    location /proxy/api-mysql {
        internal;
        gunzip on;
        proxy_pass https://www.mysql.com/;
        proxy_set_header Host www.mysql.com;
    }
    

    这里的subrequest_output_buffer_size 配置数值根据自己的场景需求进行调整即可。再次重启服务,会看到我们已经能够获取正确的远程接口数据内容了。

    从远端获取的数据内容

    编写具备聚合功能的程序

    因为我们要聚合多个接口,所以我们将 NJS 代码和 Nginx 配置同时进行一些调整。

    我在这里就不演示很挫的顺序执行模式了,因为对于这些无上下文依赖的接口,使用异步并发获取的方式可以消耗尽可能少的时间来提供结果。当然,串行请求也是有场景的,我会在后面的文章中提到如何灵活使用 NJS 控制请求流程。

    // https://github.com/nginx/njs/issues/352#issuecomment-721126632
    function resolveAll(promises) {
      return new Promise((resolve, reject) => {
        var n = promises.length;
        var rs = Array(n);
        var done = () => {
          if (--n === 0) {
            resolve(rs);
          }
        };
        promises.forEach((p, i) => {
          p.then((x) => {
            rs[i] = x;
          }, reject).then(done);
        });
      });
    }
    
    function aggregation(req) {
      var apis = ["/proxy/api-mysql/common/chat/chat-translation-data.json", "/proxy/api-redis/wp-content/themes/wpx/proxy/signup_proxy.php"];
      resolveAll(apis.map((api) => req.subrequest(api)))
        .then((responses) => {
          var result = responses.reduce((prev, response) => {
            var uri = response.uri;
            var prop = uri.split("/proxy/api-")[1].split("/")[0];
            try {
              var parsed = JSON.parse(response.responseText);
              if (response.status === 200) {
                prev[prop] = parsed;
              }
            } catch (err) {
              req.error(`Parse ${uri} failed.`);
            }
            return prev;
          }, {});
          req.headersOut["Content-Type"] = "application/json;charset=UTF-8";
          req.return(200, JSON.stringify(result));
        })
        .catch((e) => req.return(501, e.message));
    }
    
    export default { aggregation };
    

    接着对 Nginx 配置文件中的部分进行调整:

    ...
    location / {
        js_content app.aggregation;
    }
    
    subrequest_output_buffer_size 200k;
    
    location /proxy/api-mysql {
        internal;
        gunzip on;
        proxy_pass https://www.mysql.com/;
        proxy_set_header Host www.mysql.com;
    }
    
    location /proxy/api-redis {
        internal;
        gunzip on;
        proxy_pass https://redislabs.com/;
        proxy_set_header Host redislabs.com;
    }
    ...
    

    最后再次启动服务,来验证我们能否拿到正确的远程数据,并将数据们进行聚合。

    It works

    看样子,我们已经拿到了我们想要的结果,接着来简单聊聊容器封装。

    使用容器对 NJS 应用进行封装

    前文提到,NJS 模块由 Nginx 官方镜像默认支持,我们可以直接使用 nginx:1.19.8-alpine 为基础来进行镜像构建。

    镜像文件非常简单,只需要三行:

    FROM nginx:1.19.8-alpine
    COPY nginx.conf /etc/nginx/nginx.conf
    COPY app.js /etc/nginx/script/app.js
    

    将上面的内容保存为 Dockerfile,然后使用 docker build -t njs-api . 构建出我们的镜像。

    如果你选择使用 docker images 查看镜像,你会发现我们构建的镜像非常小巧,几乎能够和 Nginx 官方镜像尺寸保持一致,所以在公网分发的时候,会有非常大的优势,根据 docker 增量分发的特性,我们其实只会分发上面那三行配置中的后两行构建结果(layers),差不多几 KB。

    njs-api                                       latest                         f4b6de5dacb8   3 minutes ago       22.6MB
    nginx                                         1.19.8-alpine                  5fd75c905b52   7 days ago          22.6MB
    

    在构建镜像之后,使用 docker run --rm -it -p 8090:80 njs-api 可以进一步验证服务是否能够正常运行,不出意外,会得到上一小节图片中的结果。

    最后

    好了,来总结一下。

    本篇文章中,因为我们没有使用任何非 Nginx 镜像外的 Runtime ,所以得到的镜像结果非常小巧,十分利于进行网络分发。

    同时因为 NJS 和 Nginx 简单清晰的设计理念,NJS 程序伴随请求生命周期结束而释放,NJS 引擎执行效率比较高,以及NJS 引擎本身只是实现了 ECMA 的一个子集(整体复杂度低),加之子请求的生命周期非常短暂,所以我们的服务可以使用非常低的资源(接近于 Nginx 原生资源占用)提供一个接近 Nginx 原生服务的性能。

    如果你经常写业务代码,你会发现本文留下了一些明显可以改进性能的话题没有诉诸笔墨:如何提聚合接口的性能,如何在定制过的 Nginx 镜像、环境中和三方模块一起工作,以及 NJS 到底能够干哪些更复杂的活?

    下一篇 NJS 内容,我将展开聊聊这些。

    –EOF


    本文使用「署名 4.0 国际 (CC BY 4.0)」许可协议,欢迎转载、或重新修改使用,但需要注明来源。 署名 4.0 国际 (CC BY 4.0)

    本文作者: 苏洋

    创建时间: 2021年03月18日
    统计字数: 9759字
    阅读时间: 20分钟阅读
    本文链接: https://soulteary.com/2021/03/18/use-docker-and-nginx-njs-to-implement-api-aggregation-service-part-1.html

    展开全文
  • 从封装 Nginx NJS 工具镜像聊起 最近发现有不少需求可以通过 Nginx JavaScript (NJS)来完成,相比较运行一套完整的 Web 服务来说,轻量高效的方案总是惹人喜爱,更何况这套方案是由 Nginx 官方团队推出,并搭上了...

    从封装 Nginx NJS 工具镜像聊起

    最近发现有不少需求可以通过 Nginx JavaScript (NJS)来完成,相比较运行一套完整的 Web 服务来说,轻量高效的方案总是惹人喜爱,更何况这套方案是由 Nginx 官方团队推出,并搭上了繁荣的 JavaScript 生态。

    本篇文章先从 NJS 容器封装、以及容器镜像优化来聊聊。

    写在前面

    NJS 目前还处于相对早期的版本,截止本篇文章发布,官方最新的版本是 0.5.0,官网并没有二进制文件可以下载,软件随 Nginx 应用的各版本软件包提供,目前并未独立提供。

    不过为了更方便的进行脚本调试,能够使用显式声明的使用 NJS 的运行时,我创建了一个开源项目,包含了 NJS 目前的主要版本的容器镜像:https://github.com/soulteary/docker-njs

    相比较官方镜像动辄 20MB 来说,最小的版本不到 1MB,更小的尺寸带来的是更轻量和快速的体验。如果你想获取最新的镜像,可以访问 DockerHub 官方仓库

    下面来聊聊如何针对 NJS 进行镜像封装以及过程中的一些思考。

    基于官方镜像进行镜像构建

    构建 NJS 镜像的最简单的方式是从官方容器中直接提取我们所需要的可执行文件。况且,相对于自行编译,官方构建产物更让人用的放心一些。

    通过分析发现 NJS 依赖 libpcrelibeditlibncursesw,所以除了将 njsbin 文件提取之外,还需要将上述依赖库进行拷贝 。

    以最新版本的 NJS 封装为例:

    FROM nginx:1.19.6-alpine AS builder
    
    FROM alpine:3.12
    COPY --from=builder /usr/bin/njs /usr/bin/njs
    COPY --from=builder /usr/lib/libpcre.so.1.2.12 \
                        /usr/lib/libedit.so.0.0.63 \
                        /usr/lib/libncursesw.so.6.2 \
                        /usr/lib/
    RUN ln -sf /usr/lib/libpcre.so.1.2.12 /usr/lib/libpcre.so.1 && \
        ln -sf /usr/lib/libedit.so.0.0.63 /usr/lib/libedit.so.0 && \
        ln -sf /usr/lib/libncursesw.so.6.2 /usr/lib/libncursesw.so.6
    
    ENTRYPOINT [ "njs" ]
    

    这样一个基础的 NJS 镜像就构建好了。

    针对不同版本进行构建

    常常使用容器的小伙伴都知道 Nginx 官方提供了 Alpine / Debian 两个版本的镜像,而 NJS 目前也有三个小版本:0.3.x / 0.4.x 以及最新的 0.5.x,而这几个版本对于上述依赖库的版本、以及基础 Nginx 依赖都略有不同。

    为了减少代码重复,以及提高代码可维护性,可以将不同版本的依赖单独声明为 .env 配置文件,然后搭配一个抽象度比较高的容器配置文件,对多个版本进行构建。以0.5.0 的 NJS 为例:

    DIST_OS=debian:10
    NGX_VER=1.19.6
    PCRE_VER=1.2.12
    EDIT_VER=0.0.63
    CURSESW_VER=6.2
    

    将上面的文件保存为 .env 保存至 njs/0.5.0/.env,接着开始编写 Dockerfile:

    ARG DIST_OS=alpine:3.12
    ARG NGX_VER=1.19.6-alpine
    
    FROM "nginx:$NGX_VER" AS builder
    
    FROM "$DIST_OS"
    COPY --from=builder /usr/bin/njs /usr/bin/njs
    COPY --from=builder /usr/lib/libpcre.so.* \
                        /usr/lib/libedit.so.* \
                        /usr/lib/libncursesw.so.* \
                        /usr/lib/
    RUN ls /usr/lib/libpcre.so.*.* | xargs -I {} ln -sf {} $(echo {} | cut -b 1-21) && \
        ls /usr/lib/libedit.so.*.* | xargs -I {} ln -sf {} $(echo {} | cut -b 1-21) && \
        ls /usr/lib/libncursesw.so.*.* | xargs -I {} ln -sf {} $(echo {} | cut -b 1-25)
    
    ENTRYPOINT [ "njs" ]
    

    其他几个版本也可以如法炮制,最终整个项目结构如下:

    ├── Dockerfile
    ├── LICENSE
    ├── README.md
    ├── docker-build.sh
    ├── docker-slim.sh
    └── njs
        ├── 0.3.9
        ├── 0.3.9-alpine
        ├── 0.4.4
        ├── 0.4.4-alpine
        ├── 0.5.0
        └── 0.5.0-alpine
    

    为了能够自动化的构建各个版本的 NJS 镜像,我们需要编写一个 BASH 脚本:

    #!/bin/bash
    
    RELEASE_DIR='./njs';
    REPO_NAME='soulteary/docker-njs'
    
    for njs_ver in $RELEASE_DIR/*; do
        tag=$(echo $njs_ver | cut -b 7-);
        echo "Build: $tag";
        set -a
            . "$njs_ver/.env"
        set +a
    
        docker build --build-arg DIST_OS=$DIST_OS --build-arg NGX_VER=$NGX_VER --build-arg PCRE_VER=$PCRE_VER --build-arg EDIT_VER=$EDIT_VER --build-arg CURSESW_VER=$CURSESW_VER --tag $REPO_NAME:$tag .
    done
    

    将上面的内容保存为 make-image.sh,然后执行它之后就能得到各个版本的镜像了。

    REPOSITORY      TAG              IMAGE ID       CREATED         SIZE
    njs             0.5.0-alpine     0f6e379160a1   About a minute ago    8.07MB
    njs             0.5.0            80071066f7ca   About a minute ago    115MB
    njs             0.4.4-alpine     5b9fb7872be3   About a minute ago    8.06MB
    njs             0.4.4            d6663992a6ec   About a minute ago    115MB
    njs             0.3.9-alpine     155e2a710c02   About a minute ago    7.97MB
    njs             0.3.9            7a041ccd4f86   About a minute ago    101MB
    

    使用 docker-slim 优化镜像尺寸

    上文构建完毕的镜像尺寸略大了一些,可以借助 Docker Slim 进行精简。下载Docker Slim 后,使用命令对原有镜像进行二次构建,即可构建出新的小巧的镜像:

    docker-slim build --target soulteary/njs:0.5.0 --tag soulteary/njs:0.5.0-slim --http-probe=false
    

    为了减少后续维护成本,我们可以和之前构建不同版本 NJS 一样,准备一个 slim.sh 脚本,简化后续操作:

    #!/bin/bash
    
    RELEASE_DIR='./njs';
    REPO_NAME='soulteary/docker-njs'
    
    for njs_ver in $RELEASE_DIR/*; do
        tag=$(echo $njs_ver | cut -b 7-);
        echo "Build: $tag";
        set -a
            . "$njs_ver/.env"
        set +a
    
        docker-slim build --target $REPO_NAME:$tag --tag $REPO_NAME:$tag-slim --http-probe=false
    done
    

    脚本执行完毕,可以看到本地镜像尺寸有了大幅的减少,如果我们推送到 DockerHub,官方镜像仓库会对镜像进一步压缩,最终最小的镜像尺寸会在 1MB 以内,非常利于快速启动,进行调试。

    REPOSITORY      TAG                 IMAGE ID       CREATED              SIZE
    njs             0.5.0-alpine-slim   9cd49bb22a26   About a minute ago   2.17MB
    njs             0.5.0-slim          766a3f6ef92b   About a minute ago   2.96MB
    njs             0.4.4-alpine-slim   2f401dee2bd6   About a minute ago   2.16MB
    njs             0.4.4-slim          d62a8af54253   About a minute ago   2.96MB
    njs             0.3.9-alpine-slim   5dc7a6799f66   About a minute ago   2.03MB
    njs             0.3.9-slim          32b0ec660c4b   About a minute ago   2.28MB
    

    生成批量推送镜像脚本

    一次性生成多个镜像之后,如果是手动推送到 DockerHub 其实挺繁琐的,这个时候可以使用Docker Image 命令 来“偷懒”:

    docker images soulteary/docker-njs --format "docker push {{.Repository}}:{{.Tag}}"
    

    使用格式参数,可以快速生成带 docker push 的命令,然后不论是在命令行中通过管道符执行,还是保存为文件执行,都可以做到批量推送镜像啦:

    docker push soulteary/docker-njs:0.5.0-alpine-slim
    docker push soulteary/docker-njs:0.5.0-slim
    docker push soulteary/docker-njs:0.5.0-alpine
    docker push soulteary/docker-njs:0.5.0
    

    其他

    Nginx 在去年十二月发布了主线版本的例行更新,版本升级到了 1.19.6,官方对于本次升级只有聊聊数语,未曾提到 NJS 相关的事情:

    Changes with nginx 1.19.6                                        15 Dec 2020
    
        *) Bugfix: "no live upstreams" errors if a "server" inside "upstream"
           block was marked as "down".
    
        *) Bugfix: a segmentation fault might occur in a worker process if HTTPS
           was used; the bug had appeared in 1.19.5.
    
        *) Bugfix: nginx returned the 400 response on requests like
           "GET http://example.com?args HTTP/1.0".
    
        *) Bugfix: in the ngx_http_flv_module and ngx_http_mp4_module.
           Thanks to Chris Newton.
    

    但是因为折腾 NJS 运行时镜像,发现了这个隐藏在主版本更新日志外的变更,发现了这个时隔三个月大量更新的 0.5.0。

    最后

    我创建了一个名为 njs-learning-materials 的开源仓库,目前已经整理了 NJS 相关的一些开源参考资料,后续会随着更深入的折腾,不断更新和补充内容。

    如果你感兴趣的话,欢迎加入我一起折腾。

    –EOF


    我现在有一个小小的折腾群,里面聚集了一些喜欢折腾的小伙伴。

    在不发广告的情况下,我们在里面会一起聊聊软件、HomeLab、编程上的一些问题,也会在群里不定期的分享一些技术沙龙的资料。

    喜欢折腾的小伙伴欢迎扫码添加好友。(请注明来源和目的、实名入群,否则不会通过审核)

    关于折腾群入群的那些事


    本文使用「署名 4.0 国际 (CC BY 4.0)」许可协议,欢迎转载、或重新修改使用,但需要注明来源。 署名 4.0 国际 (CC BY 4.0)

    本文作者: 苏洋

    创建时间: 2021年01月10日
    统计字数: 5051字
    阅读时间: 11分钟阅读
    本文链接: https://soulteary.com/2021/01/10/let-us-start-with-the-mirroring-of-the-nginx-njs-tool-package.html

    展开全文
  • nginx-njs-usecases-源码

    2021-07-24 12:08:24
    nginx-njs-用例 这是一个展示 NGINX NJS 用法和能力的项目。 在这个代码库中,有几个 NJS 用例实现。 所有用例都在单独的文件夹中。 从源码安装,nginx 版本、模块、编译过程等详细信息请参见Dockerfile 。 有关...
  • 用于nginx +的njs 设置 将您的nginx-repo.crt和nginx-repo.key复制到nginx-plus / licenses / # # access from local vault # # access secret from google secretsmanager secretName= " my-nginx-secrets " ...
  • 该模板使用和将TypeScript源代码编译为njs和的单个JavaScript文件,并通过对NGINX服务器运行集成测试。 目录 先决条件 要在本地构建和运行此项目,您将需要: Linux系统[ 1 ] ,macOS [ 2 ]或Windows 10+(x64) ...
  • babel-preset-njsNJS的Babel预设-NGINX JavaScript
  • <div><p>Ref http://nginx.org/en/docs/njs/changes.html#njs0.3.0</p><p>该提问来源于开源项目:alpinelinux/aports</p></div>
  • njs 是 JavaScript/ECMAscript 的子集 。它实现了大部分的 JavaScript 语言的功能,没有完全遵从 ECMAScript 标准,同时抛弃了...

    640?wx_fmt=png

    njs 是 JavaScript/ECMAscript 的子集 。它实现了大部分的 JavaScript 语言的功能,没有完全遵从 ECMAScript 标准,同时抛弃了 JavaScript 比较难懂的部分。njs 不是通过 V8 引擎实现的,而是通过一个更小、能耗更低、更符合 Nginx 应用场景的小虚拟机(VM)来实现,可以理解为 Nginx 为其实现了一套自己的词法解析。

    njs 是以 Nginx 插件的方式存在,名字就叫:njs 。和其他 Nginx 插件一样,我们需要重新编译 Nginx 来完成安装。

    而在近日,njs插件被安全研究员Alisa Shevchenko

    640?wx_fmt=png

    640?wx_fmt=png

    Nginx 也在回复中声明,ZDI-CAN-8296 was fixed in the nJS 0.3.2 release。

    并附带了两个链接。

    也就是下面这个github issues中提到的问题,从中也许你们可以找到一些线索去发现和构造。

    https://github.com/nginx/njs/issues/131

    640?wx_fmt=png

    640?wx_fmt=png

    热心网友甚至还给出了patch方法

    640?wx_fmt=png

    https://github.com/nginx/njs/issues/159

    640?wx_fmt=png

    由于Nginx在自2009年以来的,仅发现了大约20个漏洞,其中没有一个RCE漏洞。

    因此导致此次事件的关注程度在web安全界的得到了轰炸。

    甚至不亚于

    因此,戏精再次出现,外网一位安全研究员发布了一个POC。

    640?wx_fmt=png

    640?wx_fmt=png

    然后该条推文得到了疯狂转发。

    再转发得到600多量后,作者发布了一篇文章。

    https://medium.com/@notdan/curl-slight-of-hand-exploit-hysteria-29a82e5851d

    640?wx_fmt=png

    文章里面完全披露了他是如何构造一个假的poc从开玩笑

    640?wx_fmt=png

    同时,他也解释了这条语句的含义,黑鸟还是从中学到了一些技巧,也算是感谢他这波教你做人了。

    curl -gsS (link: https://victim.server.here:443/../../../%00/nginx-handler?/usr/lib/nginx/modules/ngx_stream_module.so:127.0.0.1:80:/bin/sh%00) victim.server.here/../../../%00/n…\<'protocol:TCP' -O 0x0238f06a#PLToffset |sh; nc /dev/tcp/localhost

    640?wx_fmt=png

    除此之外,还教导了一些钓鱼的思路,也是很骚,怪不得能钓到这么多人,也是奇才。

    640?wx_fmt=png

    不过恐怕,昨天的流传POC已经出现,说的就是这个玩意吧。

    再次强调:目前公开渠道并未披露任何细节(除Gayhub外)。

    640?wx_fmt=png

    <上期看点>

    黑鸟威胁情报中心,只做最正确的情报,不传谣不信谣,欢迎扫码持续关注。

    点击菜单栏,扫码加入每日更新的知识星球原价299,现价269

    640?wx_fmt=png

    640?wx_fmt=png

    展开全文
  • cache_purge,nginx_upstream_check_module ) 3、编译安装 Njs的安装,此处可选择跳过 njs下载 或 cd openresty-1.17.8.1rc1/bundle wget http://hg.nginx.org/njs/archive/tip.tar.gz tar -zxvf tip.tar.gz 或 ...

    一、编译安装

    1、Openresty下载编译包

    OpenResty® 提供 官方预编译包,确保你首先用这种方式来安装,选择你要的包。
    在这里插入图片描述
    在这里插入图片描述
    或者使用wget下载:

    #  推荐去下载最新版
    wget https://openresty.org/download/openresty-1.17.8.1rc1.tar.gz 
    tar -zxvf openresty-1.17.8.1rc1.tar.gz
    cd openresty-1.17.8.1rc1/bundle
     #  去下载最新版
    wget http://labs.frickle.com/files/ngx_cache_purge-2.3.tar.gz
    tar -zxvf ngx_cache_purge-2.3.tar.gz
    wget https://github.com/yaoweibin/nginx_upstream_check_module/archive/v0.3.0.tar.gz 
    tar -zxvf v0.3.0.tar.gz
    

    2、检查文件

    检查./openresty/bundle目录下有没有这两个文件包(ngx_cache_purge,nginx_upstream_check_module

    3、编译安装

    Njs的安装,此处可选择跳过
    njs下载
    在这里插入图片描述

    cd openresty-1.17.8.1rc1/bundle
    wget http://hg.nginx.org/njs/archive/tip.tar.gz
    tar -zxvf tip.tar.gz 或 tar -zxvf njs-92838d964b19.tar.gz
    mv njs-92838d964b19/ njs/
    

    编译Nginx

    ./configure --prefix=/usr/local/openresty --with-luajit --with-http_ssl_module --user=root --group=root --with-http_realip_module --add-module=./bundle/njs/nginx/
    
    
    --add-module=./bundle/ngx_cache_purge-2.3/ --add-module=./bundle/nginx_upstream_check_module-0.3.0/
    

    你还想要一些其他的模块随意去选择去添加就行了!

    安装Nginx

    make & make install
    

    添加环境变量

    echo "export PATH=$PATH:/usr/local/openresty/nginx/sbin" >> /etc/profile
    source /etc/profile
    

    测试

    nginx.conf

    
    http {
        include       mime.types;
        default_type  application/octet-stream;
        sendfile        on;
        js_include hello_world.js;
        server {
            listen       80;
            server_name  localhost;
    		location / {
                root   html;
                index  index.html index.htm;
            }
    
            location /lua {
    	    charset UTF-8;
                #content_by_lua_file conf/hello.lua;
                rewrite_by_lua_file conf/hello.lua;
            }
            
    		location /helloNjs {
    	   	    js_content hello;
            }
    
            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   html;
            }
        }
    

    hello.lua

    ngx.say('ngx.var.request_uri  '..ngx.var.request_uri);
    ngx.say('ngx.var.host  '..ngx.var.host);
    ngx.say('ngx.var.request_method  '..ngx.var.request_method);
    

    hello_world.js

    function hello(r) {
        r.return(200, "Hello world");
    }
    
    
    展开全文
  • NGINX JavaScript示例 内容 证明书阅读客户证书中的替代主题[http / certs / subject_alternative] 代理 子请求加入[http / join_subrequests] 子请求链接[http / subrequests_chaining] 修改回应 修改或删除...
  • 具有支持模块njs和Brotli的nginx容器,也使用BoringSSL代替OpenSSL。 测验 $ docker run -p 8080:80 ngxjs:v0.0.2 $ ./tests/test.sh Test passed ! 用法 在您的Dockerfile中,只需在FROM指令中使用并将您的资产...
  • Nginx 允许引入第三方模块来扩展 Nginx 的功能。...njsNginx + JavaScript 的缩写,简单来说,就是 Nginx 里面可以运行 JavaScript,用 JavaScript 来构建动态的 Web 应用。Nginx NJS 包含两个 Nginx 扩展模块
  • 在继续 Nginx NJS 实战之前,我们可以先了解下如何相对快速、安全的使用 Nginx 三方模块。 写在前面 本文中的三方模块相关代码,已经提交至开源仓库:https://github.com/soulteary/prebuilt-nginx-modules,欢迎...
  • 开发四年只会写业务代码,分布式高并发都不会还做程序员? njs 是 JavaScript/ECMAscript 的子集...
  • nginx

    2019-01-07 14:24:55
    nginx[engine x]是一个HTTP和反向代理服务器,一个邮件代理服务器和一个通用的TCP/UDP代理服务器,起初是...通过Netcraft,nginx在2018年12月服务或代理了25.98%的最忙碌的网站。 基本的Http服务器特征 服务静态文...
  • 带有nginx 1.19.5 (主线),HTTP / 3(QUIC),TLSv1.3、0-RTT,brotli,NJS,Cookie-Flag支持的Alpine Linux映像。 全部建立在最前沿。 建在边缘,为边缘。 的聪明人通过项目提供的HTTP / 3支持。 可在上找到...
  • nginx,inc工程师,说明了如何使用njs设置S3网关并创建njs单元测试。 项目 通过身份验证 @Vasiliy Soshnikov的S3代理 JWT由@ lombax85 @ihironao的Mod Security 的OAuth / OIDC代理通过@kaz @Patrick Nelson...
  • NJS旨在成为NGINX的通用脚本框架。 NJS允许用户用脚本的方式给复杂业务场景下的流量负载添加处理逻辑。 它是扩展NGINX 应用的一把利刃。 编者注: 文本所涉及源码均可通过访问以下github 代码仓库获得。 ...
  • Nginx安装(yum) yum安装Nginx,需要使用到epel-release源。这个可以先通过yum安装源。不过这个源安装的Nginx可能版本比较老。 你也可以自己弄一个Nginx源 写一个文件: [root@shuai-01 ~]# vim /etc/yum.repos...
  • Nginx 动态模块

    2019-11-22 16:12:01
    Nginx 动态模块 本节介绍如何在NGINX Plus中使用...动态模块为NGINX Plus添加了功能,例如通过IP地址对用户进行地理位置定位,调整图像大小以及将njs或Lua脚本嵌入到NGINX Plus事件处理模型中。模块由NGINX,In...
  • Nginx 安装

    2018-05-21 16:23:21
    Nginx 官方文档 这里只讲在 Linux 安装编译好的二进制包。源码安装参考 这里。 安装稳定版本 稳定版本安全可靠。 RHEL/CentOS 对于 RHEL/CentOS,需要创建 /etc/yum.repos.d/nginx.repo 文件,内容如下,注意...
  • yum配置nginx官方源来安装nginx

    千次阅读 2019-03-10 14:16:17
    1、由于 yum 源中没有我们想要的 nginx,那么我们就需要创建一个“/etc/yum.repos.d/nginx.repo”的文件,其实就是新增一个 yum 源。 [root@niaoyun~]# vim /etc/yum.repos.d/nginx.repo 2、然后将下面的内容...
  • Ubuntu安装Nginx

    2021-02-03 14:41:49
    安装Nginx Ubuntu默认Nginx Ubuntu14.04默认安装的是Nginx 1.4.6: ...采用该方式也可以下载Nginx的prebulid模块,如nginx-module-njs。 可以在source.list中增加Nginx源: 下载nginx_signing.key wget http:/

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 654
精华内容 261
热门标签
关键字:

nginxnjs