Skip to content

Commit ae7eb20

Browse files
committed
just use aws-observability/aws-otel-python-instrumentation
1 parent d8bc28e commit ae7eb20

File tree

9 files changed

+131
-466
lines changed

9 files changed

+131
-466
lines changed

infrastructure/aws/cdk/app.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ def __init__(
4343
id: str,
4444
memory: int = 1024,
4545
timeout: int = 30,
46-
runtime: aws_lambda.Runtime = aws_lambda.Runtime.PYTHON_3_12,
4746
concurrent: Optional[int] = None,
4847
permissions: Optional[List[iam.PolicyStatement]] = None,
4948
environment: Optional[Dict] = None,
@@ -138,7 +137,12 @@ def __init__(
138137
**environment,
139138
"TITILER_MULTIDIM_ROOT_PATH": app_settings.root_path,
140139
"TITILER_MULTIDIM_CACHE_HOST": redis_cluster.attr_redis_endpoint_address,
141-
"OTEL_TRACES_ENABLED": "true",
140+
"OTEL_METRICS_EXPORTER": "none", # Disable metrics - only using traces
141+
"OTEL_PYTHON_DISABLED_INSTRUMENTATIONS": "aws-lambda,requests,urllib3,aiohttp-client", # Disable aws-lambda auto-instrumentation (handled by otel_wrapper.py)
142+
"OTEL_PROPAGATORS": "tracecontext,baggage,xray",
143+
"OPENTELEMETRY_COLLECTOR_CONFIG_URI": "/opt/collector-config/config.yaml",
144+
"AWS_LAMBDA_LOG_FORMAT": "JSON",
145+
"AWS_LAMBDA_EXEC_WRAPPER": "/opt/otel-instrument", # Enable OTEL wrapper to avoid circular import
142146
},
143147
log_retention=logs.RetentionDays.ONE_WEEK,
144148
vpc=vpc,
@@ -209,6 +213,17 @@ def __init__(
209213
)
210214
)
211215

216+
# Add X-Ray permissions for tracing
217+
perms.append(
218+
iam.PolicyStatement(
219+
actions=[
220+
"xray:PutTraceSegments",
221+
"xray:PutTelemetryRecords",
222+
],
223+
resources=["*"],
224+
)
225+
)
226+
212227

213228
lambda_stack = LambdaStack(
214229
app,

infrastructure/aws/lambda/Dockerfile

Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,36 @@
11
ARG PYTHON_VERSION=3.12
22

3-
# Build stage - includes all build tools and dependencies
4-
FROM public.ecr.aws/lambda/python:${PYTHON_VERSION} AS builder
3+
# Stage 1: Build dependencies
4+
5+
# Download the OpenTelemetry Layer with AppSignals Support:
6+
FROM public.ecr.aws/lambda/python:${PYTHON_VERSION} as otel-builder
7+
RUN dnf install -y unzip wget && \
8+
wget https://github.com/aws-observability/aws-otel-python-instrumentation/releases/latest/download/layer.zip -O /tmp/layer.zip && \
9+
mkdir -p /opt-builder && \
10+
unzip /tmp/layer.zip -d /opt-builder/
511

6-
# Copy uv for faster dependency management
12+
# install python dependencies
13+
FROM public.ecr.aws/lambda/python:${PYTHON_VERSION} AS builder
714
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
815

9-
# Install system dependencies needed for compilation
1016
RUN dnf install -y gcc-c++ && dnf clean all
1117

12-
# Set working directory for build
1318
WORKDIR /build
1419

15-
# Copy dependency files first for better caching
1620
COPY README.md uv.lock .python-version pyproject.toml ./
1721
COPY src/titiler/ ./src/titiler/
1822

23+
COPY infrastructure/aws/lambda/otel-requirements.txt ./
24+
1925
# Install dependencies to temporary directory with Lambda-specific optimizations
26+
# Install OTEL packages to /opt/python (where otel-instrument wrapper expects them)
27+
RUN uv pip install \
28+
--target /opt-python \
29+
--no-cache-dir \
30+
--disable-pip-version-check \
31+
-r otel-requirements.txt
32+
33+
# Install app packages (excluding OTEL packages since they're already installed)
2034
RUN uv export --locked --no-editable --no-dev --extra lambda --format requirements.txt -o requirements.txt && \
2135
uv pip install \
2236
--compile-bytecode \
@@ -26,7 +40,9 @@ RUN uv export --locked --no-editable --no-dev --extra lambda --format requiremen
2640
--disable-pip-version-check \
2741
-r requirements.txt
2842

43+
2944
# Aggressive cleanup to minimize size and optimize for Lambda container
45+
# Clean up app dependencies in /deps
3046
WORKDIR /deps
3147
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
3248
RUN <<EOF
@@ -50,30 +66,53 @@ find . -type f -name '*.so*' -not -path "*/numpy.libs/*" -exec strip --strip-unn
5066
du -sh . > /tmp/package_size.txt
5167
EOF
5268

53-
# Final runtime stage - minimal Lambda image optimized for container runtime
69+
# Clean up OTEL dependencies in /opt-python (lighter cleanup to preserve CLI tools)
70+
WORKDIR /opt-python
71+
RUN <<EOF
72+
# Remove test files and docs
73+
find . -type d -a -name 'tests' -print0 | xargs -0 rm -rf
74+
find . -type d -a -name 'test' -print0 | xargs -0 rm -rf
75+
# Keep bin/ directory for opentelemetry-instrument CLI
76+
find . -name '*.mo' -delete
77+
find . -name '*.po' -delete
78+
find . -name 'LICENSE*' -delete
79+
find . -name 'README*' -delete
80+
find . -name '*.md' -delete
81+
EOF
82+
83+
# Stage 3: Final runtime stage - minimal Lambda image optimized for container runtime
5484
FROM public.ecr.aws/lambda/python:${PYTHON_VERSION}
5585

5686
# Set Lambda-specific environment variables for optimal performance
57-
ENV PYTHONPATH=${LAMBDA_RUNTIME_DIR} \
58-
PYTHONUNBUFFERED=1 \
59-
PYTHONDONTWRITEBYTECODE=1 \
60-
AWS_LWA_ENABLE_COMPRESSION=true
87+
# PYTHONPATH includes both /opt/python (OTEL packages) and LAMBDA_RUNTIME_DIR (app packages)
88+
ENV PYTHONUNBUFFERED=1 \
89+
PYTHONDONTWRITEBYTECODE=1
90+
91+
# Copy OTEL code
92+
COPY --from=otel-builder /opt-builder/ /opt/
93+
COPY infrastructure/aws/lambda/collector-config.yaml /opt/collector-config/config.yaml
94+
95+
# Copy dependencies from builder stage
96+
# OTEL packages to /opt/python (includes opentelemetry-instrument CLI)
97+
COPY --from=builder /opt-python /opt/python
98+
99+
# App packages to LAMBDA_RUNTIME_DIR
100+
COPY --from=builder /deps ${LAMBDA_RUNTIME_DIR}/
61101

62-
# Copy only the cleaned dependencies from builder stage
63102
# Copy required system library
64-
COPY --from=builder /deps /usr/lib64/libexpat.so.1 ${LAMBDA_RUNTIME_DIR}/
103+
COPY --from=builder /usr/lib64/libexpat.so.1 ${LAMBDA_RUNTIME_DIR}/
65104

66105
# Copy application handler and OpenTelemetry configuration
67-
COPY infrastructure/aws/lambda/handler.py infrastructure/aws/lambda/otel_config.py ${LAMBDA_RUNTIME_DIR}/
106+
COPY infrastructure/aws/lambda/handler.py ${LAMBDA_RUNTIME_DIR}/
68107

69108
# Ensure handler is executable and optimize permissions
70109
RUN <<EOF
71110
chmod 644 "${LAMBDA_RUNTIME_DIR}"/handler.py
111+
chmod -R 755 /opt/
72112
# Pre-compile the handler for faster cold starts
73113
python -c "import py_compile; py_compile.compile('${LAMBDA_RUNTIME_DIR}/handler.py', doraise=True)"
74114
# Create cache directories with proper permissions
75115
mkdir -p /tmp/.cache && chmod 777 /tmp/.cache
76116
EOF
77117

78-
# Set the Lambda handler
79118
CMD ["handler.lambda_handler"]
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
extensions:
2+
# AWS Proxy extension - forwards X-Ray segments to local X-Ray daemon via UDP
3+
# This avoids the awsxray exporter making HTTPS API calls
4+
awsproxy:
5+
endpoint: 127.0.0.1:2000
6+
7+
receivers:
8+
otlp:
9+
protocols:
10+
grpc:
11+
endpoint: localhost:4317
12+
http:
13+
endpoint: localhost:4318
14+
15+
processors:
16+
batch:
17+
timeout: 1s
18+
send_batch_size: 50
19+
20+
exporters:
21+
# Export to AWS X-Ray via local X-Ray daemon (UDP, no internet required)
22+
# The awsproxy extension bridges the collector to the daemon at 127.0.0.1:2000
23+
awsxray:
24+
endpoint: http://127.0.0.1:2000
25+
local_mode: true # Use local X-Ray daemon instead of direct API calls
26+
index_all_attributes: true
27+
28+
# Debug exporter to see traces in CloudWatch logs
29+
debug:
30+
verbosity: detailed
31+
32+
service:
33+
extensions: [awsproxy]
34+
pipelines:
35+
traces:
36+
receivers: [otlp]
37+
processors: [batch]
38+
exporters: [debug, awsxray]
Lines changed: 11 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,20 @@
1-
"""AWS Lambda handler optimized for container runtime."""
1+
"""AWS Lambda handler optimized for container runtime with OTEL instrumentation."""
22

33
import logging
44
import warnings
55
from typing import Any, Dict
66

7-
# Initialize OpenTelemetry BEFORE importing the FastAPI app
8-
from otel_config import setup_otel
7+
from mangum import Mangum
8+
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
9+
from opentelemetry.instrumentation.logging import LoggingInstrumentor
910

10-
setup_otel()
11-
12-
from mangum import Mangum # noqa: E402
13-
14-
from titiler.multidim.main import app # noqa: E402
11+
from titiler.multidim.main import app
1512

1613
# Configure root logger to WARN level by default
14+
# Use simple format - AWS Lambda will handle JSON formatting when AWS_LAMBDA_LOG_FORMAT=JSON
1715
logging.basicConfig(
1816
level=logging.WARN,
19-
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
17+
format="[%(levelname)s] %(name)s: %(message)s",
2018
)
2119

2220
# Set titiler loggers to INFO level
@@ -31,15 +29,8 @@
3129
warnings.filterwarnings("ignore", category=UserWarning)
3230
warnings.filterwarnings("ignore", category=FutureWarning)
3331

34-
35-
# Pre-import commonly used modules for faster cold starts
36-
try:
37-
import numpy # noqa: F401
38-
import pandas # noqa: F401
39-
import rioxarray # noqa: F401
40-
import xarray # noqa: F401
41-
except ImportError:
42-
pass
32+
LoggingInstrumentor().instrument(set_logging_format=False)
33+
FastAPIInstrumentor.instrument_app(app)
4334

4435
handler = Mangum(
4536
app,
@@ -55,10 +46,5 @@
5546

5647

5748
def lambda_handler(event: Dict[str, Any], context: Any) -> Dict[str, Any]:
58-
"""Lambda handler with container-specific optimizations."""
59-
response = handler(event, context)
60-
61-
return response
62-
63-
64-
handler.lambda_handler = lambda_handler
49+
"""Lambda handler with container-specific optimizations and OTEL tracing."""
50+
return handler(event, context)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
aws-opentelemetry-distro>=0.12.1
2+
opentelemetry-instrumentation-botocore
3+
opentelemetry-instrumentation-fastapi
4+
opentelemetry-instrumentation-logging

infrastructure/aws/lambda/otel_config.py

Lines changed: 0 additions & 132 deletions
This file was deleted.

0 commit comments

Comments
 (0)