Hateburo: kazeburo hatenablog

Operations Engineer / 運用系小姑 / Perl Monger

HAProxy+libslzによるHTTPレスポンスのGZIP圧縮の検証

少し前になりますが、4/8 にさくらのクラウドの高機能ロードバランサーサービスである エンハンスドロードバランサ にレスポンスボディのGZIP圧縮機能を追加しました。

cloud-news.sakura.ad.jp

エンハンスドロードバランサのコントールパネルやAPIで、GZIP圧縮を有効にすることで、手軽にWebサイトの表示にかかる時間を短くすることができますので、お試しいただければと思います。

この記事にあるとおり、エンハンスドロードバランサはソフトウェアとしてHAProxyを使っています。

qiita.com

今回、レスポンスのGZIP圧縮対応にあたり、HAProxyにlibslzという圧縮ライブラリを組み込んでおります。GZIP圧縮といえばzlibがもっとも使われると思いますが、事前に比較検証を行った上でlibslzを選択しているので、その紹介です。

ライブラリとベンチマークの環境

www.libslz.org

libslzはほぼ聞いたことがない圧縮ライブラリでしhaproxyの作者と同じ方がつくっている、省メモリ、CPUコストの低いGZIP互換の圧縮用ライブラリになります。(解凍の機能はありません)

このlibslzとzlibに加え、ちょうど安定版(2.0.0)がでたzlib-ngとlibslzでベンチマークを行い比較を行いました。

github.com

zlib-ngはzlibと互換のAPIを提供しながら、新しめのCPUの命令を使うなどして高速化を行ったライブラリです。また Cloudflare-zlibやintelの最適化も取り込んでいます。

ベンチマーク環境は、さくらのクラウドにて、CPU 12コア/メモリ 128GBのサーバをたて、ローカルスイッチににて接続しています。(さくらのクラウドではサーバの搭載するメモリ量によって使える帯域が変わりますが、128GBでは5Gbpsの通信が可能になります)

manual.sakura.ad.jp

3台のサーバはそれぞれ、

  • Go app (http-dump-request)
  • haporxy: Aにreverse proxy
  • benchmark (wrk): Bに対してベンチマーク実行

としました。

haproxy サーバのセットアップ

まず zlib-ngのインストール

この記事を書いている現在の最新は、2.0.2ですが、テストした時は2.0.0でしたので、2.0.0を使っております。

# wget https://github.com/zlib-ng/zlib-ng/archive/refs/tags/2.0.0.tar.gz
# tar zxf 2.0.0.tar.gz
# cd zlib-ng-2.0.0
# ./configure --zlib-compat --prefix=/opt/zlib-ng
# make
# make test
# make install
# ll /opt/zlib-ng/lib
total 312
-rw-r--r-- 1 root root 184986 Mar 17 09:15 libz.a
lrwxrwxrwx 1 root root     22 Mar 17 09:15 libz.so -> libz.so.1.2.11.zlib-ng
lrwxrwxrwx 1 root root     22 Mar 17 09:15 libz.so.1 -> libz.so.1.2.11.zlib-ng
-rwxr-xr-x 1 root root 126736 Mar 17 09:15 libz.so.1.2.11.zlib-ng
drwxr-xr-x 2 root root   4096 Mar 17 09:15 pkgconfig

haproxyのインストール。libslzを有効にしたものと、GZIPを有効にしたバイナリをビルドします。 libslzはyumでインストールしました

yum install -y libslz-devel
wget http://www.haproxy.org/download/x/src/haproxy-$HAPROXY_VERSION.tar.gz

tar xvfz haproxy-$HAPROXY_VERSION.tar.gz
cd haproxy-$HAPROXY_VERSION
# USE_SLZ=1を指定すると、libslzが有効
make TARGET=linux-glibc USE_THREAD=1 USE_SLZ=1 USE_OPENSSL=1 SSL_INC=/usr/local/include/openssl SSL_LIB=/usr/local/lib64
make install
mv /usr/local/sbin/haproxy /usr/local/sbin/haproxy_slz
cd ..

rm -rf haproxy-$HAPROXY_VERSION

tar xvfz haproxy-$HAPROXY_VERSION.tar.gz
cd haproxy-$HAPROXY_VERSION
# USE_ZLIB=1を指定すると、zlibが有効
make TARGET=linux-glibc USE_THREAD=1 USE_ZLIB=1 USE_OPENSSL=1 SSL_INC=/usr/local/include/openssl SSL_LIB=/usr/local/lib64
make install

zlib-ngを有効にするときは、LD_PRELOADを使います

# /usr/local/sbin/haproxy -vv|grep zlib
Built with zlib version : 1.2.7
Running on zlib version : 1.2.7
# LD_PRELOAD=/opt/zlib-ng/lib/libz.so /usr/local/sbin/haproxy -vv|grep zlib
Built with zlib version : 1.2.7
Running on zlib version : 1.2.11.zlib-ng

haproxyの設定は、実際の環境に近づけるためエンハンスドロードバランサの開発環境からコピー・変更しています。また、httpsを有効にし、ベンチマークhttpsにて行っています。

関係ありそうなところだけ抜き出すとこうなります。

frontend test-163.43.241.14:443
        mode http

        bind 0.0.0.0:443 ssl crt /path/to/test.pem alpn h2,http/1.1 tls-ticket-keys /path/to/test-tls-ticket-keys.txt
        default_backend test-backend-default

        compression algo gzip
        compression type text/html text/plain text/plain text/css text/javascript application/x-javascript application/javascript application/json text/js text/xml application/xml application/xml+rss image/svg+xml

        use_backend test-backend-group1 if { path_reg ^/.*$ }

backend test-backend-group1
        mode http
        balance leastconn
        retries 3

        option tcp-check
        server 192.168.0.101:3000 192.168.0.101:3000 check inter 10s

haproxyは次のように起動しました。

zlib

# /usr/local/sbin/haproxy -f /path/to/proxy.cfg

zlib-ng

# LD_PRELOAD=/opt/zlib-ng/lib/libz.so /usr/local/sbin/haproxy -f /path/to/proxy.cfg

libslz

# /usr/local/sbin/haproxy_slz -f /path/to/proxy.cfg

ベンチマーク

まず、wrkを使い9KBぐらいのデータが返るURLに対してベンチマークをしました。

無圧縮

$ wrk -d 30 'https://192.168.0.102/nogzip/source?plain'
Running 30s test @ https://192.168.0.102/nogzip/source?plain
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   596.21us  231.33us   8.14ms   91.47%
    Req/Sec     8.49k     1.50k   10.88k    59.47%
  508831 requests in 30.10s, 4.40GB read
Requests/sec:  16904.68
Transfer/sec:    149.66MB

zlib

$ wrk -d 30 -H 'Accept-Encoding: deflate, gzip, br' 'https://192.168.0.102/nogzip/source?plain'
Running 30s test @ https://192.168.0.102/nogzip/source?plain
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   832.53us  315.55us  10.56ms   87.28%
    Req/Sec     6.11k   690.37     7.61k    64.45%
  365934 requests in 30.10s, 1.10GB read
Requests/sec:  12157.29
Transfer/sec:     37.49MB

zlib-ng

$ wrk -d 30 -H 'Accept-Encoding: deflate, gzip, br' 'https://192.168.0.102/nogzip/source?plain'
Running 30s test @ https://192.168.0.102/nogzip/source?plain
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   705.03us  371.54us  11.29ms   92.63%
    Req/Sec     7.37k   702.16     9.36k    74.04%
  440795 requests in 30.10s, 1.62GB read
Requests/sec:  14644.69
Transfer/sec:     55.07MB

libslz

$ wrk -d 30 -H 'Accept-Encoding: deflate, gzip, br' 'https://192.168.0.102/nogzip/source?plain'
Running 30s test @ https://192.168.0.102/nogzip/source?plain
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   550.74us  213.55us  10.69ms   93.44%
    Req/Sec     9.24k     1.04k   11.72k    61.56%
  552642 requests in 30.10s, 2.11GB read
Requests/sec:  18360.29
Transfer/sec:     71.94MB

無圧縮で 16904.68req/secでていたのものが、zlibで圧縮を行うようにすると、12157.29req/secまで下がります。zlib-ngになると、14644.69req/secまであがります。そしてlibslzでは 18360.29req/secとさらにあがり、無圧縮よりスループットがでるようになっています。

この際のhaproxyサーバのcpu使用率(user:vmstatで計測)ですが、無圧縮では15%前後、zlibでは44%前後に上昇、zlib-ngは35%まで下がります。libslzは37%とzlib-ngより若干高くなるようですが、25%スループットがよいのでその影響もあるでしょう。

コンテンツのサイズを、140byteほどのSmall、3KBのMedium、6KBほどのLargeとしてベンチマーク結果をまとめたのが次になります。

f:id:kazeburo:20210424114635p:plain

もっともレスポンスのサイズが小さくなるのはzlibになりますが、CPU使用率・スループットへの影響が大きくなります。それに対して、zlib-ngはレスポンスサイズはzlibより大きくなりますが、CPU使用率がさがり、スループットも改善します。ただ、小さいサイズではzlibよりもCPU使用率があがってしまうことがあるようです。libslzでは、zlib-ngよりも若干レスポンスのサイズは増えるものの、CPU使用率はより低くできるようでした。

まとめ

以上のようなベンチマークの結果や、yumでインストールが可能という入手のしやすさ、また、エンハンスドLBにおいては転送量課金ではなくCPS(秒間の新規コネクション数)なので、圧縮率が多少悪くても問題になりにくいことなどを考慮にいれ、libslzを選択し、haproxyに組み込んだ上でエンハンスドロードバランサにてGZIP圧縮オプションの提供を開始しております。

エンハンスドロードバランサをはじめ、今後もさまざまなサービスの機能追加、改善を行っていきますので、よろしくお願いします。