From 19cd2fdc667f312720f5b228920870ecdc79892a Mon Sep 17 00:00:00 2001 From: jvstme <36324149+jvstme@users.noreply.github.com> Date: Thu, 31 Oct 2024 12:35:43 +0000 Subject: [PATCH] Increase request body size limit for services (#1934) This commit increases the limit from 1 MiB to 64 MiB and makes it configurable with a new dstack-server environment variable. The design will also allow to make the limit configurable per-gateway and/or per-service in the future. --- docs/docs/reference/cli/index.md | 1 + gateway/src/dstack/gateway/core/nginx.py | 11 ++++++++++- gateway/src/dstack/gateway/core/store.py | 3 +++ gateway/src/dstack/gateway/registry/routes.py | 1 + gateway/src/dstack/gateway/registry/schemas.py | 1 + .../src/dstack/gateway/resources/nginx/service.jinja2 | 1 + .../_internal/server/services/gateways/__init__.py | 1 + .../_internal/server/services/gateways/client.py | 2 ++ src/dstack/_internal/server/settings.py | 3 +++ 9 files changed, 23 insertions(+), 1 deletion(-) diff --git a/docs/docs/reference/cli/index.md b/docs/docs/reference/cli/index.md index a3fbdab8f..737723a7c 100644 --- a/docs/docs/reference/cli/index.md +++ b/docs/docs/reference/cli/index.md @@ -421,6 +421,7 @@ $ dstack pool delete --help * `DSTACK_DATABASE_URL` – (Optional) The database URL to use instead of default SQLite. Currently `dstack` supports Postgres. Example: `postgresql+asyncpg://myuser:mypassword@localhost:5432/mydatabase`. Defaults to `None`. * `DSTACK_SERVER_CLOUDWATCH_LOG_GROUP` – (Optional) The CloudWatch Logs group for workloads logs. If not set, the default file-based log storage is used. * `DSTACK_SERVER_CLOUDWATCH_LOG_REGION` — (Optional) The CloudWatch Logs region. Defaults to `None`. + * `DSTACK_DEFAULT_SERVICE_CLIENT_MAX_BODY_SIZE` – (Optional) Request body size limit for services, in bytes. Defaults to 64 MiB. * `DSTACK_SERVER_DIR` – (Optional) Sets path to store data and server configs. Defaults to `~/.dstack/server`. ??? info "Internal environment variables" diff --git a/gateway/src/dstack/gateway/core/nginx.py b/gateway/src/dstack/gateway/core/nginx.py index aee5a363a..45dd29c83 100644 --- a/gateway/src/dstack/gateway/core/nginx.py +++ b/gateway/src/dstack/gateway/core/nginx.py @@ -37,6 +37,8 @@ class ServiceConfig(SiteConfig): project: str service_id: str auth: bool + # Default for compatibility with older state.json. TODO: remove after a few releases + client_max_body_size: int = 1024 * 1024 servers: Dict[str, str] = {} @@ -75,7 +77,13 @@ async def set_acme_settings( ) async def register_service( - self, project: str, service_id: str, domain: str, https: bool, auth: bool + self, + project: str, + service_id: str, + domain: str, + https: bool, + auth: bool, + client_max_body_size: int, ): config_name = self.get_config_name(domain) conf = ServiceConfig( @@ -84,6 +92,7 @@ async def register_service( domain=domain, https=https, auth=auth, + client_max_body_size=client_max_body_size, ) async with self._lock: diff --git a/gateway/src/dstack/gateway/core/store.py b/gateway/src/dstack/gateway/core/store.py index e2a221ea8..f4d86dc61 100644 --- a/gateway/src/dstack/gateway/core/store.py +++ b/gateway/src/dstack/gateway/core/store.py @@ -38,6 +38,8 @@ class Service(BaseModel): domain: str https: bool = True auth: bool + # Default for compatibility with older state.json. TODO: remove after a few releases + client_max_body_size: int = 1024 * 1024 options: dict replicas: List[Replica] = [] @@ -107,6 +109,7 @@ async def register_service(self, project: str, service: Service, ssh_private_key service.domain, service.https, service.auth, + service.client_max_body_size, ) stack.push_async_callback( supress_exc_async(self.nginx.unregister_domain, service.domain) diff --git a/gateway/src/dstack/gateway/registry/routes.py b/gateway/src/dstack/gateway/registry/routes.py index ad8e6390d..79e4709cf 100644 --- a/gateway/src/dstack/gateway/registry/routes.py +++ b/gateway/src/dstack/gateway/registry/routes.py @@ -24,6 +24,7 @@ async def post_register_service( https=body.https, auth=body.auth, options=body.options, + client_max_body_size=body.client_max_body_size, ), body.ssh_private_key, ) diff --git a/gateway/src/dstack/gateway/registry/schemas.py b/gateway/src/dstack/gateway/registry/schemas.py index b0227bdb8..92d2f9121 100644 --- a/gateway/src/dstack/gateway/registry/schemas.py +++ b/gateway/src/dstack/gateway/registry/schemas.py @@ -8,6 +8,7 @@ class RegisterServiceRequest(BaseModel): domain: str https: bool = True auth: bool = True + client_max_body_size: int options: dict = {} ssh_private_key: str diff --git a/gateway/src/dstack/gateway/resources/nginx/service.jinja2 b/gateway/src/dstack/gateway/resources/nginx/service.jinja2 index f2537af01..57b687fa7 100644 --- a/gateway/src/dstack/gateway/resources/nginx/service.jinja2 +++ b/gateway/src/dstack/gateway/resources/nginx/service.jinja2 @@ -11,6 +11,7 @@ server { server_name {{ domain }}; access_log /var/log/nginx/dstack.access.log dstack_stat; + client_max_body_size {{ client_max_body_size }}; location / { {% if auth %} diff --git a/src/dstack/_internal/server/services/gateways/__init__.py b/src/dstack/_internal/server/services/gateways/__init__.py index 729cd834c..4ee0c386b 100644 --- a/src/dstack/_internal/server/services/gateways/__init__.py +++ b/src/dstack/_internal/server/services/gateways/__init__.py @@ -412,6 +412,7 @@ async def _register_service_in_gateway( service_https=service_https, gateway_https=gateway_https, auth=run_spec.configuration.auth, + client_max_body_size=settings.DEFAULT_SERVICE_CLIENT_MAX_BODY_SIZE, options=service_spec.options, ssh_private_key=run_model.project.ssh_private_key, ) diff --git a/src/dstack/_internal/server/services/gateways/client.py b/src/dstack/_internal/server/services/gateways/client.py index b73b879dc..87d190008 100644 --- a/src/dstack/_internal/server/services/gateways/client.py +++ b/src/dstack/_internal/server/services/gateways/client.py @@ -47,6 +47,7 @@ async def register_service( service_https: bool, gateway_https: bool, auth: bool, + client_max_body_size: int, options: dict, ssh_private_key: str, ): @@ -59,6 +60,7 @@ async def register_service( "domain": domain, "https": service_https, "auth": auth, + "client_max_body_size": client_max_body_size, "options": options, "ssh_private_key": ssh_private_key, } diff --git a/src/dstack/_internal/server/settings.py b/src/dstack/_internal/server/settings.py index ff79a6d86..d515af678 100644 --- a/src/dstack/_internal/server/settings.py +++ b/src/dstack/_internal/server/settings.py @@ -50,6 +50,9 @@ ACME_SERVER = os.getenv("DSTACK_ACME_SERVER") ACME_EAB_KID = os.getenv("DSTACK_ACME_EAB_KID") ACME_EAB_HMAC_KEY = os.getenv("DSTACK_ACME_EAB_HMAC_KEY") +DEFAULT_SERVICE_CLIENT_MAX_BODY_SIZE = int( + os.getenv("DSTACK_DEFAULT_SERVICE_CLIENT_MAX_BODY_SIZE", 64 * 1024 * 1024) +) USER_PROJECT_DEFAULT_QUOTA = int(os.getenv("DSTACK_USER_PROJECT_DEFAULT_QUOTA", 10))