ข้ามไปยังเนื้อหา

Deployment และ Cross-cutting Concerns

คุณแยก monolith ออกเป็นชิ้น ๆ แล้ว วาดเส้นแบ่ง service ให้สะอาด ให้แต่ละ service มี database ของตัวเอง และต่อสายเรื่อง communication, reliability กับ observability ครบ บนกระดานไวท์บอร์ดดูเหมือนเสร็จสมบูรณ์ จนกระทั่งคุณลองรันจริง จากเดิมที่ monolith เป็น process เดียวและ deploy ครั้งเดียว ตอนนี้คุณมียี่สิบ service ที่อาจเขียนคนละภาษา แต่ละตัวต้องเริ่มทำงานใน environment ที่รู้ค่าแน่ชัด ต้องหา dependency ของตัวเองเจอ และต้องทำงานเหมือนกันทั้งบน staging และ production

โจทย์เปลี่ยนไปแล้ว การเขียน service ไม่ใช่ส่วนที่แพงอีกต่อไป แต่การ package การวางลงเครื่อง การส่ง configuration ที่ถูกต้องให้ การทำให้ traffic ระหว่าง service ปลอดภัย และการทำให้ทุก service เปิด health check กับ metric ในรูปแบบเดียวกัน ต่างหากที่กินแรงทีมส่วนใหญ่ในตอนนี้

คำถามไม่กี่ข้อตัดสินว่าการรันหลาย service จะยั่งยืนหรือกลายเป็นไฟไหม้ประจำวัน:

คุณจะ package service อย่างไรให้รันเหมือนกันทั้งบน laptop ใน CI และใน production จะรันอย่างไรโดยไม่ต้องมานั่งปรับแต่งทีละ host จะรักษาเรื่อง networking ทั้ง encryption, retry และ timeout ให้สอดคล้องกันข้าม service ที่คนละทีมเขียนด้วยคนละภาษาได้อย่างไร และจะเลี่ยงการเขียน logging, metric กับ health check ชุดเดิมซ้ำในทุก service ได้อย่างไร

ถ้าแต่ละทีมตอบคำถามเหล่านี้ต่างกัน คุณจะได้กองทัพของ snowflake: service ที่เริ่มทำงานในแบบที่ต่างกันเล็กน้อย ตั้งค่าตัวเองจากกลไกนับสิบแบบ และแต่ละตัวก็ implement เรื่อง security และ observability ผิดไปนิดหน่อย

โมดูลนี้จัดกลุ่ม pattern ออกเป็นสองชั้น ชั้นแรกว่าด้วย วิธีที่ service รัน คือเรื่อง package และ runtime ส่วนชั้นที่สองว่าด้วย cross-cutting baseline ที่ทุก service ที่กำลังรันใช้ร่วมกัน ได้แก่ networking, plumbing ส่วนกลาง และ configuration

flowchart TB
  subgraph Run[How a service runs]
    SPC[Service per Container]
    SLD[Serverless Deployment]
  end
  subgraph Cross[Cross-cutting baseline]
    SM[Service Mesh]
    MC[Microservice Chassis]
    EC[Externalized Configuration]
  end
  Img[Built artifact] --> SPC
  Img --> SLD
  SPC --> SM
  SLD --> EC
  SM --> EC
  MC --> EC
สองชั้น: วิธีที่ service รัน และ cross-cutting concerns ที่ทุก service ที่กำลังรันแชร์ร่วมกัน

pattern ทั้งห้าในโมดูลนี้ประกอบเข้าด้วยกันแบบนี้:

  • Service per Container — package แต่ละ service เป็น container image แล้วรันแค่ service เดียวต่อหนึ่ง container เพื่อให้เริ่มทำงานเร็ว แยกส่วนชัดเจน และ deploy เหมือนกันทุกที่ โดยมี orchestrator คอยจัดสรร container ลงเครื่องต่าง ๆ
  • Serverless Deployment — รัน service เป็น function บน platform ที่จัดการ server ให้คุณ scale ลงไปถึงศูนย์เมื่อว่าง และ scale แบบ per-request ภายใต้โหลด แลกกับ cold start และข้อจำกัดของ platform
  • Service Mesh — ผลักเรื่อง networking เช่น mutual TLS, retry, timeout และ traffic shaping เข้าไปไว้ใน sidecar proxy ที่อยู่ข้างแต่ละ service โดยมี control plane ส่วนกลางควบคุม เพื่อให้ทุก service ได้พฤติกรรมเดียวกันโดยไม่ต้องเขียนเอง
  • Microservice Chassis — เริ่มทุก service ใหม่บน framework หรือฐานร่วมที่เดินสายเรื่อง configuration, logging, metric, health check และ service discovery ไว้ให้แล้ว เพื่อให้ทีมเขียน business logic แทน plumbing
  • Externalized Configuration — เก็บ endpoint, credential และ feature flag ไว้นอก image และ inject เข้าไปตอน runtime เพื่อให้ artifact ที่ไม่เปลี่ยนแปลงเพียงชิ้นเดียวรันได้โดยไม่ต้องแก้ในทุกสภาพแวดล้อม

เราเริ่มจากโมเดล runtime สองแบบ — container และ serverless — เพราะทุกอย่างที่เหลือต้องสมมติว่ามีวิธีรัน service จริง ๆ ก่อน จากนั้นเราครอบคลุม cross-cutting concerns ที่เปลี่ยนกอง process ที่กำลังรันให้กลายเป็นกองทัพที่ operate ได้: mesh เพื่อ networking ที่สอดคล้องกัน, chassis เพื่อ plumbing ใน process ที่สอดคล้องกัน และ externalized configuration เพื่อให้ artifact เดียวเป็นกลางต่อทุกสภาพแวดล้อม แต่ละบทเรียนคงรูปแบบเดียวกัน: สถานการณ์ที่นำไปสู่ pattern, แรงที่ขัดแย้งกัน, pattern ทำอะไร, ไดอะแกรม, ตัวอย่าง config ที่เป็นรูปธรรม และการมองตามความจริงว่า pattern นั้นทำให้คุณต้องเสียอะไรบ้าง

  • Service per Container — เริ่มจากตัวนี้ เพราะเป็นวิธีมาตรฐานที่ service รันกันในปัจจุบัน
  • Externalized Configuration — pattern ที่ทำให้ artifact ที่ build แล้วเพียงชิ้นเดียวรันได้ทุกที่
ทำไม deployment และ operation จึงกลายเป็นความท้าทายหลักเมื่อคุณมีหลาย service?
โมดูลนี้จัดกลุ่ม pattern อย่างไร
pattern ใดทำให้ artifact ที่ build แล้วและไม่เปลี่ยนแปลงเพียงชิ้นเดียวรันได้โดยไม่ต้องแก้ในทุกสภาพแวดล้อม?