Service Catalog กับ Ownership
ไอเดียหลักในหนึ่งประโยค
หัวข้อที่มีชื่อว่า “ไอเดียหลักในหนึ่งประโยค”Service Catalog ตอบคำถามที่ metric, log, หรือ trace ตอบเองไม่ได้สักตัว: ใครเป็นเจ้าของ service นี้ มันขึ้นกับอะไร และโค้ดของมันอยู่ที่ไหนจริง ๆ
Definition ไม่ใช่ query
หัวข้อที่มีชื่อว่า “Definition ไม่ใช่ query”Metric, log, และ trace ล้วนเป็นสิ่งที่ service ปล่อยออกมา Service Catalog entry ต่างออกไป — มันคือ definition ที่เราประกาศเกี่ยวกับ service นั้น ปรับให้เป็น billing-service:
{ "apiVersion": "v3", "kind": "service", "metadata": { "name": "billing-service", "owner": "billing-team", "contacts": [ { "name": "Billing oncall", "type": "slack", "contact": "https://my-org.slack.com/archives/billing" } ], "links": [ { "name": "Runbook", "type": "runbook", "url": "https://runbook.example.com/billing-service" } ] }, "spec": { "dependsOn": ["service:checkout-service"] }, "datadog": { "codeLocations": [ { "repositoryURL": "https://github.com/org/billing-service.git", "paths": ["**"] } ] }}metadata.owner คือคนที่ต้อง page metrics dashboard ไม่บอกว่าต้อง notify ใครตอนตี 3 ที่มัน fire log บรรทัดหนึ่งไม่บอกว่าใครเขียน service ที่ผลิตมันขึ้นมา trace ไม่พก Slack channel ไปด้วย metadata.owner คือ field ที่หายไปตัวนั้นเป๊ะ — pointer ที่ทนทานไปยังทีมหนึ่ง อิสระจากว่า alert routing ของทีมนั้นถูกต่อเข้ากับ monitor ถูกต้องหรือเปล่าวันนี้ schema จริงยังอนุญาต metadata.additionalOwners (array ของ {name, type}) สำหรับ service ที่มีมากกว่าหนึ่งทีมรับผิดชอบ และ metadata.contacts/metadata.links (แสดงข้างบน) สำหรับรายละเอียดแบบ “จะ ping ใครจริง ๆ runbook อยู่ไหน” ที่ไม่เคยมีที่อยู่ใน metric หรือ log บรรทัดไหนเลย
spec.dependsOn: edge ที่เราประกาศเอง ไม่ใช่ที่ traffic infer ให้
หัวข้อที่มีชื่อว่า “spec.dependsOn: edge ที่เราประกาศเอง ไม่ใช่ที่ traffic infer ให้”เทียบ spec.dependsOn กับ Service Map จาก module APM Service Map ถูก auto-generate จากการสังเกต trace traffic จริงระหว่าง service — มันแสดงว่าอะไรเรียกอะไรจริง ๆ ผ่าน network ตอนนี้ spec.dependsOn ต่างออกไป: มันคือ edge ที่ประกาศเองใน Catalog ซึ่งหมายความว่ามันจับ relationship ที่ไม่เคยสร้าง span สักตัวได้ billing-service อาจขึ้นกับ message queue ที่ checkout-service publish เข้าไปแบบ async — ไม่มี direct call ไม่มี trace เชื่อมมันเลย — หรือขึ้นกับ nightly batch job ที่อ่าน database table ที่ checkout-service เป็นเจ้าของ Service Map แสดงว่าอะไรคุยกับอะไร; dependsOn ให้เราประกาศได้ด้วยว่าอะไรขึ้นกับอะไร รวม relationship ที่ tracing ไม่มีทางเห็นตั้งแต่แรกด้วย
datadog.codeLocations: mapping service เข้ากับ source ของมัน
หัวข้อที่มีชื่อว่า “datadog.codeLocations: mapping service เข้ากับ source ของมัน”repositoryURL และ paths ผูก identity นามธรรมของ billing-service เข้ากับ git repository จริง ทำให้ tooling ที่สร้างบน Catalog — code security scanning, ownership-based review requirement — รู้เป๊ะว่าโค้ดที่ผลิต telemetry ของ service นี้อยู่ที่ไหน แทนที่จะให้ใครสักคนดูแล mapping นั้นไว้ใน spreadsheet หรือหน้า wiki ที่ล้าสมัยทันทีที่เขียนเสร็จ
v2 เทียบกับ v3: schema เปลี่ยนรูปทรงจริง ไม่ใช่แค่เลขเวอร์ชัน
หัวข้อที่มีชื่อว่า “v2 เทียบกับ v3: schema เปลี่ยนรูปทรงจริง ไม่ใช่แค่เลขเวอร์ชัน”apiVersion: v3 ข้างบนไม่ใช่การ bump เลขเวอร์ชันแบบผิวเผินจาก document สไตล์ v2 เก่า — ทั้งสองเข้ากันไม่ได้เชิงโครงสร้าง และ schema จริงบังคับใช้แบบนั้น สร้าง document ในรูปทรง เก่า (schema-version/dd-service/team อยู่ระดับบนสุด รูปแบบที่ตัวอย่างทางการของ Terraform provider เองยังแสดงเป็นอันแรก) แล้วรันผ่าน entity.schema.json จริงตัวเดียวกันเป๊ะ:
// @expect-error real jsonschema.Draft7Validator error, v2.1-shaped document under v3 rules{ "schema-version": "v2.1", "dd-service": "billing-service", "team": "billing-team", "contacts": [ { "name": "Billing oncall", "type": "slack", "contact": "https://my-org.slack.com/archives/billing" } ]}$ python validate.py v2-doc.json{"ok": false, "error": "[]: 'apiVersion' is a required property; []: 'kind' is a required property; []: 'metadata' is a required property; []: Additional properties are not allowed ('contacts', 'dd-service', 'schema-version', 'team' were unexpected)"}error จริงเจาะจงจาก schema จริง: entity.schema.json ประกาศ additionalProperties: false ที่ระดับบนสุด ดังนั้น field ระดับบนสุดของ document v2.1 เอง อย่าง dd-service/team/schema-version/contacts ถูกปฏิเสธตรง ๆ นอกเหนือจาก field ที่จำเป็นสามตัวที่หายไปตอนนี้ (apiVersion, kind, metadata) schema v3 ไม่ได้แค่เปลี่ยนชื่อ schema-version เป็น apiVersion — มันย้าย ownership, contacts, และ links เข้าไปอยู่ใต้ object metadata ที่ซ้อนอยู่ทั้งหมด ซึ่งเป็นเหตุผลเป๊ะว่าทำไม find-and-replace ตรง ๆ จาก document เก่าถึงไม่ได้ document ใหม่ที่ valid
Terraform gotcha: provider เองยังคาดหวัง shape เก่าอยู่
หัวข้อที่มีชื่อว่า “Terraform gotcha: provider เองยังคาดหวัง shape เก่าอยู่”datadog_service_definition_yaml คือ Terraform resource สำหรับจัดการ Catalog entry เป็นโค้ด — argument เดียวของมันคือ service_definition string YAML/JSON ดิบ (registry.terraform.io/providers/DataDog/datadog/latest/docs/resources/service_definition_yaml) รัน terraform init + terraform validate (provider ที่ pin ไว้ 4.22.0 ไม่มี credential, validate = false บน provider block เลยไม่มีอะไรไปถึง API ของ Datadog) กับทั้งสอง shape เทียบกัน:
resource "datadog_service_definition_yaml" "billing_v2" { service_definition = <<EOF3schema-version: v2.1dd-service: billing-serviceteam: billing-teamEOF3}# @expect-error Must be a supported service schema: service_definition (terraform validate, provider 4.22.0, client-side)resource "datadog_service_definition_yaml" "billing_v3" { service_definition = <<EOF2apiVersion: v3kind: servicemetadata: name: billing-service owner: billing-teamspec: dependsOn: - service:checkout-serviceEOF2}$ terraform validateError: Must be a supported service schema: service_definition
with datadog_service_definition_yaml.billing_v3, on main.tf line 17, in resource "datadog_service_definition_yaml" "billing_v3": 17: service_definition = <<EOF2 ...resource v2.1 validate ผ่านสะอาด (ยืนยันแยกต่างหาก: Success! The configuration is valid.); resource v3 — document ตัวเดียวกันเป๊ะที่เพิ่ง validate ผ่านสำเร็จกับ entity.schema.json จริงตัวปัจจุบันข้างบน — ถูก terraform validate เองปฏิเสธ ทั้งหมดฝั่ง client ก่อนมี API call ไหนเลยด้วยซ้ำ provider datadog ที่ pin ไว้ 4.22.0 มี local schema-version allowlist ของตัวเองสำหรับ resource นี้ และ ณ เวอร์ชันนี้ มันยังไม่รู้จัก apiVersion: v3 นี่คือ version-skew gap จริงที่พิสูจน์ได้ระหว่างสองส่วนของ tooling ของ Datadog เอง: JSON Schema repo กับ API ย้ายไป v3 แล้ว แต่ local pre-validation ของ provider เวอร์ชันนี้สำหรับ datadog_service_definition_yaml ยังตามไม่ทัน — ตัวอย่างทางการของ Terraform สำหรับ resource นี้เป๊ะ ๆ แสดงแค่ v2/v2.1/v2.2 ไม่มี v3 เลย ถ้าต้องจัดการ service definition ผ่าน Terraform resource นี้วันนี้ ต้องใช้ shape v2.x ไม่ว่า shape อื่น ๆ ทั้งหมดจะถือว่าอะไรเป็นมาตรฐานปัจจุบันก็ตาม
flowchart LR S["billing-service\n(Service Catalog entry)"] --> O["owner: billing-team"] S --> D["dependsOn: checkout-service"] S --> C["codeLocations: github.com/org/billing-service"] D -.not necessarily seen in.-> Tr[APM traces]
Reading AI-generated code
หัวข้อที่มีชื่อว่า “Reading AI-generated code”🐛 หา bug ในโค้ดนี้
# AI comment: "Define billing-service in the catalog using the current v3 schema"resource "datadog_service_definition_yaml" "billing_service" {service_definition = <<-EOT apiVersion: v3 kind: service metadata: name: billing-service owner: billing-team spec: dependsOn: - service:checkout-serviceEOT}เฉลย
YAML ที่ embed อยู่ตรงนี้ถูกตำราเป๊ะแบบ v3 — มันจะผ่าน entity.schema.json/service.schema.json จริงตัวเดียวกันที่พิสูจน์ไว้ก่อนหน้าในบทนี้ และ AI model ที่ train ด้วยเอกสารปัจจุบันของ Datadog มีเหตุผลทุกอย่างที่จะเลือก apiVersion: v3 เป็น “schema ปัจจุบัน” บั๊กที่พิสูจน์แล้วข้างบนด้วยการรัน terraform validate จริงกับ provider ที่ pin ไว้ 4.22.0: validation ฝั่ง client ของ Terraform resource ตัวนี้เองยังไม่รับ apiVersion: v3 เลย — terraform validate fail ด้วย Error: Must be a supported service schema: service_definition ฝั่ง client ล้วน ๆ ก่อนมี API call ไหนเลย ถูกต้องเทียบกับ JSON Schema และ API ไม่เหมือนกับถูกต้องเทียบกับ terraform validate ของ provider เวอร์ชันนี้เวอร์ชันเดียว การแก้ จนกว่า provider จะถูกอัปเดต: เขียน service_definition ของ resource นี้ด้วย shape v2.x (schema-version: v2.1 หรือ v2.2, dd-service, team อยู่ระดับบนสุด) ให้ตรงกับตัวอย่างทางการของ provider เอง ที่ทุกอันแสดง v2.x ไม่มีอันไหนเป็น v3 เลย แม้ว่าการอัปโหลด JSON/YAML ดิบผ่าน API หรือ UI จะรับ document v3 ที่พิสูจน์ไว้ก่อนหน้าในบทนี้ก็ตาม
Mental Model
หัวข้อที่มีชื่อว่า “Mental Model”มองว่า schema ของ Service Catalog เหมือนรูปแบบบัตรประจำตัวภายในของบริษัทที่เปลี่ยนไป — โต๊ะรักษาความปลอดภัยของ “ตึก” (API ของ Datadog กับ JSON Schema ที่อธิบายว่ามันรับอะไร) อัปเดตเป็นรูปแบบบัตรใหม่ไปหลายเดือนแล้ว แต่เครื่องพิมพ์บัตรของ vendor รายหนึ่ง (Terraform provider เวอร์ชันที่ pin ไว้นี้) ยังปั๊มบัตรรูปแบบเก่าอยู่ และมันปฏิเสธที่จะพิมพ์บัตรรูปแบบใหม่เลย — ไม่ใช่เพราะรูปแบบใหม่ผิด แต่เพราะไม่มีใครอัปเดตเครื่องนั้นเลย ระบบสองระบบต่างก็สอดคล้องในตัวเองและถูกต้องตามกติกาของตัวเองได้ แล้วยังไม่ตรงกันได้อยู่ดี การแก้ไม่เคยเป็น “อันไหนถูกจริง ๆ” (JSON Schema คือปัจจุบันอย่างไม่มีข้อโต้แย้ง) — มันคือ “ตอนนี้เรากำลังคุยกับใครอยู่” เพราะนั่นคือตัวที่กติกาของมันใช้กับการโต้ตอบครั้งนี้
Trade-offs
หัวข้อที่มีชื่อว่า “Trade-offs”- จัดการ Catalog entry ผ่าน API/UI ตรง ๆ เทียบกับผ่าน
datadog_service_definition_yaml— API รับ shapev3ปัจจุบัน (พิสูจน์แล้วข้างบนผ่าน schema validation จริง) และให้ feedback ทันที; Terraform ให้ infrastructure-as-code ที่ version ได้ review ได้ reproduce ได้สำหรับ entry เดียวกัน แลกกับการสืบทอด schema-version lag ของ provider release ตัวนี้เอง (พิสูจน์แล้วข้างบน: แค่v2.xณ4.22.0) - โครงสร้างซ้อนของ
v3(metadata/spec/datadog) เทียบกับ field ระดับบนสุดแบบแบนของv2.x— การซ้อนของv3รวม field ที่เกี่ยวข้องกันเข้าด้วยกัน (ownership/contact ทั้งหมดอยู่ใต้metadata, integration ของ Datadog product ทั้งหมดอยู่ใต้datadog) และแยก “สิ่งที่เป็นจริงเกี่ยวกับ service นี้” ออกจาก “สิ่งที่ Datadog ทำกับมัน”; โครงสร้างแบนของv2.xเขียนเองง่ายกว่าสำหรับ service เล็ก ๆ แต่ไม่มีการแยกแบบนั้นเลย ทุก field อยู่ระดับเดียวกันหมดไม่ว่าจะเป็นข้อมูลประเภทไหน spec.dependsOn(ประกาศเอง) เทียบกับ Service Map (สังเกตได้) — edge ที่ประกาศเองจับ relationship ที่ tracing เห็นไม่ได้เชิงโครงสร้าง (queue, batch job) แต่ล้าสมัยทันทีที่ dependency จริงเปลี่ยนแล้วไม่มีใครอัปเดต Catalog entry; Service Map ไม่ล้าสมัยแบบนั้นเลย (มันสะท้อนสิ่งที่เกิดขึ้นจริงตอนนี้) แต่มองไม่เห็น relationship ไหนก็ตามที่ไม่เคยสร้าง trace
Common Misconceptions
หัวข้อที่มีชื่อว่า “Common Misconceptions”- “JSON document ที่ validate ผ่าน v3 JSON Schema ปัจจุบันจะ validate ผ่านใน
terraform validateสำหรับdatadog_service_definition_yamlด้วย” ไม่จริง — พิสูจน์แล้วข้างบน: documentv3ตัวเดียวกันเป๊ะที่ผ่านjsonschemavalidation จริงกับentity.schema.jsonถูกterraform validateปฏิเสธกับ provider ที่ pin ไว้4.22.0ฝั่ง client ล้วน ๆ ด้วย errorMust be a supported service schemaจริง - “v2 กับ v3 แค่ bump เลขเวอร์ชัน document เก่าส่วนใหญ่ยังใช้ได้” ไม่จริง — พิสูจน์แล้วข้างบน: document สไตล์
v2.1จริงที่รันผ่านv3JSON Schema จริง fail สี่จุดพร้อมกัน (field ที่จำเป็นหายไปสามตัว, field ระดับบนสุดที่ถูกห้ามแล้วสี่ตัว) เพราะv3ย้าย field เข้าไปอยู่ใต้ objectmetadataที่ซ้อนอยู่ แทนที่จะแค่เปลี่ยนชื่อในที่เดิม - “
spec.dependsOnซ้ำซ้อนกับ Service Map เพราะทั้งคู่อธิบาย dependency เหมือนกัน” ไม่จริง —spec.dependsOnคือ edge ที่ประกาศเองที่จับ relationship (queue ที่ share กัน, batch job) ที่สร้าง trace traffic เป็นศูนย์ได้ และเลยไม่เคยโผล่บน Service Map เลย ทั้งสองตอบคำถามที่ overlap กันแต่ต่างกัน - “Service Catalog มีประโยชน์แค่กับ service ที่ปล่อย telemetry อยู่แล้ว” ไม่จริง — Catalog entry (owner, dependency, code location) มีประโยชน์แม้กับ service ที่ไม่มี traffic เลยตอนนี้ ไม่มี metric, log, หรือ trace ไหนที่มีอยู่จะตอบ “ใครเป็นเจ้าของสิ่งนี้” สำหรับ service ที่เงียบหรือเพิ่งสร้างใหม่ได้เลย ซึ่งเป็นช่องว่างเป๊ะที่บทนี้ทั้งบทอธิบาย