การทดสอบโหลด (Load Testing) ด้วย wrk

การทำเว็บ เพื่อรองรับผู้ใช้จำนวนมาก การทดสอบโหลดนั้น มีความจำเป็นอย่างมาก เพราะเราจะได้รู้ว่า เว็บของเรา จะสามารถรองรับผู้ใช้งานได้มากที่สุดเท่าไหร่กันแน่

cat-load-test

แล้วการทดสอบโหลด คือ อะไรกันแน่?

การทดสอบโหลด หรือ Load Testing นั้น เป็นการจำลองการเข้าใช้งานพร้อมๆกันของผู้ใช้งาน เพื่อดูว่าเว็บเซิฟเวอร์ของเรานั้น จะสามารถรองรับผู้ใช้งานเหล่านั้นได้หรือไม่ โดยการใช้โปรแกรมจำลองการเข้าใช้งานของผู้ใช้พร้อมๆกัน โดยโปรแกรมที่ผมจะมาแนะนำในวันนี้คือ wrk (https://github.com/wg/wrk)


wrk – Modern HTTP benchmarking tool

ตัว wrk นั้นเขียนด้วยภาษาซี (C Programming Language) สามารถเขียนสคริป เพื่อช่วยในการสร้างรีเควสที่ซับซ้อนได้ โดยใช้ Lua (Lua Programming Language) โดยรันอยู่บน LuaJIT (Just In Time) นอกจาก Lua Script ที่ใช้ในการสร้างรีเควสแล้ว Lua Script ที่ใช้สามารถเอามาจัดการกับค่าต่างๆได้ด้วย เช่น เอาไปสร้างรีพอร์ท

การติดตั้ง wrk

การติดตั้ง wrk จำเป็นต้องดาวน์โหลดซอร์สโค้ดมาคอมไพล์เอง โดยตัวอย่าง จะเป็นการติดตั้งบน Debian และเครื่องต้องติดตั้ง git และ make ไว้แล้ว

ติดตั้ง git และ make (build-essential)

sudo apt install git build-essential -y

จากนั้นก็โคลนโค้ดมาจาก GitHub แล้วก็สั่ง make

git clone https://github.com/wg/wrk.git
cd wrk
make

หลังจากสั่ง make ไปแล้ว จะได้ไฟล์ชื่อ wrk มา การสั่งรัน ทำได้โดยใช้ ./wrk

./wrk
Usage: wrk <options> <url>                            
  Options:                                            
    -c, --connections <N>  Connections to keep open   
    -d, --duration    <T>  Duration of test           
    -t, --threads     <N>  Number of threads to use   

    -s, --script      <S>  Load Lua script file       
    -H, --header      <H>  Add header to request      
        --latency          Print latency statistics   
        --timeout     <T>  Socket/request timeout     
    -v, --version          Print version details      

  Numeric arguments may include a SI unit (1k, 1M, 1G)
  Time arguments may include a time unit (2s, 2m, 2h)

หลังจากติดตั้งเสร็จแล้ว ก็ทำการทดสอบโหลดกัน โดยพารามิเตอร์ที่จำเป็น มีดั้งนี้

-t <N> อันนี้คือ จำนวน threads ที่จะใช้งาน
-c <N> อันนี้คือ จำนวน connections หรือ concurrent ที่จะส่งไป
-d <T> อันนี้คือ เวลาที่ใช้ในการสร้างโหลด
โดยค่า <N> ใช้หน่วยต่อท้ายได้ เช่น 1k, 1M, 1G
ส่วนค่า <T> ใช้หน่วยเวลา เช่น 5s, 1m, 1h

ก่อนทำการโหลดเทส เราสร้าง HTTP Server สำหรับทดสอบกันก่อน โดยส่วนตัวผมเอง ถนัดการใช้ Nginx + Lua หรือ OpenResty ในตัวอย่างต่อไปนี้จะเป็น nginx config file + Lua Script ครับ โดยสร้างไดเรคทอรี่มาดังนี้

mkdir -p wrk-test/{conf,logs}
cd wrk-test

หลังจากนั้น สร้างไฟล์ชื่อ nginx.conf ในไดเรคทอรี่ wrk-test/conf โดยมีโค้ดต่อไปนี้

worker_processes  auto;

error_log  logs/error.log;

pid        logs/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/json;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;
    access_log off;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    gzip  on;
    server_tokens off;
    more_set_headers 'Server: NGiNX';

    resolver 8.8.8.8;

    server {
        listen  8080;

        location / {
            content_by_lua_block {
                local cjson = require 'cjson.safe'
                ngx.say(cjson.encode({message = 'Hello, World!'}))
                ngx.exit(ngx.HTTP_OK)
            }
        }
    }
}

หลังจากเพิ่ม nginx.conf ด้วยโค้ดด้านบนแล้ว ทำการรัน nginx เพื่อเริ่มการทำงานของ HTTP Server ด้วยคำสั่งนี้

nginx -c conf/nginx.conf -p $(pwd)

โดยก่อนรันคำสั่งด้านบน เราต้องอยู่ที่ไดเรคทอรี wrk-test ก่อนน่ะครับ หลังจากรัน HTTP Server ไปแล้ว ทดลองเรียกดูหน้าเว็บ จะใช้เว็บเบราเซอร์เปิด หรือใช้ curl ก็ได้

$ curl localhost:8080
{"message":"Hello, World!"}

หลังจากหน้าเว็บแสดงผลได้แล้ว ก็ทำการโหลดเทสกันเลยครับ โดยรันคำสั่งนี้

./wrk -t 4 -c 1k -d 1m http://localhost:8080/

screen-shot-2559-08-02-at-5-35-55-pm

จากคำสั่งคือ จำลองการเข้าใช้งานพร้อมๆกัน 1,000 คน (-c 1k) โดยใช้เวลาในการทดสอบ 1 นาที (-d 1m) ส่วน -t 4 คือจำนวนเธร็ดที่ใช้ และผลที่ได้คือเว็บสามารถตอบกลับมาได้ถึง 31932.05 Requests/sec

หลังจากเราทดสอบโหลดการเรียกดูหน้าเว็บแล้ว ต่อมาเรามาทำการทดสอบที่ซับซ้อนขึ้นมานิดหนึ่ง คือการทดสอบการ POST ข้อมูลไปยัง HTTP Server โดยการใช้ Lua Script ช่วย (wrk -s post.lua ...)

ก่อนอื่น เพิ่มค่าคอนฟิกไปที่ nginx.conf เพื่อทำการรับค่าจากการ POST โดยเพิ่มค่าต่อไปนี้

location /post {
    content_by_lua_block {
        ngx.req.read_body()
        local args = ngx.req.get_post_args()
        local a, b = args.a, args.b
        ngx.say(a + b)
        ngx.exit(ngx.HTTP_OK)
    }
}

หลังจากแก้ไข nginx.conf แล้ว ต้องทำการ reload nginx ก่อน เพื่อเรียกใช้ค่าคอนฟิกใหม่ โดยรันคำสั่งนี้

nginx -c conf/nginx.conf -p $(pwd) -s reload

จากนั้นใช้ curlทดสอบเรียกดู

curl localhost:8080/post -d 'a=1' -d 'b=1'
2
curl localhost:8080/post -d 'a=1' -d 'b=2'
3
curl localhost:8080/post -d 'a=2' -d 'b=2'
4

จากโค้ดคอนฟิกคือ เอาค่าที่ส่งไปคือ a และ b ไปบวกกันแล้วส่งกลับมา เมื่อได้ค่าตามด้านบนแล้ว เตรียม Lua Script โดยชื่อไฟล์ post.lua โดยมีโค้ดดังนี้

-- post.lua., https://github.com/wg/wrk/blob/master/scripts/post.lua
-- example HTTP POST script which demonstrates setting the
-- HTTP method, body, and adding a header

wrk.method = "POST"
wrk.body   = "a=1&b=2"
wrk.headers.content_type = "application/x-www-form-urlencoded"

ต่อมาหลังจากบันทึกไฟล์ post.lua ไว้แล้วที่เดียวกับ wrk จากนั้นทำการทดสอบด้วยคำสั่งต่อไปนี้

./wrk -t 4 -c 100 -d 5s -s post.lua http://localhost:8080/post

จากคำสั่งด้านบน จำลองคนเข้าใช้งานพร้อมกัน 100 คน (-c 100) โดยใช้เวลาทดสอบ 5 วินาที (-d 5s) ผลที่ได้คือ

Running 5s test @ http://localhost:8080/post
  4 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     7.70ms   15.41ms 131.38ms   91.08%
    Req/Sec     8.13k     2.68k   21.72k    68.50%
  162624 requests in 5.07s, 25.89MB read
Requests/sec:  32096.68
Transfer/sec:      5.11MB

จากผลที่ได้ เว็บของเราสามารถรับค่าจากผู้ใช้ แล้วบวกกันได้ถึง 32096.68 Requests/sec

จบแล้วครับ สำหรับการทดสอบโหลดด้วย wrk โดยผมขอสรุปคร่าวๆไว้ดังนี้

การทำเว็บสำหรับการรองรับผู้ใช้จำนวนมาก การไม่ทำโหลดเทส เป็นบาปส์