Skip to content

Commit f455fd5

Browse files
svallerue2b-bot[bot]
authored andcommitted
feat(embed): point the stack at an OpenTelemetry collector with one setting
GitOrigin-RevId: 59edca760c98daf8c8042b5c8bd4cf62f690139e
1 parent d13ee7e commit f455fd5

22 files changed

Lines changed: 1039 additions & 72 deletions

‎embed/Makefile‎

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
SHELL := /bin/bash
2-
.PHONY: lint config test images stores-check vector-validate sync-configs kubernetes-render terraform-validate
2+
.PHONY: lint config test images stores-check vector-validate otel-validate sync-configs kubernetes-render terraform-validate
33

44
# Every compose invocation runs against compose/, where compose.yaml and .env
55
# live; the other two install shapes have their own directories beside it.
@@ -14,7 +14,7 @@ BATS_FILES := $(strip $(wildcard tests/*.bats))
1414
config:
1515
$(COMPOSE) config -q
1616

17-
lint: config vector-validate kubernetes-render terraform-validate
17+
lint: config vector-validate otel-validate kubernetes-render terraform-validate
1818
@if [ -n "$(strip $(SHELL_FILES))" ]; then shellcheck -x $(SHELL_FILES); fi
1919
@if [ -n "$(strip $(BATS_FILES))" ]; then shellcheck -x -s bash $(BATS_FILES); fi
2020

@@ -38,9 +38,20 @@ vector-validate:
3838
docker run --rm -v "$(CURDIR)/compose/config/vector/vector.toml:/etc/vector/vector.toml:ro" \
3939
timberio/vector:0.51.1-alpine validate --no-environment /etc/vector/vector.toml
4040

41-
# After editing compose/config/clickhouse/config.xml or
42-
# compose/config/vector/vector.toml: rewrite the inline copies in
43-
# compose/compose.yaml and their SHA-256 stamps.
41+
# The collector parses the config and builds every pipeline without starting
42+
# them, so a pipeline naming a component the config does not define fails
43+
# here rather than on a host. The image is the one compose.yaml pins, and the
44+
# Kubernetes copy of the config is this file as is.
45+
OTEL_IMAGE := $(shell sed -n 's/^ image: \(otel\/opentelemetry-collector-contrib:[^ ]*\)$$/\1/p' compose/compose.yaml)
46+
otel-validate:
47+
@test -n "$(OTEL_IMAGE)" || { echo "otel-validate: compose/compose.yaml pins no otel/opentelemetry-collector-contrib image" >&2; exit 1; }
48+
docker run --rm -v "$(CURDIR)/compose/config/otel/otel-collector.yaml:/etc/otelcol-contrib/config.yaml:ro" \
49+
$(OTEL_IMAGE) validate --config=/etc/otelcol-contrib/config.yaml
50+
51+
# After editing compose/config/clickhouse/config.xml,
52+
# compose/config/vector/vector.toml or compose/config/otel/otel-collector.yaml:
53+
# rewrite the inline copies in compose/compose.yaml, their SHA-256 stamps and
54+
# the Kubernetes copies under kubernetes/config/.
4455
sync-configs:
4556
python3 compose/scripts/dev/sync-configs.py
4657

‎embed/README.md‎

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ deployment see [e2b.dev/enterprise](https://e2b.dev/enterprise).
5656
each one. Those reach the sandbox through the header routing above, at
5757
the address in `E2B_DASHBOARD_HOST` (default `localhost`); set it when a
5858
browser on another machine opens the dashboard without a tunnel.
59+
- **OpenTelemetry out.** One setting sends the E2B services' metrics, traces
60+
and logs to your collector. Point it at the built-in one, a second line
61+
away, to keep the metrics the dashboard's charts draw. The
62+
[reference](docs/REFERENCE.md#observability) says what each choice gives
63+
you.
5964
- **One version everywhere.** Embed is released at the platform version once
6065
that release is tagged, and that release moves every platform pin in
6166
[`compose/.env`](compose/.env) and the kustomization. To pin an install, pin
@@ -90,9 +95,9 @@ network edge.
9095
| 5109 | api | the machine only | edge gRPC |
9196

9297
Port 5008 is the one to guard most: nothing authenticates it, and anyone who
93-
reaches it has the whole orchestrator. The stores, Vector and the pprof
94-
endpoints stay on loopback; [What runs where](docs/REFERENCE.md#what-runs-where)
95-
lists them.
98+
reaches it has the whole orchestrator. The stores, Vector, the built-in
99+
collector and the pprof endpoints stay on loopback;
100+
[What runs where](docs/REFERENCE.md#what-runs-where) lists them.
96101

97102
## Not supported
98103

@@ -116,13 +121,14 @@ lists them.
116121
tests.
117122
- `make test` runs the bats suite in `tests/`.
118123
- `make stores-check` runs the store-level integration check.
119-
- `make sync-configs` re-inlines the 2 configs into the compose file.
124+
- `make sync-configs` re-inlines the 3 configs into the compose file.
120125

121126
What each target needs, and when the stack images have to be rebuilt, is in
122127
[Developing](docs/REFERENCE.md#developing).
123128

124129
## Reference
125130

126131
[`docs/REFERENCE.md`](docs/REFERENCE.md) has the rest: what runs where and in
127-
which order, how logs reach ClickHouse, the secrets each shape holds, how
128-
images and pins are released, building templates, and the developer tooling.
132+
which order, how logs and metrics reach ClickHouse, the secrets each shape
133+
holds, how images and pins are released, building templates, and the
134+
developer tooling.

‎embed/compose/.env‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ RUNTIME_COMMIT=7278c2a380767c9989da73cf4c04b1af1b32da18
2929
#SANDBOX_ACCESS_TOKEN_HASH_SEED=
3030
# Optional: the address a browser uses to reach sandbox traffic through client-proxy (port 3002). Unset, the dashboard tells the browser http://localhost:3002, which is right when you open the dashboard on the machine itself or through an SSH tunnel. Opening it from another machine without a tunnel needs that machine's address here, then `up` again.
3131
#E2B_DASHBOARD_HOST=
32+
# Optional: the host:port of an OpenTelemetry collector that api, the orchestrator, client-proxy and dashboard-api send their metrics, traces and logs to over OTLP/gRPC (plaintext). Unset or empty, nothing is exported. 127.0.0.1:4317 is the built-in collector the next line starts; for a collector of your own, put its address here and leave the next line commented. Change either, then `up` again; to stop the built-in collector, comment both lines out and run `docker compose rm -sf otel-collector` before `up`.
33+
#E2B_OTEL_COLLECTOR_GRPC_ENDPOINT=127.0.0.1:4317
34+
# Optional: run the built-in collector (the otel-collector service), which keeps the e2b.* metrics in this stack's ClickHouse for the api's metrics endpoints and the dashboard's charts. Compose reads this variable itself.
35+
#COMPOSE_PROFILES=otel
3236

3337
# The three stack images this package builds, published by the same release;
3438
# tests/pins.bats keeps them in step with kubernetes/kustomization.yaml and

‎embed/compose/README.md‎

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,8 @@ refill loop spins and logs `no free slots` every few seconds on an idle host.
179179
`FORCE_REBUILD=1`, settable in the
180180
same two places, forces `base-template` to rebuild even when a `base` row
181181
already shows `ready`, for a stale or broken template. `TEAM_API_KEY`,
182-
`ADMIN_TOKEN` and `SANDBOX_ACCESS_TOKEN_HASH_SEED` are under Secrets below.
182+
`ADMIN_TOKEN` and `SANDBOX_ACCESS_TOKEN_HASH_SEED` are under Secrets below,
183+
and `E2B_OTEL_COLLECTOR_GRPC_ENDPOINT` under Telemetry.
183184

184185
An optional, git-ignored `env/api.local.env` can add api variables. Create
185186
the `env/` directory next to `compose.yaml` yourself, since the two-file
@@ -329,14 +330,44 @@ for the api process only. Read them from the volume instead:
329330
`docker compose run --rm --no-deps --entrypoint cat
330331
api-secrets /run/e2b/api.env`.
331332

333+
### Telemetry
334+
335+
Nothing is exported by default, so the dashboard's monitoring charts and the
336+
SDK's `getMetrics` (`get_metrics` in Python) stay empty. To run the built-in
337+
OpenTelemetry collector, which keeps those metrics in this stack's
338+
ClickHouse, uncomment the two lines that ship commented at the end of the
339+
optional block in `.env`:
340+
341+
```bash
342+
E2B_OTEL_COLLECTOR_GRPC_ENDPOINT=127.0.0.1:4317
343+
COMPOSE_PROFILES=otel
344+
```
345+
346+
Then run `docker compose up -d --wait`, which starts `otel-collector` and
347+
recreates the four services that export to it; recreating the orchestrator
348+
ends the sandboxes running on it. While the collector runs,
349+
`curl -s 127.0.0.1:13133` answers `Server available`, and once a sandbox has
350+
been running for a few seconds its rows show up:
351+
`docker compose exec clickhouse clickhouse-client --user clickhouse --password clickhouse --query "SELECT count() FROM sandbox_metrics_gauge"`.
352+
353+
For a collector of your own, uncomment only the first line and put that
354+
collector's address in it. To turn the built-in one off again, comment both
355+
lines out, run `docker compose rm -sf otel-collector` and then
356+
`docker compose up -d --wait`. The reference's
357+
[Observability](../docs/REFERENCE.md#observability) says what each choice
358+
gives you and how to forward traces and logs as well.
359+
332360
### Upgrading
333361

334362
Download `compose.yaml` and `.env` again, with the same command as
335-
[Install](#install), and run `docker compose up -d --wait --pull always`. The
336-
`.env` you get pins the stack images that go with those files, so the two
337-
always move together. To take a particular commit rather than the newest, put
338-
the commit in place of `main` in the two URLs, as in
339-
`raw.githubusercontent.com/e2b-dev/runtime/<commit>/embed/compose/…`; the raw
363+
[Install](#install), and run `docker compose up -d --wait --pull always`.
364+
Before that `up`, carry the optional lines you had set, such as
365+
`E2B_DASHBOARD_HOST` or the two under [Telemetry](#telemetry), over into the
366+
new `.env`: it ships them commented again, and without the Telemetry pair the
367+
services stop exporting. The `.env` you get pins the stack images that go with
368+
those files, so the two always move together. To take a particular commit
369+
rather than the newest, put the commit in place of `main` in the two URLs, as
370+
in `raw.githubusercontent.com/e2b-dev/runtime/<commit>/embed/compose/…`; the raw
340371
host ignores `?ref=`. Binary checksums live inside the tools
341372
image, so bumping a Firecracker artifact means taking newer files rather than
342373
editing anything on the host. Running sandboxes end when the orchestrator
@@ -356,7 +387,9 @@ restarts; a single machine has nowhere to drain them to.
356387
- Expected log noise, all harmless: an OIDC warning from api at startup,
357388
because no identity provider is configured, and, every ten seconds from both
358389
api and the orchestrator, `failed to upload metrics: exporter export
359-
timeout`, because no OTEL collector is configured and the endpoint is empty.
390+
timeout`, because no OpenTelemetry collector is configured and the endpoint
391+
is empty. That line stops once `E2B_OTEL_COLLECTOR_GRPC_ENDPOINT` names a
392+
collector that answers ([Telemetry](#telemetry)).
360393
dashboard-api also logs `ADMIN_AUTH_PROVIDER_CONFIG is not configured` once
361394
at startup; the management endpoints it guards are not used here.
362395
- To run without the dashboard, remove the `dashboard` and `dashboard-api`

‎embed/compose/compose.yaml‎

Lines changed: 127 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
# variable. Without it Compose renders a blank image name or a blank kernel
66
# version and only warns, and the operator meets either a confusing "neither
77
# an image nor a build context" error or a fetch-artifacts failure after the
8-
# host has already been mutated. The eight knobs that are genuinely optional and
9-
# are absent from .env (TEAM_API_KEY, ADMIN_TOKEN, SANDBOX_ACCESS_TOKEN_HASH_SEED
10-
# and E2B_DASHBOARD_HOST only as commented lines) keep the ${VAR:-...} form: HUGEPAGES, NBDS_MAX, PF_MIN_FREE_GIB,
11-
# FORCE_REBUILD and E2B_DASHBOARD_HOST carry a working default, while
8+
# host has already been mutated. The nine knobs that are genuinely optional and
9+
# are absent from .env (TEAM_API_KEY, ADMIN_TOKEN, SANDBOX_ACCESS_TOKEN_HASH_SEED,
10+
# E2B_DASHBOARD_HOST and E2B_OTEL_COLLECTOR_GRPC_ENDPOINT only as commented
11+
# lines) keep the ${VAR:-...} form: HUGEPAGES, NBDS_MAX, PF_MIN_FREE_GIB,
12+
# FORCE_REBUILD, E2B_DASHBOARD_HOST and E2B_OTEL_COLLECTOR_GRPC_ENDPOINT carry a
13+
# working default (the last an empty one, which exports no telemetry), while
1214
# TEAM_API_KEY, ADMIN_TOKEN and SANDBOX_ACCESS_TOKEN_HASH_SEED carry none — the
1315
# stack generates all three per install (seed for the key, api-secrets for the
1416
# api's two), and a value set in .env or the shell overrides the generated one.
@@ -102,6 +104,30 @@ services:
102104
timeout: 2s
103105
retries: 12
104106

107+
# The built-in OpenTelemetry collector, run only with the otel profile:
108+
# COMPOSE_PROFILES=otel in .env, which Compose reads itself. It receives
109+
# OTLP/gRPC on 127.0.0.1:4317, the address to give
110+
# E2B_OTEL_COLLECTOR_GRPC_ENDPOINT, and writes the e2b.* metrics into
111+
# ClickHouse (config/otel/otel-collector.yaml says what it keeps). The image
112+
# has no shell, so there is no healthcheck; `curl -s 127.0.0.1:13133` on the
113+
# host is the check. No service depends on it, `ready` included.
114+
otel-collector:
115+
image: otel/opentelemetry-collector-contrib:0.146.0
116+
profiles: [otel]
117+
restart: unless-stopped
118+
network_mode: host
119+
depends_on:
120+
clickhouse-migrator:
121+
condition: service_completed_successfully
122+
environment:
123+
# sha256 of config/otel/otel-collector.yaml, same purpose as
124+
# VECTOR_CONFIG_SHA256 on the vector service.
125+
OTEL_CONFIG_SHA256: 96621f22e4e8ab2b420a6bc659c0b47239535f90627951aba449a309f614ce5f
126+
command: ["--config=/etc/otelcol-contrib/config.yaml"]
127+
configs:
128+
- source: otel-collector-config
129+
target: /etc/otelcol-contrib/config.yaml
130+
105131
db-migrator:
106132
image: ${E2B_DB_MIGRATOR_IMAGE:?fetch the .env that ships beside compose.yaml (README, Install)}
107133
restart: "no"
@@ -240,6 +266,10 @@ services:
240266
REDIS_URL: 127.0.0.1:6379
241267
CLICKHOUSE_CONNECTION_STRING: clickhouse://clickhouse:clickhouse@127.0.0.1:9000/default
242268
LOGS_COLLECTOR_ADDRESS: http://127.0.0.1:30006
269+
# Where this service and api, client-proxy and dashboard-api export their
270+
# metrics, traces and logs over OTLP/gRPC. Empty unless .env sets
271+
# E2B_OTEL_COLLECTOR_GRPC_ENDPOINT, and empty exports nothing.
272+
OTEL_COLLECTOR_GRPC_ENDPOINT: ${E2B_OTEL_COLLECTOR_GRPC_ENDPOINT:-}
243273
TEMPLATE_STORAGE_URL: file:///var/lib/e2b/storage/templates
244274
BUILD_CACHE_STORAGE_URL: file:///var/lib/e2b/storage/build-cache
245275
ARTIFACTS_REGISTRY_PROVIDER: Local
@@ -304,6 +334,7 @@ services:
304334
# LOKI_URL, and tests/pins.bats refuses that pin.
305335
LOGS_READ_CONFIG: "true"
306336
LOGS_COLLECTOR_ADDRESS: http://127.0.0.1:30006
337+
OTEL_COLLECTOR_GRPC_ENDPOINT: ${E2B_OTEL_COLLECTOR_GRPC_ENDPOINT:-}
307338
API_INTERNAL_GRPC_PORT: "5009"
308339
API_EDGE_GRPC_PORT: "5109"
309340
PPROF_PORT: "6060"
@@ -388,6 +419,7 @@ services:
388419
POSTGRES_CONNECTION_STRING: postgres://postgres:postgres@127.0.0.1:5432/postgres?sslmode=disable
389420
REDIS_URL: 127.0.0.1:6379
390421
CLICKHOUSE_CONNECTION_STRING: clickhouse://clickhouse:clickhouse@127.0.0.1:9000/default
422+
OTEL_COLLECTOR_GRPC_ENDPOINT: ${E2B_OTEL_COLLECTOR_GRPC_ENDPOINT:-}
391423
AUTH_PROVIDER_CONFIG: '{"jwt":[]}'
392424
volumes:
393425
- seed-state:/run/e2b:ro
@@ -472,6 +504,7 @@ services:
472504
API_INTERNAL_GRPC_ADDRESS: 127.0.0.1:5009
473505
PROXY_PORT: "3002"
474506
HEALTH_PORT: "3003"
507+
OTEL_COLLECTOR_GRPC_ENDPOINT: ${E2B_OTEL_COLLECTOR_GRPC_ENDPOINT:-}
475508
depends_on:
476509
api:
477510
condition: service_healthy
@@ -853,3 +886,93 @@ configs:
853886
skip_unknown_fields = false
854887
compression = "gzip"
855888
batch.timeout_secs = 1
889+
otel-collector-config:
890+
content: |
891+
# The built-in OpenTelemetry collector. The services export to it once their
892+
# collector endpoint is 127.0.0.1:4317. It keeps the e2b.* metrics and writes
893+
# them into the stack's own ClickHouse, into the metrics_gauge and metrics_sum
894+
# tables the clickhouse-migrator creates; the api's metrics endpoints read
895+
# what those two feed. Traces, logs and the other metrics are dropped; the
896+
# commented otlp/upstream exporter below forwards traces and logs instead.
897+
# Every listener binds loopback: the collector shares the machine's network
898+
# with the services, and nothing off the machine has a reason to reach it.
899+
receivers:
900+
otlp:
901+
protocols:
902+
grpc:
903+
endpoint: 127.0.0.1:4317
904+
max_recv_msg_size_mib: 100
905+
906+
processors:
907+
# Batches traces and logs on their way to nop, or to otlp/upstream once
908+
# that forwards them.
909+
batch:
910+
timeout: 5s
911+
912+
batch/clickhouse:
913+
timeout: 5s
914+
send_batch_size: 50000
915+
916+
filter/external_metrics:
917+
metrics:
918+
include:
919+
match_type: regexp
920+
metric_names:
921+
- "e2b.*"
922+
923+
extensions:
924+
# `curl -s 127.0.0.1:13133` on the machine answers while the collector runs.
925+
# The image has no shell, so there is nothing to check from inside it.
926+
health_check:
927+
endpoint: 127.0.0.1:13133
928+
929+
exporters:
930+
clickhouse:
931+
endpoint: tcp://127.0.0.1:9000
932+
database: default
933+
username: clickhouse
934+
password: clickhouse
935+
async_insert: true
936+
create_schema: false
937+
metrics_tables:
938+
gauge:
939+
name: metrics_gauge
940+
sum:
941+
name: metrics_sum
942+
943+
# Accepts traces and logs and discards them. Without a pipeline for them the
944+
# receiver would answer Unimplemented, and every service would log each
945+
# batch it refused.
946+
nop:
947+
948+
# To forward traces and logs to a collector of your own, uncomment this
949+
# exporter, put that collector's OTLP/gRPC address in its endpoint, and
950+
# name it in place of nop in the traces and logs pipelines at the end.
951+
# otlp/upstream:
952+
# endpoint: collector.example.com:4317
953+
# tls:
954+
# insecure: true
955+
956+
service:
957+
telemetry:
958+
logs:
959+
level: warn
960+
# No internal metrics: by default the collector serves its own on
961+
# 127.0.0.1:8888, one more listener on the machine, and refuses to start
962+
# when something else holds that port.
963+
metrics:
964+
level: none
965+
extensions: [health_check]
966+
pipelines:
967+
metrics/external:
968+
receivers: [otlp]
969+
processors: [filter/external_metrics, batch/clickhouse]
970+
exporters: [clickhouse]
971+
traces:
972+
receivers: [otlp]
973+
processors: [batch]
974+
exporters: [nop]
975+
logs:
976+
receivers: [otlp]
977+
processors: [batch]
978+
exporters: [nop]

0 commit comments

Comments
 (0)