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

Service Mesh

service ของคุณคุยกันอยู่ตลอดเวลา และทุก call เหล่านั้นต้องการมาตรการป้องกันชุดเดียวกันไม่กี่อย่าง: traffic ควรถูกเข้ารหัส, request ที่ล้มเหลวควรถูก retry ตามจำนวนที่จำกัด, callee ที่ช้าควรชน timeout แทนที่จะค้าง caller ไว้ และทุก call ควรปล่อย metric และ trace span ออกมา ในโมดูล reliability และ observability คุณได้เรียนรู้การ build สิ่งเหล่านี้เข้าไปใน service — circuit breaker, retry, การ instrument

ปัญหาคือตอนนี้คุณมียี่สิบ service เขียนด้วยสี่ภาษา และทุกตัวต้อง implement เรื่องพวกนี้ให้ถูกและเหมือนกันหมด retry logic ของทีม Go ต่างจากของทีม Java นิดหน่อย mTLS ตั้งค่าไว้สามแบบไม่เหมือนกัน แล้วมี service หนึ่งลืมปล่อย trace ไปเลย สรุปคือ networking logic ชุดเดียวกันถูก implement ซ้ำแบบไม่สอดคล้องกันกระจายอยู่ทุกที่

คุณอยากให้ทุก call ระหว่าง service ถูกเข้ารหัส มี retry มี timeout ถูกเก็บ telemetry และ route ตาม policy ชุดเดียวที่สอดคล้องกัน โดยไม่ต้องให้ทุกทีมในทุกภาษามานั่งเขียนและดูแล logic นั้นเอง คุณอยากปรับ policy จากส่วนกลาง เช่น บีบ timeout ให้แคบลง หรือเลื่อน traffic 5% ไปหา canary โดยไม่ต้อง redeploy service และอยากให้ทำงานเหมือนกันหมดไม่ว่า service จะเขียนด้วย Go, Java, Python หรือ Rust

แรงที่ขัดกันจึงเป็นแบบนี้ คุณต้องการพฤติกรรม networking ที่สอดคล้องกันและคุมได้จากส่วนกลาง ครอบระบบแบบ polyglot ทั้งหมด และต้องใช้กับ service ที่แบก business logic อยู่ได้โดยไม่ต้องแก้ code หรือ redeploy

คำตอบคือใช้ service mesh ซึ่งย้ายเรื่อง communication ทั้งหมดออกจาก service ไปไว้ใน sidecar proxy คือ proxy process เล็ก ๆ ที่ deploy อยู่ในหน่วยเดียวกับ service instance แต่ละตัว ทุก byte ที่ service ส่งออกหรือรับเข้าจะวิ่งผ่าน sidecar และตัวที่ทำ mutual TLS ใส่ retry และ timeout บังคับ routing rule และเก็บ metric กับ trace ก็คือ sidecar ไม่ใช่ code ของคุณ

เพราะ proxy วางอยู่บนเส้นทาง network และไม่ผูกกับภาษาใด ตัว service จึงไม่ต้องรู้ด้วยซ้ำว่ามี proxy อยู่ Go service กับ Java service ได้พฤติกรรม mTLS, retry และ telemetry เหมือนกันเป๊ะ เพราะ proxy ตัวเดียวกันเป็นคน implement ให้ทั้งคู่

กองทัพ sidecar ทั้งหมดเรียกว่า data plane ที่เป็นตัวแบก traffic จริง ส่วน control plane ที่แยกออกมาต่างหากทำหน้าที่ตั้งค่าให้ คุณประกาศ policy ครั้งเดียว เช่น route นี้ timeout นี้ traffic split นี้ แล้ว control plane จะ push configuration ที่ได้ไปยังทุก sidecar การเปลี่ยน policy จึงเป็นแค่การ update control plane ไม่ใช่การ redeploy service

flowchart TB
  CP[Control plane: policy + certificates]
  subgraph A[Order Service pod]
    AS[order-service]
    AP[sidecar proxy]
    AS <--> AP
  end
  subgraph B[Payment Service pod]
    BS[payment-service]
    BP[sidecar proxy]
    BS <--> BP
  end
  AP -->|mTLS, retries, timeouts| BP
  CP -. config .-> AP
  CP -. config .-> BP
แต่ละ service รันข้าง sidecar proxy (data plane); control plane push policy และ certificate ไปยังทุก sidecar

คุณอธิบายพฤติกรรม traffic เป็น policy ที่ control plane กระจายออกไป policy นี้ตั้ง timeout และ retry แบบจำกัดสำหรับ call ไปยัง payment service และ split traffic เพื่อให้ 90% ไปยังเวอร์ชัน stable และ 10% ไปยัง canary — โดยไม่ต้องไปแตะ code ของ payment service เลย

apiVersion: networking.mesh.io/v1
kind: TrafficPolicy
metadata:
name: payment-routing
spec:
host: payment-service
timeout: 2s
retries:
attempts: 3
perTryTimeout: 800ms
retryOn: 5xx,connect-failure
mtls:
mode: STRICT
trafficSplit:
- version: stable
weight: 90
- version: canary
weight: 10

control plane แปลสิ่งนี้เป็น configuration สำหรับทุก sidecar ที่ call payment-service เพื่อเลื่อน canary ไปข้างหน้า คุณเปลี่ยน weight ที่นี่ ไม่มี service ใดถูก build ใหม่หรือ redeploy

สิ่งที่คุณได้:

  • networking แบบ cross-cutting ที่สอดคล้องกันทั้งระบบ mTLS, retry, timeout และ telemetry ทำงานเหมือนกันในทุก service ไม่ว่าเขียนด้วยภาษาอะไร เพราะ proxy ตัวเดียวเป็นคน implement ให้ทั้งหมด
  • การควบคุมจากส่วนกลางโดยไม่ต้องเขียน code policy ด้าน routing, security และ resilience อยู่ใน control plane การปล่อย canary, การเลื่อน traffic และการ timeout ที่เข้มขึ้นเป็นการเปลี่ยน configuration ไม่ใช่การ redeploy
  • observability และ zero-trust ในตัว ทุก hop ถูก trace และวัดอัตโนมัติ และ traffic ระหว่าง service ถูกเข้ารหัสและพิสูจน์ตัวตนโดยค่าเริ่มต้น

สิ่งที่คุณต้องเสีย:

  • latency และการใช้ resource ที่เพิ่มขึ้น ทุก call ตอนนี้ผ่าน proxy สองตัว เพิ่มภาษี latency เล็กน้อยแต่มีจริง และเพิ่ม CPU และ memory เท่ากับ sidecar หนึ่งตัวต่อหนึ่ง service instance
  • ความซับซ้อนด้าน operation mesh เป็นระบบ distributed ที่ซับซ้อนซึ่งคุณต้องติดตั้ง, upgrade และ debug control plane ที่ตั้งค่าผิดสามารถทำลาย communication ทั่วทั้งกองทัพได้
  • อีกชั้นหนึ่งที่ต้องคิดถึง เมื่อ call ล้มเหลว คำตอบอาจอยู่ใน service ของคุณ, sidecar หรือ policy ของ control plane — มีชิ้นส่วนที่ขยับได้มากขึ้นระหว่างต้นเหตุกับอาการ

กฎที่ใช้ได้จริงคือ mesh จะคุ้มก็ต่อเมื่อคุณมี service แบบ polyglot จำนวนมาก และมีความต้องการจริงเรื่อง mTLS การควบคุม traffic และ telemetry ที่สม่ำเสมอ ถ้ามี service แค่ไม่กี่ตัว mesh มักเป็นเครื่องจักรที่ใหญ่เกินกว่าปัญหาที่มี

  • Service per Container — sidecar มักเป็น container ตัวที่สองที่ deploy ข้าง service container
  • Microservice Chassis — ทางเลือกแบบ in-process สำหรับบางเรื่องเดียวกัน mesh และ chassis มักถูกใช้ร่วมกัน
  • Externalized Configuration — policy ของ mesh เองก็เป็น externalized configuration ที่ถูก push โดย control plane
ข้อดีข้อแลกเปลี่ยน
traffic management, retry, circuit breaker ทำที่ infrastructure — ไม่ต้องแก้ codeเพิ่ม complexity ของ infrastructure มาก — sidecar ทุก pod
mTLS ระหว่าง service อัตโนมัติ — zero-trust securitylatency เพิ่มเล็กน้อยจาก sidecar proxy
observability ครบ: metrics, tracing, logs สำหรับทุก service callต้องการ expertise ในการ configure และ debug Istio/Linkerd
เปลี่ยน policy ได้โดยไม่ต้อง redeploy serviceresource overhead — sidecar แต่ละตัวใช้ memory และ CPU

Service Mesh ตั้งแต่วันแรก — adopt service mesh ก่อนที่จะมี service หลายตัวจริง ๆ อาการ:

  • มี 3 service แต่ใช้ Istio ทั้ง cluster
  • ทีมเสีย 80% ของเวลากับ mesh config แทนที่จะ build feature
  • complexity เกินความจำเป็น

Misconfigured mTLS — config mTLS ผิดทำให้ service คุยกันไม่ได้ อาการ:

  • service ใหม่เพิ่มเข้า cluster แล้วคุยกับ service เก่าไม่ได้
  • certificate expire ทำให้ production ล่ม

💡 ตัวอย่างจากของจริง

Google (Istio):

  • สร้าง Istio จาก internal service mesh ที่ใช้ใน Google production
  • ใช้ Envoy proxy เป็น sidecar — battle-tested ที่ Lyft และ Airbnb

Lyft:

  • ผู้สร้าง Envoy proxy — เริ่มจาก internal need
  • ใช้ mesh สำหรับ observability ก่อน — ได้ tracing ทั้งระบบโดยไม่ต้องแก้ service
service mesh วางเรื่อง communication เช่น mTLS, retry และ timeout ไว้ที่ไหน?
อะไรคือความแตกต่างระหว่าง data plane และ control plane?
ทำไม service mesh จึงใช้พฤติกรรมเดียวกันกับ service ที่เขียนด้วยภาษาต่างกันได้?
ข้อใดคือต้นทุนจริงของการรัน service mesh?