-
Notifications
You must be signed in to change notification settings - Fork 84
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
docs: Add usage example for multi proc behavior
- Loading branch information
Showing
3 changed files
with
112 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
# Example `prom-multi-proc-gunicorn` | ||
|
||
Minimal example that shows integration of FastAPI (Gunicorn) with the Prometheus | ||
client library in multi process mode without Prometheus FastAPI Instrumentator. | ||
Highlights missing metrics that are not supported in multi process mode. | ||
|
||
To run the example, you must have run `poetry install` and `poetry shell` in the | ||
root of this repository. The following commands are executed relative to this | ||
directory. | ||
|
||
Set environment variable to an unused location: | ||
|
||
```shell | ||
export PROMETHEUS_MULTIPROC_DIR=/tmp/python-testing-pfi/560223ba-887f-429a-9c48-933df56a68ba | ||
``` | ||
|
||
Start the app with Gunicorn using two Uvicorn workers: | ||
|
||
```shell | ||
rm -rf "$PROMETHEUS_MULTIPROC_DIR" | ||
mkdir -p "$PROMETHEUS_MULTIPROC_DIR" | ||
gunicorn main:app \ | ||
--config gunicorn.conf.py \ | ||
--workers 2 \ | ||
--worker-class uvicorn.workers.UvicornWorker \ | ||
--bind 0.0.0.0:8080 | ||
``` | ||
|
||
Interact with app: | ||
|
||
```shell | ||
for i in {1..5}; do curl localhost:8080/ping; done | ||
curl localhost:8080/metrics | ||
``` | ||
|
||
You should see something like this: | ||
|
||
```txt | ||
# TYPE ping_total counter | ||
ping_total 5.0 | ||
# HELP metrics_total Number of metrics calls. | ||
# TYPE metrics_total counter | ||
metrics_total 1.0 | ||
# HELP main_total Counts of main executions. | ||
# TYPE main_total counter | ||
main_total 2.0 | ||
``` | ||
|
||
Check the returned metrics: | ||
|
||
- `main_total` is `2`, because Gunicorn is using two workers. | ||
- There are no `created_by` metrics. These are not supported by the Prometheus | ||
client library in multi process mode. | ||
- No metrics for things like CPU and memory. They come from components like the | ||
`ProcessCollector` and `PlatformCollector` which are not supported by the | ||
Prometheus client library in multi process mode. | ||
|
||
Links: | ||
|
||
- <https://github.com/prometheus/client_python#multiprocess-mode-eg-gunicorn> | ||
- <https://docs.gunicorn.org/en/stable/settings.html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from prometheus_client import multiprocess | ||
|
||
|
||
def child_exit(server, worker): | ||
multiprocess.mark_process_dead(worker.pid) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import os | ||
|
||
from fastapi import FastAPI | ||
from prometheus_client import ( | ||
CONTENT_TYPE_LATEST, | ||
CollectorRegistry, | ||
Counter, | ||
generate_latest, | ||
multiprocess, | ||
) | ||
from starlette.responses import Response | ||
|
||
if "PROMETHEUS_MULTIPROC_DIR" not in os.environ: | ||
raise ValueError("PROMETHEUS_MULTIPROC_DIR must be set to existing empty dir.") | ||
|
||
|
||
PING_TOTAL = Counter("ping", "Number of pings calls.") | ||
METRICS_TOTAL = Counter("metrics", "Number of metrics calls.") | ||
|
||
MAIN_TOTAL = Counter("main", "Counts of main executions.") | ||
MAIN_TOTAL.inc() | ||
|
||
|
||
app = FastAPI() | ||
|
||
|
||
@app.get("/ping") | ||
def get_ping(): | ||
PING_TOTAL.inc() | ||
return "pong" | ||
|
||
|
||
@app.get("/metrics") | ||
def get_metrics(): | ||
METRICS_TOTAL.inc() | ||
|
||
# Note the ephemeral registry being used here. This follows the Prometheus | ||
# client library documentation. It comes with multiple caveats. Using a | ||
# persistent registry might work on first glance but it will lead to issues. | ||
# For a long time PFI used a persistent registry, which was wrong. | ||
registry = CollectorRegistry() | ||
multiprocess.MultiProcessCollector(registry) | ||
resp = Response(content=generate_latest(registry)) | ||
|
||
resp.headers["Content-Type"] = CONTENT_TYPE_LATEST | ||
return resp |