歡迎光臨
每天分享高質量文章

使用 Docker 和 Nginx 打造高性能的二維碼服務

本文將演示如何使用 Docker 完整打造一個基於 Nginx 的高性能二維碼服務,以及對整個服務鏡像進行優化的方法。如果你的網絡狀況良好,完整操作和體驗時間應不超過 15 分鐘。
動手前的腦洞

最近有一個小需求,需要在頁面中快速生成一些二維碼。
說到生成二維碼,方法很多,比如按照 QRCode 演算法進行計算之後:
  • 使用各種服務端語言,然後呼叫 GD 繪圖庫在語言中的 API 進行繪製,並生成圖片,然後配合能夠提供 HTTP 服務的軟體對用戶提供圖片訪問地址。

  • 使用服務端語言,然後使用 CSS 和 HTML 生成可以識別的頁面圖案,然後配合能夠提供 HTTP 服務的軟體對用戶提供圖片訪問地址。

  • 使用客戶端腳本,使用 Canvas 生成二維碼圖片,或者和上一個方案一樣,生成DOM 圖案。

  • ……

但是只是為了一個功能,就去配置一套完整的語言環境,引入一堆三方依賴,總有一種殺雞用牛刀的感覺,並且在資源利用效率上來說,也不是最優解。
而使用客戶端進行生成,現在雖然不存在太多的兼容問題,但是需要額外引入腳本資源,圖片生成效率也相對較慢。
那麼有沒有什麼環保高效的方案呢?
自然是有的,還是選擇服務端生成,但是扔掉語言運行時,直接使用 Nginx 提供服務。
使用 Nginx 進行二維碼生成

這裡可以使用一個現成的開源模塊 ngx_http_qrcode_module[1]。
它通過將用戶請求引數進行轉換,並呼叫使用 C 實現的二維碼快速生成庫 libqrencode[2] 的 QRcode_encodeString 實現二維碼快速生成,在未開啟快取的情況下,測試平均生成圖片在 10ms 左右。
為了方便大家理解全部的安裝配置過程,我先提供一個“啰嗦”版本的 Dockerfile:
FROM ubuntu:18.04

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
RUN apt update && \
    apt install -y unzip wget

WORKDIR /data

# https://github.com/fukuchi/libqrencode
RUN apt install -y autoconf automake autotools-dev libtool pkg-config libpng-dev && \
    cd /data && wget https://github.com/fukuchi/libqrencode/archive/master.zip && unzip master.zip && rm -rf master.zip && \
    cd libqrencode-master && ./autogen.sh && ./configure && make && make install && ldconfig && \
    cd .. && rm -rf libqrencode-master

RUN apt install -y libgd-dev

ADD ngx_http_qrcode /data/ngx_http_qrcode
ADD nginx-1.15.5.tar.gz /data
ADD nginx.conf /data

RUN apt install -y libpcre3 libpcre3-dev && \
    cd nginx-1.15.5 && ./configure --add-module=../ngx_http_qrcode/ && \
    make && make install && mv /data/nginx.conf /usr/local/nginx/conf/nginx.conf && \
    cd .. && rm -rf ngx_http_qrcode

將上面的檔案儲存完畢。接下來我們來配置 Nginx。
worker_processes  1;

events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;

    keepalive_timeout  65;

    server {
        listen       80;
        server_name  localhost;

        location / {

            set $fg_color 000000;
            set $bg_color FFFFFF;
            set $level 0;
            set $hint 2;
            set $size 300;
            set $margin 80;
            set $version 2;
            set $case 0;
            set $txt "https://soulteary.com";

            if ( $arg_fg_color ){
                set $fg_color $arg_fg_color;
            }
            if ( $arg_bg_color ){
                set $bg_color $arg_bg_color;
            }
            if ( $arg_level ){
                set $level $arg_level;
            }
            if ( $arg_hint ){
                set $hint $arg_hint;
            }
            if ( $arg_size ){
                set $size $arg_size;
            }
            if ( $arg_margin ){
                set $margin $arg_margin;
            }
            if ( $arg_ver ){
                set $version $arg_ver;
            }
            if ( $arg_case ){
                set $case $arg_case;
            }
            if ( $arg_txt ){
                set $txt $arg_txt;
            }


            qrcode_fg_color $fg_color;
            qrcode_bg_color $bg_color;

            qrcode_level    $level;
            qrcode_hint     $hint;
            qrcode_size     $size;
            qrcode_margin   $margin;
            qrcode_version  $version;
            qrcode_casesensitive $case;
            qrcode_urlencode_txt $txt;

            qrcode_gen;
        }

    }
}

將上面的配置儲存為 nginx.conf,然後使用下麵的命令進行鏡像構建。
docker build -t docker.lab.com/qrcode.lab.com .

如果你的網絡通暢,5分鐘之內,這個鏡像就構建完畢了。接下來,我們對它進行一下可用性驗證。
將下麵的配置檔案儲存為 docker-compose.yml,然後使用 docker-compose up 命令啟動,一個支持 HTTP/HTTPS,域名為 qrcode.lab.com 的網站就準備就緒了。
這裡我使用了 Traefik 進行服務發現,一旦你開始使用並掌握了它,你會發現搭建高可擴展的 Web 服務變的更簡單了。
version: '3'

services:

  qrcode:
    image: docker.lab.com/qrcode.lab.com:0.0.2
    expose:
      - 80
    networks:
      - traefik
    labels:
      - "traefik.enable=true"
      - "traefik.port=80"
      - "traefik.frontend.rule=Host:qrcode.lab.com"
      - "traefik.frontend.entryPoints=http,https"

networks:
  traefik:
    external: true

然後我們在瀏覽器中分別訪問,來驗證二維碼服務是否就緒:
  • https://qrcode.lab.com

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

看來服務是正常運行的,本文的基礎需求到這裡就解決了,並且,為了這個服務能夠更好的被使用,我們可以在書簽中輸入下麵的腳本代碼:
javascript:(function(){document.location.href='https://qrcode.lab.com/?size=150&txt;='+encodeURIComponent(document.location.href);})()

當你點擊書簽的時候,會將當前頁面自動轉換為一個可以掃描的二維碼。
通過整合陳述句優化容器鏡像

雖然上面的內容已經滿足了我們的基礎需求,但是作為一個有追求的開發者,我們不光是要追求執行效率,還要追求儲存效率。
雖然 Nginx 的運行資源占用不多。
top - 09:50:29 up 21 days, 19 min,  0 users,  load average: 0.030.050.05
Tasks:   4 total,   1 running,   3 sleeping,   0 stopped,   0 zombie
%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
KiB Mem :  6101684 total,   332268 free,  3649484 used,  2119932 buff/cache
KiB Swap:   998396 total,   936632 free,    61764 used.  2122020 avail Mem 

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                                                             
    8 nobody    20   0   72352   4792   3124 S   0.7  0.1   0:00.02 nginx                                                                                                               
    1 root      20   0   70800   4996   4240 S   0.0  0.1   0:00.01 nginx

但是使用 docker images 命令查看鏡像詳情,我們可以看到這個鏡像還是挺大的,有 400+MB。
REPOSITORY                            TAG                   IMAGE ID            CREATED              SIZE
docker.lab.com/qrcode.lab.com         0.0.1                 d98376b43ae9        About a minute ago   454MB

這裡我們修改一下上面的鏡像 Dockerfile,嘗試重新進行鏡像構建。
FROM ubuntu:18.04

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

WORKDIR /tmp

RUN apt update && apt install -y unzip wget autoconf automake autotools-dev libtool pkg-config libpng-dev libgd-dev libpcre3 libpcre3-dev && \
    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 && \
    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 && \
    wget https://github.com/fukuchi/libqrencode/archive/master.zip && unzip master.zip && mv libqrencode-master libqrencode && \
    cd libqrencode && ./autogen.sh && ./configure && make && make install && ldconfig && \
    cd /tmp/nginx-1.15.5 && ./configure --add-module=../ngx_http_qrcode_module/ && make && make install && \
    apt remove -y unzip wget autoconf automake autotools-dev libtool pkg-config && \
    rm -rf /tmp/* && rm -rf /var/cache/

ADD nginx.conf /usr/local/nginx/conf/nginx.conf

EXPOSE 80

ENTRYPOINT [ "/usr/local/nginx/sbin/nginx""-g""daemon off;" ]

再次構建完畢,我們會發現鏡像只是減少了 35MB,相比較 400MB 多的整體體積,優化部分杯水車薪。
REPOSITORY                            TAG                   IMAGE ID            CREATED             SIZE
docker.lab.com/qrcode.lab.com         0.0.1                 a24ffc73121a        1 minutes ago      420MB

那麼優化就到此為止了麽?顯然不是。
通過優化基礎鏡像來優化容器鏡像

這裡我們選擇使用體積更小的 Linux 鏡像,Alpine來進行同樣功能的二維碼服務的容器鏡像。
因為 Alpine 和 Ubuntu 不是一個社區進行維護,所以軟體包很多名稱是不同的,這裡我直接提供我已經查找修改完畢的鏡像檔案。
如果你也有類似的需求,需要將不同系統的軟體進行遷移安裝,可以在 https://pkgs.alpinelinux.org/packages 查找你所需要的軟體包的名稱。
FROM alpine:3.8

RUN cat /etc/apk/repositories | sed -e "s/dl-cdn.alpinelinux.org/mirrors.aliyun.com/" | tee /etc/apk/repositories && \
    apk --update add openssl-dev pcre-dev zlib-dev wget build-base autoconf automake libtool libpng-dev libgd pcre pcre-dev pkgconfig gd-dev && \
    cd /tmp && \
    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 && \
    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 && \
    wget https://github.com/fukuchi/libqrencode/archive/master.zip && unzip master.zip && mv libqrencode-master libqrencode && \
    cd libqrencode && ./autogen.sh && ./configure && make && make install && ldconfig || true && \
    cd /tmp/nginx-1.15.5 && ./configure --add-module=../ngx_http_qrcode_module/ && make && make install && \
    apk del build-base autoconf automake pkgconfig && \
    rm -rf /tmp/* && rm -rf /var/cache/apk/*

ADD nginx.conf /usr/local/nginx/conf/nginx.conf

EXPOSE 80

ENTRYPOINT [ "/usr/local/nginx/sbin/nginx""-g""daemon off;" ]

當鏡像打包完畢,我們再次查看鏡像體積,可以看到體積有了明顯的優化效果。
REPOSITORY                            TAG                   IMAGE ID            CREATED             SIZE
docker.lab.com/qrcode.lab.com         0.0.2                 d236b96c8950        1 minutes ago   79.1MB

最後

還記得本文標題中的關鍵詞“高性能”嘛,雖說我個人測試單實體的響應時間都在 10ms左右,但是如果你真的考慮使用它做對外服務的話,可以使用下麵的命令,根據自己情況對節點進行動態擴容,成倍提高服務響應能力。
docker-compose scale qrcode=4

或者使用:
docker-compose up --scale qrcode=2 -d

如果你也是 Traefik 用戶,你將會看到你的實體被成功進行掛載以及流量負載均衡。
另外,為了避免被惡意利用,還需要考慮使用 Nginx / iptable 的 req_limit等模塊限制訪問頻率,以及適當修改 ngx_http_qrcode_module 生成內容和圖片尺寸的判斷。
相關鏈接:
  1. https://github.com/dcshi/ngx_http_qrcode_module

  2. https://github.com/fukuchi/libqrencode

本文轉載自: 為了不折騰而去折騰的那些事,點擊查看原文

Kubernetes應用實戰培訓

Kubernetes應用實戰培訓將於2018年11月9日在北京開課,3天時間帶你系統學習Kubernetes本次培訓包括:容器特性、鏡像、網絡;Docker特性、架構、組件、概念、Runtime;Docker安全;Docker實踐;Kubernetes架構、核心組件、基本功能;Kubernetes設計理念、架構設計、基本功能、常用物件、設計原則;Kubernetes的實踐、運行時、網絡、插件已經落地經驗;微服務架構、DevOps等,點擊下方圖片查看詳情。

赞(0)

分享創造快樂