Worked review 2: Agent บน Kubernetes cluster
Feature ที่ขอ
หัวข้อที่มีชื่อว่า “Feature ที่ขอ”Prompt คือ “Deploy Datadog Agent ลง EKS cluster ด้วย Helm — APM, log จากทุก container, Cluster Agent และ Redis integration สำหรับ Deployment cache ขอเวอร์ชัน Operator ด้วย เผื่อจะย้าย” ด้านล่างคือ draft แรก มันมีสาม artifact: Helm values.yaml, manifest DatadogAgent สำหรับ Operator และ Deployment ของ Redis พร้อม Autodiscovery annotation helm lint ผ่านกับอันแรก ส่วนอีกสองอันดูเป็น YAML ที่ reviewer ทั่วไปพยักหน้าให้ผ่าน
ใช้อะไรรีวิว และขีดจำกัดของแต่ละอย่าง:
helm templateกับ chart จริงdatadog/datadog3.250.3 (Helm v4.3.0) — DaemonSet ที่ render ออกมาคือความจริงว่า values ทำอะไร ไม่ได้ติดตั้งอะไรที่ไหนเลย- CRD
DatadogAgentของ Operator จาก chartdatadog-operator2.27.0 (Operator app version 1.30.0, CRD sub-chart 2.24.0) CRD นี้ serve เวอร์ชันเดียวคือv2alpha1ถูกแปลงเป็น JSON Schema แบบ strict (additionalProperties: falseทุกที่ที่ CRD ไม่ได้ตั้งx-kubernetes-preserve-unknown-fields) ซึ่งเลียนแบบสิ่งที่kubectl apply --validate=strictขอให้ API server ทำ ไม่ได้รันกับ API server จริง — ไม่มี cluster และโหมด--k8sของ harness ต้องการ clusterkindที่ไม่มีอยู่ที่นี่ agent configcheckจาก containerdatadog/agent7.83.3 จริง (API key ปลอม, start ด้วย--network noneเพื่อไม่ให้อะไรออกจาก container ได้, ลบทิ้งภายหลัง) โดย mount template Redis ที่แก้แล้วจากดิสก์- JSON schema
apps/v1Deploymentของ Kubernetes 1.37 จากคอร์ส Kubernetes สำหรับ Deployment
โค้ดที่ AI สร้าง
หัวข้อที่มีชื่อว่า “โค้ดที่ AI สร้าง”# @expect-error as generated -- see the review below (helm lint passes; helm template renders)datadog: apiKey: "<your-api-key>" site: https://api.datadoghq.com tags: env:prod apm: enabled: true logs: enabled: true collectAll: true dogstatsd: port: 8125agents: image: tag: latestclusterAgent: enabled: true# @expect-error the Operator manifest as generated -- v1alpha1-shaped fields under v2alpha1apiVersion: datadoghq.com/v2alpha1kind: DatadogAgentmetadata: name: datadog namespace: datadogspec: credentials: apiKey: "<your-api-key>" global: clusterName: prod-eks-1 site: https://api.datadoghq.com features: apm: enabled: true logCollection: enabled: true containerCollectAll: true agent: image: name: datadog/agent:latest clusterAgent: enabled: true# @expect-error Autodiscovery annotations on the Deployment, keyed to the wrong container nameapiVersion: apps/v1kind: Deploymentmetadata: name: cache namespace: shop annotations: ad.datadoghq.com/redis.checks: | { "redisdb": { "init_config": {}, "instances": [{ "host": "%%host%%", "port": "6379" }] } } ad.datadoghq.com/redis.logs: '[{"source": "redis", "service": "cache"}]'spec: replicas: 1 selector: matchLabels: app: cache template: metadata: labels: app: cache spec: containers: - name: cache image: redis:latestเดินรีวิวทีละข้อ
หัวข้อที่มีชื่อว่า “เดินรีวิวทีละข้อ”ส่วนที่ 1 — Helm values ทุก finding ตรงนี้คือหนึ่งบรรทัดของ DaemonSet ที่ render ออกมา
site: https://api.datadoghq.com — URL ในที่ที่ต้องเป็น domain
หัวข้อที่มีชื่อว่า “site: https://api.datadoghq.com — URL ในที่ที่ต้องเป็น domain”helm lint ผ่าน (1 chart(s) linted, 0 chart(s) failed) และ helm template exit 0 container agent ที่ render:
DD_SITE = https://api.datadoghq.comchart ส่งต่อค่าตรงๆ และ Agent ก็เช่นกัน เมื่อ start ด้วยค่านั้น agent status แสดงว่า Agent ประกอบ hostname ของ intake อย่างไร (พฤติกรรมเดียวกับ entry 6 ของ bug catalog รันซ้ำสำหรับรีวิวนี้):
$ docker exec dd-reading-b agent status # DD_SITE=https://api.datadoghq.com https://process.https://api.datadoghq.com. - API Key ending with:$ docker exec dd-reading-b agent status # DD_SITE=datadoghq.com https://process.datadoghq.com. - API Key ending with:🐛 หา bug ในโค้ดนี้
datadog:site: https://api.datadoghq.comเฉลย
site คือ site domain ของ Datadog (datadoghq.com, datadoghq.eu, …) ไม่ใช่ API URL; hostname ของ intake ประกอบโดยเอามันไปเป็น prefix (รายการ site: docs.datadoghq.com/getting_started/site/) ทั้ง Helm และ Agent ไม่ validate — ความล้มเหลวคือ Agent ที่รันอยู่และไม่เคยส่งถึง วิธีแก้: site: datadoghq.com
API key ที่ commit ใน values.yaml
หัวข้อที่มีชื่อว่า “API key ที่ commit ใน values.yaml”apiKey: "<your-api-key>" — ที่นี่เป็น placeholder แต่รูปแบบคือ key จริงใน ไฟล์ที่อยู่ใน git เมื่อ render chart แปลงมันเป็น Secret ที่ api-key เป็นแค่ base64 ของสตริง:
dd-datadog api-key <your-api-key> (base64-decoded from the rendered Secret)values.yaml ของ chart เองบอกทางเลือกไว้: datadog.apiKeyExistingSecret — “Use existing Secret which stores API key … The value should be set with the api-key key inside the secret” และ “If set, this parameter takes precedence over apiKey” เมื่อตั้งค่านี้ ผล render ไม่มี object Secret ที่เก็บ key เลย (0 เอกสาร kind: Secret ใน render ที่แก้แล้ว)
🐛 หา bug ในโค้ดนี้
datadog:apiKey: "<your-api-key>"เฉลย
key ที่ใช้งานได้ใน values file แปลว่า key อยู่ใน git history, ใน CI log ที่พิมพ์ helm template และในทุก screenshot ของการรีวิว manifest ของ Operator ด้านล่างทำผิดซ้ำในที่ที่ schema อนุญาต วิธีแก้: สร้าง Secret นอกระบบ (kubectl create secret generic datadog-secret --from-literal api-key=...) แล้วอ้างด้วย apiKeyExistingSecret
tags: env:prod — string ในที่ที่ต้องเป็น list
หัวข้อที่มีชื่อว่า “tags: env:prod — string ในที่ที่ต้องเป็น list”chart ระบุ datadog.tags เป็น “List of static tags” Helm ไม่บ่นเรื่อง string และ chart ทำ toJson กับอะไรก็ตามที่ได้รับ:
values tags: env:prod -> DD_TAGS = "env:prod" (string, JSON-quoted)values tags: [env:prod] -> DD_TAGS = ["env:prod"]สิ่งที่ Agent ทำกับแต่ละแบบ จาก agent config และ agent status ใน container 7.83.3 จริงที่ start ด้วยค่า DD_TAGS สองค่านั้นเป๊ะ:
DD_TAGS='"env:prod"' tags: - '"env:prod"'DD_TAGS='["env:prod"]' tags: - env:prod host tags: env:prodรูปแบบ scalar สร้าง tag ที่ตัวอักษรมี double quote อยู่ด้วย — มันจะไม่มีวัน match env:prod ใน scope ของ monitor หรือ filter ของ dashboard
🐛 หา bug ในโค้ดนี้
datadog:tags: env:prodเฉลย
type ผิดและไม่มีอะไรบังคับ: values.schema.json ของ chart อธิบายแค่ไม่กี่ key (property ระดับบนคือ registryMigrationMode กับ datadog และใต้ datadog มีแค่ instrumentationCrd, networkPath, apm, appsec) ดังนั้น tags ไม่ถูกเช็ก วิธีแก้: YAML list — tags: แล้ว - env:prod ดียิ่งกว่านั้นคือไม่ใส่ env ตรงนี้เลย unified service tagging ตั้งให้ต่อ workload
logs.collectAll — option ที่ chart ไม่มี
หัวข้อที่มีชื่อว่า “logs.collectAll — option ที่ chart ไม่มี”คำขอคือ “log จากทุก container” container ที่ render:
DD_LOGS_ENABLED = trueDD_LOGS_CONFIG_CONTAINER_COLLECT_ALL = falsekey จริงคือ datadog.logs.containerCollectAll (default false; comment ของ chart: “Enable this to allow log collection for all containers”) collectAll ถูกเพิกเฉย หลักฐานคือ diff: render logs: {enabled: true, collectAll: true} เทียบกับ logs: {enabled: true} ได้ ผลเหมือนกันทุกประการ ยกเว้นบรรทัด token, checksum และ install_id ของ Cluster Agent ที่สุ่ม และเพราะ Helm พา key ที่ไม่รู้จักไปเฉยๆ helm lint จึงมองไม่เห็นเช่นกัน
เมื่อ enabled: true ระบบท่อ log อยู่ครบ — DaemonSet ที่ render mount /var/log/pods, /var/log/containers และ pointer directory — ความล้มเหลวจึงแนบเนียน docs (docs.datadoghq.com/containers/kubernetes/log/) บอกกฎ: “When set to false (default), you need to specify Autodiscovery log configurations to enable log collection.” ทุก container ที่ไม่มี logs annotation ของตัวเองถูกข้ามแบบเงียบๆ
🐛 หา bug ในโค้ดนี้
logs: enabled: true collectAll: trueเฉลย
collectAll ดูถูกและถูกเพิกเฉยเงียบๆ key คือ containerCollectAll: true การเปิดมีต้นทุนที่ assistant ไม่ได้บอก: stdout ของ ทุก container รวมถึง sidecar ที่ noisy และตัว Agent เองถูก ingest; ใช้ datadog.containerExcludeLogs ตัดทอน (trade-off อยู่ในหัวข้อด้านล่าง)
agents.image.tag: latest
หัวข้อที่มีชื่อว่า “agents.image.tag: latest”Render ได้: registry.datadoghq.com/agent:latest บน container agent, trace-agent, system-probe, init-* และ seccomp-setup ค่า default ของ chart เองเมื่อไม่ตั้ง tag คือ registry.datadoghq.com/agent:7.82.3 ที่ pin ไว้สำหรับ chart 3.250.3 — assistant ทำให้การ deploy ทำซ้ำได้น้อยลง กว่าการไม่ทำอะไร tag เหล่านี้มีอยู่จริงบน Docker Hub (docker manifest inspect datadog/cluster-agent:7.83.3 resolve ได้; tag ของ Agent ถูก pull และรันสำหรับรีวิวนี้) latest เปลี่ยนใต้ DaemonSet ที่กำลังรัน: node ถัดไปที่ pull ได้ Agent ต่างจาก node ข้างเคียง
🐛 หา bug ในโค้ดนี้
agents:image: tag: latestเฉลย
วันนี้ยังไม่พัง - ความเสี่ยงคือไม่มีใครบอกได้ว่า Agent ตัวไหนสร้าง metric หนึ่งๆ และการ restart node กลางสัปดาห์อัปเกรด node หนึ่งแบบเงียบๆ ได้ ให้ pin agents.image.tag และ clusterAgent.image.tag (Cluster Agent มี setting ของ image ตัวเองซึ่ง AI ไม่ได้ตั้ง) แล้วอัปเดตทั้งคู่โดยตั้งใจ วิธีแก้: tag: 7.83.3
Token ของ Cluster Agent เปลี่ยนทุกครั้งที่ render
หัวข้อที่มีชื่อว่า “Token ของ Cluster Agent เปลี่ยนทุกครั้งที่ render”clusterAgent.enabled: true โดยไม่มี token และไม่มี tokenExistingSecret comment ของ chart: “autogenerated if empty, needs to be at least 32 characters” helm template สองครั้งติดกันกับ values เดิม:
render 1 dd-datadog-cluster-agent token PIjV... checksum/clusteragent_token: b2b26b5d...render 2 dd-datadog-cluster-agent token qlPr... checksum/clusteragent_token: 82e66f08...(แสดงเฉพาะตัวอักษรแรกๆ ของแต่ละ token) token เปลี่ยนทุก render และค่าถูก hash ลงใน annotation ของ pod template ดังนั้น ทุก helm upgrade roll ทั้ง DaemonSet และ Cluster Agent แม้ไม่มีอะไรอื่นเปลี่ยน เมื่อตั้ง tokenExistingSecret ผล render ที่แก้แล้วเหมือนกันทุก byte ระหว่างการรัน ยกเว้น install_id ที่ไม่เกี่ยวข้อง
🐛 หา bug ในโค้ดนี้
clusterAgent:enabled: trueเฉลย
pre-shared token ระหว่าง node Agent กับ Cluster Agent ต้องคงที่ วิธีแก้: สร้าง Secret ที่มี key token (32+ ตัวอักษร) ครั้งเดียว แล้วตั้ง clusterAgent.tokenExistingSecret นี่คือ bug ประเภทเดียวกับ API key ที่ hard-code แต่ขั้วตรงข้าม: ที่นี่ค่าที่ generate สดเกินไป
ไม่มี clusterName
หัวข้อที่มีชื่อว่า “ไม่มี clusterName”DaemonSet ที่ render ไม่มี DD_CLUSTER_NAME เลย (การไม่มีถูกตรวจ ไม่ได้สันนิษฐาน: template ปล่อยมันเฉพาะใน if .Values.datadog.clusterName) comment ของ chart บน key นี้: “Set a unique cluster name to allow scoping hosts and Cluster Checks easily” เอกสาร tag (docs.datadoghq.com/containers/kubernetes/tag/) ระบุ kube_cluster_name โดยมี source “DD_CLUSTER_NAME envvar or cloud provider integration” — ดังนั้นบน cluster ที่ไม่มี cloud integration ตรงกัน ก็ไม่มี tag cluster ให้ scope monitor ไปที่ “prod-eks-1” แทน “cluster ไหนก็ได้”
🐛 หา bug ในโค้ดนี้
datadog:site: https://api.datadoghq.comtags: env:prodเฉลย
ไม่มีอะไรใน block นี้ตั้งชื่อ cluster draft ของ Operator ตั้ง clusterName ไว้ ซึ่งเป็นเหตุผลที่สอง artifact ขัดกัน วิธีแก้: datadog.clusterName: prod-eks-1 (ตัวพิมพ์เล็ก ตัวเลข และ hyphen; chart เอง fail เมื่อชื่อไม่ถูกต้อง)
dogstatsd.port: 8125 — ตั้ง port แต่ไม่เปิดอะไรออกไป
หัวข้อที่มีชื่อว่า “dogstatsd.port: 8125 — ตั้ง port แต่ไม่เปิดอะไรออกไป”port ของ container agent ที่ render:
agent [{'containerPort': 8125, 'name': 'dogstatsdport', 'protocol': 'UDP'}]trace-agent [{'containerPort': 8126, 'hostPort': 8126, 'name': 'traceport', 'protocol': 'TCP'}]APM มี hostPort (apm.enabled: true ที่ deprecated ยังเปิดมันอยู่ — ดู Misconceptions); DogStatsD ไม่มี เพราะ port คือ container port และ default ของ chart สำหรับ datadog.dogstatsd.useHostPort คือ false แอปที่ส่ง metric ไป status.hostIP:8125 — pattern เดียวกับ bug 11 ของ รีวิว Node — ไปไม่ถึงอะไรเลย comment ของ chart สำหรับ useHostPort: “Sets the hostPort to the same value of the container port. Needs to be used for sending custom metrics.” เมื่อ useHostPort: true ผล render แสดง 'hostPort': 8125 transport อื่นที่รองรับ ซึ่งเปิดเป็น default คือ Unix socket (useSocketVolume: true) ที่ pod ของแอปต้อง mount
🐛 หา bug ในโค้ดนี้
dogstatsd: port: 8125เฉลย
port: 8125 แค่ย้ำค่า default คำถามที่สำคัญคือ แอปเข้าถึง DogStatsD อย่างไร — hostPort หรือ socket volume — และ values file ไม่เคยตอบ วิธีแก้: ตัดสินใจ แล้วตั้ง useHostPort: true (หรือ mount socket ในแอป)
ส่วนที่ 2 — manifest ของ Operator
field รูปแบบ v1alpha1 ใต้ apiVersion: v2alpha1
หัวข้อที่มีชื่อว่า “field รูปแบบ v1alpha1 ใต้ apiVersion: v2alpha1”CRD ใน operator chart 2.27.0 ประกาศเวอร์ชันเดียวเท่านั้น: [('v2alpha1', True, True)] (name, served, storage) ดังนั้น apiVersion: datadoghq.com/v1alpha1 — รูปแบบที่ assistant เรียนจาก tutorial เก่า — ไม่ถูก serve เลย (คำตอบของ API server คือ error ทั่วไป no matches for kind "DatadogAgent" in version; ไม่ได้รันที่นี่) draft นี้ได้ apiVersion ถูกแต่ body ผิด การเช็ก strict schema:
$ python3 vcr.py bad-dda.yamlapiVersion: datadoghq.com/v2alpha1 /spec -> Additional properties are not allowed ('agent', 'clusterAgent', 'credentials' were unexpected)spec ใน v2alpha1 มี property สามตัวพอดี: features, global, override credentials ย้ายไป global.credentials; agent.image กลายเป็น override.nodeAgent.image (แยก name กับ tag); การตั้งค่าของ Cluster Agent อยู่ใต้ override.clusterAgent (และ image ของมันใต้ override.clusterAgent.image) docs ของ Kubernetes เองอธิบายว่า API server ทำอะไรกับ field ที่ไม่รู้จัก: “Possible levels of validation are Ignore, Warn (default), and Strict” (kubernetes.io/docs/reference/using-api/api-concepts/#field-validation) และ kubectl apply --help บนเครื่องนี้ (client v1.36.1) ระบุ --validate='strict' เป็น default ด้วยสองระดับแรก field ที่ไม่รู้จักไม่ใช่ error; CRD ที่มี structural schema จะ prune มัน ดังนั้น DatadogAgent ที่เก็บไว้จะขาด block เหล่านั้น (การ prune นี้ไม่ได้รันที่นี่ — ไม่มี API server) และ Agent ที่ไม่มี credentials คือผลตามธรรมชาติ
🐛 หา bug ในโค้ดนี้
spec:credentials: apiKey: "<your-api-key>"agent: image: name: datadog/agent:latestclusterAgent: enabled: trueเฉลย
block เหล่านั้นเป็น field ที่ถูกต้องของ v1alpha1 ในเอกสาร v2alpha1 การเช็ก strict schema จับได้; การ apply แบบผ่อนปรนทิ้งมันเงียบๆ ซึ่งเป็นผลที่แย่ที่สุด — DatadogAgent ที่ดูสุขภาพดีแต่ไม่มีการตั้งค่าที่ตั้งใจเลย วิธีแก้: spec.global.credentials, spec.override.nodeAgent.image, spec.override.clusterAgent.image
schema เขียวแต่ manifest ยังผิด
หัวข้อที่มีชื่อว่า “schema เขียวแต่ manifest ยังผิด”ย้ายสาม block ที่วางผิดที่ไปยังที่ของมันใน v2alpha1 โดยไม่เปลี่ยนอย่างอื่น (site ยังเป็น URL, key ยัง literal, latest ยังอยู่):
$ python3 vcr.py mid-dda.yamlapiVersion: datadoghq.com/v2alpha1OK: no schema violationsschema validate โครงสร้างและ type ไม่ใช่ความหมาย CRD ไม่มี pattern ให้ site และ credentials.apiKey เป็น string ที่ถูกต้อง ปัญหาเรื่องค่าสามอย่างจากส่วนที่ 1 — site ที่เป็น URL, key แบบ plaintext, tag ที่ลอย — ผ่านไปได้หมด (negative control แสดงว่า checker ไม่ได้ว่างเปล่า: containerCollectall ให้ Additional properties are not allowed ('containerCollectall' was unexpected) ที่ /spec/features/logCollection และ enabled: "yes" ให้ 'yes' is not of type 'boolean')
🐛 หา bug ในโค้ดนี้
global: site: https://api.datadoghq.com credentials: apiKey: "<your-api-key>"เฉลย
“มัน validate ผ่าน” เป็นข้อความเกี่ยวกับ schema API key ควรอยู่ใน spec.global.credentials.apiSecret (secretName และ keyName); site คือ datadoghq.com ทั้งสองเป็นรายการรีวิวที่ schema แบบ offline ยกขึ้นมาไม่ได้
ส่วนที่ 3 — Autodiscovery และ Deployment ของ Redis
Autodiscovery annotation บน Deployment
หัวข้อที่มีชื่อว่า “Autodiscovery annotation บน Deployment”annotation อยู่บน metadata ของ Deployment เอง Autodiscovery อ่าน annotation ของ pod จาก docs (docs.datadoghq.com/containers/kubernetes/integrations/): “If you define pods indirectly (with deployments, ReplicaSets, or ReplicationControllers) add pod annotations under spec.template.metadata.” pod template คือสิ่งที่ Deployment ใช้สร้าง pod; metadata ของ Deployment เองไม่ถูกคัดลอกไปที่ pod ตรวจสอง manifest ด้วย schema Deployment ของ Kubernetes และ script เล็กๆ:
bad-cache.yaml schema: OK Deployment.metadata.annotations keys : ['ad.datadoghq.com/redis.checks', 'ad.datadoghq.com/redis.logs'] pod template annotations keys : []good-cache.yaml schema: OK Deployment.metadata.annotations keys : [] pod template annotations keys : ['ad.datadoghq.com/cache.checks', 'ad.datadoghq.com/cache.logs']ทั้งสอง ผ่าน schema kubectl apply ตอบ deployment.apps/cache created ได้ทั้งคู่; ความต่างเห็นได้เฉพาะบน pod ที่รันอยู่ ใน agent configcheck ของ node Agent ขั้นสุดท้ายนั้นต้องมี cluster และไม่ได้รันที่นี่ — ข้อกล่าวอ้างอิงตามการอ้าง docs ข้างบน
🐛 หา bug ในโค้ดนี้
kind: Deploymentmetadata:name: cacheannotations: ad.datadoghq.com/redis.checks: | { ... }เฉลย
metadata.annotations ของ Deployment เป็นเอกสารสำหรับคนและเครื่องมือ; pod ได้ spec.template.metadata.annotations วิธีแก้: ย้ายทั้งสอง annotation ไปใต้ spec.template.metadata กฎเดียวกันใช้กับ StatefulSet, DaemonSet, Job และ CronJob (spec.jobTemplate.spec.template.metadata สำหรับตัวหลัง)
ad.datadoghq.com/redis.checks — identifier คือชื่อ container ไม่ใช่ชื่อเทคโนโลยี
หัวข้อที่มีชื่อว่า “ad.datadoghq.com/redis.checks — identifier คือชื่อ container ไม่ใช่ชื่อเทคโนโลยี”แม้อยู่บน pod template key ของ annotation คือ ad.datadoghq.com/<container-name>.checks container ชื่อ cache; annotation เขียน redis docs อธิบาย identifier ในรูปแบบ annotation ว่า “an identifier to match against the names (spec.containers[i].name, not spec.containers[i].image) of the containers that correspond to your integration” script ข้างบนใช้กฎนี้กับทั้งสอง manifest:
deployment: ad.datadoghq.com/redis.checks JSON ok; identifier 'redis' matches a container name: Falsepod-template: ad.datadoghq.com/cache.checks JSON ok; identifier 'cache' matches a container name: Trueตัว template เอง รันผ่าน Agent จริงเป็น config Autodiscovery แบบไฟล์ที่มี ad_identifiers: [cache] คือสิ่งที่ annotation ที่แก้แล้วขยายออกมา agent configcheck -v ใน container 7.83.3 แสดงมัน และแสดงรูปแบบ unresolved ที่ node Agent รอ match กับ container ที่รัน:
Auto-discovery IDs: cachecheck_name: redisdbinit_config: nullinstances:- host: '%%host%%' port: "6379"logs_config: nullรายการนั้น (ใต้ “Collected configs (matched and unmatched)”) พิสูจน์ว่า template parse ได้และถูกลงทะเบียนใต้ identifier cache; มัน ไม่ได้ พิสูจน์ว่ามี pod match — container listener ไม่มี Kubernetes ให้ดูในการรันนี้ ส่วน “Services (matched and unmatched)” ที่มาคู่กันจึงว่าง identifier ที่ผิดอย่าง redis จะถูกลงทะเบียนอย่างเต็มใจเท่ากันและไม่มีวัน match
🐛 หา bug ในโค้ดนี้
ad.datadoghq.com/redis.checks: |...containers:- name: cacheเฉลย
prefix ของ annotation ที่ตั้งชื่อเทคโนโลยีแทน container เป็นความผิดพลาด Autodiscovery ที่พบบ่อยอันดับสอง (รองจากวางผิด object ด้านบน) ความผิดทั้งสองบังกันในการรีวิวเพราะแต่ละอันดูสมเหตุสมผลเมื่ออยู่เดี่ยวๆ วิธีแก้: ad.datadoghq.com/cache.checks และ ad.datadoghq.com/cache.logs บน pod template รูปแบบ v1 สามคีย์ (check_names, init_configs, instances) ยังถูกระบุคู่กับ v2 ในหน้าเดียวกัน จึงไม่ใช่ bug ตรงนี้; v2 (.checks, Agent 7.36+) เป็นแค่รูปแบบ annotation เดียว
โค้ดที่แก้แล้ว
หัวข้อที่มีชื่อว่า “โค้ดที่แก้แล้ว”datadog: apiKeyExistingSecret: datadog-secret site: datadoghq.com clusterName: prod-eks-1 tags: - env:prod apm: portEnabled: true dogstatsd: useHostPort: true logs: enabled: true containerCollectAll: trueagents: image: tag: 7.83.3clusterAgent: enabled: true image: tag: 7.83.3 tokenExistingSecret: datadog-cluster-agent-tokenkubectl -n datadog create secret generic datadog-secret \ --from-literal api-key=<your-api-key>kubectl -n datadog create secret generic datadog-cluster-agent-token \ --from-literal token=<random-string-of-32-or-more-characters>helm lint รายงาน 1 chart(s) linted, 0 chart(s) failed เมื่อ render container agent มี:
DD_API_KEY = secretKeyRef datadog-secret / api-keyDD_CLUSTER_NAME = prod-eks-1DD_TAGS = ["env:prod"]DD_SITE = datadoghq.comDD_CLUSTER_AGENT_AUTH_TOKEN = secretKeyRef datadog-cluster-agent-token / tokenDD_LOGS_CONFIG_CONTAINER_COLLECT_ALL = trueagent containerPort 8125 hostPort 8125 (UDP)trace-agent containerPort 8126 hostPort 8126 (TCP)และ render สองครั้งติดกันต่างกันแค่ UUID install_id ที่ไม่เกี่ยวข้อง ตัวเทียบเท่าของ Operator ซึ่งผ่านการเช็ก strict v2alpha1 (OK: no schema violations):
apiVersion: datadoghq.com/v2alpha1kind: DatadogAgentmetadata: name: datadog namespace: datadogspec: global: clusterName: prod-eks-1 site: datadoghq.com credentials: apiSecret: secretName: datadog-secret keyName: api-key clusterAgentTokenSecret: secretName: datadog-cluster-agent-token keyName: token tags: - env:prod features: apm: enabled: true hostPortConfig: enabled: true hostPort: 8126 dogstatsd: hostPortConfig: enabled: true hostPort: 8125 logCollection: enabled: true containerCollectAll: true admissionController: enabled: true clusterChecks: enabled: true useClusterChecksRunners: true override: nodeAgent: image: name: datadog/agent tag: 7.83.3 clusterAgent: image: name: datadog/cluster-agent tag: 7.83.3apiVersion: apps/v1kind: Deploymentmetadata: name: cache namespace: shopspec: replicas: 1 selector: matchLabels: app: cache template: metadata: labels: app: cache annotations: ad.datadoghq.com/cache.checks: | { "redisdb": { "init_config": {}, "instances": [{ "host": "%%host%%", "port": "6379" }] } } ad.datadoghq.com/cache.logs: '[{"source": "redis", "service": "cache"}]' spec: containers: - name: cache image: redis:7.4ad_identifiers: - cacheinit_config:instances: - host: "%%host%%" port: "6379"fence สุดท้ายเป็นคู่แฝดแบบไฟล์ของ annotation และเป็น fence ที่ถูกเก็บ: harness ของคอร์ส mount มันเข้า Agent container จริงแล้วรัน agent configcheck
เปลี่ยนอะไรและทำไม
หัวข้อที่มีชื่อว่า “เปลี่ยนอะไรและทำไม”| # | Bug | วิธีแก้ | พิสูจน์ด้วย |
|---|---|---|---|
| 1 | site: https://api.datadoghq.com | site: datadoghq.com | DD_SITE ที่ render; URL intake ใน agent status |
| 2 | apiKey แบบ literal ใน values | apiKeyExistingSecret | Secret ที่ render decode ได้เป็น key; 0 Secret หลังแก้ |
| 3 | tags: env:prod เป็น string | YAML list | DD_TAGS = "env:prod"; agent config แสดง tag ที่มี quote |
| 4 | logs.collectAll (key ที่แต่งขึ้น) | logs.containerCollectAll | COLLECT_ALL = false; render เหมือนกันมีหรือไม่มี key |
| 5 | agents.image.tag: latest | pin tag ของ Agent และ Cluster Agent | image ที่ render; default ของ chart คือ 7.82.3 |
| 6 | ไม่มี token ของ Cluster Agent | tokenExistingSecret | token และ checksum/clusteragent_token ต่างกันทุก render |
| 7 | ไม่มี clusterName | datadog.clusterName | ไม่มี DD_CLUSTER_NAME ที่ render |
| 8 | DogStatsD port ไม่มี hostPort | useHostPort: true (หรือ socket) | port ของ container ที่ render |
| 9 | field รูปแบบ v1alpha1 ใน v2alpha1 | global.credentials, override.* | strict CRD schema: 3 property ที่ไม่คาดคิด |
| 10 | ค่าถูก schema แต่ผิด | apiSecret, datadoghq.com | OK: no schema violations บน manifest ที่ยังผิด |
| 11 | AD annotation บน Deployment | spec.template.metadata.annotations | คำอ้างจาก docs; ทั้งสอง manifest ผ่าน schema |
| 12 | prefix annotation redis แต่ container cache | ad.datadoghq.com/cache.* | script + configcheck -v ลงทะเบียน template ใต้ cache |
สิ่งที่ reviewer เขียนใน PR
หัวข้อที่มีชื่อว่า “สิ่งที่ reviewer เขียนใน PR”12 finding ไม่มีอันไหนเป็น syntax error —
helm lintเขียว และทุก manifest เป็น YAML ที่ถูกต้อง:
- Helm values:
siteคือ domain (datadoghq.com) ไม่ใช่ URL;tagsเป็น list; มี API key แบบ literal (ใช้apiKeyExistingSecret);logs.collectAllไม่มีอยู่ — key คือcontainerCollectAllและวันนี้DD_LOGS_CONFIG_CONTAINER_COLLECT_ALLrender เป็นfalse- ทำซ้ำได้: pin
agents.image.tagและclusterAgent.image.tag; ตั้งclusterAgent.tokenExistingSecret— วันนี้ทุกhelm upgradegenerate token ใหม่และ roll DaemonSet- ขาด:
datadog.clusterNameและการตัดสินใจว่าแอปเข้าถึง DogStatsD อย่างไร (useHostPortหรือ socket)- manifest ของ Operator:
credentials,agentและclusterAgentเป็น field ของ v1alpha1; ใน v2alpha1 คือglobal.credentials,override.nodeAgentและoverride.clusterAgentรัน strict schema — และจำไว้ว่า “valid” ยังไม่พูดอะไรเรื่อง key แบบ plaintext หรือ site ที่เป็น URL- Redis Autodiscovery: annotation ไปที่
spec.template.metadataและ prefix ของ key คือชื่อ container (cache) ไม่ใช่redis- ยังไม่ได้ยืนยันที่นี่ ช่วยเช็กบน cluster จริง:
agent configcheckบน node Agent สำหรับ podcacheและว่า log จาก container ที่ไม่มี annotation มาถึงจริง
Mental Model
หัวข้อที่มีชื่อว่า “Mental Model”flowchart TD V["values.yaml"] -->|"1, 3, 5, 7: wrong value shape or missing"| R["helm template"] V -->|"2: literal key in git"| R V -->|"4: invented key ignored"| R V -->|"6: token differs per render"| R V -->|"8: port without hostPort"| R R --> D["DaemonSet + Cluster Agent"] O["DatadogAgent manifest"] -->|"9: v1alpha1 fields"| S["v2alpha1 CRD schema"] O -->|"10: valid but wrong values"| S S --> D A["Deployment annotations"] -->|"11: wrong object"| P["pod"] A -->|"12: wrong container identifier"| P D -->|"node Agent reads pod annotations"| P
ดูว่าอะไรจับอะไร helm template ทำให้ bug 1, 3, 4, 5, 6, 7 และ 8 มองเห็นได้เพราะ DaemonSet ที่ render เป็นข้อความธรรมดาที่อ่านได้ — แต่ต้องมองหาสิ่งที่ หายไป (DD_CLUSTER_NAME, hostPort) strict CRD schema จับ 9 ได้ และโดยการออกแบบจับ 10 ไม่ได้ Bug 11 และ 12 มองไม่เห็นด้วย offline tool ทุกตัวในรีวิวนี้: ทั้งสอง manifest validate ผ่าน schema ของ Kubernetes จุดยืนที่ซื่อสัตย์คือตารางที่มีคอลัมน์ชื่อ “ต้องมี cluster”
Trade-offs
หัวข้อที่มีชื่อว่า “Trade-offs”- Helm chart vs Operator chart render เป็น manifest ธรรมดาที่
helm templateและ diff ใน CI ได้; Operator เปลี่ยน custom resource เล็กๆ เป็น manifest เหล่านั้นตอน runtime ดังนั้น “อะไรจะถูก deploy” ถูกตัดสินภายใน cluster และคุณรีวิวเจตนา ไม่ใช่ผลลัพธ์ strict schema ปกป้องทาง Operator; การ render ปกป้องทาง chart containerCollectAll: trueเป็นวิธีเร็วที่สุดของ “log จากทุกอย่าง” และมันส่งทุกอย่าง: sidecar ที่ noisy, chatter ของ health check, log ของ Agent เอง annotationad.datadoghq.com/<container>.logsต่อ workload พร้อม exclusion filter ต้องตั้งค่ามากกว่าแต่คุมบิลได้hostPortvs socket สำหรับ APM และ DogStatsDhostPortเข้าถึงได้ด้วยstatus.hostIPอย่างเดียวโดยไม่ต้อง mount volume แลกกับการเปิด port บนทุก node; Unix socket ต้อง mount hostPath ใน pod ของแอปแต่ไม่มี network hop ทั้งสองรองรับโดย chart และ Operator; หน้าที่ของรีวิวคือต้องมีการเลือกหนึ่งอย่าง
Common Misconceptions
หัวข้อที่มีชื่อว่า “Common Misconceptions”- “
helm lintผ่าน แปลว่า values ถูก” พิสูจน์แล้วข้างบน: lint ผ่านกับ values file ที่มี URL เป็น site, key logs ที่แต่งขึ้น และ string ในที่ที่ต้องเป็น list - “
apm.enabled: trueเป็น bug เพราะ deprecated” มัน deprecated (comment ของ chart: “DEPRECATED. Use datadog.apm.portEnabled instead”) แต่ใช้ได้: ผล render แสดงDD_APM_ENABLED = true,DD_APM_RECEIVER_PORT = 8126และhostPort: 8126บน containertrace-agentเขียนใหม่เพื่ออนาคต แต่อย่าเรียกว่าพัง - “ถ้า CRD schema validate ผ่าน
DatadogAgentก็ถูก” พิสูจน์แล้วข้างบน: manifest ที่ยังผิดคืนOK: no schema violations - “Autodiscovery annotation วางตรงไหนของ workload ก็ได้” ต้องอยู่บน pod (
spec.template.metadataสำหรับ Deployment) และ key ด้วยชื่อ container ทั้งสองแบบผ่านkubectl apply - “annotation v1 (
check_names) ถูกถอดแล้ว” หน้า docs ยังแสดง v1 คู่กับ v2; v2 ต้องการ Agent 7.36+ ซึ่ง Agent ปัจจุบันทุกตัวเป็นไปตามนั้น