Skip to content

Commit 24f0036

Browse files
authored
Enhancement 2121: overwrite computational services limits (#2129)
* disable sidecar timeout * read limitations from docker labels * add service settings model * add testing of model
1 parent d9591fa commit 24f0036

File tree

5 files changed

+149
-9
lines changed

5 files changed

+149
-9
lines changed
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
from typing import Any, List
2+
3+
from pydantic import BaseModel, Extra, Field
4+
5+
6+
class ServiceSetting(BaseModel):
7+
name: str = Field(..., description="The name of the service setting")
8+
setting_type: str = Field(
9+
...,
10+
description="The type of the service setting (follows Docker REST API naming scheme)",
11+
alias="type",
12+
)
13+
value: Any = Field(
14+
...,
15+
description="The value of the service setting (shall follow Docker REST API scheme for services",
16+
)
17+
18+
class Config:
19+
extra = Extra.forbid
20+
schema_extra = {
21+
"examples": [
22+
# constraints
23+
{
24+
"name": "constraints",
25+
"type": "string",
26+
"value": ["node.platform.os == linux"],
27+
},
28+
# resources
29+
{
30+
"name": "Resources",
31+
"type": "Resources",
32+
"value": {
33+
"Limits": {"NanoCPUs": 4000000000, "MemoryBytes": 17179869184},
34+
"Reservations": {
35+
"NanoCPUs": 100000000,
36+
"MemoryBytes": 536870912,
37+
"GenericResources": [
38+
{"DiscreteResourceSpec": {"Kind": "VRAM", "Value": 1}}
39+
],
40+
},
41+
},
42+
},
43+
# mounts
44+
{
45+
"name": "mount",
46+
"type": "object",
47+
"value": [
48+
{
49+
"ReadOnly": True,
50+
"Source": "/tmp/.X11-unix", # nosec
51+
"Target": "/tmp/.X11-unix", # nosec
52+
"Type": "bind",
53+
}
54+
],
55+
},
56+
# environments
57+
{"name": "env", "type": "string", "value": ["DISPLAY=:0"]},
58+
]
59+
}
60+
61+
62+
class ServiceSettings(BaseModel):
63+
__root__: List[ServiceSetting]
64+
65+
def __iter__(self):
66+
return iter(self.__root__)
67+
68+
def __getitem__(self, item):
69+
return self.__root__[item]
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# pylint:disable=unused-variable
2+
# pylint:disable=unused-argument
3+
# pylint:disable=redefined-outer-name
4+
5+
from typing import Any, Dict
6+
7+
import pytest
8+
from models_library.service_settings import ServiceSetting, ServiceSettings
9+
10+
11+
@pytest.mark.parametrize("example", ServiceSetting.Config.schema_extra["examples"])
12+
def test_service_setting(example: Dict[str, Any]):
13+
service_setting_instance = ServiceSetting.parse_obj(example)
14+
assert service_setting_instance
15+
16+
17+
def test_service_settings():
18+
service_settings_instance = ServiceSettings.parse_obj(
19+
ServiceSetting.Config.schema_extra["examples"]
20+
)
21+
assert service_settings_instance

services/sidecar/Dockerfile

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,6 @@ ENV PATH="${VIRTUAL_ENV}/bin:$PATH"
4242

4343
# environment variables
4444
ENV SWARM_STACK_NAME=""\
45-
SIDECAR_SERVICES_MAX_NANO_CPUS=4000000000 \
46-
SIDECAR_SERVICES_MAX_MEMORY_BYTES=2147483648 \
47-
SIDECAR_SERVICES_TIMEOUT_SECONDS=1200 \
4845
SIDECAR_INPUT_FOLDER=/home/scu/input \
4946
SIDECAR_OUTPUT_FOLDER=/home/scu/output \
5047
SIDECAR_LOG_FOLDER=/home/scu/log
@@ -62,7 +59,7 @@ ENV SC_BUILD_TARGET=build
6259

6360
RUN apt-get update \
6461
&& apt-get install -y --no-install-recommends \
65-
build-essential \
62+
build-essential \
6663
&& apt-get clean \
6764
&& rm -rf /var/lib/apt/lists/*
6865

services/sidecar/src/simcore_service_sidecar/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
os.environ.get("SIDECAR_SERVICES_MAX_MEMORY_BYTES", 2 * pow(1024, 3))
1515
)
1616
SERVICES_TIMEOUT_SECONDS: int = int(
17-
os.environ.get("SIDECAR_SERVICES_TIMEOUT_SECONDS", 20 * 60)
17+
os.environ.get("SIDECAR_SERVICES_TIMEOUT_SECONDS", 0)
1818
)
1919
SWARM_STACK_NAME: str = os.environ.get("SWARM_STACK_NAME", "simcore")
2020

services/sidecar/src/simcore_service_sidecar/executor.py

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,17 @@
66
import time
77
import zipfile
88
from pathlib import Path
9+
from pprint import pformat
910
from typing import Dict, Optional
1011

1112
import aiopg
1213
import attr
1314
from aiodocker import Docker
1415
from aiodocker.containers import DockerContainer
1516
from aiodocker.exceptions import DockerContainerError, DockerError
17+
from models_library.service_settings import ServiceSettings
1618
from packaging import version
19+
from pydantic import BaseModel
1720
from servicelib.logging_utils import log_decorator
1821
from servicelib.utils import fire_and_forget_task, logged_gather
1922
from simcore_sdk import node_data, node_ports_v2
@@ -70,6 +73,13 @@ def log_error(_, path, excinfo):
7073
shutil.rmtree(str(folder), onerror=log_error)
7174

7275

76+
class ServiceResources(BaseModel):
77+
memory_reservation: int = 0
78+
memory_limit: int = config.SERVICES_MAX_MEMORY_BYTES
79+
nano_cpus_reservation: int = 0
80+
nano_cpus_limit: int = config.SERVICES_MAX_NANO_CPUS
81+
82+
7383
@attr.s(auto_attribs=True)
7484
class Executor:
7585
db_engine: aiopg.sa.Engine = None
@@ -80,6 +90,7 @@ class Executor:
8090
stack_name: str = config.SWARM_STACK_NAME
8191
shared_folders: TaskSharedVolumes = None
8292
integration_version: version.Version = version.parse("0.0.0")
93+
service_settings: ServiceSettings = []
8394

8495
@log_decorator(logger=log)
8596
async def run(self):
@@ -187,6 +198,10 @@ async def _process_task_inputs(self) -> Dict:
187198
for port in (await PORTS.inputs).values()
188199
]
189200
)
201+
await self._post_messages(
202+
LogType.LOG,
203+
"[sidecar]Downloaded inputs.",
204+
)
190205
return input_ports
191206

192207
@log_decorator(logger=log)
@@ -225,6 +240,17 @@ async def _pull_image(self):
225240
image_cfg["Config"]["Labels"]["io.simcore.integration-version"]
226241
)["integration-version"]
227242
)
243+
# get service settings
244+
self.service_settings = ServiceSettings.parse_raw(
245+
image_cfg["Config"]["Labels"].get("simcore.service.settings", "[]")
246+
)
247+
log.debug(
248+
"found following service settings: %s", pformat(self.service_settings)
249+
)
250+
await self._post_messages(
251+
LogType.LOG,
252+
f"[sidecar]Pulled {self.task.image['name']}:{self.task.image['tag']}",
253+
)
228254

229255
@log_decorator(logger=log)
230256
async def _create_container_config(self, docker_image: str) -> Dict:
@@ -247,6 +273,32 @@ async def _create_container_config(self, docker_image: str) -> Dict:
247273
)
248274
host_log_path = await get_volume_mount_point(config.SIDECAR_DOCKER_VOLUME_LOG)
249275

276+
# get user-defined IT limitations
277+
async def _get_resource_limitations() -> Dict[str, int]:
278+
resource_limitations = {
279+
"Memory": config.SERVICES_MAX_MEMORY_BYTES,
280+
"NanoCPUs": config.SERVICES_MAX_NANO_CPUS,
281+
}
282+
for setting in self.service_settings:
283+
if not setting.name == "Resources":
284+
continue
285+
if not isinstance(setting.value, dict):
286+
continue
287+
288+
limits = setting.value.get("Limits", {})
289+
resource_limitations["Memory"] = limits.get(
290+
"MemoryBytes", config.SERVICES_MAX_MEMORY_BYTES
291+
)
292+
resource_limitations["NanoCPUs"] = limits.get(
293+
"NanoCPUs", config.SERVICES_MAX_NANO_CPUS
294+
)
295+
log.debug(
296+
"Current resource limitations are %s", pformat(resource_limitations)
297+
)
298+
return resource_limitations
299+
300+
resource_limitations = await _get_resource_limitations()
301+
250302
docker_container_config = {
251303
"Env": env_vars,
252304
"Cmd": "run",
@@ -255,12 +307,12 @@ async def _create_container_config(self, docker_image: str) -> Dict:
255307
"user_id": str(self.user_id),
256308
"study_id": str(self.task.project_id),
257309
"node_id": str(self.task.node_id),
258-
"nano_cpus_limit": str(config.SERVICES_MAX_NANO_CPUS),
259-
"mem_limit": str(config.SERVICES_MAX_MEMORY_BYTES),
310+
"nano_cpus_limit": str(resource_limitations["NanoCPUs"]),
311+
"mem_limit": str(resource_limitations["Memory"]),
260312
},
261313
"HostConfig": {
262-
"Memory": config.SERVICES_MAX_MEMORY_BYTES,
263-
"NanoCPUs": config.SERVICES_MAX_NANO_CPUS,
314+
"Memory": resource_limitations["Memory"],
315+
"NanoCPUs": resource_limitations["NanoCPUs"],
264316
"Init": True,
265317
"AutoRemove": False,
266318
"Binds": [
@@ -272,6 +324,7 @@ async def _create_container_config(self, docker_image: str) -> Dict:
272324
],
273325
},
274326
}
327+
275328
return docker_container_config
276329

277330
@log_decorator(logger=log)

0 commit comments

Comments
 (0)