As of now, Agent Control is supported on Linux (x86_64 and aarch64). The program is written in Rust, and for
multiplatform compilation we leverage cargo-zigbuild and musl libc.
To compile and run locally:
rustup target add x86_64-unknown-linux-musl aarch64-unknown-linux-musl).cargo-zigbuild with cargo install --locked cargo-zigbuild.cargo zigbuild --bin newrelic-agent-control --target <ARCH>-unknown-linux-musl, where <ARCH> is either
x86_64 or aarch64, depending on your system.
On macOS, you might run into an error like the following:
❯ cargo zigbuild --bin newrelic-agent-control-onhost --target aarch64-unknown-linux-musl
[...]
= note: some arguments are omitted. use `--verbose` to show all linker arguments
= note: error: unable to search for static library /<SOME_PATH_TO_RLIB_FILE>.rlib: ProcessFdQuotaExceeded
This is a known issue. To address it, increase the number of file descriptors for the current shell session with:
ulimit -n 4096
newrelic-agent-control binary will be generated at
./target/<ARCH>-unknown-linux-musl/debug/newrelic-agent-controlPrepare a local_config.yaml file in /etc/newrelic-agent-control/local-data/agent-control, example:
fleet_control:
endpoint: https://opamp.service.newrelic.com/v1/opamp
headers:
api-key: YOUR_INGEST_KEY
agents:
nr-otel-collector:
agent_type: "newrelic/com.newrelic.opentelemetry.collector:0.1.0"
Place values files in the folder /etc/newrelic-agent-control/local-data/{AGENT-ID}/ where AGENT-ID is a key in
the
agents: list. Example:
config: |
# the OTel collector config here
# receivers:
# exporters:
# pipelines:
sudo ./target/debug/newrelic-agent-controlThe steps below work for the x86_64-pc-windows-msvc target only. It is also possible to compile the project for the
x86_64-pc-windows-gnu target using cargo-zigbuild.
rustup target add x86_64-pc-windows-msvcInstall cargo-xwin and dependencies. Example for macOs:
❯ brew install cmake ninja llvm
❯ cargo install --locked cargo-xwin
Compile agent-control:
❯ cargo xwin build --bin newrelic-agent-control --target x86_64-pc-windows-msvc --release
⚠️ This method doesn’t work for building debug binaries (--release is required). As cargo-xwin doesn’t provide
msvcrtd.lib (the debug version of the C runtime library).
On Linux, Agent Control is expected to run as a system service, managed by systemd.
Such service file is packaged in the .deb and .rpm installers by goreleaser and it is installed and enabled by the
postinstall.sh script.
On the other hand, on Windows, Agent Control is expected to run as a Windows Service that is installed using the
install.ps1 script that takes care of registering the service and install AC.
On both Operating Systems, an environment_variables.yaml file is created to store environment variables that Agent
Control will use when running as a service. This file is created at installation time and it is located at the Agent
Control configuration directory.
We use minikube and tilt to launch a local cluster and
deploy the Agent Control charts.
rustup target add x86_64-unknown-linux-musl aarch64-unknown-linux-musl).cargo-zigbuild with cargo install --locked cargo-zigbuild.minikube for local Kubernetes cluster emulation.tilt installed for managing local development environments.local/agent-control-tilt.yml.Note: Adding the 'chart_repo' setting, pointing to
the New Relic charts on a local path, allows using local
helm charts.
minikube start --driver='docker'
make tilt-up
On macOS, you might run into an error like the following:
❯ cargo zigbuild --bin newrelic-agent-control-onhost --target aarch64-unknown-linux-musl
[...]
= note: some arguments are omitted. use `--verbose` to show all linker arguments
= note: error: unable to search for static library /<SOME_PATH_TO_RLIB_FILE>.rlib: ProcessFdQuotaExceeded
This is a known issue. To address it, increase the number of file descriptors for the current shell session with:
ulimit -n 4096
To build the Agent Control container image locally (the same image used for the Kubernetes deployment), without
needing minikube/tilt:
make image
This compiles the newrelic-agent-control-k8s and newrelic-agent-control-k8s-cli binaries and builds both images,
loading them into your local docker daemon as newrelic/newrelic-agent-control:local and
newrelic/newrelic-agent-control-cli:local. ARCH defaults to arm64; on an amd64 host, pass it explicitly, e.g.
make image ARCH=amd64.
Useful variables to override (see the Makefile for the full list): ARCH, IMAGE_TAG,
DOCKER_IMAGE_NAME_AGENT_CONTROL[_CLI], PUSH. For example, to build only the main image with a custom tag:
make image/agent-control IMAGE_TAG=my-test
This same make image/agent-control[-cli] target is used by the CI workflows that build and publish the image, so a
local build failure here is a good early signal that the image build is broken.
See diagnose issues with agent control logging.
Users can disable remote management just by commenting its configuration out from
/etc/newrelic-agent-control/local-data/agent-control/local_config.yaml (on-host):
# fleet_control:
# endpoint: https://opamp.service.newrelic.com/v1/opamp
# signature_validation:
# public_key_server_url: https://publickeys.newrelic.com/r/blob-management/global/agentconfiguration/jwks.json
# headers:
# api-key: API_KEY_HERE
# fleet_id: FLEET_ID_HERE
# auth_config:
# token_url: PLACEHOLDER
# client_id: PLACEHOLDER
# provider: PLACEHOLDER
# private_key_path: PLACEHOLDER
Or by placing enabled: false under the fleet_control section in the Agent Control configuration values (k8s):
# For K8s, inside the Helm values:
agent-control-deployment:
image:
imagePullPolicy: Always
config:
fleet_control:
enabled: false
# ...
There is a service that ultimately exposes a /status endpoint for Agent Control itself. This service performs a series
of checks to determine the output (both in HTTP status code and message):
{
"agent_control": {
"healthy": true
},
"opamp": {
"enabled": true,
"endpoint": "https://opamp.service.newrelic.com/v1/opamp",
"reachable": true
},
"agents": {
"nr-otel-collector": {
"agent_id": "nr-otel-collector",
"agent_type": "newrelic/com.newrelic.opentelemetry.collector:0.1.0",
"healthy": true
},
"nr-infra-agent": {
"agent_id": "nr-infra-agent",
"agent_type": "newrelic/com.newrelic.infrastructure:0.1.1",
"healthy": false,
"last_error": "process exited with code: exit status: 1"
}
}
}
Users need to enable the local server by adding the following setting in the Agent Control configuration file:
server:
enabled: true
# default values (change if needed)
#host: "127.0.0.1"
#port: 51200
For Kubernetes, the status endpoint is enabled by default. You can access this easily by performing a Kubernetes port-forward, using the following commands on separate shells:
$ kubectl port-forward ac-agent-control-6558446569-rtwh4 -n newrelic 51200:51200
Forwarding from 127.0.0.1:51200 -> 51200
Forwarding from [::1]:51200 -> 51200
$ curl localhost:51200/status | jq
# ... contents will appear here formatted and highlighted
Besides the Rust-specific checks (cargo fmt, cargo clippy), CI also lints GitHub Actions workflows, shell scripts and PowerShell scripts. These checks can be run locally too:
make lint/actions # requires actionlint: https://github.com/rhysd/actionlint
make lint/shell # requires shellcheck: https://www.shellcheck.net
make lint/powershell # requires pwsh with the PSScriptAnalyzer module installed
Install shellcheck and actionlint with your package manager of choice (e.g. brew install shellcheck actionlint). For PowerShell, install PowerShell and then the module:
Install-Module -Name PSScriptAnalyzer -Scope CurrentUser
cargo test --workspace --exclude 'newrelic_agent_control' --all-targets
On Windows some of the tests need elevated permissions and are ignored by default but can be executed by adding:
cargo test --workspace --exclude 'newrelic_agent_control' --all-targets -- --include-ignored
We have Makefiles containing targets for testing. Inspect them for more details.
Those Makefiles are not prepared to use Powershell or cmd. They assume bash is present in the system.
On Windows, we can use Git Bash for example. If it’s installed, the following command will start the bash shell.
.'C:\Program Files\Git\bin\bash.exe'
onhostRunning tests for the agent control lib excluding root-required tests (on-host)
make -C agent-control test/onhost
Run tests agent control integration tests excluding root-required tests.
make -C agent-control test/onhost/integration
Running tests that require root user can be not straight-forward, as the Rust toolchain installers like rustup tend to
not install them globally on a system, so doing sudo cargo won’t work. An easy way to run the root-required tests is
spinning up a container where the user is root and running them there with:
make -C agent-control test/onhost/root/integration
k8sRunning basic tests, not requiring an existing Kubernetes cluster.
make -C agent-control test/k8s
For full integration tests using Flux:
make -C agent-control test/k8s/integration
For full integration tests without Flux:
make -C agent-control test/k8s/integration-without-flux
Generate coverage information easily by running the following make recipe from the root directory (will install
cargo-llvm-cov if it’s not installed already):
make coverage
By default, this will generate a report in lcov format on coverage/lcov.info that IDEs such as VSCode can read
via certain extensions. To
modify the output format and the output location, use the variables COVERAGE_OUT_FORMAT and COVERAGE_OUT_FILEPATH:
COVERAGE_OUT_FORMAT=json COVERAGE_OUT_FILEPATH=jcov-info.json make coverage
Heap profiling is supported via the dhat crate and is gated behind the
dhat-heap feature flag. It can be used to detect memory leaks and analyze heap allocations.
To build with heap profiling enabled, use the release-debug profile (which inherits from release but keeps debug
symbols) together with the feature flag:
cargo build -p newrelic_agent_control --profile release-debug --features dhat-heap
Running the resulting binary will:
stderr on exit.dhat-heap.json file to the current working directory (or / when running as a systemd service).Open dhat-heap.json in the DHAT Viewer to inspect the results.
Ad-hoc profiling is also supported via dhat and is gated behind the dhat-ad-hoc feature flag. Unlike heap profiling,
it requires manually annotating the code you want to profile — no annotations are added by default at the time of
writing this. See the dhat ad-hoc profiling documentation
for details on how to instrument your code.
cargo build -p newrelic_agent_control --profile release-debug --features dhat-ad-hoc
You can run heap profiling inside a Kubernetes cluster by using pre-built container images that have the dhat-heap
feature compiled in. A dedicated GitHub Actions workflow (⏯️🚀 Build dhat-heap images to ghcr.io) is available to
build and push multi-arch (linux/amd64, linux/arm64) images to the GitHub Container Registry.
Navigate to Actions → ⏯️🚀 Build dhat-heap images to ghcr.io → Run workflow and run it:
The workflow produces a new image:
ghcr.io/<org>/newrelic-agent-control-dev:dhat-heap-<image-tag>Because the container filesystem is ephemeral, the DHAT report must be written to a persistent volume so it can be
retrieved after the pod exits. Below is an example using a hostPath PersistentVolume suitable for single-node
clusters (adapt the storage class for production environments).
# dhat-pv.yaml
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: agent-control-profiles-pv
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
hostPath:
path: /data/agent-control-profiles
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: agent-control-profiles-pvc
namespace: newrelic-agent-control
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
Apply it:
kubectl apply -f dhat-pv.yaml
Create a helper pod running a basic utility image so you can kubectl exec into it and access the shared volume.
# dhat-debug-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: profile-accessor
namespace: newrelic-agent-control
spec:
containers:
- name: accessor
image: alpine:latest
command: ["sleep", "infinity"]
volumeMounts:
- name: profiles
mountPath: /var/profiles
volumes:
- name: profiles
persistentVolumeClaim:
claimName: agent-control-profiles-pvc
kubectl apply -f dhat-debug-pod.yaml
Update your agent-control-deployment Helm values to use the dhat-heap image and mount the shared volume.
# dhat-helm-values.yaml
# ... other values
agentControlDeployment:
chartValues:
resources:
requests:
memory: "100Mi"
limits:
memory: "1Gi"
image:
registry: ghrc.io
repository: newrelic/newrelic-agent-control-dev
tag: dhat-heap-dev-1
pullPolicy: Always
extraEnv:
- name: DHAT_HEAP_OUTPUT_PATH
value: "/var/profiles/dhat-heap.json"
extraVolumes:
- name: profiles
persistentVolumeClaim:
claimName: agent-control-profiles-pvc
extraVolumeMounts:
- name: profiles
mountPath: /var/profiles
config:
# ... remaining AC config
# ... other values
Note on memory limits: DHAT serialises the entire heap profile into memory before writing it to disk. The memory limit must be high enough to accommodate this spike.
1Giis a reasonable starting point; you may need to increase it for long-running processes or workloads with heavy allocation.
Upgrade your release:
helm upgrade --install agent-control newrelic/agent-control-deployment \
--namespace newrelic \
--values dhat-helm-values.yaml
Once Agent Control has been stopped or restarted (which triggers the DHAT profiler to write the file on Drop),
retrieve the report from the debug pod:
kubectl exec -n newrelic-agent-control profile-accessor -- ls -la /var/profiles
kubectl cp newrelic-agent-control/profile-accessor:/var/profiles/dhat-heap.json ./dhat-heap.json
Open dhat-heap.json in the DHAT Viewer to inspect the results.
Remove the debug pod and the PersistentVolume when you are done:
kubectl delete -f dhat-debug-pod.yaml
kubectl delete -f dhat-pv.yaml
Codeql is executed automatically in GitHub pipelines, in order to check the results locally you need to install the tool and download rust queries:
❯ brew install codeql
❯ codeql pack download codeql/rust-queries
Build the source-code database:
❯ codeql database create codeql-db \
--language=rust \
--command="cargo build" \
--source-root=. \
--overwrite
Analyze:
❯ codeql database analyze codeql-db \
codeql/rust-queries:codeql-suites/rust-security-extended.qls \
--format=sarif-latest \
--output=results.sarif
Check the results
❯ cat results.sarif | jq '.runs[0].results[] | {ruleId, message: .message.text, location: .locations[0].physicalLocation}'
We maintain separate directories for other documented topics under this docs directory and in other Markdown files
throughout the codebase. The latter will be centralized under the docs directory over time. Feel free to check these
documents and ask doubts or propose changes!