Skip to main content

ทดสอบความตึงเครียด (stress) ใน Docker container กับ Swap Memory เพื่อหาค่าที่เหมาะสม

Kongvut Sangkla

Intro

สวัสดีครับ บทความนี้เป็นการทดสอบความตึงเครียด (stress) กับ Memory swap เป็นการทำ PoC เพื่อทดสอบสมมุติฐานว่า "ถ้าสมมุติ Swap Memory ไม่เพียงพอจะเกิดอะไรขึ้น" เป็นบทความเสริมจาก "วิธีการจัดการ Resouces limits ของ CPU (cores) และ Memory ใน Docker containers"

เพื่อเสริมเพิ่มความเข้าใจเกี่ยวกับการใช้งาน Memory swap ใน Docker container ช่วยเพิ่มเข้าใจและการปรับค่าที่เหมาะสม

Swap Memory คืออะไร

Swap Memory คือ Virtual memory (RAM disk) หลักการทำงานคือเมื่อถึงจุด ๆ หนึ่งที่ Main memory (RAM) ไม่พอใช้แล้วระบจะ Swap ไปใช้ Virtual memory (RAM disk)

Swap Memory on Container conditions

ตามปกติแล้ว Default Swap Memory ใน Docker Container = MAX นั่นหมายความว่า

  • Swap Memory ใน Container จะใช้ Swap Memory ของ Host machine
  • ถ้าไม่ได้กำหนด --memory-swap Container Swap Memory = MAX (host)
  • ถ้าไม่ได้กำหนด --memory-swap แต่กำหนด --memory Container จะจำกัด Swap (Container) = Memory (Container)
  • หากกำหนด --memory-swap ต้องมีค่ามากกว่า --memory
  • หากกำหนด --memory-swap และ --memory (เอง) จะไม่สามารถกำหนดเป็นค่าเดียวกันได้
  • หากกำหนด --memory-swap -1 จะสามารถใช้ Swap Memory ของ Host ได้ไม่จำกัด
  • หากกำหนด เช่น --memory=256m และ --memory-swap=512m จะทำให้ Memory = 256m และ Swap = 256m (512m - 256m)
  • รายละเอียดและเงื่อนไขอื่น ๆ สามารถอ่านเพิ่มเติมได้จาก --memory-swap details

ตรวจสอบ Swap Memory และ RAM บนเครื่อง Host

$ free -h
total used free shared buff/cache available
Mem: 1.9Gi 285Mi 1.0Gi 888Ki 702Mi 1.6Gi
Swap: 1.0Gi 512Ki 1.0Gi

ผลลัพธ์ Swap Memory และ RAM บนเครื่อง Host

  • RAM=1.9Gi
  • Swap=1.0Gi

เตรียม Stress Test Tool

https://linux.die.net/man/1/stress

FROM arm64v8/ubuntu

RUN apt-get update -y && \
apt-get install -y stress

ENTRYPOINT ["/usr/bin/stress", "--verbose"]

ทดสอบความตึงเครียด (Stress Testing)

Round 1 🔥

RAM=Max, Swap=Max, vm-test=128m
$ docker run --rm --name stress stress -t 20s --vm 1 --vm-bytes 128M --vm-keep
stress: info: [1] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
stress: dbug: [1] using backoff sleep of 3000us
stress: dbug: [1] setting timeout to 20s
stress: dbug: [1] --> hogvm worker 1 [7] forked
stress: dbug: [7] allocating 134217728 bytes ...
stress: dbug: [7] touching bytes in strides of 4096 bytes ...
...
stress: dbug: [7] touching bytes in strides of 4096 bytes ...
stress: dbug: [1] <-- worker 7 signalled normally
stress: info: [1] successful run completed in 20s

OK ผ่าน ✅

  • 1.9Gi RAM เพียงพอ
  • 1.0Gi Swap แทบไม่ใช้

สามารถตรวจสอบการใช้ Swap ได้ที่ /sys/fs/cgroup/memory.swap.current

Round 2 🔥

RAM=128m, Swap=128m, vm-test=128m
$ docker run --rm -m 128m --name stress stress -t 20s --vm 1 --vm-bytes 128M --vm-keep
stress: info: [1] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
stress: dbug: [1] using backoff sleep of 3000us
stress: dbug: [1] setting timeout to 20s
stress: dbug: [1] --> hogvm worker 1 [7] forked
stress: dbug: [7] allocating 134217728 bytes ...
stress: dbug: [7] touching bytes in strides of 4096 bytes ...
...
stress: dbug: [7] touching bytes in strides of 4096 bytes ...
stress: dbug: [1] <-- worker 7 signalled normally
stress: info: [1] successful run completed in 20s

OK ผ่าน ✅

  • 128M RAM ไม่เพียงพอ
  • 128M Swap (used 1-2M)

Round 3 🔥

RAM=128m, Swap=128m, vm-test=220M
$ docker run --rm -m 128m --name stress stress -t 20s --vm 1 --vm-bytes 220M --vm-keep
stress: info: [1] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
stress: dbug: [1] using backoff sleep of 3000us
stress: dbug: [1] setting timeout to 20s
stress: dbug: [1] --> hogvm worker 1 [7] forked
stress: dbug: [7] allocating 230686720 bytes ...
stress: dbug: [7] touching bytes in strides of 4096 bytes ...
...
stress: dbug: [7] touching bytes in strides of 4096 bytes ...
stress: dbug: [1] <-- worker 7 signalled normally
stress: info: [1] successful run completed in 20s

OK ผ่าน ✅

  • 128M RAM ไม่เพียงพอ
  • 128M Swap (used 92-93M)

Round 4 🔥

RAM=128m, Swap=128m, vm-test=250M
$ docker run --rm -m 128m --name stress stress -t 20s --vm 1 --vm-bytes 250M --vm-keep
stress: info: [1] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
stress: dbug: [1] using backoff sleep of 3000us
stress: dbug: [1] setting timeout to 20s
stress: dbug: [1] --> hogvm worker 1 [7] forked
stress: dbug: [7] allocating 262144000 bytes ..
stress: dbug: [7] touching bytes in strides of 4096 bytes ...
...
stress: dbug: [7] touching bytes in strides of 4096 bytes ...
stress: dbug: [1] <-- worker 7 signalled normally
stress: info: [1] successful run completed in 20s

OK ผ่าน ✅

  • 128M RAM ไม่เพียงพอ
  • 128M Swap (used 123-124M) ปริ่ม ๆ

Round 5 🔥

RAM=128m, Swap=128m, vm-test=256M
$ docker run --rm -m 128m --name stress stress -t 20s --vm 1 --vm-bytes 256M --vm-keep
stress: info: [1] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
stress: dbug: [1] using backoff sleep of 3000us
stress: dbug: [1] setting timeout to 20s
stress: dbug: [1] --> hogvm worker 1 [7] forked
stress: dbug: [7] allocating 268435456 bytes ...
stress: dbug: [7] touching bytes in strides of 4096 bytes ...
stress: FAIL: [1] (416) <-- worker 7 got signal 9
stress: WARN: [1] (418) now reaping child worker processes
stress: FAIL: [1] (422) kill error: No such process
stress: FAIL: [1] (452) failed run completed in 1s

ไม่ผ่าน ❌ (แม้ว่า vm-test=256M จะพอดีกับ RAM และ Swap)

  • 128M RAM ไม่เพียงพอ
  • 128M Swap ไม่เพียงพอ

Round 6 🔥

RAM=128m, Swap=384m, vm-test=256M
$ docker run --rm -m 128m --memory-swap 512M --name stress stress -t 20s --vm 1 --vm-bytes 256M --vm-keep
stress: info: [1] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
stress: dbug: [1] using backoff sleep of 3000us
stress: dbug: [1] setting timeout to 20s
stress: dbug: [1] --> hogvm worker 1 [7] forked
stress: dbug: [7] allocating 268435456 bytes ...
stress: dbug: [7] touching bytes in strides of 4096 bytes ...
...
stress: dbug: [1] <-- worker 7 signalled normally
stress: info: [1] successful run completed in 20s

รอบนี้ลองเพิ่ม --memory-swap 512M

OK ผ่าน ✅ ใส ๆ

  • 128M RAM ไม่เพียงพอ
  • 384M Swap (used 128-130M)

Swap คือ 384M (ไม่ได้เขียนผิด เพราะอะไรอธิบายไว้ที่ Swap Memory on Container conditions)

Round 7 🔥

RAM=128m, Swap=Unlimited, vm-test=256M
$ docker run --rm -m 128m --memory-swap -1 --name stress stress -t 20s --vm 1 --vm-bytes 256M --vm-keep
stress: info: [1] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
stress: dbug: [1] using backoff sleep of 3000us
stress: dbug: [1] setting timeout to 20s
stress: dbug: [1] --> hogvm worker 1 [7] forked
stress: dbug: [7] allocating 268435456 bytes ...
stress: dbug: [7] touching bytes in strides of 4096 bytes ...
...
stress: dbug: [1] <-- worker 7 signalled normally
stress: info: [1] successful run completed in 20s

กำหนด --memory-swap -1 (Unlimited)

OK ผ่าน ✅ อยู่แล้ว

  • 128M RAM ไม่เพียงพอ
  • 1.0Gi Swap (used 128-130M)

Swappiness คืออะไร และมีความสำคัญอย่างไร

Swappiness คือ Linux kernel parameter เป็นค่า Threshold สำหรับกำหนดไว้จัดการ Swap เมื่อใช้พื้นที่หน่วยความจำ RAM ถึงค่า Threshold ที่กำหนดไว้ก็จะทำการ Swapping out

โดยค่า Default ส่วนใหญ่ของ Linux กำหนดไว้คือ 60 นั่นก็หมายความว่า จะเริ่มใช้งาน Swap Memory เมื่อพื้นที่หน่วยความจำของ RAM เหลือน้อยกว่า 40% ดังนั้นเราสามารถปรับค่า Swappiness ให้เหมาะสมเพื่อประสิทธิภาพที่ดี

บน Docker ใช้ Options --memory-swappiness สำหรับการกำหนดค่า Swappiness โดยมีเงื่อนไขดังนี้

  • หากไม่กำหนด Docker จะใช้ค่า Swappiness เดียวกันกับ Host (default)
  • กำหนดค่า 0 คือการปิดใช้งาน Swapping
  • สามารถกำหนดค่าได้ระหว่าง 0 - 100
Host Swappiness (default)
$ cat /proc/sys/vm/swappiness
60

ขอแนะนำให้กำหนดค่าระหว่าง 10 หรือ 15 ตัวอย่าง ถ้า swappiness คือ 10 นั่นหมายความว่าระบบจะเริ่ม Swapping out processes เมื่อพื้นที่ของ RAM เหลือน้อยกว่า 90%

สามารถศึกษาวิธีปรับค่า swappiness ของ Host ได้ที่ Change the Swappiness Value in Linux Kernel

สรุป

บทความนี้เป็นการทำ PoC เพื่อทดลองการกำหนดค่า --memory-swap เพื่อหา Best Practices โดยขอสรุปแนวทางที่ดีที่สุดดังนี้

  1. เพื่อประสิทธิภาพแล้วพยายามใช้ RAM ก่อนเพื่อให้มีประโยชน์มากที่สุด มีความรวดเร็วในการประมวลผล
  2. Host ควรมีการกำหนด Swap Memory ไว้
  3. ถ้ามี RAM จำกัดให้ใช้ --memory (Memory limits) + --memory-swap (Swap limits) ร่วมกัน
  4. คอยตรวจสอบดูการใช้ Swap ของ Container ว่าเพียงพอหรือไม่
  5. กำหนด Swappiness ของ Host เป็นค่าระหว่าง 10 หรือ 15
  6. การกำหนด --memory-swap ไว้เยอะ ๆ ก็ใช่ว่าจะเป็นเรื่องดีเสมอไป เพราะต้องเข้าใจก่อนว่า Swap คือการทำ RAM disk ดังนั้นการใช้ Swap เยอะ ๆ ก็จะส่งผลกับประสิทธิภาพ I/O ของ Disk ด้วย
  7. อีกแนวทางหนึ่งที่น่าสนใจเกี่ยวกับการทำ Swap คือเราสามารถใช้ Disk ตัวรองอื่น ๆ (เช่น NVMe ที่มีความเร็ว อ่าน/เขียน สูง) Mount เพื่อทำ Swap แยกได้เช่นกัน

References

Loading...