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 服务、以及针对这种积木模式产生的服务进行了简单的性能测试
本文来自博客园,站在巨人的肩膀上,坚持开源精神,遵循开源协议:Apache Licene 2.0协议。
浙公网安备 33010602011771号