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

Service Catalog กับ Ownership

Service Catalog ตอบคำถามที่ metric, log, หรือ trace ตอบเองไม่ได้สักตัว: ใครเป็นเจ้าของ service นี้ มันขึ้นกับอะไร และโค้ดของมันอยู่ที่ไหนจริง ๆ

Metric, log, และ trace ล้วนเป็นสิ่งที่ service ปล่อยออกมา Service Catalog entry ต่างออกไป — มันคือ definition ที่เราประกาศเกี่ยวกับ service นั้น ปรับให้เป็น billing-service:

service-catalog/billing-service.json
{
"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 กับ 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 ไม่มีทางเห็นตั้งแต่แรกด้วย

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

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 เทียบกัน:

terraform/service-catalog-v2.tf
resource "datadog_service_definition_yaml" "billing_v2" {
service_definition = <<EOF3
schema-version: v2.1
dd-service: billing-service
team: billing-team
EOF3
}
# @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 = <<EOF2
apiVersion: v3
kind: service
metadata:
name: billing-service
owner: billing-team
spec:
dependsOn:
- service:checkout-service
EOF2
}
$ terraform validate
Error: 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]
entry ใน Catalog ที่ไม่ขึ้นกับ telemetry สด

🐛 หา bug ในโค้ดนี้

terraform/service-catalog.tf
# 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-service
EOT
}
เฉลย

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 ที่พิสูจน์ไว้ก่อนหน้าในบทนี้ก็ตาม

มองว่า schema ของ Service Catalog เหมือนรูปแบบบัตรประจำตัวภายในของบริษัทที่เปลี่ยนไป — โต๊ะรักษาความปลอดภัยของ “ตึก” (API ของ Datadog กับ JSON Schema ที่อธิบายว่ามันรับอะไร) อัปเดตเป็นรูปแบบบัตรใหม่ไปหลายเดือนแล้ว แต่เครื่องพิมพ์บัตรของ vendor รายหนึ่ง (Terraform provider เวอร์ชันที่ pin ไว้นี้) ยังปั๊มบัตรรูปแบบเก่าอยู่ และมันปฏิเสธที่จะพิมพ์บัตรรูปแบบใหม่เลย — ไม่ใช่เพราะรูปแบบใหม่ผิด แต่เพราะไม่มีใครอัปเดตเครื่องนั้นเลย ระบบสองระบบต่างก็สอดคล้องในตัวเองและถูกต้องตามกติกาของตัวเองได้ แล้วยังไม่ตรงกันได้อยู่ดี การแก้ไม่เคยเป็น “อันไหนถูกจริง ๆ” (JSON Schema คือปัจจุบันอย่างไม่มีข้อโต้แย้ง) — มันคือ “ตอนนี้เรากำลังคุยกับใครอยู่” เพราะนั่นคือตัวที่กติกาของมันใช้กับการโต้ตอบครั้งนี้

  • จัดการ Catalog entry ผ่าน API/UI ตรง ๆ เทียบกับผ่าน datadog_service_definition_yaml — API รับ shape v3 ปัจจุบัน (พิสูจน์แล้วข้างบนผ่าน 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
  • “JSON document ที่ validate ผ่าน v3 JSON Schema ปัจจุบันจะ validate ผ่านใน terraform validate สำหรับ datadog_service_definition_yaml ด้วย” ไม่จริง — พิสูจน์แล้วข้างบน: document v3 ตัวเดียวกันเป๊ะที่ผ่าน jsonschema validation จริงกับ entity.schema.json ถูก terraform validate ปฏิเสธกับ provider ที่ pin ไว้ 4.22.0 ฝั่ง client ล้วน ๆ ด้วย error Must be a supported service schema จริง
  • “v2 กับ v3 แค่ bump เลขเวอร์ชัน document เก่าส่วนใหญ่ยังใช้ได้” ไม่จริง — พิสูจน์แล้วข้างบน: document สไตล์ v2.1 จริงที่รันผ่าน v3 JSON Schema จริง fail สี่จุดพร้อมกัน (field ที่จำเป็นหายไปสามตัว, field ระดับบนสุดที่ถูกห้ามแล้วสี่ตัว) เพราะ v3 ย้าย field เข้าไปอยู่ใต้ object metadata ที่ซ้อนอยู่ แทนที่จะแค่เปลี่ยนชื่อในที่เดิม
  • “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 ที่เงียบหรือเพิ่งสร้างใหม่ได้เลย ซึ่งเป็นช่องว่างเป๊ะที่บทนี้ทั้งบทอธิบาย
metadata.owner ใน Service Catalog entry ตอบคำถามอะไรที่ trace หรือ metric ตอบไม่ได้
spec.dependsOn ต่างจาก Service Map ที่สร้างไว้ใน module APM ยังไง
service definition สไตล์ v2.1 (schema-version/dd-service/team อยู่ระดับบนสุด) ถูก validate กับ v3 JSON Schema จริงปัจจุบัน (entity.schema.json) output จริงของ jsonschema.Draft7Validator พิสูจน์อะไร
Terraform resource datadog_service_definition_yaml ได้รับ document apiVersion: v3 ที่ validate ผ่าน v3 JSON Schema จริงแยกต่างหากอยู่แล้ว terraform validate จริง (provider ที่ pin ไว้ 4.22.0) พิสูจน์อะไร
datadog.codeLocations map service definition เข้ากับอะไร
ข้อไหนที่ Service Catalog ตอบได้ ที่ metrics, logs, และ traces อย่างเดียวตอบไม่ได้