สถาปัตยกรรมของ Agent
ไอเดียหลักในหนึ่งประโยค
หัวข้อที่มีชื่อว่า “ไอเดียหลักในหนึ่งประโยค”Agent คือ process tree ที่มี supervisor คุม รอบ process หลักหนึ่งตัว ซึ่งข้างในเป็น pipeline ตรง ๆ — check scheduler ส่งงานให้ runner worker กลุ่มคงที่ sample ของมันลง aggregator จากนั้น aggregator flush ไปที่ serializer และ forwarder เป็นเจ้าของทุก HTTP request กับทุก retry — และ agent status ก็คือการ dump ทีละ section ของ stage เหล่านั้นพอดี
process tree
หัวข้อที่มีชื่อว่า “process tree”Phase 3 ลิสต์ component ไว้ ส่วนนี้คือสิ่งที่ container จริงทำกับมัน ps ใน dd-internals ตัดให้สั้น:
PID COMMAND 1 s6-svscan -t0 /var/run/s6/services 424 s6-supervise agent 426 s6-supervise trace 427 s6-supervise process 430 s6-supervise security 429 s6-supervise sysprobe 431 s6-supervise data-plane 432 agent run 434 /opt/datadog-agent/embedded/bin//trace-agent --config=/etc/datadog-agent/datadog.yaml 435 /opt/datadog-agent/embedded/bin/system-probe-lite run --socket /opt/datadog-agent/run/sysprobe.sock ...ถาม s6-svstat ของแต่ละ service ที่ถูก supervise:
agent: up (pid 432) 57 secondstrace: up (pid 434) 57 secondsprocess: down (exitcode 0) 57 seconds, normally up, ready 57 secondssecurity: down (exitcode 0) 46 seconds, normally up, ready 46 secondssysprobe: up (pid 435) 57 secondsdata-plane: down (exitcode 0) 51 seconds, normally up, ready 51 secondsมีสามอย่างที่มองเห็นตรงนี้ ซึ่ง component diagram ซ่อนไว้
- ทุก component เป็น service ที่ถูก supervise แยกกัน และแต่ละตัวตัดสินตอน startup เองว่ามีงานให้ทำไหม
securityexit ด้วย0(“normally up” เป็นคำของ s6 ที่แปลว่า “จะถูก restart ถ้าตายด้วย code ไม่ใช่ 0”) เพราะ compliance กับ runtime security ไม่ได้เปิด นี่คือการ exit เงียบ ๆ ตอนไม่มีงาน ไม่ใช่ crash - Trace Agent เป็น binary ของมันเอง ไม่ใช่ thread ของ core Agent log file
/var/log/datadog/trace-agent.logขึ้นต้นด้วยTRACE-LOADER | INFO | Listening to TCP receiver at port 8126...และListening to unix receiver at path /var/run/datadog/apm.socketใน build นี้ loader ตัวเล็กเปิด socket ที่ listen ก่อน และ log ก็บอกListening to otlp port 5003ด้วย — Trace Agent คือปลายทางของ OTLP trace path ด้วย (ดู OTLP and Open Standards) - Process Agent ไม่ได้เป็น process แยกที่รันอยู่บน Linux ใน 7.83.3 อีกแล้ว
process: down (exitcode 0)ข้างบน แต่ process collection เปิดอยู่ — เพราะ log ของ core Agent เองบอกว่าStarting process-component with enabled checks=[process rtprocess]และStarting CheckSubmitter for host=internals-host, endpoints=[http://127.0.0.1:1]sectionProcess Agentของagent statusจึงอ่านได้ว่าStatus: Not running or unreachableขณะที่ sectionProcess Componentที่อยู่ถัดลงมาลิสต์Enabled Checks: [process rtprocess]ถ้าคุณเคยเรียนมาว่า “Process Agent = PID ของตัวเอง” ให้ตรวจ version — code ย้ายเข้า core process แล้ว และ binary เก่า exit เงียบ ๆ เมื่อเห็นว่าไม่ต้องทำงาน
Cluster Agent ไม่อยู่ในภาพนี้เลย เพราะเป็น คนละ image (datadog/cluster-agent) ที่ deploy หนึ่งตัวต่อ cluster แทนที่จะเป็นหนึ่งตัวต่อ node — จึงแสดงจาก datadog/agent:7 container ตัวเดียวไม่ได้ และบทนี้ไม่แกล้งทำเป็นว่าแสดงได้ สิ่งที่ docs บอกว่ามันมีไว้ทำอะไร (อ้างจาก docs.datadoghq.com/containers/cluster_agent/): “By acting as a proxy between the API server and node-based Agents, the Cluster Agent helps to alleviate server load,” มัน “relays cluster level metadata to node-based Agents,” และรองรับ “Horizontal Pod Autoscaling (HPA) with custom Kubernetes metrics and external metrics.” รูปแบบการ deploy บน Kubernetes อยู่ในบท Phase 3 ที่ลิงก์ไว้ข้างบน
pipeline ภายใน core Agent
หัวข้อที่มีชื่อว่า “pipeline ภายใน core Agent”flowchart LR AD["Autodiscovery + conf.d"] --> SCH["Check scheduler"] SCH -->|"every min_collection_interval"| RUN["Runner: 4 workers"] RUN --> AGG["Aggregator"] DSD["DogStatsD server"] --> AGG AGG --> SER["Serializer"] SER --> FWD["Forwarder + retry queue"] FWD -->|"HTTPS, one endpoint per payload type"| INT["Datadog intake"] TA["Trace Agent (own process)"] -.->|"own writer"| INT LA["Logs Agent"] -.->|"own pipeline"| INT
เส้นประสำคัญ: Trace Agent กับ Logs Agent ไม่ผ่าน core forwarder ทั้งคู่มี writer และการตั้งค่า endpoint ของตัวเอง หลักฐานอยู่ใน section ถัดไปอีกสองหัวข้อ
Proof: check scheduler และ runner
หัวข้อที่มีชื่อว่า “Proof: check scheduler และ runner”log ของ core Agent บอกการตัดสินใจของ scheduler ตามที่เกิดขึ้นจริง:
2026-09-30 02:04:13 UTC | CORE | INFO | (pkg/collector/runner/runner.go:121 in ensureMinWorkers) | Runner 1 added 4 workers (total: 4)2026-09-30 02:04:18 UTC | CORE | INFO | (pkg/collector/scheduler/scheduler.go:97 in Enter) | Scheduling check cpu with an interval of 15s2026-09-30 02:04:18 UTC | CORE | INFO | (pkg/collector/scheduler/scheduler.go:97 in Enter) | Scheduling check disk:67cc0574430a16ba with an interval of 15sสองข้อเท็จจริงได้จากบรรทัดเหล่านี้ runner มี 4 worker — เป็นค่า default ของ setting check_runners ซึ่ง agent config --all ยืนยัน (check_runners: 4) — ดังนั้นมี check instance รันพร้อมกันได้มากสุดสี่ตัว และ check ที่ช้าจะยึด worker หนึ่งตัวตลอดการรัน และ scheduler ทำงานตาม interval ไม่ใช่ “เมื่อรอบก่อนจบ”: Scheduling check cpu with an interval of 15s การอ่าน Last Execution Date ของ cpu จาก agent status สามครั้งห่างกัน 15 วินาที (มี sleep 16 คั่น) ได้ 02:05:25.274, 02:05:55.274 และ 02:06:10.274 — ทั้งหมดมี millisecond ท้าย .274 เท่ากัน และห่างจากครั้งแรกเป็นทวีคูณของ 15 วินาที คือ scheduler เดินตามตารางคงที่ ไม่เลื่อนไปตามเวลารัน
agent health เปิดโครงสร้างเดียวกันเป็น component ที่มีชื่อ และ queue ของ scheduler อยู่ในลิสต์ตรง ๆ:
Agent health: PASS=== 18 healthy components ===ad-config-provider-do-query-actions, ad-processlistener, ad-scheduler-controller, ad-servicelistening,aggregator, collector-queue-15s, collector-queue-60s, collector-queue-900s, dogstatsd-main, forwarder,healthcheck, healthcheck, logs-agent, tagger-store, tagger-workloadmeta, workloadmeta-process,workloadmeta-puller, workloadmeta-storecollector-queue-15s, -60s และ -900s คือ queue ของ scheduler แยกตาม interval: check ที่ min_collection_interval 15 วินาทีอยู่ queue หนึ่ง check 60 วินาทีอยู่อีก queue agent check cpu --check-rate คือทางลัดที่ข้ามทั้งหมดนี้: รัน check instance เดียวหนึ่งครั้งแบบ synchronous แล้วพิมพ์ sample (system.cpu.idle.total … พร้อม "host": "internals-host") แทนที่จะส่งให้ aggregator
อ่าน agent status ทีละ section
หัวข้อที่มีชื่อว่า “อ่าน agent status ทีละ section”ทุก header ใน agent status จริงตรงกับหนึ่ง stage ข้างบน section จริงตามลำดับที่ Agent พิมพ์ และแต่ละอันบอกอะไร:
| Section | รายงานอะไร | อ่านเมื่อ |
|---|---|---|
Agent (v7.83.3) | version, PID, path ของ Config File, conf.d, checks.d | ”config file ไหนถูกโหลดจริง?” |
Hostname | hostname: internals-host, hostname provider: configuration | tag/host ไม่ตรง — provider บอกว่า ทำไม hostname นี้ชนะ |
Clocks | NTP offset, Intake offset | timestamp ถูกปฏิเสธหรือกราฟเลื่อน |
Collector | ทุก check instance พร้อม Total Runs, Metric Samples: Last Run/Total, Last Execution Date, Configuration Source | check เงียบ — กำลังรัน, error หรือไม่ได้ถูก schedule? |
Aggregator | จำนวน sample ขาเข้า, series ที่ flush ออก | pipeline ยังมีชีวิตแต่ไม่มีอะไรมาถึง |
APM Agent | receiver port, Endpoints, receiver และ writer ของนาทีก่อน | trace หาย |
DogStatsD | ตัวนับ packet แยกตาม transport, parse error | custom metric หาย |
Endpoints | URL ของ core forwarder กับท้าย API key | site หรือ proxy ผิด |
Forwarder | ตัวนับ transaction, error แยกตามชนิด, สถานะ API key | ข้อมูลถูกสร้างแต่ส่งไม่ถึง |
Logs Agent | transport, LogsProcessed/LogsSent, backpressure | log หายหรือช้า |
OTLP | Status: Enabled, Collector status: Running | เส้นทาง OTLP ingest |
Process Component | process check ที่เปิด, submission error | Live Processes ว่าง |
นี่คือสามอันจากการรันจริงแบบเต็ม
Aggregator (180 วินาทีหลัง start — Status date 02:07:13, Agent start 02:04:13):
Aggregator==========
Checks Metric Sample: 3,042 Dogstatsd Metric Sample: 1,369 Event: 1 Events Flushed: 1 Number Of Flushes: 11 Series Flushed: 2,400 Service Check: 1 Service Checks Flushed: 12Checks Metric Sample และ Dogstatsd Metric Sample คือสองประตูขาเข้าของ aggregator — check runner กับ DogStatsD server — และ Series Flushed คือสิ่งที่ออกไป Number Of Flushes: 11 ใน 180 วินาทีคือ flush ประมาณทุก 15 วินาที (status ครั้งแรกของ run ก่อนหน้าที่ 45 วินาที แสดง Number Of Flushes: 3 — อัตราเดียวกัน) tick นี้เป็นเหตุผลหนึ่งที่ค่าไม่ปรากฏใน Datadog ทันทีที่คุณสร้างมัน (การ bucket ของ DogStatsD ที่เพิ่มเข้ามาอยู่ใน DogStatsD and Aggregation)
Forwarder เมื่อ intake ไม่ตอบโดยตั้งใจ:
Forwarder=========
Transactions ============ Dropped: 0 Requeued: 572 Retried: 16 RetryQueueSize: 6
Transaction Errors ================== Total number: 24 Errors By Type: ConnectionErrors: 24
On-disk storage =============== On-disk storage is disabled. Configure `forwarder_storage_max_size_in_bytes` to enable it.
API Keys status =============== API key ending with ke00: Unable to reach the API Key validation endpointนี่คือ forwarder ทำงานเดียวของมัน ทุก payload กลายเป็น transaction transaction ที่ล้มเหลวจะถูก requeue (572 ครั้งที่นี่ เพราะ payload หกตัววน retry ไม่สำเร็จ) และรออยู่ใน RetryQueueSize: 6 ในหน่วยความจำ พร้อม backoff (มีเพดานที่ forwarder_backoff_max) ไม่ถูกทิ้ง (Dropped: 0) log บอก URL ต่อชนิด payload ที่ forwarder POST ไป:
Error while processing transaction: error while sending transaction, rescheduling it: Post "http://127.0.0.1:1/api/v1/collector": dial tcp 127.0.0.1:1: connect: connection refused... Post "http://127.0.0.1:1/api/v1/check_run": ...... Post "http://127.0.0.1:1/api/v2/series": .../api/v2/series คือ metric, /api/v1/check_run คือ service check, /api/v1/collector คือ host metadata ขอบเขตของ retry queue คือ forwarder_retry_queue_payloads_max_size: 15728640 (15 MiB จาก agent config --all) โดยมี forwarder_backoff_max: 64 (วินาที) และ forwarder_num_workers: 1 อยู่ข้างกัน payload ที่ไม่พอดีขอบเขตนั้นคือตัวที่ถูกนับใน Dropped — ตัวเลขที่ควรจับตาสำหรับ “Agent ทำข้อมูลหาย” (run ของบทนี้ไม่เคยทำให้ queue เต็ม Dropped จึงเป็น 0 ตลอด เส้นทาง overflow จึงไม่ได้ reproduce ที่นี่) — และ On-disk storage is disabled หมายความว่า restart จะล้าง queue
Endpoints กับ writer อีกสองตัว — หลักฐานว่า core forwarder ไม่ใช่เส้นทางเดียว การรัน container ครั้งแรกตั้ง แค่ DD_DD_URL=http://127.0.0.1:1 section จริง:
Endpoints========= http://127.0.0.1:1 - API Key ending with: - ke00
APM Agent========= ... Endpoints: https://trace.agent.datadoghq.com.
Logs Agent========== Reliable: Sending uncompressed logs in SSL encrypted TCP to agent-intake.logs.datadoghq.com. on port 10516 ...
Process Endpoints================= https://process.datadoghq.com. - API Key ending with: - ke00dd_url ย้าย writer หนึ่งตัว จากสี่ตัว endpoint ของ APM, logs และ process คงค่า default เดิม (และใน run แรกนั้นพยายามต่อไปจริงด้วย key ปลอม: section Forwarder แสดง HTTP Errors By Code: 403: 4 ซึ่งคือ intake ของ Datadog ปฏิเสธ key ปลอม — เป็นเหตุผลที่ run ข้างบนเปลี่ยนทั้งสี่) แต่ละตัวมี setting ของตัวเอง: apm_config.apm_dd_url, logs_config.logs_dd_url, process_config.process_dd_url
Mental Model
หัวข้อที่มีชื่อว่า “Mental Model”มอง Agent เป็น pipeline เดียวที่มี buffer ขนาดคงที่ระหว่าง stage บวก sidecar process ที่เป็นเจ้าของ pipeline ของตัวเอง check เป็น producer บนตาราง timer (scheduler + runner 4 worker), DogStatsD เป็น producer บน socket ทั้งคู่ป้อน aggregator และ tick การ flush ของ aggregator คือ metronome ของเส้นทาง metric ทั้งเส้น forwarder เป็น stage เดียวที่แตะ network ในเส้นทางนั้น และเป็น queue แบบ store-and-retry ที่มีขอบเขตหน่วยความจำ ข้อมูล trace, logs และ (ใน build เก่า) process ไม่ใช้ queue นั้น — มี endpoint และพฤติกรรม retry ของตัวเอง เมื่ออะไรหาย ให้เดินผ่าน stage จากซ้ายไปขวา แล้วแต่ละ section ของ agent status จะบอกว่า stage ไหนหยุด
Trade-offs
หัวข้อที่มีชื่อว่า “Trade-offs”| ทางเลือก | ข้อดี | ข้อเสีย |
|---|---|---|
| process แยกที่ถูก supervise ต่อ component | Trace Agent crash ไม่ทำให้ metric ล่ม; เปิด/ปิดแต่ละตัวได้อิสระ | PID และ memory มากขึ้น; component อาจ “down (exitcode 0)” แต่ดูปกติในการตรวจ process แบบตรง ๆ |
runner pool คงที่ (check_runners: 4) | ใช้ resource คาดเดาได้; check ที่บ้าคลั่งสร้าง thread ไม่จำกัดไม่ได้ | check ช้าตัวเดียว (network call ค้าง) ยึด worker ได้; ขยาย pool ต้องจ่าย CPU |
| retry queue ในหน่วยความจำ (default 15 MiB) | ผ่านช่วง intake สะดุดโดยไม่มี disk I/O | restart ระหว่างที่ intake ล่ม queue หาย เว้นแต่ตั้ง forwarder_storage_max_size_in_bytes |
| Cluster Agent เป็น deployment แยก | Agent ต่อ node เลิกยิง API server; มีที่เดียวสำหรับ cluster check และ HPA metric | ต้อง deploy, รักษาความปลอดภัย (token) และคุม version ให้เข้ากันเพิ่มอีกตัว |
| setting endpoint แยกต่อ writer | ใส่ proxy หรือ private link แยกตามชนิดข้อมูลได้ | การเปลี่ยนแบบ “route ทุกอย่างผ่าน X” ต้องแก้ ทุก writer (ดู SpotTheBug) |
Common Misconceptions
หัวข้อที่มีชื่อว่า “Common Misconceptions”- “Agent เป็น process เดียว” พิสูจน์ข้างบน:
psแสดงagent runกับtrace-agentแยก บวก supervisor ที่มีหกตัว service - “Process Agent เป็น process แยกเสมอ” ใน 7.83.3 process check รันใน core Agent (
Starting process-component ...) และ serviceprocessexit0;agent statusรายงานทั้งProcess Agent: Not running or unreachableและProcess Component: Enabled Checks: [process rtprocess]ทั้งสองบรรทัดจริงพร้อมกัน - “
dd_urlส่งทราฟฟิกทั้งหมดของ Agent ไปที่อื่น” พิสูจน์ข้างบน — endpoint ของ APM, logs และ process คงค่า default ของ datadoghq - “ถ้า intake ล่ม Agent ทิ้งข้อมูลทันที” พิสูจน์ข้างบน: connection error 24 ครั้ง,
Dropped: 0, payload หกตัวรออยู่ในRetryQueueSizeข้อมูลเริ่มหายก็ต่อเมื่อ queue 15 MiB ล้น หรือ process restart ทั้งที่ on-disk storage ปิดอยู่ - “
Total Runsเพิ่มแปลว่า check สุขภาพดี” มันแค่แปลว่า scheduler รันมันMetric Samples: Last Run: 0บน check ที่ควรรายงานคือสัญญาณจริง
Reading AI-generated code
หัวข้อที่มีชื่อว่า “Reading AI-generated code”โมเดลที่ถูกสั่งให้ “ส่งทราฟฟิก Agent ทั้งหมดผ่าน egress proxy ของเรา” มักเขียน setting เดียวกับ comment ที่มั่นใจเกินจริง
🐛 หา bug ในโค้ดนี้
api_key: <your-api-key>site: datadoghq.com
# Route ALL Agent traffic (metrics, traces, logs, processes) via the internal egress gatewaydd_url: https://egress.internal.example:3834
apm_config:enabled: truelogs_enabled: trueprocess_config:process_collection: enabled: trueเฉลย
dd_url เปลี่ยนปลายทางแค่ core forwarder — ตัวที่ POST ไป /api/v2/series, /api/v1/check_run และ /api/v1/collector agent status จริงข้างบนที่รันโดยตั้งแค่ DD_DD_URL ยังแสดง https://trace.agent.datadoghq.com. ใต้ APM Agent, agent-intake.logs.datadoghq.com. on port 10516 ใต้ Logs Agent และ https://process.datadoghq.com. ใต้ Process Endpoints trace, log และข้อมูล process จะพยายามออกตรง ๆ ต่อไป แล้วถูกบล็อก (หรือแย่กว่านั้น คือผ่านไปเงียบ ๆ และข้าม gateway ที่คุณกำลัง audit) หากต้องการส่งทั้งหมดผ่าน proxy ให้ใช้ block proxy ของ Agent ซึ่งใช้กับทุก writer ตาม proxy configuration docs:
api_key: <your-api-key>site: datadoghq.com
proxy: https: http://egress.internal.example:3128 no_proxy: - 169.254.169.254
apm_config: enabled: truelogs_enabled: trueprocess_config: process_collection: enabled: true