Fleet is Elastic’s unified agent management solution, and it’s the primary way you’ll deploy Elastic APM.
Here’s an APM transaction flowing through a Node.js application instrumented with the Elastic APM Node.js agent, observed by Fleet:
{
"@timestamp": "2023-10-27T10:30:00.123Z",
"trace.id": "a1b2c3d4e5f67890a1b2c3d4e5f67890",
"transaction.id": "f0e9d8c7b6a54321",
"transaction.name": "GET /users/:id",
"transaction.type": "http",
"service.name": "my-web-app",
"service.version": "1.2.0",
"http.request.method": "GET",
"http.request.url.path": "/users/123",
"http.response.status_code": 200,
"event.duration": 150000,
"agent": {
"name": "elastic-node",
"version": "8.10.0"
},
"log.level": "info",
"message": "Transaction completed",
"host.name": "my-app-server-01"
}
This single document, ingested into Elasticsearch and visualized in Kibana, tells you everything about that specific web request: its duration, the endpoint, the status code, which service it belongs to, and even the version of the APM agent collecting the data. Fleet orchestrates the deployment and configuration of the agent that generated this data.
The core problem APM solves is making distributed systems observable. When you have microservices, a single user request might traverse dozens of independent components. If one of those components fails or slows down, pinpointing the root cause can feel like finding a needle in a haystack. APM ties together the requests across these services, showing you the full journey of a transaction and highlighting where latency is introduced or errors occur.
Fleet acts as the control plane for Elastic’s agents, including the APM integration. You define an "agent policy" in Kibana, which is essentially a blueprint for how an agent should behave. This policy specifies which integrations to install, their configurations, and any output destinations (like your APM Server). You then enroll an agent on your hosts, assign it to a policy, and Fleet pushes the configuration and necessary binaries. The agent then starts collecting and sending APM data.
The APM integration within Fleet allows you to manage the configuration for the APM agent itself. This includes setting the APM Server URL, any authentication tokens, and service-specific settings. When you add the APM integration to a Fleet policy, you configure these parameters. For example, you’d set the APM Server URL to http://your-apm-server:8200 and potentially add an API Key for secure communication.
Once the Fleet agent is installed on your application servers and assigned an APM integration policy, the APM agent binaries are automatically downloaded and managed. Your application code then needs to be instrumented. For a Node.js application, this typically involves a single line in your application’s entry point:
require('elastic-apm-node').start({
serviceName: 'my-web-app',
serverUrl: 'http://your-apm-server:8200',
secretToken: 'YOUR_SECRET_TOKEN' // or apiKey
});
Fleet ensures that the serverUrl and secretToken (or apiKey) configured in the Kibana integration are correctly applied to the running APM agent. If you update the configuration in Fleet, the agent will automatically pick up the changes.
The most surprising true thing about APM is that it doesn’t just measure your code’s performance; it measures the performance of all the dependencies your code relies on, including external HTTP services and database calls, and attributes the latency or errors to your service.
Let’s say you have a service checkout-service that calls an external payment-gateway-service. If a request to payment-gateway-service takes 5 seconds, APM will report that 5 seconds of latency for your checkout-service transaction, even though the time was spent entirely outside your code. This is crucial for understanding end-to-end request performance and identifying bottlenecks in your entire system, not just within a single service’s boundaries.
The actual magic behind distributed tracing in APM involves a concept called "context propagation." When a transaction starts in one service, a unique trace.id is generated. As this transaction moves to another service (e.g., via an HTTP request), this trace.id, along with a span.id (representing the current operation), is injected into the outgoing request headers. The receiving service then reads these headers and uses the same trace.id to link its subsequent operations back to the original trace. This allows APM to stitch together the entire journey. For HTTP requests, common headers used are traceparent and tracestate (W3C Trace Context standard), or older X-Elastic-APM-Traceparent headers. Fleet ensures the APM agent is configured to send and receive these headers correctly.
The next concept you’ll likely explore is setting up Service Maps in Kibana, which visually represent the dependencies between your instrumented services based on the distributed traces.