Skip to content

Commit 855bb59

Browse files
committed
fix(scheduler): create application config (env secrets) outside of deploy / scale to reduce k8s thrasing
1 parent 58b07f5 commit 855bb59

3 files changed

Lines changed: 56 additions & 28 deletions

File tree

rootfs/api/models/app.py

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,9 @@ def scale(self, user, structure): # noqa
389389

390390
def _scale_pods(self, scale_types):
391391
release = self.release_set.latest()
392-
envs = release.config.values
392+
version = "v{}".format(release.version)
393+
image = release.image
394+
envs = self._build_env_vars(release.build.type, version, image, release.config.values)
393395

394396
# see if the app config has deploy batch preference, otherwise use global
395397
batches = release.config.values.get('DEIS_DEPLOY_BATCHES', settings.DEIS_DEPLOY_BATCHES)
@@ -413,7 +415,7 @@ def _scale_pods(self, scale_types):
413415
'tags': release.config.tags,
414416
'envs': envs,
415417
'registry': release.config.registry,
416-
'version': "v{}".format(release.version),
418+
'version': version,
417419
'replicas': replicas,
418420
'app_type': scale_type,
419421
'build_type': release.build.type,
@@ -429,14 +431,17 @@ def _scale_pods(self, scale_types):
429431
self._scheduler.scale,
430432
namespace=self.id,
431433
name=self._get_job_id(scale_type),
432-
image=release.image,
434+
image=image,
433435
entrypoint=self._get_entrypoint(scale_type),
434436
command=self._get_command(scale_type),
435437
**kwargs
436438
)
437439
)
438440

439441
try:
442+
# create the application config in k8s (secret in this case) for all deploy objects
443+
self._scheduler.set_application_config(self.id, envs, version)
444+
440445
async_run(tasks)
441446
except Exception as e:
442447
err = '(scale): {}'.format(e)
@@ -469,7 +474,10 @@ def deploy(self, release, force_deploy=False):
469474

470475
# deploy application to k8s. Also handles initial scaling
471476
deploys = {}
472-
envs = release.config.values
477+
image = release.image
478+
version = "v{}".format(release.version)
479+
envs = self._build_env_vars(release.build.type, version, image, release.config.values)
480+
473481
for scale_type, replicas in self.structure.items():
474482
# only web / cmd are routable
475483
# http://docs.deis.io/en/latest/using_deis/process-types/#web-vs-cmd-process-types
@@ -487,7 +495,7 @@ def deploy(self, release, force_deploy=False):
487495
'registry': release.config.registry,
488496
# only used if there is no previous RC
489497
'replicas': replicas,
490-
'version': "v{}".format(release.version),
498+
'version': version,
491499
'app_type': scale_type,
492500
'build_type': release.build.type,
493501
'healthcheck': release.config.healthcheck,
@@ -509,13 +517,16 @@ def deploy(self, release, force_deploy=False):
509517
raise AlreadyExists('Deployment for {} is already in progress'.format(name))
510518

511519
try:
520+
# create the application config in k8s (secret in this case) for all deploy objects
521+
self._scheduler.set_application_config(self.id, envs, version)
522+
512523
# gather all proc types to be deployed
513524
tasks = [
514525
functools.partial(
515526
self._scheduler.deploy,
516527
namespace=self.id,
517528
name=self._get_job_id(scale_type),
518-
image=release.image,
529+
image=image,
519530
entrypoint=self._get_entrypoint(scale_type),
520531
command=self._get_command(scale_type),
521532
**kwargs
@@ -682,13 +693,16 @@ def pod_name(size=5, chars=string.ascii_lowercase + string.digits):
682693
name = self._get_job_id(scale_type) + '-' + pod_name()
683694
self.log("{} on {} runs '{}'".format(user.username, name, command))
684695

696+
image = release.image
697+
version = "v{}".format(release.version)
698+
envs = self._build_env_vars(release.build.type, version, image, release.config.values)
685699
kwargs = {
686700
'memory': release.config.memory,
687701
'cpu': release.config.cpu,
688702
'tags': release.config.tags,
689-
'envs': release.config.values,
703+
'envs': envs,
690704
'registry': release.config.registry,
691-
'version': "v{}".format(release.version),
705+
'version': version,
692706
'build_type': release.build.type,
693707
'deploy_timeout': deploy_timeout
694708
}
@@ -697,7 +711,7 @@ def pod_name(size=5, chars=string.ascii_lowercase + string.digits):
697711
exit_code, output = self._scheduler.run(
698712
self.id,
699713
name,
700-
release.image,
714+
image,
701715
self._get_entrypoint(scale_type),
702716
[command],
703717
**kwargs
@@ -775,3 +789,27 @@ def _scheduler_filter(self, **kwargs):
775789
labels.update({'type': kwargs['type']})
776790

777791
return labels
792+
793+
def _build_env_vars(self, build_type, version, image, envs):
794+
"""
795+
Build a dict of env vars, setting default vars based on app type
796+
and then combining with the user set ones
797+
"""
798+
799+
# mix in default environment information deis may require
800+
default_env = {
801+
'DEIS_APP': self.id,
802+
'WORKFLOW_RELEASE': version
803+
}
804+
805+
# Check if it is a slug builder image.
806+
if build_type == 'buildpack':
807+
# overwrite image so slugrunner image is used in the container
808+
default_env['SLUG_URL'] = image
809+
default_env['BUILDER_STORAGE'] = settings.APP_STORAGE
810+
default_env['DEIS_MINIO_SERVICE_HOST'] = settings.MINIO_HOST
811+
default_env['DEIS_MINIO_SERVICE_PORT'] = settings.MINIO_PORT
812+
813+
# merge envs on top of default to make envs win
814+
default_env.update(envs)
815+
return default_env

rootfs/api/settings/production.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,11 @@
290290
ROUTER_HOST = os.environ.get('DEIS_ROUTER_SERVICE_HOST', '127.0.0.1')
291291
ROUTER_PORT = os.environ.get('DEIS_ROUTER_SERVICE_PORT', 80)
292292

293+
# minio information
294+
MINIO_HOST = os.environ.get('DEIS_MINIO_SERVICE_HOST', '127.0.0.1')
295+
MINIO_PORT = os.environ.get('DEIS_MINIO_SERVICE_PORT', 80)
296+
APP_STORAGE = os.environ.get('APP_STORAGE')
297+
293298
# check if we can register users with `deis register`
294299
REGISTRATION_MODE = os.environ.get('REGISTRATION_MODE', 'enabled')
295300

rootfs/scheduler/__init__.py

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -392,12 +392,6 @@ def _build_pod_manifest(self, namespace, name, image, **kwargs):
392392
# set the image pull policy that is associated with the application container
393393
kwargs['image_pull_policy'] = settings.DOCKER_BUILDER_IMAGE_PULL_POLICY
394394

395-
# mix in default environment information deis may require
396-
default_env = {
397-
'DEIS_APP': namespace,
398-
'WORKFLOW_RELEASE': kwargs.get("version")
399-
}
400-
401395
# Check if it is a slug builder image.
402396
if build_type == "buildpack":
403397
# only buildpack apps need access to object storage
@@ -422,20 +416,11 @@ def _build_pod_manifest(self, namespace, name, image, **kwargs):
422416
'readOnly': True
423417
}]
424418

425-
default_env['SLUG_URL'] = image
426-
default_env['BUILDER_STORAGE'] = os.getenv("APP_STORAGE")
427-
default_env['DEIS_MINIO_SERVICE_HOST'] = os.getenv("DEIS_MINIO_SERVICE_HOST")
428-
default_env['DEIS_MINIO_SERVICE_PORT'] = os.getenv("DEIS_MINIO_SERVICE_PORT")
429-
430419
# overwrite image so slugrunner image is used in the container
431420
image = settings.SLUGRUNNER_IMAGE
432421
# slugrunner pull policy
433422
kwargs['image_pull_policy'] = settings.SLUG_BUILDER_IMAGE_PULL_POLICY
434423

435-
envs = kwargs.get('envs', {})
436-
default_env.update(envs)
437-
kwargs['envs'] = default_env
438-
439424
# create the base container
440425
container = {}
441426

@@ -469,6 +454,8 @@ def run(self, namespace, name, image, entrypoint, command, **kwargs):
469454
kwargs['command'] = entrypoint
470455
kwargs['args'] = command
471456

457+
# create application config and build the pod manifest
458+
self.set_application_config(namespace, kwargs.get('envs', {}), kwargs.get('version'))
472459
manifest = self._build_pod_manifest(namespace, name, image, **kwargs)
473460

474461
url = self._api("/namespaces/{}/pods", namespace)
@@ -530,7 +517,7 @@ def run(self, namespace, name, image, entrypoint, command, **kwargs):
530517
# cleanup
531518
self.delete_pod(namespace, name)
532519

533-
def set_application_config(self, namespace, env, version):
520+
def set_application_config(self, namespace, envs, version):
534521
# env vars are stored in secrets and mapped to env in k8s
535522
try:
536523
labels = {
@@ -540,7 +527,7 @@ def set_application_config(self, namespace, env, version):
540527

541528
# secrets use dns labels for keys, map those properly here
542529
secrets_env = {}
543-
for key, value in env.items():
530+
for key, value in envs.items():
544531
secrets_env[key.lower().replace('_', '-')] = str(value)
545532

546533
# dictionary sorted by key
@@ -574,8 +561,6 @@ def _set_container(self, namespace, container_name, data, **kwargs):
574561
data['env'] = []
575562

576563
if env:
577-
self.set_application_config(namespace, env, kwargs.get('version'))
578-
579564
# map application configuration (env secret) to env vars
580565
secret_name = "{}-{}-env".format(namespace, kwargs.get('version'))
581566
for key in env.keys():

0 commit comments

Comments
 (0)