Repository navigation
Fix: Bake the shim on the app's own OpenTelemetry version #1311
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -42,11 +42,20 @@ USER root | |
| # Our own static uv, pinned by digest: a builder-stage image may have neither | ||
| # uv nor pip in its runtime layer. | ||
| COPY --from=ghcr.io/astral-sh/uv:0.9.24@sha256:816fdce3387ed2142e37d2e56e1b1b97ccc1ea87731ba199dc8a25c04e4997c5 /uv /usr/local/bin/uv | ||
| # One contrib release for everything, so a bake never silently upgrades the | ||
| # OTel set under an app. -distro is the SDK; the eight others — server | ||
| # (starlette/asgi/fastapi), client (httpx/requests/aiohttp/urllib3) and | ||
| # -threading — are the instrumentors build-otel-shim.sh's interlock probes | ||
| # for; keep the two lists in sync. | ||
| # One contrib release for everything, and the app chooses it. Each release | ||
| # requires one exact OpenTelemetry core (0.63b1 requires | ||
| # opentelemetry-api==1.42.1, through opentelemetry-semantic-conventions), so | ||
| # the install is pinned to the opentelemetry-api the app already carries and | ||
| # the resolver lands on the one release paired with it: the bake does not move | ||
| # the api/sdk under the app. An app that carries none, or one older than | ||
| # 1.34.0, is pinned to OTEL_CONTRIB_VERSION instead; there the bake may move | ||
| # the core, and the check below decides. (1.34.0 pairs with contrib 0.55b0: | ||
| # earlier releases instrument only starlette<0.15, and before 0.52b0 they lack | ||
| # the initialize() the hook calls.) | ||
| # -distro is the SDK; the eight others — server (starlette/asgi/fastapi), | ||
| # client (httpx/requests/aiohttp/urllib3) and -threading — are the | ||
| # instrumentors build-otel-shim.sh's interlock probes for; keep the two lists | ||
| # in sync. | ||
| # `-urllib3` covers clients that bypass `requests`: boto3/botocore (S3 and | ||
| # every AWS-style store) drive urllib3 directly. Without it an object read | ||
| # lands in a trace of its own (seen live: a MinIO GetObject escaping into a | ||
|
|
@@ -58,16 +67,21 @@ COPY --from=ghcr.io/astral-sh/uv:0.9.24@sha256:816fdce3387ed2142e37d2e56e1b1b97c | |
| # `--system --break-system-packages` lets the install land in a non-venv | ||
| # python too; both flags are inert when --python is a venv. | ||
| ARG OTEL_CONTRIB_VERSION=0.65b0 | ||
| RUN uv pip install --no-cache --python "${VENV_PYTHON}" --system --break-system-packages \ | ||
| "opentelemetry-distro==${OTEL_CONTRIB_VERSION}" \ | ||
| "opentelemetry-instrumentation-starlette==${OTEL_CONTRIB_VERSION}" \ | ||
| "opentelemetry-instrumentation-asgi==${OTEL_CONTRIB_VERSION}" \ | ||
| "opentelemetry-instrumentation-fastapi==${OTEL_CONTRIB_VERSION}" \ | ||
| "opentelemetry-instrumentation-httpx==${OTEL_CONTRIB_VERSION}" \ | ||
| "opentelemetry-instrumentation-requests==${OTEL_CONTRIB_VERSION}" \ | ||
| "opentelemetry-instrumentation-aiohttp-client==${OTEL_CONTRIB_VERSION}" \ | ||
| "opentelemetry-instrumentation-urllib3==${OTEL_CONTRIB_VERSION}" \ | ||
| "opentelemetry-instrumentation-threading==${OTEL_CONTRIB_VERSION}" | ||
| RUN pin="$("${VENV_PYTHON}" -c 'from importlib.metadata import version; v = version("opentelemetry-api"); \ | ||
| assert tuple(map(int, v.split(".")[:2])) >= (1, 34); print("opentelemetry-api==" + v)' 2>/dev/null)" \ | ||
| || pin="opentelemetry-distro==${OTEL_CONTRIB_VERSION}"; \ | ||
| echo "the bake is pinned to ${pin}"; \ | ||
| uv pip install --no-cache --python "${VENV_PYTHON}" --system --break-system-packages "${pin}" \ | ||
| opentelemetry-distro \ | ||
| opentelemetry-instrumentation-starlette \ | ||
| opentelemetry-instrumentation-asgi \ | ||
| opentelemetry-instrumentation-fastapi \ | ||
| opentelemetry-instrumentation-httpx \ | ||
| opentelemetry-instrumentation-requests \ | ||
| opentelemetry-instrumentation-aiohttp-client \ | ||
| opentelemetry-instrumentation-urllib3 \ | ||
| opentelemetry-instrumentation-threading \ | ||
| || { echo "REFUSING: the shim could not be installed alongside ${pin} (uv's error is above; if it found no solution, no contrib release is paired with that version)" >&2; exit 1; } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: when "That version" is |
||
| # The install resolves into the app's OWN environment, so it may move a package | ||
| # an app dependency pins (wrapt, deprecated, typing-extensions are the usual | ||
| # suspects) — and the attestations import only OpenTelemetry, never the app. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -55,6 +55,7 @@ attestation runs before the load and prints nothing when it passes). | |
| Fail `REFUSING to bake … already instruments …` (exit 3): the app instruments itself — go to step 3 **without** `APP_CONTAINER`/`APP_IMAGE` (capture only). | ||
| Fail `REFUSING to bake … no runnable Python found` (exit 3): outside the shim's envelope (DESIGN "The envelope") — same, capture only; or pass the interpreter as arg 3 if you know it. | ||
| Fail `REFUSING to bake … is not present locally` (exit 3): wrong `IMAGE` — see Inputs; nothing was built. | ||
| Fail `REFUSING: the shim could not be installed alongside …` (the build fails): no contrib release is paired with the app's `opentelemetry-api` (a core newer than its contrib release, or a package index that lags) — uv's own error is above the line. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: every other This line can also be reached with no paired-release problem at all, on the fallback pin. A Python 3.9 app with no OpenTelemetry lands here because uv reports |
||
| Fail `ATTESTATION FAILED` (exit 4): the bake itself is broken (the image was not loaded) — read the assertion it prints; not an app property. | ||
|
|
||
| ## 3. Attach (once per Deployment) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: the floor disappears when the image sets
PYTHONOPTIMIZE.RUNinherits the base image'sENV, and withPYTHONOPTIMIZEset Python skips theassert, so a core from 1.31 to 1.33 is pinned to itself instead of falling back toOTEL_CONTRIB_VERSION.I ran this RUN's shell (lines joined the way the Dockerfile joins them) against a venv holding
opentelemetry-api==1.32.1:the bake is pinned to opentelemetry-distro==0.65b0PYTHONOPTIMIZE=1:the bake is pinned to opentelemetry-api==1.32.1, anduv pip install --dry-runresolvesopentelemetry-instrumentation-starlette==0.53b1. That is thestarlette<0.15release this floor exists to keep out.Nothing after this catches it. With that set installed next to starlette 1.7.0:
uv pip checkpasses;verify_propagates's snippet passes, because 0.53b1 already hasinitialize();StarletteInstrumentor().is_instrumented_by_opentelemetryisFalse. The only sign is aDependencyConflict: requested: "starlette >= 0.13, <0.15" but found: "starlette 1.7.0"log line.So a pure-Starlette app bakes and attests clean, then splits into separate traces at runtime. (
verify_propagatesalso usesassert, so under the same env it would pass even for a core below 1.31, whereinitialize()is missing. That's older code outside this diff.)The fix can't be an
if: the Dockerfile joins the continued lines into one, and a compound statement can't follow;. So the check has to stay an expression. I tested this withPYTHONOPTIMIZEunset, 1 and 2, on cores 1.32.1, 1.43.0 and none: