Kubernetes
If your PostgreSQL runs in Kubernetes, under CloudNativePG, Crunchy, Zalando, or a StatefulSet you wrote yourself, run the collector there too, with the official Helm chart.
For a database on your laptop see Local development with Docker. For RDS or Aurora see AWS RDS and Aurora; the AWS path there deploys a Fargate task and does not involve Kubernetes.
Before you start
Section titled “Before you start”- Helm 3.8 or newer. The chart is distributed as an OCI artifact, which older Helm cannot pull.
- Kubernetes 1.25 or newer.
- Collector credentials. In the DBGorilla app, open Collectors → New Collector. It issues an agent ID, a tenant ID and a secret. The secret is shown once and cannot be retrieved again.
- A database login with the right grants. See Grants below.
- A namespace. Not an arbitrary choice; see Choosing a namespace.
The collector opens outbound connections only. It needs your databases on 5432, and
otlp.dbgorilla.com plus auth.dbgorilla.com on 443. The chart renders no Service and no inbound
ports.
The chart models nothing about your database
Section titled “The chart models nothing about your database”You supply a complete collector.toml and the chart writes it to a ConfigMap verbatim, mounted at
/etc/dbg-collector/collector.toml. The chart owns the image, the pod spec, the ServiceAccount and
secret injection, and nothing else. Anything under config: other than inline is rejected at install
rather than quietly ignored.
Grants
Section titled “Grants”Create the dbg_readonly login on each database server the collector watches, exactly as in
Collector Installation.
USAGE and SELECT are per-database. The example below watches
app and orders, so those two grants have to be run twice, once connected to each, and once per
schema within them. Run them in postgres only and the collector reports metrics normally while
capturing nothing.
Point at the primary
Section titled “Point at the primary”Address the read-write service, not a read-only or round-robin one. For a CloudNativePG cluster
named app-pg, that is app-pg-rw, not app-pg-ro or app-pg-r.
Two things break otherwise. pg_stat_replication is empty on a standby, so the collector discovers
no cluster members. And a node in recovery has frozen statistics. Replicas apply changes through
WAL replay, which never moves a counter, so the collector will not use one as its source of
truth.
The collector finds the replicas itself, from the primary. You do not list them.
TLS against an internal CA
Section titled “TLS against an internal CA”Operator-managed PostgreSQL almost always presents a certificate from the operator’s own authority.
Mount that CA into the collector and point ca_cert at it:
extraVolumes: - name: pg-ca secret: secretName: app-pg-ca # CloudNativePG names it <cluster>-ca
extraVolumeMounts: - name: pg-ca mountPath: /etc/dbg-collector-certs readOnly: truessl_mode = "verify-full"ca_cert = "/etc/dbg-collector-certs/ca.crt"If you would rather not manage a CA bundle, ssl_mode = "require" is the documented fallback:
encrypted, unverified, and reasonable inside a cluster you control.
Choosing a namespace
Section titled “Choosing a namespace”Two constraints usually decide this for you, and both are easier to satisfy than to work around.
A Secret cannot be mounted across namespaces. If you mount the database CA, the collector must run in the namespace holding it, or you copy that Secret, which then drifts when the operator rotates its CA.
NetworkPolicy. If your database namespace runs a default-deny policy, the collector must be admitted by it. Many policies admit by namespace; some admit only pods carrying a particular label.
Running the collector in the same namespace as the database satisfies both constraints at once, and is the shortest path for a single cluster.
Turn off node_exporter for containerized databases
Section titled “Turn off node_exporter for containerized databases”A database in a pod has nothing listening on port 9100, and the default auto mode spends the full
probe timeout on every node on every discovery pass. Turn it off unless your databases run on hosts
you have put node_exporter on:
[component.provider.node_exporter]mode = "disabled"Install
Section titled “Install”collector.toml:
[dbgorilla]agent_id = "<agent id from the app>"tenant_id = "<tenant id from the app>"secret = { env = "DBG_SERVER_SECRET" }
[topology]interval = "60s"
[commands]enabled = false
[[component]]name = "app-pg"engine = "postgres"
[component.provider]type = "self_hosted"
[component.provider.node_exporter]mode = "disabled"
[component.auth]method = "password"user = "dbg_readonly"password = { env = "APP_PG_PASSWORD" }
[component.connect]host = "app-pg-rw.databases.svc.cluster.local"port = 5432ssl_mode = "verify-full"ca_cert = "/etc/dbg-collector-certs/ca.crt"databases = ["app", "orders"]values.yaml:
secrets: serverSecret: "<secret from the app>" databasePasswords: APP_PG_PASSWORD: "<the dbg_readonly password>"
extraVolumes: - name: pg-ca secret: secretName: app-pg-ca
extraVolumeMounts: - name: pg-ca mountPath: /etc/dbg-collector-certs readOnly: trueThen:
helm install dbg-collector oci://dbgorillapublic.azurecr.io/charts/dbg-collector \ --namespace databases \ --values ./values.yaml \ --set-file config.inline=./collector.tomlvalues.yaml holds live secrets. collector.toml does not, as long as you kept the { env = … }
references above rather than pasting literals into it. Keep values.yaml out of version control,
or supply secrets.existingSecret
and manage the Secret yourself, with an ExternalSecret, for instance. If you do, it must contain
the DBG_SERVER_SECRET key plus every database password key your TOML references; the chart cannot
read your config, so it cannot derive those names for you.
The image comes from dbgorillapublic.azurecr.io, which allows anonymous pulls. No image pull
secret is needed.
Check it worked
Section titled “Check it worked”kubectl -n databases logs -f deploy/dbg-collectorFour lines say it is healthy:
opamp websocket connectedopamp handshake completeotelcol starteddiscovered componentThen look at what it found. For a three-node cluster you want three nodes: one writer and two
readers. One node where you expect three points at the pg_monitor grant.
This warning means one node cannot be reached:
no instance baseline and a node is unreachable; deferring topology post until all nodes are probedThe collector holds back the whole component until every node answers, so a single unreachable
replica means no data at all, including from the primary. Usual causes are a NetworkPolicy that
admits the collector to the primary’s service but not to individual pods, or a ca_cert path that
does not exist in the container.
Several databases, one collector
Section titled “Several databases, one collector”Add a [[component]] block per database server. One collector can watch as many as it can reach,
each with its own credentials and TLS settings:
[[component]]name = "app-pg"# …
[[component]]name = "billing-pg"# …Give each component’s password its own environment variable name and list them all under
secrets.databasePasswords.
Upgrading and removing
Section titled “Upgrading and removing”helm upgrade dbg-collector oci://dbgorillapublic.azurecr.io/charts/dbg-collector \ --namespace databases \ --values ./values.yaml \ --set-file config.inline=./collector.toml
helm uninstall dbg-collector --namespace databaseshelm uninstall removes the workload but does not deprovision the collector’s identity in
DBGorilla. To retire a collector permanently, delete it in the app as well, and note that a new
install always means a new collector, never the old one resumed.