nginx dockerfile 模块

一、构建基础编译镜像(参考官方dockerfile)写出基础编译环境的Dockerfile
1.1 创建baseImage目录
mkdir -p baseImage
1.2 创建 .env 变量文件构建参数进行存储
vim ./baseImage/.env
NGINX_VERSION=1.19.8
NGINX_VERSION=1.19.8
NGINX_SHASUM=3e6d39a714f6716861286630a5f9df3044668d5a
1.3 创建Dockerfile
vim Dockerfile
ARG NGINX_VERSION=1.19.8
FROM nginx:${NGINX_VERSION}-alpine
RUN cat /etc/apk/repositories | sed -e "s/dl-cdn.alpinelinux.org/mirrors.aliyun.com/" | tee /etc/apk/repositories
ARG NGINX_SHASUM=3e6d39a714f6716861286630a5f9df3044668d5a
RUN apk add --no-cache --virtual .build-deps gcc libc-dev make openssl-dev pcre-dev zlib-dev linux-headers libxslt-dev gd-dev geoip-dev perl-dev libedit-dev mercurial bash alpine-sdk findutils && \
    mkdir -p /usr/src && cd /usr/src && \
    curl -L "http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz" -o nginx.tar.gz && \
    echo "$NGINX_SHASUM  nginx.tar.gz" | shasum -c && \
    tar -zxC /usr/src -f nginx.tar.gz && \
    cd /usr/src && \
    mv /usr/src/nginx-$NGINX_VERSION /usr/src/nginx && \
    CONFARGS=$(nginx -V 2>&1 | sed -n -e 's/^.*arguments: //p') \
    CONFARGS=${CONFARGS/-Os -fomit-frame-pointer -g/-Os} && \
    export CONFARGS=$CONFARGS;

1.4 创建编译基础镜像脚本
#!/bin/bash

RELEASE_DIR="./baseImage";
REPO_NAME="soulteary/prebuilt-nginx-modules"

set -a
    . "$RELEASE_DIR/.env"
set +a

TAG=base-$NGINX_VERSION-alpine;
DIST=$REPO_NAME:$TAG
echo "Build: $DIST";
echo $NGINX_VERSION:$NGINX_SHASUM
echo "docker build --build-arg NGINX_VERSION=$NGINX_VERSION --build-arg NGINX_SHASUM=$NGINX_SHASUM --tag $DIST -f "$RELEASE_DIR/Dockerfile" ."
docker build --build-arg NGINX_VERSION="$NGINX_VERSION" --build-arg NGINX_SHASUM="$NGINX_SHASUM" --tag $DIST -f "$RELEASE_DIR/Dockerfile" .

1.5 运行编译基础镜像脚本
.
├── baseImage
│   ├── Dockerfile
│   └── nginx-1.19.8.tar.gz
└── build.sh

bash build.sh

#结果:
Executing busybox-1.34.1-r3.trigger
OK: 370 MiB in 111 packages
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 1048k  100 1048k    0     0   109k      0  0:00:09  0:00:09 --:--:--  148k
nginx.tar.gz: OK
Removing intermediate container fe836d91084c
 ---> 70bf1f1e372c
Successfully built 70bf1f1e372c
Successfully tagged soulteary/prebuilt-nginx-modules:base-1.21.6-alpine

二、使用容器编译nginx模块

有了构建环境,编译模块的步骤将能大幅简化,以常用的 Nginx 三方模块“headers-more-nginx-module”为例子,基于前文中的构建环境,我们编写一个模块构建脚本也很容易:
ARG NGINX_VERSION=1.19.8
FROM soulteary/prebuilt-nginx-modules:base-${NGINX_VERSION}-alpine AS Builder
ARG MODULE_CHECKSUM=7d6af910dae98f0dbc67bf77e82eab8b7da5d0b1
ARG MODULE_VERSION=0.33
ARG MODULE_NAME=headers-more-nginx-module

RUN cd /usr/src && \
    curl -L "https://github.com/openresty/headers-more-nginx-module/archive/v${MODULE_VERSION}.tar.gz" -o "v${MODULE_VERSION}.tar.gz" && \
    echo "${MODULE_CHECKSUM}  v${MODULE_VERSION}.tar.gz" | shasum -c && \
    tar -zxC /usr/src -f v${MODULE_VERSION}.tar.gz && \
    cd /usr/src && \
    mv ${MODULE_NAME}-${MODULE_VERSION}/ ${MODULE_NAME} && \
    cd /usr/src/nginx && \
    CONFARGS=$(nginx -V 2>&1 | sed -n -e 's/^.*arguments: //p') \
    CONFARGS=${CONFARGS/-Os -fomit-frame-pointer -g/-Os} && \
    echo $CONFARGS && \
    ./configure --with-compat $CONFARGS --add-dynamic-module=../${MODULE_NAME}/ && \
    make modules

FROM scratch
COPY --from=Builder /usr/src/nginx/objs/ngx_http_headers_more_filter_module.so /

2.1 解析Dockerfile
第一部分,负责抽象我们使用的 Nginx 环境和代码版本
以及我们使用的模块名称、版本、代码包校验和,为我们今后能够尽可能少的写代码来维护镜像做准备

第二部分,准备源代码、调整代码目录结构、复用官方构建命令、对模块进行静态编译

第三部分,使用一个特殊的空镜像,将我们的构建产物保留,以供未来生产环境的镜像快速复用

2.2 创建 .env 变量文件构建参数进行存储
NGINX_VERSION=1.19.8
MODULE_CHECKSUM=7d6af910dae98f0dbc67bf77e82eab8b7da5d0b1
MODULE_VERSION=0.33
MODULE_NAME=headers-more-nginx-module


三、 完成批量模块的构建
为了能够对多个模块进行构建管理,我们来了解下如何编写“支持多个模块构建”的构建脚本
假设项目目录结构类似下面的形式:

mkdir -p modules/{misc,echo,headers-more,http-redis, memc,misc,redis2,srcache,waf}
touch modules/{misc,echo,headers-more,http-redis, memc,misc,redis2,srcache,waf}/Dockerfile
.
├── README.md
├── baseImage
│   └── Dockerfile
├── make-base.sh
├── make-image.sh
├── modules
│   ├── echo
│   │   └── Dockerfile
│   ├── headers-more
│   │   └── Dockerfile
│   ├── http-redis
│   │   └── Dockerfile
│   ├── memc
│   │   └── Dockerfile
│   ├── misc
│   │   └── Dockerfile
│   ├── redis2
│   │   └── Dockerfile
│   ├── srcache
│   │   └── Dockerfile
│   └── waf
│       └── Dockerfile
└── push-image.sh

对之前的“基础编译环境”的构建脚本进行适当调整和修改,可以得到支持批量构建模块的脚本:
mv build.sh make-base.sh && vim make-base.sh

#!/bin/bash

RELEASE_DIR="./modules";
REPO_NAME="soulteary/prebuilt-nginx-modules"

for moduleName in $RELEASE_DIR/*; do
    set -a
        . "$moduleName/.env"
    set +a

    tag=$(echo $moduleName | cut -b 11-);
    BUILD_NAME="$REPO_NAME:$tag-$NGINX_VERSION-alpine"
    echo "Build: $BUILD_NAME";
    BUILD_ARGS=$(tr '\n' ';' < "$moduleName/.env" | sed 's/;$/\n/' | sed 's/^/ --build-arg /' | sed 's/;/ --build-arg /g')

    docker build $BUILD_ARGS --tag $BUILD_NAME -f $moduleName/Dockerfile .
done
然后,只要执行这个脚本,就能够根据每个模块的不同配置信息,构建出可复现的稳定结果了

四、基于容器快速使用 Nginx 三方模块
目前为止,我们已经了解了如何在容器内快速编译构建 Nginx 三方模块,接下来我们可以步入正题,如何快速使用这些模块。
假设我们现在需要一个能够直接返回简单 JSON 的接口,接口包含当前服务器端端时间,并且这个接口有很高的调用压力,诸如活动、秒杀等场景的高频调用,可以使用 Nginx 借助 Nginx Echo 和 Set Misc 模块来进行实现
相关代码: https://github.com/soulteary/docker-nginx-time-api


五、编写使用预编译模块的容器文件 (只需要将编译好的模块文件复制到目标镜像即可)
cd modules/misc/
vim Dockerfile
FROM nginx:1.19.8-alpine
COPY --from=soulteary/prebuilt-nginx-modules:misc-1.19.8-alpine /ndk_http_module.so          /etc/nginx/modules/
COPY --from=soulteary/prebuilt-nginx-modules:misc-1.19.8-alpine /ngx_http_set_misc_module.so /etc/nginx/modules/
COPY --from=soulteary/prebuilt-nginx-modules:echo-1.19.8-alpine /ngx_http_echo_module.so /etc/nginx/modules/

# 因为 Set Misc 模块依赖 NDK 模块,所以这里要复制三个文件
# 将上面的内容保存为 Dockerfile
# 然后执行构建第一个基于预编译模块的 Nginx 镜像
 docker build -t nginx-time-api:1.19.8-alpine

准备nginx 启动配置文件和文件
docker run --name nginx -p 80:80 -d --rm nginx
mkdir -p /data/nginx/{conf,web,file,logs}
docker run -it'd --name=nginx nginx:latest
docker cp nginx:/etc/nginx/conf.d /data/nginx/conf/
docker cp nginx:/etc/nginx/fastcgi_params /data/nginx/conf/
docker cp nginx:/etc/nginx/mime.types /data/nginx/conf/
docker cp nginx:/etc/nginx/modules /data/nginx/conf/
docker cp nginx:/etc/nginx/nginx.conf /data/nginx/conf/ 
docker cp nginx:/etc/nginx/scgi_params /data/nginx/conf/
docker cp nginx:/etc/nginx/uwsgi_params /data/nginx/conf/

准备nginx 配置文件
vim /data/nginx/nginx.conf (添加如下3行 *.so)

load_module modules/ndk_http_module.so;
load_module modules/ngx_http_set_misc_module.so;
load_module modules/ngx_http_echo_module.so;

user nginx;
worker_processes auto;
....

准备nginx 默认server配置文件
server {
    listen 80;
    server_name localhost;
    charset utf-8;
    gzip on;

    location / {
        default_type application/json;
        set_formatted_gmt_time $timestr "%a %b %e %H:%M:%S %Y GMT";
        echo $timestr;
    }
}

启动容器yml
version: '3'
services:
  ngx-time-api:
    image: nginx-time-api:1.19.8-alpine
    container_name: nginx-time-api
    hostname:nginx-time-api
    restart: always
    ports:
      - 80:80
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
      - /data/nginx:/etc/nginx

或 docker run
docker run -itd \
-p 80:80 -p 443:443 \
-v /data/nginx/conf:/etc/nginx \
-v /data/nginx/logs:/var/log/nginx/
-v /data/nginx/web:/opt/web \
-v /data/nginx/file:/opt/file \
--hostname=nginx \
--name=nginx \
--restart=always \
nginx-time-api:1.19.8-alpine

六 测试
这里直接使用 wrk 做一个简单测试,可以看到在容器环境下,经过 NAT 转发,依旧能够达到每秒 2万 QPS

wrk -t2 -c 100 -d 10s http://localhost:80
Running 10s test @ http://localhost:80
  2 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     4.26ms    1.77ms  27.69ms   76.80%
    Req/Sec    11.67k     1.21k   13.81k    62.00%
  232312 requests in 10.01s, 44.74MB read
Requests/sec:  23218.65
Transfer/sec:      4.47MB

七、开箱即用的模块
开箱即用的模块
https://github.com/soulteary/prebuilt-nginx-modules
这个开源仓库中,目前有以下常见模块可以直接使用,后续我会根据需求逐步将更多的常用、好用的模块加进来

soulteary/prebuilt-nginx-modules:headers-more-1.19.8-alpine
soulteary/prebuilt-nginx-modules:http-redis-1.19.8-alpine
soulteary/prebuilt-nginx-modules:echo-1.19.8-alpine
soulteary/prebuilt-nginx-modules:set-misc-1.19.8-alpine
soulteary/prebuilt-nginx-modules:redis2-1.19.8-alpine
soulteary/prebuilt-nginx-modules:memc-1.19.8-alpine
soulteary/prebuilt-nginx-modules:srcache-1.19.8-alpine
soulteary/prebuilt-nginx-modules:base-1.19.8-alpine
soulteary/prebuilt-nginx-modules:waf-1.19.8-alpine

八、针对老模块进行动态模块编译
Nginx 发展十余年,许多公司目前还是在宿主机上使用,所以不会提供动态模块,这时我们就需要进行动态模块改造一般情况下我们只需要调整 config 文件,添加动态模块编译依赖,以及调整编译使用的目标脚本即可

# 假设原始的 config 文件内容如下:
ngx_addon_name=ngx_http_hello_world_module
HTTP_MODULES="$HTTP_MODULES ngx_http_hello_world_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_hello_world_module.c"

如果模块代码不需要针对 Nginx 核心模块进行 Patch,一般情况,只需要简
单调整为类似下面的格式,即可完成“动态模块”编译改造
ngx_addon_name=ngx_http_hello_world_module

if test -n "$ngx_module_link"; then
  ngx_module_type=HTTP
  ngx_module_name=ngx_http_hello_world_module
  ngx_module_srcs="$ngx_addon_dir/ngx_http_hello_world_module.c"
  . auto/module
else
    HTTP_MODULES="$HTTP_MODULES ngx_http_hello_world_module"
    NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_hello_world_module.c"
fi

九、总结
本篇文章中,我们了解了 Nginx 模块的通用构建方式、容器环境下相对通用的 Nginx 模块构建文件、如何快速使用预编译的三方模块制作定制的 Nginx 服务、以及针对这种积木模式产生的服务进行了简单的性能测试

 

posted on 2023-10-12 15:37  luokeli  阅读(223)  评论(0)    收藏  举报

导航