Coding Gun

การทำ Load Test ด้วย Locust

Locust คือ เครื่องมือในการทำ Load test ที่เป็น opensource ที่พัฒนาขึ้นมาด้วย Python สามารถนำมาใช้งานได้ฟรี สามารถเขียน Code เพื่อกำหนดรูปแบบของ load และเนื่องจากการเป็น Python เราเลยสามารถ random รูปแบบของ load ได้ง่ายมากๆ

ซึ่งข้อดีของเจ้าตั๊กแตนเขียว(locust แปลว่า ตั๊กแตน) คือ

  1. Define user behaviour in code สามารถสร้าง User Behavior ด้วย Python ได้เลย ไม่ต้องเรียนรู้การใช้งาน UI หรือ ไม่ต้องสร้าง file xml ที่กลับมาแก้ไขได้ค่อนข้างยาก
  2. Distributed and Scaled สามารถ run load test ได้จากหลายๆเครื่องพร้อมๆกัน(Distributed) เลยทำให้เราสามารถ simulate user เป็นหลักล้านได้
  3. Proven & battle tested Locust เป็น Open source ที่ถูกนำไปใช้งานในหลายๆ Application ซึ่งรูปแบบของการทำ Load test ในแต่ละ Application นั้นไม่เหมือนกัน ดังนั้นเราจึงมั้นใจได้ว่า Locust สามารถนำมาใช้ใน Application ของเราได้แน่นอน

Locust vs jMeter

เครื่องมือสำหรับการทำ Load Test ที่มีผู้ใช้งานมากที่สุดคือ jMeter แต่เนื่องจาก jMeter มีอายุมากกว่า 20 ปีมาแล้วดังนั้นจึงไม่แปลกที่ jMeter จะมีฐานผู้ใช้งานที่กว้างกว่า แต่การเติบโตของ Locust ก็เป็นสิ่งที่ต้องจับตามองเช่นกัน ดังนั้นถ้าเราลองเทียบฟังก์ชั่นการทำงานของ jMeter และ Locust จะออกมาเป็นตารางนี้

jMeter Locust
พัฒนาขึ้นมาด้วย Java พัฒนาขึ่้นมาด้วย Python
ใช้งานผ่าน GUI เป็นหลัก ต้องเขียน Python
สามารถ export jmx file ออกไปใช้งานผ่าน command line ผลลัพธ์ที่ได้เป็น python ที่สามารถ run ผ่าน command line ได้อยู่แล้ว
สามารถ Integrate กับ Monitoring tool อย่าง PrefMon ได้ ไม่สามารถเชื่อมต่อกับ PerfMon ได้
ไม่สามารถจัดการ Version ของ Test code ได้เพราะต้อง export ออกมาจาก GUI สามารถจัดการ Version ได้เพราะเป็น Source Code
ใช้ CPU และ Memory มากกว่า ใช้ CPU และ Memory น้อยกว่า
มี Script Recording ไม่มี Script Recording(ต้องเขียน code)
สามารถทดสอบได้หลาย Protocols ใช้ทดสอบ Web application เป็นหลัก

การสร้าง User Behavior

ในการทำ Load test นั้นจะแตกต่างจากการทำ Stress Test ตรงที่เราต้องการจำลองการใช้งานระบบของ User เราจึงต้องสร้าง User Behavior ให้เหมือนหรือใกล้เคียงกับการทำงานของ User มากที่สุด ดังนั้นถ้าเราทำ Load Test ให้คิดถึง Senario ที่มีคนเข้ามาใช้งาเยอะที่สุด อย่ายิง load เข้าหน้า main อย่างเดียวมันไม่ได้ประโยชน์อะไร

ขั้นตอนการทำ Load testing

  1. ก่อนเริ่มต้นใช้งาน Locust เราต้องติดตั้ง locust cli ก่อน โดยเข้าไปที่ terminal(MacOS) หรือ PowerShell(windows) แล้วทำการ run command pip install หรือ pip3 install ในกรณีที่เราต้องการ run locust ผ่าน docker container ให้ลงไปดูด้านล่าง

    $ pip3 install locust
    

    หลังจากนั้นให้ตรวจสอบ ว่า locust นั้นใช้งานได้ด้วยคำสั่ง

    $ locust -v
    

    หรือถ้าเราอยากจะระบุชื่อ locust file ที่ต้องการ run (โดย default จะใช้เป็นlocustfile.py)

    $ locust -f custom-locustfile.py
    

    และถา้อยากระบุ host เข้าไปทาง command line เลยก็ทำได้โดยที่ทำการใส่ option เข้าไปแบบนี้(ไม่ต้องใส่ / ต่อท้าย)

    $ locust - -host=<hostname>
    
  2. ขั้นตอนต่อไปเราก็จะเริ่มเขียน Code โดยที่เริ่มจากการสร้างไฟล์ locustfile.py ไว้ใน project ของเรา ไฟล์นี้คุณตั้งชื่อตาม User Behavior ได้เลย หรือถ้าไฟล์ที่ใช้ในการทดสอบมีเยอะมากก็ตั้ง folder load-test ขึ้นมาเก็บ load test file ต่างๆเอาไว้ จะได้จัดการได้ง่ายขึ้น

  3. import locust เข้ามาใช่งานก่อน

    from locust import HttpUser, task
    
  4. เขียน Code เพื่อสร้าง User Behavior ซึ่ง Locustfile จะทำงานได้ก็ต่อเมื่อมีอย่างน้อย 1 Class ที่ inherit มาจาก HttpUser

    class HelloWorldUser(HttpUser):
        @task
        def hello_world(self):
            self.client.get('/')
    
  5. ทำ Load test ด้วยคำสั่ง

    $ locust
    

    หลังจากนั้นจะได้ Web application ของ Locust หน้าตาแบบนี้

    Load Test with Locust
    กำหนดปริมาณของ load ที่เราต้องการทดสอบ

    ถ้า Browser ของคุณไม่ได้เด้งขึ้นมาโดยอัตโนมัติให้เข้าไปที่ http://localhost:8090

  6. ระบุจำนวนของ Users และ URL ของ Web Application หรือ Web API ที่เราต้องการทำ Load test

    • Number of users ใส่จำนวนของ users ที่เข้าไปพร้อมกันมากที่สุด(Peak concurrent users)
    • Spawn rate จำนวน users ที่ start task ขึ้นมาทำงานภายใน 1 วินาที
    • Host Base URL ของ target(path เราจะใส่ไว้ใน Task)
  7. หลังจากกด Start Swaming Lucust ก็จะพาเข้าไปสู่หน้า report ของการทำ Load test

    Load Test with Locust
    สถิติที่ locust สร้าง request ไปยัง target ที่ URL ไหนจำนวนเท่าไหร่บ้าง
    Load Test with Locust
    กราฟแสดงจำนวน Request ต่อวินาที(Total requests per second)
    Load Test with Locust
    กราฟแสดง Response times หน่วยเป็น millisecond
    Load Test with Locust
    กราฟแสดง Number of users

Option ต่างๆที่สามารถใส่เพิ่มเติมได้

Waittime Attribute

เราสามารถ delay request ในแต่ละ task ได้ด้วยการกำหนด waittime attribute เข้าไปใน class แบบนี้

1
2
3
4
5
6
class HelloWorldUser(HttpUser):
    wait_time = between(1, 5)

    @task
    def hello_world(self):
        self.client.get('/')

ในตัวอย่างนี้จะเป็นการ delay request ในแต่ละ task ออกไป 1-5 วินาที

กำหนด Event on_start

เราสามารถระบุสิ่งที่ต้องทำก่อนการ run load test เช่น login ด้วยการใส่ on_start เข้าไปใน class แบบนี้

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class HelloWorldUser(HttpUser):
    wait_time = between(1, 5)

    def on_start(self):
        self.client.post("/login", {
            "username": "test_user",
            "password": ""
        })

    @task
    def hello_world(self):
        self.client.get('/')

กำหนด Weight ให้กับแต่ละ task ได้

โดยปกติ locust จะกระจาย load ให้กับทุกๆ task เท่าๆกัน ถ้าเราอยากให้หน้าไหนหรือ task ไหมมีการใช้งานมากกว่าคนอื่นสามารถกำหนด Weight เข้าไปได้แบบนี้

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class HelloWorldUser(HttpUser):
    wait_time = between(1, 5)

    @task(3)
    def index(self):
        self.client.get('/')
    
    @task
    def about(self):
        self.client.get('/about')

ในตัวอย่างนี้ locust จะทำการส่ง request เข้าไปที่หน้า index มากกว่า หน้า about 3 เท่าโดยประมาณเพราะเรากำหนด weight เป็นอัตรส่วน 3:1

การใช้งาน locust ผ่านทาง docker

แน่นอนว่าการทำ load test ในปัจจุบันเราต้องการทำ Automation ดังนั้นเราจึงหลีกเลี่ยงการใช้ Locust ผ่านทาง docker ไม่ได้ ซึ่งขั้นตอนการ run locust ผ่านทาง docker มีดังนี้

  1. เราต้องสร้าง locustfile.py ขึ้นมาก่อน
  2. run docker command เพื่อสร้าง locust container ซึ่งเราต้อง run command นี้ใน path ที่มี locustfile.py
    $ docker run -p 8089:8089 \
                 -v $PWD:/mnt/locust locustio/locust \
                 -f /mnt/locust/locustfile.py
    
    เราใช้ parameters
    • -p เพื่อ bind port 8090 ออกมาข้างนอกเพราะ locust จะ run web application ขึ้นมาที่ port ouh
    • -v ทำ bind mount เพื่อนำทุกไฟล์ที่ีอยู่ใน folder ปัจจุบันเข้าไปไว้ใน container ของ locust โดยจะไปเก็บไว้ใน /mnt/locust(path นี้เป็น path ภายใน container)
    • ส่วน -f จะเป็น parameter ของ locust เพราะวางอยู่หลังชื่อ image(locustio/locust) โดยที่ -f จะระบุชื่อ locustfile ซึ่งในกรณีนี้เราต้องการอ้างถึง path ที่เราได้ทำ bind mount เข้ามาใน container(/mnt/locust) เราจึงต้องใส่ -f /mnt/locust/locustfile.py
  3. Locust จะสร้าง web application ขึ้นมาที่ http://localhost:8090
  4. กดคลิก New Test ที่มุมขวาบน
  5. ระบุจำนวนของ Users และ URL ของ Web Application หรือ Web API ที่เราต้องการทำ Load test
    • Number of users ใส่จำนวนของ users ที่เข้าไปพร้อมกันมากที่สุด(Peak concurrent users)
    • Spawn rate จำนวน users ที่ start task ขึ้นมาทำงานภายใน 1 วินาที
    • Host Base URL ของ target(path เราจะใส่ไว้ใน Task)
  6. หลังจากนั้นกดปุ่ม Start swarming แล้วเข้าไปดูผลลัพธ์ของการทำ load test ได้่เลย

การใช้งาน locust ด้วย Docker Compose

อีกหนึ่งทางเลือกของการ run locust container คือ docker-compose ซึ่งมีขั้นตอนดังนี้

  1. เราจะต้องสร้าง docker-compose.yml ขึ้นมาก่อน

  2. ใส่เนื้อหานี้ลงไปใน docker-compose.yml

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    
    version: '3'
    
    services:
        master:
            image: locustio/locust
            ports:
            - "8089:8089"
            volumes:
            - ./:/mnt/locust
            command: -f /mnt/locust/locustfile.py --master -H http://master:8089
    
        worker:
            image: locustio/locust
            volumes:
            - ./:/mnt/locust
            command: -f /mnt/locust/locustfile.py --worker --master-host master
    

    เราสามารถ run locust ขึ้นมาได้ด้วยคำสั่ง

    $ docker-compose up -d
    

    แต่เนื่องจากในตัวอย่างนี้เรามีการนิยาม master และ worker เราจึงสามารถ scale ตัว worker ได้ด้วยคำสั่ง

    $ docker-compose up --scale worker=4
    

    นอกจากการทำ Distributed load test ด้วย docker compose แล้วเรายังสามารถ deploy locust ลงไปใน Kubernetes cluster เพื่อทำ Distributed load test ได้อีกด้วยลองอ่านต่อที่บทความ การทำ Distributed load test บน Kubernetes cluster

Phanupong Permpimol
Follow me

Software Engineer ที่เชื่อในเรื่องของ Process เพราะเมื่อ Process ดี Product ก็จะดีตาม ปัจจุบันเป็นอาจารย์และที่ปรึกษาด้านการออกแบบและพัฒนา Software และ Web Security