Serf เป็นเครื่องมือที่ช่วยให้ การจัดการ กับ ระบบคลัสเตอร์ทำได้ง่ายขึ้น  ทำงานโดยผ่าน GOSSIP โปรโตคอล ซึง Serf ถูกออกแบบมาเป็นโครงสร้างที่  ไม่ซับซ้อน, ใช้ทรัพยากรน้อย, ทำงานได้เร็ว และ ยืดหยุ่น อยากให้เปรียบเทียบกับ Hearbeat แม้ฟังก์ชั่นอาจจะไม่ตรงตัวนักแต่จะทำให้เข้าใจได้ง่ายกว่า Serf พัฒนาโดย HarshiCorp  ซึ่งเป็นคนพัฒนา Vagrant เขียนด้วย ภาษา Go (Golang)

สาเหตุที่ผมเลือกใช้ Serf ในการนำเอามาใช้งานคือ ต้องการหลีกเลี่ยงการออกแบบระบบให้กลายเป็น (Single point of failure) ขณะเดียวกัน ลดความยุ่งยากของระบบที่ทุกสิ่งทุกอย่างรวมอยู่ด้วยกัน (Centralized) นอกจากนั้นผมต้องการนำเอาระบบนี้มาใช้ใน Docker และ ขณะเดียวกันก็ลดความยุ่งยากของการ deployment ระบบ สรุปภาพรวมของความต้องการผมได้ดังนี้

  • หลีกเลี่ยง จุดตาย (Single point Of Failure)
  • หลีกเลี่ยง ระบบที่รวมอยู่ที่ศูนย์กลาง (De-centralized)
  • อยากได้การทำงานของ ระบบที่ไม่ซับซ้อน เพื่อง่ายต่อการทำความเข้าใจ หรือ การเรียนรู้  และ ง่ายต่อการติดตั้ง ปรับแต่ง
  • etcd  ต้องการ Server etcd ไม่งั้นจะคอย report ERROR ไม่สามารถเชื่อมต่อกับ Node Datacenter ได้
  • Zookeeper ระบบมีความซับซ้อน
  • Dooserd ดูจาก repository ไม่ค่อยมีการ update และ issue fix  มานานแล้วผมจึงใช้
  • Google Kubernates มีขนาดใหญ่ไปและผมต้องการระบบอะไร ที่มันมีความซับซ้อนน้อยที่สุด

นั่นคือเหตุผลที่เลือกใช้ Serf ทีนี้มีดูกันว่าหัวข้อแต่ละหัวข้อมีอะไรบ้าง

ความสามารถหลักของ Serf

  • ทำงานบนโปรโตคอล GOSSIP – GOSSIP เป็น โปรโตคอลสำหรับระบบกระจายสมัยใหม่ (Modern distribute) ซึ่ง Serf พัฒนาบนโปรโตคอลนี้  ทำให้มีฟังก์ชั่นการทำงานที่ครอบคลุม กับ ปัญหา หรือ ข้อจำกัดในระบบประมวลผลกลุ่ม (Cluster)
  • ตรวจสอบความผิดพลาด (Failure detection) – กรณีเกิดความผิดพลาดขึ้น จะมีการตรวจสอบเหตุการณ์ของ  Events ต่างๆ ในที่นี้คือ node failure เหตุการณ์ที่เกิดขึ้น  กับ Node นั้น จะถูกส่งต่อไปยัง Node อื่นๆ โดยอัตโนมัติ ขณะเดียวกันก็มีการแจ้งการทำงาน  และ ลบ Node ที่เกิดเหตุผิดพลาดขึ้นโดยอัตโนมัติ  การแจ้งเตือนนี้ จะแจ้งข้อผิดพลาดไปยัง โหนดต่างๆ ทันที
  • ปรับแต่งเหตุการณ์ได้ (Custom) Events – นอกจากเหตุการณ์มาตรฐาน (Standard Events) ที่ Serf จับเตรียมไว้ให้แล้ว เราสามารถสร้าง เหตุการณ์ต่างๆ  เพื่อให้เหมาะสมกับการทำงานของ แอพพลิเคชั่น ของเรา หรือ ซอฟต์แวร์ Stack ที่เรามีอยู่ได้  จึงทำให้ Serf มีความยืดหยุ่นมากขึ้น

เหตุการณ์มาตรฐาน (Standard Events)

  • member-join
  • member-leave
  • member-failed
  • member-updated
  • member-reap

แอพพลิเคชั่นที่ทำงาน คล้ายๆ กัน

ตัวอย่างการใช้งาน

ขอยกตัวอย่าง Case ของ Unison multi-host file replication และ ในที่นี้ผมจะพูดถึงเฉพาะ ในส่วนของ Serf จะไม่มีพูดถึง Unison software ซึ่งสามารถติดตามได้ใน บทความที่เกี่่ยวข้องต่อไป เพียงแต่ต้องการประหยัดเวลาในการทำงาน และ แยกหัวข้อออกมา เพื่อให้เข้าใจได้มากขึ้นในบทความที่ไม่ยาวเกินไป

ซอฟต์แวร์ที่นำเอามาใช้งาน

  • Docker สำหรับ ทำคอนเทนเนอร์ แอพพลิเคชั่น (package software running environment) เพื่อสะดวกต่อการ deploy และ คอนโทรล สภาวะแวดล้อมของ แอพพลิเคชั่น
  • fig สำหรับ builder docker container automation
  • Serf สำหรับทำการ register service name (unison node)
  • Unison เป็นแอพลิเคชั่นสำหรับทำการ sync ไฟล์ข้ามเครื่องแบบหลาย node (user-level file distribute replication)
  • dnsmasq สำหรับทำการ register hostname ให้รู้จักโหนดอื่นๆ
  • supervisord สำหรับดูแลบริหารจัดการ Service หลายๆ Service ที่ทำงานอยู่ใน Docker container

ปัญหา

เนื่องจาก Unison  ทำการ replication files  ไปยังโหนดหลายๆ โหนด  โดยทำการเชื่อมต่อกัน  เป็น Start topology (Hub, Spoke)  ดูจากรูปด้านล่าง

ขณะเดียวกัน เมื่อเราสั่งให้ Docker ทำการ Link connection มันจะทำการ Link connection  Docker container ที่เกี่ยวข้องเข้ามาเชื่อมโยงด้วยกัน และ ทำการเพิ่ม node แต่ละ node เข้าไปยัง  host file ให้โดยอัตโนมัติ แต่ Docker node (Spoke) แต่ละ node ไม่สามารถทำการ Link กลับไปยัง node แรกได้ (Hub) การเพิ่ม node จึงเป็นเรื่องยุ่งยากที่ต้องไป register name เพื่อให้ node อื่นๆ หรือ Node แรกรู้จักได้

สิ่งที่ยุ่งยากเพิ่มเติมคือ ระบบ Network IP ของ Docker นั้นเป็น แบบ DHCP  กรณีที่เรา ลบ node หรือ เพิ่ม node เข้าไป เราไม่สามารถรู้ได้เลย ว่าเราจะได้ เราจะได้ เบอร์ IP อะไร นอกจากกำหนดค่าให้เป็น Static IP  และ หรือ ทำการออกแบบระบบให้ใช้   Dynamic DNS แบบ local  ซึ่งก็จะทำให้ เกิดปัญหาขึ้น  กรณี  DNS service เกิดมีปัญหาขึ้นมา (Single Point Of  Failure) ทำให้ต้องออกแบบระบบต้องซับซ้อนขึ้นไป ถ้าต้องออกแบบระบบให้มีระบบสำรองโดยอัตโนมัติ (Redundant service) และ ยิ่งถ้าออกแบบระบบให้เป็นแบบ Static DNS  ต้องมาคอยเพิ่ม หรือ ลบ node และ ทำ name/ip mapping nodes (Hub, Spoke) ยิ่งจะสร้างความซับซ้อนเข้าไป

ทางแก้ ให้ ระบบทำการ register name และ ทำการ Mapping IP ให้เข้ากับชื่อ ของ Docker container ทุกหมดในครั้งแรกจาก Node ที่เป็น Hub ก่อน จากนั้นให้แต่  Node (Spoke)  ทำการ mapping name/IP โดยอัตโนมัติทีหลัง  โดยทั้งหมดจะทำโดยอัตโนมัติ เมื่อเปิดการทำงานของ ระบบขึ้น หรือ มีการ ลบ หรือ เพิ่ม  Node แต่ละ Node ออกไป (Hub, Spoke) ระบบจะทำการ update ชื่อให้เองโดยอัตโนมัติ  โดยเราไม่ต้องกังวล หรือ เสียเวลา กับ เรื่องการทำ Name/IP  mapping  อีกต่อไป

เนื่องจากผม ทำ pre-configuration ให้หมดแล้วอยู่ใน  Docker container  สามารถศึกษาการทำงาน และ การ config ได้จากใน folder source-code เลย (ดูหัวข้อ “วิธีการสร้าง Docker container และ วิธีใช้ )

ลำดับขั้นตอนการกำหนดค่า โดยรวม

  1. ดาวน์โหลด Serf Binary package จาก serfdom.io
  2. upzip ไฟล์ Serf Binary ที่ได้
  3. copy file bindary serf  ไปยัง Folder ที่อยู่ใน path executable เช่น /usr/sbin  เป็นต้น

ตั้งค่าการทำงานของ  Serf

  • Serf main configuration  ซึงจะกำหนด directory config สำหรับ Serf
  • Serf operation script  ใช้สำหรับ กรณีมี Events ต่างๆ เกิดขึ้นเช่น มีการ ลบ Node, เพิ่ม Node  ( Hub , Spoke ) จะให้ทำอะไร ซึ่งจะ script  นี้จะไปเพิ่ม name record ของ dnsmasq ให้โดยอัตโนมัติ
  • ไฟล์ Script นี้จะอยู่ที่  /etc/serf/handlers/
  • สร้าง shell script สำหรับ เพิ่ม Node ( Hub , Spoke ) โดยอัตโนมัติ เมื่อ ระบบถึงสั่งให้ทำงานครั้งแรก
  • ไฟล์นี้จะอยู่
  • กำหนด Supervisord  ให้สั่ง รัน Script นี้ครั้งแรก เท่านั้นเพื่อ register Node ที่เหลือจะเป็นการเรียกใช้ script ของ Serf เพื่อคอยจัดการเวลามีการเพิ่มโหนดลบโหนด

วิธีการสร้าง Docker container และ วิธีใช้ ผมสมมุติว่าเครื่องของผู้ที่จะทดสอบนี้เป็น ระบบปฏิบัติการ  Linux Ubuntu นะครับ

ติดตั้ง python และเครื่องมือสำหรับทำการติดตั้ง Python

apt-get install python  easy_install git

ติดตั้ง  fig

sudo pip install fig

ทำการ clone  Repository  ThaiopenSource

git clone https://github.com/udomsak/thaiopensource-tut

สั่ง Build container

cd unison

fig build central

fig build node2

fig build node3

สัง Run container

fig up -d central

fig มันจะทำการ Link connection  โดยทำการเพิ่ม record เครื่องอื่นๆ ที่เกี่ยวข้องใน Host file ให้โดยอัตโนมัติ

ดูว่าขั้นตอนการทำงาน ไม่มีอะไรผิดปกติ

docker ps

ควรจะแสดงผล  ออกมาคล้ายรูปภาพข้างล่างนี้  โดยเราจะมีอยู่ทั้งหมด 3 container คือ

  • central
  • node2
  • node3

เนื่องจากผม config  http web สำหรับ ทดสอบการทำงานของ file synchronize เอาไว้ด้วย คือ port 8000,8001,8002  ดังนั้นต้องแน่ใจว่าในเครืองเรา ไม่มี port ดังกล่าวถูกใช้งานอยู่  แต่หากต้องการที่เปลี่ยน network port mapping ให้เข้าไปเปลี่ยนที่ไฟล์  fig.yml

ทดสอบ

docker ps  ดูเลข container ID ของ container แต่ละตัว มาใช้เพื่อทำการ access contianer โดยไม่ต้องใช้ secure shell หรือใช้  snippet shell แบบนี้

$central=$(docker ps |grep central | awk '{print 1}')

$node2=$(docker ps |grep node2 | awk '{print 1}')

$node3=$(docker ps |grep node3 | awk '{print 1}')

docker  exec -it {container ID} /bin/bash

docker exec -it $central  /bin/bash

ping  ทดสอบ connection

Container ชื่อ Central (Hub)

docker exec -it $central /bin/bash

ping node2

ping node3

Contianer ชื่อ node2 (Spoke)

docker exec -it  $node2  /bin/bash

ping central

ping node3

Container ชื่อ node3 (Spoke)

docker exec -it $node3 /bin/bash

ping central

ping node2

จากที่เขียนมาน่าจะพอทำให้ทุกคนเข้าใจถึง การทำงานของ Serf และการประยุกต์ใช้งาน โดย Serf เองสามารถประยุกต์การใช้งานได้อีกมากจากความยืดหยุ่นของมัน  อาทิเช่น การเพิ่มหรือลบโหนดใน Haproxy หรือ MySQL cluster, Web farm ฯลฯ

@Proteus family.

Comments are closed.