Accessing Metrics (scraping setup)¶
Audience: SRE, Platform engineer
Part of the Observability guide. See also: Metrics reference · Alerting & SLOs · Grafana dashboards · Logging & tracing.
How to Access Metrics¶
Port forward (ad-hoc): the per-tenant AGC/proxy metrics ports require a client
certificate (mTLS), so a plain curl is rejected at the TLS handshake. Present the
published scraper bundle (see the per-tenant section
for the Secret name and an end-to-end curl --cert/--key/--cacert example).
Prometheus operator (production): the chart wires scraping automatically when
metrics.serviceMonitor.enabled=true — both the GMC manager ServiceMonitor and a
per-tenant ServiceMonitor for each provisioned tenant's AGC/proxy. The metrics port
is named metrics (:8443) on every Service. You do not hand-author these
ServiceMonitors; the sections below describe what they wire and the prerequisites.
Install-time scraping prerequisites (GMC manager)¶
The default GMC install ships the manager NetworkPolicy enabled by default
(networkPolicy.enabled=true). Selecting the manager pod flips it to default-deny on
ingress, so its /metrics endpoint admits traffic only from namespaces carrying
the right label:
- Scraping the GMC manager metrics: label your Prometheus namespace
metrics: enabled, or no scrape will reach the manager:
The validating-webhook port (container 9443) is intentionally re-allowed from
any source — the kube-apiserver that calls it is not a pod in a labeled
namespace, so a source restriction there would silently break every
ActionsGateway admission (failurePolicy: Fail). The webhook is TLS +
caBundle authenticated, so the sensitive surface stays the metrics: enabled
restriction above. No namespace label is required for CR admission.
This applies to the GMC manager only. The per-tenant AGC and proxy metrics are governed by the per-tenant NetworkPolicies the GMC generates (the AGC and proxy NPs already admit monitoring-namespace scrapes of the metrics port) and use mutual TLS — see Scraping per-tenant AGC and proxy metrics (mTLS).
Runtime enforcement of these policies depends on the CNI; kindnet's
kube-network-policiesdoes not drop all egress negatives (see the worker egress limitation in troubleshooting.md). The manager NP is verified by manifest review and is pending a Tier-A runtime check.
The ServiceMonitor integration stays opt-in, behind the
metrics.serviceMonitor.enabled chart value (default false): out-of-box
Prometheus Operator scraping. It is left off by default because the
ServiceMonitor CRD only exists once the Prometheus Operator is installed, so
rendering it unconditionally would break helm install on clusters without it.
Verifying the metrics scrape TLS (GMC manager)¶
The GMC metrics endpoint (:8443) is served over TLS. How the scrape verifies
that certificate is controlled by metrics.tls.certManager.enabled:
true(default, secure): cert-manager issues a dedicated metrics serving cert — themetrics-serving-certCertificate, minted from the sameselfsigned-issueras the webhook into themetrics-server-certSecret. The GMC serves it (--metrics-cert-path), and the renderedServiceMonitorverifies it against the issuing CA:
tlsConfig:
serverName: <namePrefix>-controller-manager-metrics-service.<namespace>.svc
ca:
secret:
name: metrics-server-cert
key: ca.crt
The scrape is authenticated end-to-end and not MITM-able. This path
requires cert-manager (it reuses the webhook's Issuer) and is automatically
inert when certManager.enabled=false.
false, orcertManager.enabled=false: the GMC falls back to controller-runtime's auto-generated self-signed metrics cert, and theServiceMonitorscrapes withtlsConfig.insecureSkipVerify: true. Prometheus cannot verify the server, so an in-cluster attacker who can intercept the scrape connection could impersonate the metrics endpoint (the bearer token still authenticates the scraper to the server, but not the server to the scraper). Use this only on clusters without cert-manager, accepting the weaker posture.
The metrics-server-cert Secret is read from the ServiceMonitor's namespace
(the GMC release namespace), where the chart creates it — no extra copying is
needed. Because verification follows certManager.enabled by default, an
install that already uses cert-manager for the webhook (the default) gets
verified metrics scraping automatically once the ServiceMonitor is enabled.
Scraping per-tenant AGC and proxy metrics (mTLS)¶
Each provisioned tenant runs its own AGC and egress-proxy pods, which serve
/metrics over mutual TLS on :8443. Unlike the GMC manager, the listener
requires a client certificate signed by that tenant's metrics CA — there is
no bearer-token or insecureSkipVerify fallback. Three things are wired
per tenant so Prometheus can scrape them:
- Metrics Services (always created). The GMC creates a
metrics-named:8443port on the proxyService(actions-gateway-proxy) and a dedicated AGCService(actions-gateway-controller), both in the tenant namespace. These exist regardless of the scrape toggle. - Per-tenant
ServiceMonitors (opt-in). Whenmetrics.serviceMonitor.enabled=true, the GMC also creates oneServiceMonitorper component in the tenant namespace (actions-gateway-proxy-metrics,actions-gateway-controller-metrics). Each selects only its own component's Service via the tenant's owner labels, so one tenant's monitor never selects another tenant's pods. - The scraper client bundle (mTLS). Each
ServiceMonitorpresents the per-tenant scraper client bundle from theactions-gateway-metrics-clientSecret in the tenant namespace —tls.crt/tls.keyauthenticate the scraper to the listener andca.crtverifies the listener's server cert.serverNameis the component's<service>.<namespace>.svcDNS name (a SAN on the server cert), so the scrape is verified end-to-end and not MITM-able:
tlsConfig:
serverName: actions-gateway-proxy.<tenant-namespace>.svc
ca: { secret: { name: actions-gateway-metrics-client, key: ca.crt } }
cert: { secret: { name: actions-gateway-metrics-client, key: tls.crt } }
keySecret: { name: actions-gateway-metrics-client, key: tls.key }
v2 EgressProxy proxy metrics¶
The names above are the classic/v1 layout, where a single proxy pool per
namespace uses fixed names. Under the v2 API, the egress proxy is a standalone
EgressProxy resource (multiple per namespace are allowed), so its proxy-metrics
objects are keyed on the EgressProxy name <ep> instead:
| Object | Classic/v1 | v2 EgressProxy |
|---|---|---|
Proxy Service (metrics port :8443) |
actions-gateway-proxy |
<ep>-proxy |
| Server bundle Secret (mounted into the proxy) | actions-gateway-metrics-tls |
<ep>-metrics-tls |
| Scraper client bundle Secret (published) | actions-gateway-metrics-client |
<ep>-metrics-client |
ServiceMonitor |
actions-gateway-proxy-metrics |
<ep>-proxy-metrics |
Each EgressProxy owns its own metrics CA — distinct from the AGC's and from
every other EgressProxy — so Prometheus must read that proxy's own
<ep>-metrics-client bundle, with serverName: <ep>-proxy.<namespace>.svc. The
same toggle (metrics.serviceMonitor.enabled → --enable-tenant-service-monitors),
the same metrics: enabled NetworkPolicy prerequisite, and the same graceful
ServiceMonitorCRDMissing handling apply; the AGC metrics under v2 keep their own
per-gateway <gateway>-agc-metrics-{tls,client} bundle.
Prerequisites:
- Prometheus Operator must be installed (the
monitoring.coreos.comServiceMonitorCRD must exist). The toggle is off by default precisely because the CRD is not present on every cluster. If the CRD is absent when the toggle is on, the GMC logs a warning and emits aServiceMonitorCRDMissingEvent on theActionsGatewayand continues — a missing scrape prerequisite never blocks tenant provisioning. - Prometheus reads the client bundle from the
ServiceMonitor's namespace (the tenant namespace), so the scraping Prometheus must be configured to selectServiceMonitors across tenant namespaces (serviceMonitorNamespaceSelector) and granted read access to the per-tenantactions-gateway-metrics-clientSecret. Each tenant has a distinct CA and client cert, which is why a single cluster-wideServiceMonitorcannot scrape them — the wiring is necessarily per tenant. - NetworkPolicy: label the Prometheus namespace
metrics: enabledso the per-tenant NetworkPolicy admits the scrape (the AGC and proxy policies admit the:8443metrics port only frommetrics=enablednamespaces).
Ad-hoc verification (mounting the published bundle locally):
ns=<tenant-namespace>
kubectl get secret actions-gateway-metrics-client -n "$ns" \
-o jsonpath='{.data.tls\.crt}' | base64 -d > client.crt
kubectl get secret actions-gateway-metrics-client -n "$ns" \
-o jsonpath='{.data.tls\.key}' | base64 -d > client.key
kubectl get secret actions-gateway-metrics-client -n "$ns" \
-o jsonpath='{.data.ca\.crt}' | base64 -d > ca.crt
kubectl port-forward -n "$ns" svc/actions-gateway-controller 8443:8443 &
curl --cert client.crt --key client.key --cacert ca.crt \
https://actions-gateway-controller.$ns.svc:8443/metrics --resolve \
"actions-gateway-controller.$ns.svc:8443:127.0.0.1"
rm -f client.crt client.key ca.crt # delete the cert material when done
(The bundle is a client certificate, not a long-lived account credential; still remove the files when finished.)
← Back to Observability