使用 Docker 和 Nginx 打造高性能的二维码服务

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

本文作者: 苏洋

创建时间: 2018年10月19日 统计字数: 7618字 阅读时间: 16分钟阅读 本文链接: https://soulteary.com/2018/10/19/use-docker-and-nginx-to-build-high-performance-qr-code-services.html


使用 Docker 和 Nginx 打造高性能的二维码服务

本文将演示如何使用 Docker 完整打造一个基于 Nginx 的高性能二维码服务,以及对整个服务镜像进行优化的方法。如果你的网络状况良好,完整操作和体验时间应不超过 15 分钟。

动手前的脑洞

最近有一个小需求,需要在页面中快速生成一些二维码。

说到生成二维码,方法很多,比如按照 QRCode 算法进行计算之后:

  • 使用各种服务端语言,然后调用 GD 绘图库在语言中的 API 进行绘制,并生成图片,然后配合能够提供 HTTP 服务的软件对用户提供图片访问地址。
  • 使用服务端语言,然后使用 CSS 和 HTML 生成可以识别的页面图案,然后配合能够提供 HTTP 服务的软件对用户提供图片访问地址。
  • 使用客户端脚本,使用 Canvas 生成二维码图片,或者和上一个方案一样,生成 DOM 图案。

但是只是为了一个功能,就去配置一套完整的语言环境,引入一堆三方依赖,总有一种杀鸡用牛刀的感觉,并且在资源利用效率上来说,也不是最优解。

而使用客户端进行生成,现在虽然不存在太多的兼容问题,但是需要额外引入脚本资源,图片生成效率也相对较慢。

那么有没有什么环保高效的方案呢?

自然是有的,还是选择服务端生成,但是扔掉语言运行时,直接使用 Nginx 提供服务。

使用 Nginx 进行二维码生成

这里可以使用一个现成的开源模块 ngx_http_qrcode_module 。

它通过将用户请求参数进行转换,并调用使用 C 实现的二维码快速生成库 libqrencode 的 QRcode_encodeString实现二维码快速生成,在未开启缓存的情况下,测试平均生成图片在 10ms 左右。

为了方便大家理解全部的安装配置过程,我先提供一个“啰嗦”版本的 Dockerfile

 
  1. FROM ubuntu:18.04
  2.  
  3. RUN cat /etc/apt/sources.list | sed -e "s/archive\.ubuntu\.com/mirrors\.aliyun\.com/" | sed -e "s/security\.ubuntu\.com/mirrors\.aliyun\.com/" | tee /etc/apt/sources.list
  4. RUN apt update && \
  5. apt install -y unzip wget
  6.  
  7. WORKDIR /data
  8.  
  9. # https://github.com/fukuchi/libqrencode
  10. RUN apt install -y autoconf automake autotools-dev libtool pkg-config libpng-dev && \
  11. cd /data && wget https://github.com/fukuchi/libqrencode/archive/master.zip && unzip master.zip && rm -rf master.zip && \
  12. cd libqrencode-master && ./autogen.sh && ./configure && make && make install && ldconfig && \
  13. cd .. && rm -rf libqrencode-master
  14.  
  15. RUN apt install -y libgd-dev
  16.  
  17. ADD ngx_http_qrcode /data/ngx_http_qrcode
  18. ADD nginx-1.15.5.tar.gz /data
  19. ADD nginx.conf /data
  20.  
  21. RUN apt install -y libpcre3 libpcre3-dev && \
  22. cd nginx-1.15.5 && ./configure --add-module=../ngx_http_qrcode/ && \
  23. make && make install && mv /data/nginx.conf /usr/local/nginx/conf/nginx.conf && \
  24. cd .. && rm -rf ngx_http_qrcode

将上面的文件保存完毕。接下来我们来配置 Nginx

 
  1. worker_processes 1;
  2.  
  3. events {
  4. worker_connections 1024;
  5. }
  6.  
  7.  
  8. http {
  9. include mime.types;
  10. default_type application/octet-stream;
  11.  
  12. sendfile on;
  13.  
  14. keepalive_timeout 65;
  15.  
  16. server {
  17. listen 80;
  18. server_name localhost;
  19.  
  20. location / {
  21.  
  22. set $fg_color 000000;
  23. set $bg_color FFFFFF;
  24. set $level 0;
  25. set $hint 2;
  26. set $size 300;
  27. set $margin 80;
  28. set $version 2;
  29. set $case 0;
  30. set $txt "https://soulteary.com";
  31.  
  32. if ( $arg_fg_color ){
  33. set $fg_color $arg_fg_color;
  34. }
  35. if ( $arg_bg_color ){
  36. set $bg_color $arg_bg_color;
  37. }
  38. if ( $arg_level ){
  39. set $level $arg_level;
  40. }
  41. if ( $arg_hint ){
  42. set $hint $arg_hint;
  43. }
  44. if ( $arg_size ){
  45. set $size $arg_size;
  46. }
  47. if ( $arg_margin ){
  48. set $margin $arg_margin;
  49. }
  50. if ( $arg_ver ){
  51. set $version $arg_ver;
  52. }
  53. if ( $arg_case ){
  54. set $case $arg_case;
  55. }
  56. if ( $arg_txt ){
  57. set $txt $arg_txt;
  58. }
  59.  
  60.  
  61. qrcode_fg_color $fg_color;
  62. qrcode_bg_color $bg_color;
  63.  
  64. qrcode_level $level;
  65. qrcode_hint $hint;
  66. qrcode_size $size;
  67. qrcode_margin $margin;
  68. qrcode_version $version;
  69. qrcode_casesensitive $case;
  70. qrcode_urlencode_txt $txt;
  71.  
  72. qrcode_gen;
  73. }
  74.  
  75. }
  76. }

将上面的配置保存为 nginx.conf,然后使用下面的命令进行镜像构建。

 
  1. docker build -t docker.lab.com/qrcode.lab.com .

如果你的网络通畅,5分钟之内,这个镜像就构建完毕了。接下来,我们对它进行一下可用性验证。

将下面的配置文件保存为 docker-compose.yml,然后使用 docker-compose up 命令启动,一个支持 HTTP/HTTPS,域名为 qrcode.lab.com 的网站就准备就绪了。

这里我使用了 Traefik 进行服务发现,感兴趣的童鞋可以参考我以前写的文章: 使用服务发现改善开发体验  更完善的 Docker + Traefik 使用方案 使用 Traefik 的一些补充细节),一旦你开始使用并掌握了它,你会发现搭建高可扩展的 Web 服务变的更简单了。

 
  1. version: '3'
  2.  
  3. services:
  4.  
  5. qrcode:
  6. image: docker.lab.com/qrcode.lab.com:0.0.2
  7. expose:
  8. - 80
  9. networks:
  10. - traefik
  11. labels:
  12. - "traefik.enable=true"
  13. - "traefik.port=80"
  14. - "traefik.frontend.rule=Host:qrcode.lab.com"
  15. - "traefik.frontend.entryPoints=http,https"
  16.  
  17. networks:
  18. traefik:
  19. external: true

然后我们在浏览器中分别访问,来验证二维码服务是否就绪:

  • https://qrcode.lab.com
  • https://qrcode.lab.com/?size=150&margin=20&txt=https%3A%2F%2Fsoulteary.com

QR Code 预览页面

看来服务是正常运行的,本文的基础需求到这里就解决了,并且,为了这个服务能够更好的被使用,我们可以在书签中输入下面的脚本代码:

 
  1. javascript:(function(){document.location.href='https://qrcode.lab.com/?size=150&txt='+encodeURIComponent(document.location.href);})()

当你点击书签的时候,会将当前页面自动转换为一个可以扫描的二维码。

通过整合语句优化容器镜像

虽然上面的内容已经满足了我们的基础需求,但是作为一个有追求的开发者,我们不光是要追求执行效率,还要追求储存效率。

虽然 Nginx 的运行资源占用不多。

 
  1. top - 09:50:29 up 21 days, 19 min, 0 users, load average: 0.03, 0.05, 0.05
  2. Tasks: 4 total, 1 running, 3 sleeping, 0 stopped, 0 zombie
  3. %Cpu(s): 0.3 us, 0.3 sy, 0.0 ni, 99.4 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
  4. KiB Mem : 6101684 total, 332268 free, 3649484 used, 2119932 buff/cache
  5. KiB Swap: 998396 total, 936632 free, 61764 used. 2122020 avail Mem
  6.  
  7. PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
  8. 8 nobody 20 0 72352 4792 3124 S 0.7 0.1 0:00.02 nginx
  9. 1 root 20 0 70800 4996 4240 S 0.0 0.1 0:00.01 nginx

但是使用 docker images 命令查看镜像详情,我们可以看到这个镜像还是挺大的,有 400+MB

 
  1. REPOSITORY TAG IMAGE ID CREATED SIZE
  2. docker.lab.com/qrcode.lab.com 0.0.1 d98376b43ae9 About a minute ago 454MB

这里我们修改一下上面的镜像 Dockerfile,尝试重新进行镜像构建。

 
  1. FROM ubuntu:18.04
  2.  
  3. RUN cat /etc/apt/sources.list | sed -e "s/archive\.ubuntu\.com/mirrors\.aliyun\.com/" | sed -e "s/security\.ubuntu\.com/mirrors\.aliyun\.com/" | tee /etc/apt/sources.list
  4.  
  5. WORKDIR /tmp
  6.  
  7. RUN apt update && apt install -y unzip wget autoconf automake autotools-dev libtool pkg-config libpng-dev libgd-dev libpcre3 libpcre3-dev && \
  8. wget https://nginx.org/download/nginx-1.15.5.tar.gz && tar -zxvf nginx-1.15.5.tar.gz && rm -rf nginx-1.15.5.tar.gz && \
  9. wget https://github.com/dcshi/ngx_http_qrcode_module/archive/master.zip && unzip master.zip && mv ngx_http_qrcode_module-master ngx_http_qrcode_module && rm -rf master.zip && \
  10. wget https://github.com/fukuchi/libqrencode/archive/master.zip && unzip master.zip && mv libqrencode-master libqrencode && \
  11. cd libqrencode && ./autogen.sh && ./configure && make && make install && ldconfig && \
  12. cd /tmp/nginx-1.15.5 && ./configure --add-module=../ngx_http_qrcode_module/ && make && make install && \
  13. apt remove -y unzip wget autoconf automake autotools-dev libtool pkg-config && \
  14. rm -rf /tmp/* && rm -rf /var/cache/
  15.  
  16. ADD nginx.conf /usr/local/nginx/conf/nginx.conf
  17.  
  18. EXPOSE 80
  19.  
  20. ENTRYPOINT [ "/usr/local/nginx/sbin/nginx", "-g", "daemon off;" ]

再次构建完毕,我们会发现镜像只是减少了 35MB,相比较 400MB 多的整体体积,优化部分杯水车薪。

 
  1. REPOSITORY TAG IMAGE ID CREATED SIZE
  2. docker.lab.com/qrcode.lab.com 0.0.1 a24ffc73121a 1 minutes ago 420MB

那么优化就到此为止了么?显然不是。

通过优化基础镜像来优化容器镜像

这里我们选择使用体积更小的 Linux 镜像, Alpine来进行同样功能的二维码服务的容器镜像。

因为 Alpine 和 Ubuntu 不是一个社区进行维护,所以软件包很多名称是不同的,这里我直接提供我已经查找修改完毕的镜像文件。

如果你也有类似的需求,需要将不同系统的软件进行迁移安装,可以在 https://pkgs.alpinelinux.org/packages 查找你所需要的软件包的名称。

 
  1. FROM alpine:3.8
  2.  
  3. RUN cat /etc/apk/repositories | sed -e "s/dl-cdn.alpinelinux.org/mirrors.aliyun.com/" | tee /etc/apk/repositories && \
  4. apk --update add openssl-dev pcre-dev zlib-dev wget build-base autoconf automake libtool libpng-dev libgd pcre pcre-dev pkgconfig gd-dev && \
  5. cd /tmp && \
  6. wget https://nginx.org/download/nginx-1.15.5.tar.gz && tar -zxvf nginx-1.15.5.tar.gz && rm -rf nginx-1.15.5.tar.gz && \
  7. wget https://github.com/dcshi/ngx_http_qrcode_module/archive/master.zip && unzip master.zip && mv ngx_http_qrcode_module-master ngx_http_qrcode_module && rm -rf master.zip && \
  8. wget https://github.com/fukuchi/libqrencode/archive/master.zip && unzip master.zip && mv libqrencode-master libqrencode && \
  9. cd libqrencode && ./autogen.sh && ./configure && make && make install && ldconfig || true && \
  10. cd /tmp/nginx-1.15.5 && ./configure --add-module=../ngx_http_qrcode_module/ && make && make install && \
  11. apk del build-base autoconf automake pkgconfig && \
  12. rm -rf /tmp/* && rm -rf /var/cache/apk/*
  13.  
  14. ADD nginx.conf /usr/local/nginx/conf/nginx.conf
  15.  
  16. EXPOSE 80
  17.  
  18. ENTRYPOINT [ "/usr/local/nginx/sbin/nginx", "-g", "daemon off;" ]

当镜像打包完毕,我们再次查看镜像体积,可以看到体积有了明显的优化效果。

 
  1. REPOSITORY TAG IMAGE ID CREATED SIZE
  2. docker.lab.com/qrcode.lab.com 0.0.2 d236b96c8950 1 minutes ago 79.1MB

最后

还记得本文标题中的关键词“高性能”嘛,虽说我个人测试单实例的响应时间都在 10ms 左右,但是如果你真的考虑使用它做对外服务的话,可以使用下面的命令,根据自己情况对节点进行动态扩容,成倍提高服务响应能力。

 
  1. docker-compose scale qrcode=4

或者使用

 
  1. docker-compose up --scale qrcode=2 -d

如果你也是 Traefik 用户,你将会看到你的实例被成功进行挂载以及流量负载均衡。

Traefik 控制台

另外,为了避免被恶意利用,还需要考虑使用 Nginx / iptable 的 req_limit 等模块限制访问频率,以及适当修改 ngx_http_qrcode_module 生成内容和图片尺寸的判断。

—EOF

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值