บทความนี้เขียนร่วมกันโดย Hart Hoover และ Ryan Walker
เมื่อเร็วๆ นี้ ทีม DevOps Automation ของ Rackspace ได้ประกาศบริการที่ส่งการแจ้งเตือนจาก New Relic ไปยังฝ่ายสนับสนุนของ Rackspace การแจ้งเตือนเหล่านี้จะสร้างตั๋ว (tickets) ให้วิศวกร DevOps ตอบสนอง ทำให้ลูกค้าสามารถนอนหลับได้สบายใจแม้จะมีการแจ้งเตือนเวลา 3 โมงเช้า เมื่อรวมกับจุดข้อมูลอื่นๆ ที่เก็บรวบรวมเกี่ยวกับสภาพแวดล้อมของลูกค้า วิศวกรของเราจะสามารถระบุจุดที่มีปัญหาและดำเนินการแก้ไขที่เหมาะสมได้
ความท้าทายด้านความปลอดภัยและการเข้าถึง
ขณะออกแบบโครงสร้างพื้นฐานสำหรับบริการนี้ เราเจอปัญหาที่พบบ่อยแต่น่าสนใจ: เราต้องจำกัดการเข้าถึงระบบภายในของ Rackspace เพื่อความปลอดภัย ขณะเดียวกันก็ต้องรักษา Public Endpoint ไว้ให้ New Relic สามารถสื่อสารได้ วิธีแก้ของเราคือการออกแบบบริการที่มี Public API Endpoints และ Private Workers แยกจากกันอย่างสมบูรณ์ Public API Endpoints จะรับการแจ้งเตือนจาก New Relic และส่งต่อไปยังอินสแตนซ์ Redis ของ ObjectRocket ซึ่งทำหน้าที่เป็นคิว (queue) บริการ Worker จะทำงานภายในเครือข่ายข้างหลัง Firewall ของ RackConnect และดึงข้อความจากคิวมาสร้างการแจ้งเตือน
การแบ่งแยกสภาพแวดล้อมแบบนี้ทำงานได้ดีมาก แต่ก็สร้างปัญหาใหม่ขึ้นมาสำหรับการรวมกลุ่มบันทึก (Log Aggregation) เรารัน ElasticSearch/Kibana Stack ภายในสภาพแวดล้อมส่วนตัว (Private Environment) ข้างหลัง Firewall เราใช้ Fluentd ส่งบันทึกไปยัง ElasticSearch โดยตรง แต่นอก Firewall ไม่สามารถเข้าถึง EK Stack ได้ เพื่อแก้ปัญหานี้ เราเริ่มใช้ Fluentd ส่งบันทึกจาก Public API Services ไปยังอินสแตนซ์ MongoDB ของ ObjectRocket ในภายใน เราใช้ Fluentd อีกครั้งเพื่อดึงบันทึกจาก ObjectRocket เข้า ElasticSearch วิธีนี้ทำให้เรามีจุดหมายปลายทางเดียวสำหรับกิจกรรมทั้งหมดของสภาพแวดล้อม
Fluentd คืออะไร?
Fluentd เป็น Data Collector แบบ Open Source ที่พยายามจัดโครงสร้างข้อมูลเป็น JSON มากที่สุดเท่าที่จะทำได้ หมายความว่าคุณไม่ต้องเขียนและบำรุงรักษาสคริปต์จำนวนมากเพื่อให้ข้อมูลบันทึกอยู่ในรูปแบบเดียวกัน ทุกอย่างเป็น JSON แล้ว
พลังของ Fluentd อยู่ที่การรองรับ Sources และ Destinations หลายแบบ ตัวอย่างเช่น คุณสามารถเก็บข้อมูลจาก Twitter Stream และแจ้งเตือนผ่าน IRC มี Community Plugins ให้ใช้งานมากมาย
การใช้ Fluentd กับ Docker
ด้วย MongoDB Fluentd Plugin สามารถส่งบันทึก (push logs) เข้า ObjectRocket ได้ง่าย ขั้นแรก ต้องกำหนด Sources เนื่องจากบริการทั้งหมดของเราใช้ Docker เราจึงต้องนำ Container Logs เข้า Fluentd มีบทความที่เกี่ยวข้องและเสริมเติมบทความนี้เกี่ยวกับการรวมบันทึกด้วย docker-gen และ Fluentd โดย Jason Wilder เมื่อ Container ของ Fluentd ทำงานแล้ว (และ docker-gen สร้าง Configuration ของ Fluentd แล้ว) คุณควรจะมี Section ประมาณนี้สำหรับแต่ละ Container ที่กำลังทำงาน:
<source> type tail format json time_key time path /var/lib/docker/containers/abcdef/abcdef-json.log pos_file /var/lib/docker/containers/abcdef/abcdef-json.log.pos tag docker.container.abcdef rotate_wait 5 </source>
ส่วนนี้จะติดตาม (tail) บันทึกของ Container และติดตามตำแหน่งในบันทึกด้วย Position File สิ่งสำคัญคือ Tag ใน Configuration Section นี้เป็น Fluentd Tag ใช้บอก Fluentd ว่าจะทำอะไรกับข้อมูลที่รวบรวมได้
การใช้ Fluentd กับ MongoDB
ฝั่ง Public (สาธารณะ) เราบอก Fluentd ว่าจะทำอะไรกับข้อมูลด้วย "match" ในกรณีนี้ ให้แทนที่ตัวแปรด้วยข้อมูลจริงจากฐานข้อมูล ObjectRocket ของคุณใน Configuration File เดียวกัน:
<match docker.**> type mongo database $DBNAME collection prod host $HOSTNAME port $PORT ssl capped capped_size 100m user $MONGOUSER password $MONGOPASS include_tag_key true </match>
การตั้งค่า include_tag_key บอก Fluentd ให้รวม Tag ใน Record สำหรับบันทึกใน MongoDB เพื่อให้เรารู้ได้ชัดเจนว่าแต่ละ Log Entry 属于 Container ไหน Fluentd จะเริ่มเติมข้อมูลเข้า MongoDB ซึ่งเราจึงสามารถดึง (pull) มาที่ฝั่ง Private ของแอปพลิเคชันได้
ฝั่ง Private (ส่วนตัว) เรายังใช้ Fluentd MongoDB Plugin แต่ครั้งนี้ตั้งเป็น Source:
<source> type mongo_tail database $DBNAME collection prod host $HOSTNAME port $PORT user $MONGOUSER password $MONGOPASS ssl time_key time wait_time 5 tag prod id_store_file /app/prod_last_id </source>
จากนั้น เรากำหนด "match" สำหรับบันทึกของเราเพื่อส่งเข้า ElasticSearch:
<match **> type forest subtype elasticsearch <template> host elasticsearch.domain.com port 9200 index_name fluentd logstash_format true buffer_type memory type_name ${tag} flush_interval 3 retry_limit 17 retry_wait 1.0 num_threads 1 </template> </match>
เรายังใช้ Forest Fluentd Plugin ซึ่งช่วยลดความซับซ้อนของ Configuration Tagging ข้ามหลายสภาพแวดล้อม
Fluentd เป็นวิธีที่ยอดเยี่ยมในการรวมกลุ่ม Docker Logs ข้ามหลาย Host และส่งไปยังฐานข้อมูล MongoDB ในกรณีของเรา ObjectRocket ทำหน้าที่เป็นสถานีกลาง (Way Station) ระหว่างสภาพแวดล้อม Public และ Private สำหรับ Log Aggregation กรณีใช้งานอื่นๆ อาจรวมถึง Real-time Analytics บนข้อมูลที่เก็บรวบรวม สิ่งที่ทีมของเราชอบที่สุดคือเราไม่ต้องจัดการ MongoDB เอง ขอบคุณความน่าเชื่อถือและความเชี่ยวชาญของ ObjectRocket