Architecture
The exporter solves common problems with running headless browsers in containers.
Background Worker Pattern
A common problem with Prometheus exporters that use headless browsers is scrape timeouts. If the exporter only starts the browser when Prometheus asks for /metrics, a slow website can cause a timeout (usually 10-15s). This drops the metrics.
To fix this, the exporter uses a background worker:
- Startup:
playwright.Install()runs once to download the browser. - Loop: A Go
time.Tickerruns the checks on a set schedule (like every 60 seconds). - Fast Scrape: When Prometheus scrapes
/metrics, the server returns the cached metrics. It does not start a browser during the scrape. This makes the response very fast.
Docker & Process Management
Running headless Chromium in Docker can be hard because of zombie processes. When Chromium opens a new tab, it creates child processes. If the main process stops without cleaning them up, they become "zombies". This uses up PID slots and can crash the container.
The Dockerfile handles this with a few techniques:
dumb-init(PID 1): We use dumb-init. It cleans up zombie processes and sends Linux signals (SIGTERM,SIGINT) to the Go app.- Multi-Stage Build: The Go app is compiled in an Alpine stage to keep the image small.
- Official Base Image: The runtime image uses
mcr.microsoft.com/playwright. This includes all system libraries like fonts. - Non-root User: The app runs as a non-root user (
uid 10001) for better security.
Resource Limits & Memory Management
Headless browsers use a lot of memory and CPU, especially on heavy JavaScript sites. Since the background worker runs UI scenarios often, you must set resource limits to avoid Out Of Memory (OOM) errors.
Best Practices for CPU/RAM Limits
- Memory (RAM): Chromium uses hundreds of megabytes of RAM per page. We recommend a minimum limit of 512Mi to 1Gi for the container, depending on your scenarios.
- CPU: Browser checks need CPU. Use at least 0.5 to 1 full CPU core to avoid timeouts.
Example Kubernetes Resources:
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1000m"If your container restarts or you see Exit Code 137, it is probably killed by OOM because the memory limit is too low.
