Jaynarol Blog

Docker : จัดการโปรเจคทั้งระบบด้วย Docker Compose – Part 2 (Structure)

สวัสดีครับ บทความนี้เป็นบทที่ 2 ต่อเนื่องจาก จัดการโปรเจคทั้งระบบด้วย Docker Compose – Part 1 ดังนั้นเพื่อความไม่งงถึงที่มาที่ไปแนะนำให้อ่าน Part 1 ก่อนนะครับ

 

โครงสร้างของโปรเจค

หัวข้อนี้ผมจะพาดูว่าข้อมูลไฟล์ภายใน Repo ที่ผมให้ Clone เพื่อทดสอบในบทที่แล้วว่าประกอบด้วยอะไรบ้าง จะได้ง่ายต่อการเข้าใจที่มาของคำสั่งต่างๆใน docker-compose.yml ซึ่งเป็นหัวใจในการทำงานของระบบครับ

 

โครงสร้างการเก็บข้อมูลในโปรเจคนี้เป็นเพียง 1 ในตัวอย่างที่ผมใช้งานอยู่ อาจไม่ใช่รูปแบบที่ดีที่สุดแต่ก็เพียงพอที่จะให้ทำให้เห็นภาพและนำไปประยุกต์ใช้ในโปรเจคอื่นๆได้ครับ โดยตัวผมเองจะมีการเก็บไฟล์ที่เป็นระบบเพื่อให้ง่ายต่อการดูและและแก้ไขในอนาคต สิ่งสำคัญที่สุดที่ต้องคำนึงถึงเสมอคือ ข้อมูลทุกอย่างในโปรเจคต้องจบในโฟลเดอร์เดียว จะไม่มีการเชื่อมไปยังข้อมูลภายนอกโดยไม่จำเป็น ทั้งหมดเพื่อให้โปรเจคสามารถสามารถห่อ (Pack) และส่งต่อ (Ship) ได้ง่ายครับ

 

  • /build โฟลเดอร์เก็บข้อมูลที่ใช้ครั้งเดียวตอนสร้าง Container แต่ละตัว โดยภายในจะมีชื่อโฟลเดอร์ที่ตรงกับชื่อ Container (ที่กำหนดใน docker-compose.yml) เพื่อให้ตรวจสอบได้ง่ายว่าข้อมูลนี้เป็นของ Container ตัวไหน ไฟล์ส่วนใหญ่ที่จะเก็บในนี้ก็คือข้อมูลพวก Dockerfile ที่ใช้สร้าง image, ไฟล์ Config ต่างๆ, ไฟล์ sql สำหรับสร้างฐานข้อมูล
  • /cmd โฟลเดอร์เก็บข้อมูลที่เป็น Shell Script ที่เขียนขึ้นเป็นพิเศษตามแต่วัตถุประสงค์ของแต่ละโปรเจค เช่น การสั่งให้มัน pack, ทำความสะอาด, คอมไพล์ อะไรพวกนี้ครับ เดี๋ยวผมจะพูดถึงอีกทีตอนท้ายๆ
  • /src โฟลเดอร์ที่เก็บ Source Code หลักของระบบ ใช้เพื่อ Volume เข้าไปใน Container เราสามารถเปลี่ยนแปลง Source Code ได้ตลอดในช่วง Runtime เช่นในโปรเจคประเภทเว็บไซต์ก็จะเป็นพวกไฟล์ html, css, js, php อะไรพวกนี้ครับ ถ้าเรารันระบบนี้ในเครื่องเดพ เราก็จะเดพกันในโฟลเดอร์นี้แหละ ในอนาคตถ้าเราลบ Container ไฟล์ Source Code ของเราก็จะยังอยู่ ดังนั้นถ้าเข้าใจภาพของ Docker เราจะเห็นว่า Container เป็นเพียงแรงงานที่เราไม่ใยดีเลยครับ จะลบจะรันเมื่อไหร่มันก็ได้ไม่ส่งผลอะไรกับข้อมูล Source Code เราเลย
  • /tmp โฟลเดอร์ที่ใช้เก็บข้อมูลที่ใช้ในระหว่าง Runtime แต่ไม่ใช่หัวใจหลักของระบบ ซึ่งจะไม่ถูกห่อและส่งไปให้คนอื่น ข้อมูลพวกนี้ได้แก่พวก Cache หรือ Logs ต่างๆรวมไปถึงไฟล์ที่เกิดจากการทำงานของ Database ด้วยครับ (หากสังเกตในไฟล์ .gitignore จะพบว่าโฟลเดอร์นี้อยู่ในลิสด้วย)

 

แกะ docker-compose.yml กัน

หัวข้อที่แล้วตั้งใจแค่รีวิวรวมๆ ไหงมันยาวยืดไปซะได้ – -* ในหัวข้อนี้เราจะมาดูไฟล์พระเอกของเรา docker-compose.yml และทำความเข้าใจมันกันครับ

อย่างที่เคยเกริ่นไปหลายๆรอบครับ docker-compose.yml คือไฟล์ที่จะระบุโครงสร้างของระบบเรา โดยข้อมูลในไฟล์เขียนด้วย Yaml Syntax ซึ่งเรียนรู้ไม่ยากเลยครับเพราะมันถูกออกแบบมาให้เป็นภาษาเชิงโครงสร้างที่มนุษย์สามารถอ่านออกได้อยู่แล้ว ใครอยากศึกษาคำสั่งใน docker-compose.yml เพิ่มเติมอ่านได้ที่ Official Document เลยครับ ผมจะพาเล่นของจริงเลยดีกว่า เดี๋ยวง่วง

 

เปิดไฟล์ขึ้นมาเราจะพบ Block ที่เป็นก้อนๆอยู่ 5 ส่วนประกอบด้วย lb, php, nginx, mysql, pma ตรงนี้แหละครับ คือส่วนที่เราใช้ตั้งค่าในแต่ละ Container แต่ละ Block คือ 1 Container เรามี 5 Block ใช่ครับ ก็ 5 Container นั่นเอง

หากสังเกตดีๆจะพบว่าการเขียนข้อมูลในไฟล์จะคล้ายตอนเราสั่ง Docker Engine ให้มันทำงานด้วย Command Line docker run ... เลยครับ ไม่ว่าจะเป็นข้อมูล port = -p,  volume = -v,  environment = -e เป็นต้นครับ ดังนั้นใครที่เคยใช้คำสั่งใน Docker Engine มาก่อนจึงแทบไม่ต้องเรียนรู้อะไรใหม่เลย

 

Load Balance Container

เราจะเริ่มจาก Block แรกที่เห็นในไฟล์ lb คือย่อมาจาก Load Balance นะครับ ผมชอบย่อให้มันสั่นๆเพราะบ้างครั้งเราต้องเอาชื่อมันไปใช้ใน Command Line

ใน Contaner นี้ผมจะใช้ image จาก dockercloud/haproxy ซึ่งสามารถเข้าไปอ่านวิธีการใช้เต็มๆได้ที่ Repo ของเขาเลยครับ สิ่งที่ผมอยากจะพูดในหัวข้อนี้ครอบคลุมแค่การตั้งค่าให้ Load Balance สามารถส่งงานไปยัง Apache และ Nginx ได้อย่างถูกต้อง หากเราสังเกตที่ Block ของ php และ nginx จะพบว่ามี environment คือ VIRTUAL_HOST_WEIGHT และ VIRTUAL_HOST เหมือนกัน ใช่ครับมันคือการตั้งค่าของ Load Balance

VIRTUAL_HOST คือการ white list ข้อมูลจาก URL ที่จะให้เข้าไปทำงานที่ Container นั้นๆ

VIRTUAL_HOST_WEIGHT คือความสำคัญของ Container ที่จะนำมาตรวจสอบ URL ก่อน-หลังครับ ยิ่งมากแสดงว่าสำคัญกว่าและได้ตรวจสอบก่อน

load-balance

ในโปรเจคนี้ผมเขียนเงื่อนไขการทำงานไว้ให้เข้าใจง่ายๆอธิบายคือ ถ้ามี */assets/* ใน url เช่น http://192.168.99.100/assets/image.jpg ให้ไปทำงานที่ nginx แต่ถ้าไม่ก็ไปทำงานที่ apache ครับ โดยเทคนิคสำคัญ คือ พยายามยืดหยุ่นให้ได้มากที่สุด เช่นโปรเจคนี้ต้องสามารถรันได้ทั้งใน http://192.168.99.100/assets/image.jpg หรือ http://localhost/assets/image.jpg หรือ http://127.0.0.1/assets/image.jpg เพื่อให้ทุกคนสามารถใช้งานได้ เป็นต้นครับ เว้นแต่ว่าเราตั้งใจให้มันผูกขาดสภาพแว้ดล้อมอันใดอันนึงเพื่อวัตถุประสงค์อื่นๆก็แล้วแต่กรณีครับ (ซึ่งจะทำให้คุณสมบัติการ Run Anywhere ของ Docker หายไปเพราะข้อจำกัดที่เพิ่มขึ้นมา)

 

Working Container

Block ถัดมาที่จะให้ดูคือ php และ nginx ครับซึ่งผมมักจะเรียกมันว่า Container ชั้นแรงงาน 555+ ในหัวข้อนี้สิ่งที่ผมจะให้ดูคือความสามารถของ Volume ที่จะทำให้ทุก Container ทำงานกับข้อมูลชุดเดียวกัน มันมีประโยชน์มากๆและนำไปใช้ได้หลายท่ามากๆครับ ดังภาพ
2

ท่ามาตรฐานคือให้ php และ nginx ชี้ไปที่เดียวกันและตั้งค่า load balance ดีๆทำให้เราไม่ต้องไปเสียเวลาแยกไฟล์ static ออกมาจาก app เลย

 

3

อีกท่านึงคือเวลาเราจะเปลี่ยน version หรือทดสอบระบบ เราสามารถให้ทุก Container ที่รันคนละเวอร์ชั่น ชี้ไปที่ Source Code เดียวกันแล้วเริ่มทดสอบตามต้องการได้เลย

 

อีกส่วนนึงของหัวข้อนี้นี่ต้องการให้เห็นคือใน Block php เราจะไม่ได้กำหนด image ที่ใช้งานไปโดยตรง แต่เรากำหนด path ของ Dockerfile แทน (รู้จักและใช้งาน Dockerfile) ทำให้เราสามารถเพิ่มความยืดหยุ่น Container เราได้มหาศาลครับ การตั้งค่าต่างๆที่เกี่ยวข้องกับ Container ตัวนั้นตัวเดียว เช่น การ Config, การจัดเตรียมไฟล์ต่างๆ เราจะไปเขียนที่ Dockerfile นั้นๆครับ จุดพีคที่ยิ่งกว่าคือพอเราสั่ง Docker Compose ให้เริ่มโปรเจคด้วยคำสั่ง docker-compose up มันจะไป Build Dockerfile เป็น image ให้เราโดยอัตโนมัติด้วย ผมชอบเรียกความสามารถพวกนี้แบบติดตลกว่า 1 hit kill รันเสร็จไปต้มกาแฟจิบรอชิวๆได้เลย XD

 

จบ Part 2

พอดีวันนี้หาช่วงเวลาว่างได้เท่านี้ครั้บ ต้องขออภัยด้วยที่จบห้วนไปหน่อย บทความหน้า Part 3 ซึ่งน่าจะเป็น Part สุดท้ายในหัวข้อนี้แล้ว ผมจะมาต่อ Block ที่เหลือซึ่งจะเกี่ยวกับการจัดการข้อมูลใน Database พร้อมเทคนิคต่างๆครับ

แล้วพบกันใหม่ สวัสดีครับ