Skip to content

Commit 84077b0

Browse files
add titiler-xarray lambda deployment and remove ecs (#1175)
* add titiler-xarray lambda deployment and remove ecs * add xarray/zarr version in /healthz
1 parent febec1a commit 84077b0

File tree

10 files changed

+342
-258
lines changed

10 files changed

+342
-258
lines changed

deployment/aws/README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,3 @@ Intro: https://developmentseed.org/titiler/deployment/aws/intro/
44

55
AWS lambda: https://developmentseed.org/titiler/deployment/aws/lambda/
66

7-
ECS/Fargate: https://developmentseed.org/titiler/deployment/aws/ecs/

deployment/aws/cdk/app.py

Lines changed: 26 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
"""Construct App."""
22

33
import os
4-
from typing import Any, Dict, List, Optional, Union
4+
from typing import Any, Dict, List, Optional
55

66
from aws_cdk import App, CfnOutput, Duration, Stack, Tags
77
from aws_cdk import aws_apigatewayv2 as apigw
8-
from aws_cdk import aws_ec2 as ec2
9-
from aws_cdk import aws_ecs as ecs
10-
from aws_cdk import aws_ecs_patterns as ecs_patterns
118
from aws_cdk import aws_iam as iam
129
from aws_cdk import aws_lambda
1310
from aws_cdk import aws_logs as logs
@@ -47,6 +44,7 @@ def __init__(
4744
permissions = permissions or []
4845
environment = environment or {}
4946

47+
# COG / STAC / MosaicJSON
5048
lambda_function = aws_lambda.Function(
5149
self,
5250
f"{id}-lambda",
@@ -79,91 +77,38 @@ def __init__(
7977
)
8078
CfnOutput(self, "Endpoint", value=api.url)
8179

82-
83-
class titilerECSStack(Stack):
84-
"""Titiler ECS Fargate Stack."""
85-
86-
def __init__(
87-
self,
88-
scope: Construct,
89-
id: str,
90-
cpu: Union[int, float] = 256,
91-
memory: Union[int, float] = 512,
92-
mincount: int = 1,
93-
maxcount: int = 50,
94-
permissions: Optional[List[iam.PolicyStatement]] = None,
95-
environment: Optional[Dict] = None,
96-
code_dir: str = "./",
97-
**kwargs: Any,
98-
) -> None:
99-
"""Define stack."""
100-
super().__init__(scope, id, *kwargs)
101-
102-
permissions = permissions or []
103-
environment = environment or {}
104-
105-
vpc = ec2.Vpc(self, f"{id}-vpc", max_azs=2)
106-
107-
cluster = ecs.Cluster(self, f"{id}-cluster", vpc=vpc)
108-
109-
task_env = environment.copy()
110-
task_env.update({"LOG_LEVEL": "error"})
111-
112-
# GUNICORN configuration
113-
if settings.workers_per_core:
114-
task_env.update({"WORKERS_PER_CORE": str(settings.workers_per_core)})
115-
if settings.max_workers:
116-
task_env.update({"MAX_WORKERS": str(settings.max_workers)})
117-
if settings.web_concurrency:
118-
task_env.update({"WEB_CONCURRENCY": str(settings.web_concurrency)})
119-
120-
fargate_service = ecs_patterns.ApplicationLoadBalancedFargateService(
80+
# Xarray
81+
xarray_lambda_function = aws_lambda.Function(
12182
self,
122-
f"{id}-service",
123-
cluster=cluster,
124-
cpu=cpu,
125-
memory_limit_mib=memory,
126-
desired_count=mincount,
127-
public_load_balancer=True,
128-
listener_port=80,
129-
task_image_options=ecs_patterns.ApplicationLoadBalancedTaskImageOptions(
130-
image=ecs.ContainerImage.from_registry(
131-
f"ghcr.io/developmentseed/titiler:{settings.image_version}",
132-
),
133-
container_port=80,
134-
environment=task_env,
83+
f"{id}-xarray-lambda",
84+
runtime=runtime,
85+
code=aws_lambda.Code.from_docker_build(
86+
path=os.path.abspath(code_dir),
87+
file="lambda/Dockerfile.xarray",
88+
platform="linux/amd64",
89+
build_args={
90+
"PYTHON_VERSION": "3.12",
91+
},
13592
),
93+
handler="handler.handler",
94+
memory_size=memory,
95+
reserved_concurrent_executions=concurrent,
96+
timeout=Duration.seconds(timeout),
97+
environment=environment,
98+
log_retention=logs.RetentionDays.ONE_WEEK,
13699
)
137-
fargate_service.target_group.configure_health_check(path="/healthz")
138100

139101
for perm in permissions:
140-
fargate_service.task_definition.task_role.add_to_policy(perm)
141-
142-
scalable_target = fargate_service.service.auto_scale_task_count(
143-
min_capacity=mincount, max_capacity=maxcount
144-
)
102+
xarray_lambda_function.add_to_role_policy(perm)
145103

146-
# https://github.com/awslabs/aws-rails-provisioner/blob/263782a4250ca1820082bfb059b163a0f2130d02/lib/aws-rails-provisioner/scaling.rb#L343-L387
147-
scalable_target.scale_on_request_count(
148-
"RequestScaling",
149-
requests_per_target=50,
150-
scale_in_cooldown=Duration.seconds(240),
151-
scale_out_cooldown=Duration.seconds(30),
152-
target_group=fargate_service.target_group,
153-
)
154-
155-
# scalable_target.scale_on_cpu_utilization(
156-
# "CpuScaling", target_utilization_percent=70,
157-
# )
158-
159-
fargate_service.service.connections.allow_from_any_ipv4(
160-
port_range=ec2.Port(
161-
protocol=ec2.Protocol.ALL,
162-
string_representation="All port 80",
163-
from_port=80,
104+
xarray_api = apigw.HttpApi(
105+
self,
106+
f"{id}-xarray-endpoint",
107+
default_integration=HttpLambdaIntegration(
108+
f"{id}-xarray-integration", handler=xarray_lambda_function
164109
),
165-
description="Allows traffic on port 80 from ALB",
166110
)
111+
CfnOutput(self, "Xarray-Endpoint", value=xarray_api.url)
167112

168113

169114
app = App()
@@ -179,18 +124,6 @@ def __init__(
179124
)
180125
)
181126

182-
183-
ecs_stack = titilerECSStack(
184-
app,
185-
f"{settings.name}-ecs-{settings.stage}",
186-
cpu=settings.task_cpu,
187-
memory=settings.task_memory,
188-
mincount=settings.min_ecs_instances,
189-
maxcount=settings.max_ecs_instances,
190-
permissions=perms,
191-
environment=settings.env,
192-
)
193-
194127
lambda_stack = titilerLambdaStack(
195128
app,
196129
f"{settings.name}-lambda-{settings.stage}",
@@ -209,7 +142,6 @@ def __init__(
209142
"Client": settings.client,
210143
}.items():
211144
if value:
212-
Tags.of(ecs_stack).add(key, value)
213145
Tags.of(lambda_stack).add(key, value)
214146

215147

deployment/aws/cdk/config.py

Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -38,42 +38,6 @@ class StackSettings(BaseSettings):
3838
# S3 key pattern to limit the access to specific items (e.g: "my_data/*.tif")
3939
key: str = "*"
4040

41-
###########################################################################
42-
# AWS ECS
43-
# The following settings only apply to AWS ECS deployment
44-
min_ecs_instances: int = 5
45-
max_ecs_instances: int = 50
46-
47-
# CPU value | Memory value
48-
# 256 (.25 vCPU) | 0.5 GB, 1 GB, 2 GB
49-
# 512 (.5 vCPU) | 1 GB, 2 GB, 3 GB, 4 GB
50-
# 1024 (1 vCPU) | 2 GB, 3 GB, 4 GB, 5 GB, 6 GB, 7 GB, 8 GB
51-
# 2048 (2 vCPU) | Between 4 GB and 16 GB in 1-GB increments
52-
# 4096 (4 vCPU) | Between 8 GB and 30 GB in 1-GB increments
53-
task_cpu: int = 256
54-
task_memory: int = 512
55-
56-
# GUNICORN configuration
57-
# Ref: https://github.com/developmentseed/titiler/issues/119
58-
59-
# WORKERS_PER_CORE
60-
# This image will check how many CPU cores are available in the current server running your container.
61-
# It will set the number of workers to the number of CPU cores multiplied by this value.
62-
workers_per_core: int = 1
63-
64-
# MAX_WORKERS
65-
# You can use it to let the image compute the number of workers automatically but making sure it's limited to a maximum.
66-
# should depends on `task_cpu`
67-
max_workers: int = 1
68-
69-
# WEB_CONCURRENCY
70-
# Override the automatic definition of number of workers.
71-
# Set to the number of CPU cores in the current server multiplied by the environment variable WORKERS_PER_CORE.
72-
# So, in a server with 2 cores, by default it will be set to 2.
73-
web_concurrency: Optional[int] = None
74-
75-
image_version: str = "latest"
76-
7741
###########################################################################
7842
# AWS LAMBDA
7943
# The following settings only apply to AWS Lambda deployment

deployment/aws/lambda/Dockerfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ COPY lambda/handler.py /asset/handler.py
2222
# Ref: https://github.com/developmentseed/titiler/discussions/1108#discussioncomment-13045681
2323
RUN cp /usr/lib64/libexpat.so.1 /asset/
2424

25-
ENV LAMBDA_TASK_ROOT /asset
25+
WORKDIR /asset
26+
RUN python -c "from handler import handler; print('All Good')"
2627

2728
CMD ["echo", "hello world"]
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
ARG PYTHON_VERSION=3.12
2+
3+
FROM --platform=linux/amd64 public.ecr.aws/lambda/python:${PYTHON_VERSION}
4+
5+
WORKDIR /tmp
6+
7+
# Install system dependencies to compile (numexpr)
8+
RUN dnf install -y gcc-c++ && dnf clean all
9+
10+
RUN python -m pip install pip -U
11+
RUN python -m pip install "titiler.xarray==0.22.3" "mangum>=0.10.0" "aiobotocore==2.17.0" "zarr" "s3fs" "aiohttp" "h5netcdf" "starlette-cramjam" "cftime" -t /asset --no-binary pydantic,xarray,numpy,pandas
12+
13+
# Reduce package size and remove useless files
14+
RUN cd /asset && find . -type f -name '*.pyc' | while read f; do n=$(echo $f | sed 's/__pycache__\///' | sed 's/.cpython-[0-9]*//'); cp $f $n; done;
15+
RUN cd /asset && find . -type d -a -name '__pycache__' -print0 | xargs -0 rm -rf
16+
RUN cd /asset && find . -type f -a -name '*.py' -print0 | xargs -0 rm -f
17+
RUN find /asset -type d -a -name 'tests' -print0 | xargs -0 rm -rf
18+
RUN rm -rdf /asset/numpy/doc/ /asset/boto3* /asset/botocore* /asset/bin /asset/geos_license /asset/Misc
19+
20+
COPY lambda/xarray_handler.py /asset/handler.py
21+
22+
# Ref: https://github.com/developmentseed/titiler/discussions/1108#discussioncomment-13045681
23+
RUN cp /usr/lib64/libexpat.so.1 /asset/
24+
25+
WORKDIR /asset
26+
RUN python -c "from handler import handler; print('All Good')"
27+
28+
CMD ["echo", "hello world"]

0 commit comments

Comments
 (0)