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

สถาปัตยกรรมของ 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 เหล่านั้นพอดี

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 seconds
trace: up (pid 434) 57 seconds
process: down (exitcode 0) 57 seconds, normally up, ready 57 seconds
security: down (exitcode 0) 46 seconds, normally up, ready 46 seconds
sysprobe: up (pid 435) 57 seconds
data-plane: down (exitcode 0) 51 seconds, normally up, ready 51 seconds

มีสามอย่างที่มองเห็นตรงนี้ ซึ่ง component diagram ซ่อนไว้

  1. ทุก component เป็น service ที่ถูก supervise แยกกัน และแต่ละตัวตัดสินตอน startup เองว่ามีงานให้ทำไหม security exit ด้วย 0 (“normally up” เป็นคำของ s6 ที่แปลว่า “จะถูก restart ถ้าตายด้วย code ไม่ใช่ 0”) เพราะ compliance กับ runtime security ไม่ได้เปิด นี่คือการ exit เงียบ ๆ ตอนไม่มีงาน ไม่ใช่ crash
  2. 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)
  3. 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] section Process Agent ของ agent status จึงอ่านได้ว่า Status: Not running or unreachable ขณะที่ section Process 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 ที่ลิงก์ไว้ข้างบน

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
เส้นทางข้อมูลของ core Agent

เส้นประสำคัญ: Trace Agent กับ Logs Agent ไม่ผ่าน core forwarder ทั้งคู่มี writer และการตั้งค่า endpoint ของตัวเอง หลักฐานอยู่ใน section ถัดไปอีกสองหัวข้อ

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 15s
2026-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-store

collector-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

ทุก header ใน agent status จริงตรงกับหนึ่ง stage ข้างบน section จริงตามลำดับที่ Agent พิมพ์ และแต่ละอันบอกอะไร:

Sectionรายงานอะไรอ่านเมื่อ
Agent (v7.83.3)version, PID, path ของ Config File, conf.d, checks.d”config file ไหนถูกโหลดจริง?”
Hostnamehostname: internals-host, hostname provider: configurationtag/host ไม่ตรง — provider บอกว่า ทำไม hostname นี้ชนะ
ClocksNTP offset, Intake offsettimestamp ถูกปฏิเสธหรือกราฟเลื่อน
Collectorทุก check instance พร้อม Total Runs, Metric Samples: Last Run/Total, Last Execution Date, Configuration Sourcecheck เงียบ — กำลังรัน, error หรือไม่ได้ถูก schedule?
Aggregatorจำนวน sample ขาเข้า, series ที่ flush ออกpipeline ยังมีชีวิตแต่ไม่มีอะไรมาถึง
APM Agentreceiver port, Endpoints, receiver และ writer ของนาทีก่อนtrace หาย
DogStatsDตัวนับ packet แยกตาม transport, parse errorcustom metric หาย
EndpointsURL ของ core forwarder กับท้าย API keysite หรือ proxy ผิด
Forwarderตัวนับ transaction, error แยกตามชนิด, สถานะ API keyข้อมูลถูกสร้างแต่ส่งไม่ถึง
Logs Agenttransport, LogsProcessed/LogsSent, backpressurelog หายหรือช้า
OTLPStatus: Enabled, Collector status: Runningเส้นทาง OTLP ingest
Process Componentprocess check ที่เปิด, submission errorLive 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: 12

Checks 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:
- ke00

dd_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

มอง 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 ไหนหยุด

ทางเลือกข้อดีข้อเสีย
process แยกที่ถูก supervise ต่อ componentTrace 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/Orestart ระหว่างที่ 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)
  • “Agent เป็น process เดียว” พิสูจน์ข้างบน: ps แสดง agent run กับ trace-agent แยก บวก supervisor ที่มีหกตัว service
  • “Process Agent เป็น process แยกเสมอ” ใน 7.83.3 process check รันใน core Agent (Starting process-component ...) และ service process exit 0; 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 ที่ควรรายงานคือสัญญาณจริง

โมเดลที่ถูกสั่งให้ “ส่งทราฟฟิก Agent ทั้งหมดผ่าน egress proxy ของเรา” มักเขียน setting เดียวกับ comment ที่มั่นใจเกินจริง

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

datadog.yaml
api_key: <your-api-key>
site: datadoghq.com
# Route ALL Agent traffic (metrics, traces, logs, processes) via the internal egress gateway
dd_url: https://egress.internal.example:3834
apm_config:
enabled: true
logs_enabled: true
process_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:

datadog.yaml
api_key: <your-api-key>
site: datadoghq.com
proxy:
https: http://egress.internal.example:3128
no_proxy:
- 169.254.169.254
apm_config:
enabled: true
logs_enabled: true
process_config:
process_collection:
enabled: true
ในการรันจริง s6-svstat แสดง security: down (exitcode 0) ... normally up สิ่งนี้บอกอะไร?
บรรทัด log Scheduling check cpu with an interval of 15s กับ agent status สามครั้งที่แสดง Last Execution Date 02:05:25.274, 02:05:55.274, 02:06:10.274 พิสูจน์อะไร?
เมื่อ intake ไม่ตอบ section Forwarder จริงแสดง Requeued: 572, RetryQueueSize: 6, Dropped: 0, ConnectionErrors: 24 อ่านอย่างไรถูก?
คุณตั้งแค่ dd_url เป็น gateway ภายใน ใน agent status จริง writer ไหนยังชี้ default ของ datadoghq?
บน Agent 7.83.3 Linux agent status แสดง Process Agent: Status: Not running or unreachable และ Process Component: Enabled Checks: [process rtprocess] ด้วย ทำไมไม่ขัดแย้งกัน?
ทำไมบทนี้แสดง Cluster Agent จาก datadog/agent:7 container ตัวเดียวไม่ได้?