Skip to content

Commit 9613a1f

Browse files
committed
feat(controller): new measurement model
1 parent f279fc6 commit 9613a1f

9 files changed

Lines changed: 150 additions & 160 deletions

File tree

charts/controller/templates/controller-cronjob-daily.yaml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,31 @@ spec:
3939
args:
4040
- python /app/manage.py load_db_state_to_k8s
4141
{{- include "controller.envs" . | indent 12 }}
42+
- image: {{.Values.image_registry}}/{{.Values.image_org}}/controller:{{.Values.image_tag}}
43+
imagePullPolicy: {{.Values.pull_policy}}
44+
name: drycc-controller-measure-apps
45+
command:
46+
- /bin/bash
47+
- -c
48+
args:
49+
- python -u /app/manage.py measure_apps
50+
{{- include "controller.envs" . | indent 12 }}
51+
- image: {{.Values.image_registry}}/{{.Values.image_org}}/controller:{{.Values.image_tag}}
52+
imagePullPolicy: {{.Values.pull_policy}}
53+
name: drycc-controller-measure-resources
54+
command:
55+
- /bin/bash
56+
- -c
57+
args:
58+
- python -u /app/manage.py measure_resources
59+
{{- include "controller.envs" . | indent 12 }}
60+
- image: {{.Values.image_registry}}/{{.Values.image_org}}/controller:{{.Values.image_tag}}
61+
imagePullPolicy: {{.Values.pull_policy}}
62+
name: drycc-controller-measure-volumes
63+
command:
64+
- /bin/bash
65+
- -c
66+
args:
67+
- python -u /app/manage.py measure_volumes
68+
{{- include "controller.envs" . | indent 12 }}
4269

charts/controller/templates/controller-cronjob-hourly.yaml

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -32,28 +32,10 @@ spec:
3232
containers:
3333
- image: {{.Values.image_registry}}/{{.Values.image_org}}/controller:{{.Values.image_tag}}
3434
imagePullPolicy: {{.Values.pull_policy}}
35-
name: drycc-controller-measure-instance
35+
name: drycc-controller-measure-networks
3636
command:
3737
- /bin/bash
3838
- -c
3939
args:
40-
- python -u /app/manage.py measure_instance
41-
{{- include "controller.envs" . | indent 12 }}
42-
- image: {{.Values.image_registry}}/{{.Values.image_org}}/controller:{{.Values.image_tag}}
43-
imagePullPolicy: {{.Values.pull_policy}}
44-
name: drycc-controller-measure-resources
45-
command:
46-
- /bin/bash
47-
- -c
48-
args:
49-
- python -u /app/manage.py measure_resources
50-
{{- include "controller.envs" . | indent 12 }}
51-
- image: {{.Values.image_registry}}/{{.Values.image_org}}/controller:{{.Values.image_tag}}
52-
imagePullPolicy: {{.Values.pull_policy}}
53-
name: drycc-controller-measure-volumes
54-
command:
55-
- /bin/bash
56-
- -c
57-
args:
58-
- python -u /app/manage.py measure_volumes
40+
- python -u /app/manage.py measure_networks
5941
{{- include "controller.envs" . | indent 12 }}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import uuid
2+
import time
3+
import logging
4+
from django.utils import timezone
5+
from django.core.management.base import BaseCommand
6+
from django.conf import settings
7+
from api.models import App
8+
from api.tasks import send_measurements
9+
10+
logger = logging.getLogger(__name__)
11+
12+
13+
class Command(BaseCommand):
14+
"""Management command for push data to manager"""
15+
16+
def handle(self, *args, **options):
17+
if settings.WORKFLOW_MANAGER_URL is not None:
18+
timestamp = time.time()
19+
task_id = uuid.uuid4().hex
20+
logger.info(f"pushing {task_id} resources to workflow_manager when {timezone.now()}")
21+
app_list = []
22+
for app in App.objects.all():
23+
app_list.extend(app.to_measurements(timestamp))
24+
if len(app_list) % 1000 == 0:
25+
send_measurements.delay(app_list)
26+
app_list = []
27+
if len(app_list) > 0:
28+
send_measurements.delay(app_list)
29+
logger.info(f"pushed {task_id} resources to workflow_manager when {timezone.now()}")
30+
self.stdout.write("done")

rootfs/api/management/commands/measure_instance.py

Lines changed: 0 additions & 100 deletions
This file was deleted.
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import uuid
2+
import time
3+
import logging
4+
from django.utils import timezone
5+
from django.core.management.base import BaseCommand
6+
from django.conf import settings
7+
from api import influxdb
8+
from api.models import Config
9+
from api.tasks import send_measurements
10+
11+
logger = logging.getLogger(__name__)
12+
13+
14+
class Command(BaseCommand):
15+
"""Management command for push data to manager"""
16+
17+
def _measure_networks(self, config_map, timestamp):
18+
stop = timestamp - (timestamp % 3600)
19+
start = stop - 3600
20+
networks = []
21+
for record in influxdb.query_network_flow(config_map.keys(), start, stop):
22+
app_id = record["namespace"]
23+
owner_id = config_map[app_id].owner_id
24+
networks.append({
25+
"app_id": app_id,
26+
"owner_id": owner_id,
27+
"name": record["pod_name"],
28+
"type": "NETWORK",
29+
"unit": "BYTES",
30+
"usage": record["rx_bytes"] + record["tx_bytes"],
31+
"timestamp": "%d" % start
32+
})
33+
send_measurements.delay(networks)
34+
35+
def handle(self, *args, **options):
36+
if settings.WORKFLOW_MANAGER_URL is not None:
37+
timestamp = int(time.time())
38+
task_id = uuid.uuid4().hex
39+
logger.info(f"pushing {task_id} limits to workflow_manager when {timezone.now()}")
40+
config_map = {}
41+
for config in Config.objects.all():
42+
config_map[config.app_d] = config
43+
if len(config_map) % 1000 == 0:
44+
send_measurements.delay(self._measure_networks(config_map, timestamp))
45+
config_map = {}
46+
if len(config_map) > 0:
47+
send_measurements.delay(self._measure_networks(config_map, timestamp))
48+
logger.info(f"pushed {task_id} limits to workflow_manager when {timezone.now()}")
49+
self.stdout.write("done")

rootfs/api/models/__init__.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,19 @@ def create_auth_token_handle(sender, instance=None, created=False, **kwargs):
259259
Token.objects.create(user=instance)
260260

261261

262+
@receiver(post_save, sender=App)
263+
def app_changed_handle(sender, instance=None, created=False, update_fields=None, **kwargs):
264+
# measure limits to workflow manager
265+
if settings.WORKFLOW_MANAGER_URL is not None and (
266+
created or (
267+
update_fields is not None and "structure" in update_fields)):
268+
timestamp = time.time()
269+
send_measurements.apply_async(
270+
args=[instance.to_measurements(timestamp), ],
271+
queue="priority.middle",
272+
)
273+
274+
262275
@receiver(post_save, sender=Config)
263276
def config_changed_handle(sender, instance=None, created=False, update_fields=None, **kwargs):
264277
# measure limits to workflow manager
@@ -268,7 +281,7 @@ def config_changed_handle(sender, instance=None, created=False, update_fields=No
268281
"cpu" in update_fields or "memory" in update_fields))):
269282
timestamp = time.time()
270283
send_measurements.apply_async(
271-
args=[instance.to_measurements(timestamp), ],
284+
args=[instance.app.to_measurements(timestamp), ],
272285
queue="priority.middle",
273286
)
274287

rootfs/api/models/app.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
from api.models.tls import TLS
2828
from api.models.appsettings import AppSettings
2929
from api.models.volume import Volume
30-
from api.utils import generate_app_name, apply_tasks
30+
from api.utils import generate_app_name, apply_tasks, unit_to_bytes, unit_to_millicpu
3131
from scheduler import KubeHTTPException, KubeException
3232

3333
logger = logging.getLogger(__name__)
@@ -1279,3 +1279,27 @@ def set_application_config(self, release):
12791279
self._scheduler.secret.create(self.id, secret_name, secrets_env, labels=labels)
12801280
else:
12811281
self._scheduler.secret.update(self.id, secret_name, secrets_env, labels=labels)
1282+
1283+
def to_measurements(self, timestamp: float):
1284+
measurements = []
1285+
config = self.config_set.latest()
1286+
for container_type, scale in self.structure.items():
1287+
measurements.append({
1288+
"app_id": self.id,
1289+
"user_id": self.owner_id,
1290+
"name": container_type,
1291+
"type": "CPU",
1292+
"unit": "MILLI",
1293+
"usage": unit_to_millicpu(config.cpu.get(container_type)) * scale,
1294+
"timestamp": "%f" % timestamp
1295+
})
1296+
measurements.append({
1297+
"app_id": self.app_id,
1298+
"user_id": self.owner_id,
1299+
"name": container_type,
1300+
"type": "MEMORY",
1301+
"unit": "BYTES",
1302+
"usage": unit_to_bytes(config.memory.get(container_type)) * scale,
1303+
"timestamp": "%f" % timestamp
1304+
})
1305+
return measurements

rootfs/api/models/config.py

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@
22
import logging
33
from django.conf import settings
44
from django.db import models
5-
from api import influxdb
65
from api.models.release import Release
76
from api.models import UuidAuditedModel
8-
from api.utils import unit_to_bytes, unit_to_millicpu
97
from api.exceptions import DryccException, UnprocessableEntity
108

119

@@ -194,36 +192,3 @@ def save(self, **kwargs):
194192
self.set_tags({'tags': {}})
195193

196194
return super(Config, self).save(**kwargs)
197-
198-
def to_measurements(self, timestamp: float):
199-
assert len(set(self.memory.keys()).difference(self.cpu.keys())) == 0
200-
stop = int(timestamp)
201-
start = stop - (stop % 3600)
202-
records = {}
203-
app_id, owner_id = str(self.app_id), str(self.owner_id)
204-
for record in influxdb.query_container_count([app_id, ], start, stop):
205-
container_type = record["container_name"].replace(f"{app_id}-", "", 1)
206-
if container_type not in records:
207-
records[container_type] = []
208-
records[container_type].append(record)
209-
cpu_measurements = [{
210-
"app_id": app_id,
211-
"user_id": owner_id,
212-
"name": container_type,
213-
"type": "CPU",
214-
"unit": "MILLI",
215-
"usage": unit_to_millicpu(
216-
self.cpu.get(container_type)) * len(records.get(container_type, [])),
217-
"timestamp": "%f" % timestamp
218-
} for container_type in self.cpu.keys()]
219-
memory_measurements = [{
220-
"app_id": app_id,
221-
"user_id": owner_id,
222-
"name": container_type,
223-
"type": "MEMORY",
224-
"unit": "BYTES",
225-
"usage": unit_to_bytes(
226-
self.memory.get(container_type)) * len(records.get(container_type, [])),
227-
"timestamp": "%f" % timestamp
228-
} for container_type in self.memory.keys()]
229-
return cpu_measurements + memory_measurements

rootfs/api/tests/test_config.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ def call_command(self, *args, **kwargs):
411411
from django.core.management import call_command
412412
out = StringIO()
413413
call_command(
414-
"measure_instance",
414+
"measure_apps",
415415
*args,
416416
stdout=out,
417417
stderr=StringIO(),
@@ -422,8 +422,8 @@ def call_command(self, *args, **kwargs):
422422
def test_measure_config(self, *args, **kwargs):
423423
# create
424424
app_id = self.create_app()
425-
url = "/v2/apps/{app_id}/config".format(**locals())
425+
url = f"/v2/apps/{app_id}/config"
426426
body = {'values': json.dumps({'PORT': 5000}), 'cpu': json.dumps({'web': '1000m'})}
427-
response = self.client.post(url, body)
427+
self.client.post(url, body)
428428
out = self.call_command()
429429
self.assertIn(out, "done\n")

0 commit comments

Comments
 (0)