2021-11
2021-11-05 - การตั้งค่า Heap Memory ให้กับ Java App เพื่อรองรับ Vertical Scale
ในการใช้งาน Cloud อาจจะมีบางครั้งที่เราไม่ได้ต้องการทำ Horizontal Scale (เพิ่มจำนวนเครื่อง) แต่เป็นการทำ Vertical Scale (เพิ่มขนาด CPU & Memory) แทน
การ Set Java options -Xms และ -Xmx ซึ่งเคยใช้กำหนดขนาด Heap size ที่ยอมให้ Application ใช้งานได้แบบ fixed size จึงไม่นิยมอีกต่อไป เพราะหากเรา freeze Application & Config ไว้เป็น VM Image แล้ว เมื่อนำมา Deploy ด้วยขนาดเครื่องซึ่งต่างออกไป ตัว Application ก็ไม่สามารถใช้งาน Memory ที่เพิ่มเข้ามาได้อย่างเต็มที่ เพราะติด limit ที่เรา fixed ไว้
การใช้ Java options ที่กำหนดขนาด Memory ที่สามารถใช้งานได้ โดยคิดจากเปอร์เซ็นต์ของ Physical Memory เครื่อง จึงมีความยืดหยุ่น และสมเหตุสมผลมากกว่า ซึ่ง Java options นี้มีมาตั้งแต่ช่วงปลายปี 2018 แล้ว เช่น JDK version 8u191
Java options ที่ใช้ในการกำหนดขนาด Memory โดยคิดจากเปอร์เซ็นต์ของ Physical Memory คือ
-XX:InitialRAMPercentage, -XX:MaxRAMPercentage และ -XX:MinRAMPercentage
InitialRAMPercentage
กำหนดขนาด Heap size ตอน Application เริ่มทำงาน คล้าย -Xms แต่หน่วยเป็นเปอร์เซ็นต์ของ Phisical Memory
ถ้า server มี Memory 1GB แล้วกำหนด -XX:InitialRAMPercentage=25.0 Application จะเริ่มทำงานโดยจอง Heap size ที่ประมาณ 250MB
หากมีการ set ค่า -Xms ไว้ด้วย ค่า -XX:InitialRAMPercentage จะไม่ทำงาน
MaxRAMPercentage และ MinRAMPercentage
กำหนดขนาด Heap size สูงสุดที่ Application สามารถใช้งานได้ คล้าย -Xmx แต่หน่วยเป็นเปอร์เซ็นต์ของ Physical Memory
ข้อแตกต่างของ MaxRAMPercentage และ MinRAMPercentage คือ MinRAMPercentage จะถูกใช้เมื่อ server มีขนาด Physical Memory น้อยกว่า 250MB แต่หากมากกว่านั้น จะใช้ค่า MaxRAMPercentage แทน
ถ้า server มี Memory 250MB แล้วกำหนด -XX:MinRAMPercentage=50.0 Application จะใช้งาน Heap size ได้สูงสุด ประมาณ 125MB
ถ้า server มี Memory 1GB แล้วกำหนด -XX:MaxRAMPercentage=75.0 Application จะใช้งาน Heap size ได้สูงสุดประมาณ 750MB
Reference
2021-11-09 - TCP Tuning for WebServer or CDN
Default config kernel ของ Linux ถูกตั้งมากลางๆ สำหรับงานจำพวก General Purpose เพราะไม่รู้ว่า user ต้องการใช้ server ไปทำงานทางด้านไหน
งานประเภท Database Server กับงานประเภท Web Server มีค่าที่เหมาะสมต่างกัน เพื่อให้ทำงานได้มีประสิทธิภาพกว่าการใช้ค่า default จึงต้องมีการปรับ Tuning ค่า
ค่า Config ที่ปรับ สำหรับงาน Web Server ซึ่งจะต้องรับ network packet จาก Client จำนวนมาก จึงเป็นดังนี้
แก้ ulimit เพื่อเพิ่ม open socket
แก้ sysctl เพื่อปรับ kernel ให้เหมาะกับงาน network มากขึ้น
Increase Ephemeral port range
ปรับ Ephemeral port range ให้เพิ่มขึ้นเพื่อให้ เปิด socket ได้มากขึ้น กรณีนี้ Application ไม่ได้ listen port สูงกว่า 10000 จึงกำหนดช่วงเป็น 10000-65000 ได้
Default Buffer size
16MB สำหรับ Interface 1GE
32MB หรือ 54MB สำหรับ Interface 10GE
ปรับลดค่า maximum read buffer size ลง เพื่อ tradeoff ระหว่าง bandwidth และ latency
Increase queue length
เพิ่มค่า net.core.netdev_max_backlog เพื่อลด packet drop
เพิ่มค่า net.core.netdev_budget เพื่อให้ดึงจำนวน packet ไปในแต่ละรอบมากขึ้น
Tuning TIME-WAIT socket
เพิ่มค่า net.ipv4.tcp_max_tw_buckets เพื่อให้มีจำนวน socket ที่อยู่ในสถานะ TIME-WAIT ได้เพิ่มขึ้น
enable net.ipv4.tcp_tw_reuse เพื่อให้สามารถนำ socket ที่อยู่ในสถานะ TIME-WAIT มารับ connection ใหม่ได้ โดยไม่ต้องผ่านขั้นตอน allocation และ initial ใหม่ซึ่งจะกิน CPU cycle
ลด net.ipv4.tcp_fin_timeout ลง เพื่อให้ socket อยู่ในสถานะ FIN-WAIT-2 สั้นลง และเข้าสู่สถานะ TIME-WAIT ต่อไป
Disable TCP slow start on idle connections
เพื่อยอมให้ TCP ส่ง packet จำนวนมากได้ขณะเริ่ม connection ทำให้ bandwidth ดีขึ้น
Reference
2021-11-12 - tomcat exporter เพื่อใช้ร่วมกับ Jmx Dashboard(tomcat)
Setup exporter
นำ JMX exporter มาจาก GitHub ของ prometheus
Download file Java agent ซึ่งจาก ณ เวลาที่เขียนบทความ จะเป็น file ดังนี้
jmx_prometheus_javaagent-0.16.1.jar
ในที่นี้สมมติให้วางไฟล์ไว้ใน folder agent ภายใต้ directory ของ tomcat
สร้างไฟล์ /usr/local/tomcat/agent/config.yaml
แก้ไข service ของ tomcat เพิ่มค่า Environment
ค่าที่ใส่จะบอกให้รู้ว่า รัน agent ของ jmx exporter และ listen port 8088 สำหรับให้ Prometheus มากวาด metrics ไป
สั่ง restart service
ทดสอบดึงข้อมูล metrics ด้วย curl
Setup Prometheus
เพิ่มส่วน config ดังนี้
จากตัวอย่างจะให้ prometheus มาดึง metrics ที่ port 8088 ของเครื่อง server ที่ติดตั้ง tomcat (172.31.0.6) หลังจากได้ค่าแล้ว เพิ่ม label app=tomcat ไปด้วย
Import Dashbord เข้า Grafana
ไฟล์ถูกแก้มาจาก Dashboard ดังนี้
https://grafana.com/grafana/dashboards/11122
2021-11-12 - การทำ EC2 Service Discovery ของ Prometheus
เพื่อให้ Prometheus สามารถเลือกเก็บ metrics จากบางเครื่อง จึงต้องทำ Service Discovery
ในที่นี้ใช้การเลือกเก็บจาก tag ซึ่งอยู่กับเครื่อง EC2 จึงใช้ ec2_sd_configs
เตรียม role ซึ่งยอมให้ list EC2 และ list tag บน EC2 ซึ่งมี permission ดังนี้
config Prometheus ตามตัวอย่างดังนี้
Prometheus จะใช้ role ec2_read_access_role ซึ่ง attach อยู่กับ server ของ Prometheus ในการ list เครื่องทั้งหมดซึ่งมี tag ตรงกับที่กำหนด ซึ่งในที่นี้คือ
Project = demo
Environment = test
Service = demoservice
หาก tag ใด tag หนึ่งขาดไป ก็จะไม่ถูก list เข้ามา หลังจากนั้นจะ scrape metrics จาก port 8088 ของเครื่องที่อยู่ใน list ทำการ relabel ให้เพิ่ม app=tomcat เข้าไป
2021-11-16 - Change Default Docker subnet
ในบางครั้งการที่องค์กรของเรามี subnet ซึ่งชนกับ subnet ที่ Docker ใช้งาน ก็ทำให้เกิดปัญหาการเชื่อมต่อกับ server ที่ติดตั้ง Docker ได้
จากรูปจะพบว่าองค์กรใช้ network 172.16.0.0/12 และมีการแบ่ง subnet 172.17.0.0/24 และ 172.31.0.0/24 มาให้กับ server ใช้งาน
แต่บนเครื่อง server 172.31.0.21 มีการติดตั้ง Docker Engine และไม่ได้แก้ไข network ภายใน Docker Engine ทำให้ได้ docker network ที่ชื่อ bridge ซึ่งถูกสร้างมาเมื่อติดตั้ง Docker Engine เป็น subnet 172.17.0.0/24 และชนกับที่องค์กรใช้งาน
ในการเชื่อมต่อระหว่างเครื่อง server 172.31.0.21 กับระบบอื่นๆในองค์กรซึ่งใช้ network อื่นที่ไม่ใช่ 172.17.0.0/24 จะไม่พบปัญหาใดๆ แต่เมื่อมีการเชื่อมต่อระหว่าง server นี้กับ server จาก network 172.17.0.0/24 จะทำให้เกิดปัญหา เพราะ server จะเข้าใจผิดว่าให้ส่ง packet network ไปที่ network ของ Docker แทน
ทางแก้ไขคือ เปลี่ยน default subnet ของ Docker ให้เป็นวงอื่นแทน ซึ่งไม่ซ้ำกับวง network ขององค์กร
วิธีทำคือ แก้ไขหรือเพิ่มไฟล์ /etc/docker/daemon.json
ไฟล์ config ดังกล่าวจะบอกให้ Docker Engine ไปเลือก subnet จาก network วง 10.0.0.0/16 แทน โดยหั่น subnet มาใช้ทีละ /24 ดังนั้น network วงแรกที่จะได้ (bridge) จะเป็น 10.0.0.0/24 หากหลังจากนั้นมีการสร้าง docker network เพิ่ม โดยไม่กำหนด subnet range ก็จะได้ 10.0.1.0/24, 10.0.2.0/24 และ 10.0.3.0/24 เป็นต้นไป
บันทึกไฟล์แล้ว restart service Docker
ลอง inspect network bridge ดู จะพบว่าเปลี่ยนเป็น subnet 10.0.0.0/24 แล้ว
Last updated