Install a probe on Kubernetes
A probe inside the cluster watches its workloads through the apiserver with its own ServiceAccount — no token to create, no kubeconfig to copy — and runs URL, API, TCP and database checks from the cluster network itself. The Helm chart installs it as a single-replica StatefulSet that registers itself with your Webserver on its first start.
What the in-cluster probe runs
The image is the slim probe: no browsers.
| Runs | Does not run |
|---|---|
| Container hosts (this cluster, other clusters, Docker hosts), URL, API, TCP, UDP, DNS, database, WebSocket, SNMP | EUM / Web UI browser journeys |
Ping, once icmp.enabled=true |
Ping without icmp.enabled — ICMP needs the NET_RAW capability |
Keep a VM or Windows probe for browser journeys.
Before you start
- Kubernetes 1.24 or later, and Helm 3.
- A Webserver API address the pod can reach, as
<host>:<port>— a DNS name works. - The
_sharedInternalvalue from the Webserver’swebserver.json. Every component must carry the same one. - The chart, from the probe release archive:
deploy/helm/mugnsoft-probe. - The image
mugnsoft/monitor:<version>-slimin a registry the cluster can pull from. To build it yourself, from the probe sources:
docker build -f deploy/docker/Dockerfile --build-arg VERSION=4.2.0 -t registry.example.com/mugnsoft/monitor:4.2.0-slim .
docker push registry.example.com/mugnsoft/monitor:4.2.0-slim
Choose how the Webserver reaches the probe
The Webserver always calls the probe on its API port, over HTTPS. Where the Webserver runs decides the Service type:
| Webserver runs | service.type |
The Webserver dials | You set |
|---|---|---|---|
| In the same cluster | ClusterIP (default) |
<release>-mugnsoft-probe.<namespace>.svc.cluster.local:8051 |
nothing |
| Outside, nodes reachable | NodePort |
a node address and the node port | advertise.address, service.nodePort |
| Outside, through a load balancer | LoadBalancer |
the balancer’s DNS name or IP | advertise.address, once the balancer exists |
Install
A Webserver inside the cluster:
helm install probe-k8s ./mugnsoft-probe \
--namespace mugnsoft --create-namespace \
--set probe.webserver=webserver.mugnsoft.svc.cluster.local:8050 \
--set probe.sharedInternal='<the _sharedInternal value>' \
--set probe.location=eu-west-1
A Webserver outside, through a load balancer restricted to the Webserver’s address:
helm install probe-k8s ./mugnsoft-probe \
--namespace mugnsoft --create-namespace \
--set probe.webserver=mugnsoft.example.com:8050 \
--set probe.sharedInternal='<the _sharedInternal value>' \
--set service.type=LoadBalancer \
--set 'service.loadBalancerSourceRanges={203.0.113.10/32}' \
--set advertise.address=probe-k8s.example.com
With NodePort, add --set service.type=NodePort --set service.nodePort=30851 --set advertise.address=<a node address>.
Keep your settings in a values file rather than on the command line for upgrades: helm upgrade probe-k8s ./mugnsoft-probe -n mugnsoft -f probe-values.yaml.
Approve the probe
The probe registers itself as soon as the Webserver answers, retrying from 15 seconds to every 5 minutes while it does not. Watch it happen:
kubectl -n mugnsoft logs statefulset/probe-k8s-mugnsoft-probe | grep autoRegister
autoRegister - registered probe-k8s-mugnsoft-probe with webserver.mugnsoft.svc.cluster.local:8050; approve it on the Webserver's Components page
Then enable it on the Webserver’s Components page, like any other probe. Until then it is running and healthy, but idle.
Monitor this cluster from it
Add a container host on the Webserver:
| Field | Value |
|---|---|
| Runtime | Kubernetes |
| Push to Probes | the probe you just approved |
| Endpoint | leave empty |
| Auth mode | inCluster |
| Namespace | empty — or the release namespace with rbac.scope=namespace |
The probe uses the ServiceAccount token Kubernetes mounts into the pod, which the kubelet rotates; it is re-read on every check. See Container monitoring for discovery, grading and limits.
Values
| Key | Default | Description |
|---|---|---|
probe.webserver |
required | The Webserver API as <host>:<port> |
probe.name |
the release’s full name | Component name on the Webserver; underscores become hyphens |
probe.sharedInternal |
"" |
The shared machine-account secret; must match the Webserver’s |
probe.existingSecret |
"" |
A Secret with a monitor.json key, used instead of rendering one |
probe.location, probe.description |
"", Kubernetes in-cluster probe |
Shown on the Webserver |
probe.port |
8051 |
The port the probe listens on |
probe.autoRegister |
true |
Register on the first start of an empty volume |
probe.settings |
{} |
Extra monitor.json settings, such as dataRetention |
advertise.address |
the Service DNS name | What the Webserver dials; required with NodePort and LoadBalancer |
advertise.port |
service.nodePort, else service.port |
The port the Webserver dials |
service.type |
ClusterIP |
ClusterIP, NodePort or LoadBalancer |
service.port, service.nodePort |
8051, "" |
Service ports |
service.loadBalancerSourceRanges |
[] |
Who may reach the probe through a load balancer |
service.annotations |
{} |
For cloud load balancer options, such as an internal balancer |
rbac.scope |
cluster |
cluster for every namespace and the nodes, namespace for the release namespace only |
persistence.enabled, persistence.size |
true, 2Gi |
The volume holding keys, certificate and databases |
persistence.storageClass |
"" |
The cluster default when empty |
icmp.enabled |
false |
Run as root with NET_RAW so ping monitors work |
image.repository, image.tag |
mugnsoft/monitor, <appVersion>-slim |
The probe image |
resources |
100m CPU, 128Mi requested; 512Mi limit | Container resources |
extraEnv |
[] |
Additional environment variables |
What the chart creates
| Object | Purpose |
|---|---|
| StatefulSet (1 replica) | The probe. One replica is one registered component; a second would be a second identity under the same name |
| PersistentVolumeClaim | Keys, certificate and databases, so a restarted pod keeps its identity and history |
| Service and a headless Service | The address the Webserver dials, and the StatefulSet’s stable pod DNS |
| Secret | monitor.json, including the shared secret |
| ServiceAccount, ClusterRole (or Role) and binding | Read-only access: get and list on pods, events, nodes, namespaces, deployments, statefulsets, daemonsets, cronjobs, jobs and pod and node metrics |
Security
- The container runs as a non-root user (UID 10001) with a read-only root filesystem, no privilege escalation, every capability dropped and the
RuntimeDefaultseccomp profile.icmp.enabledis the only exception: root withNET_RAWandDAC_OVERRIDEonly. - The ServiceAccount can read and nothing else — it cannot change the cluster.
rbac.scope=namespacenarrows it to one namespace. Node health is then unavailable.- Put the probe API behind
service.loadBalancerSourceRangesor a network policy when it is exposed outside the cluster.
Troubleshooting
| Symptom | Cause and fix |
|---|---|
The log repeats could not register ... retrying |
The pod cannot reach probe.webserver: DNS name, port, or an egress network policy |
the Webserver refused ...: a server is already defined with this name |
A component of that name already exists — a previous install, or a volume that was deleted. Delete the old component on the Webserver, or set probe.name, then restart the pod |
| Registered and approved, but the Webserver cannot reach it | The advertised address is not reachable from the Webserver: check service.type and advertise.address, then correct the component’s IP and port on the Webserver |
Ping monitors fail with operation not permitted |
Set icmp.enabled=true |
| The pod restarted and asks to register again | Persistence is disabled. Enable it; an empty volume is a new identity |
Limitations
- No EUM browser journeys in the slim image.
- One replica per release. For redundancy, install a second release under another name and push the same monitors to both probes.
- Changing the advertised address after registration is manual, on the Webserver.
- The probe API is served over HTTPS with a self-signed certificate.