From c5964da80a46e45dac50832c5d2cd83077d26736 Mon Sep 17 00:00:00 2001 From: Jason Luo Date: Mon, 25 Feb 2019 22:01:05 +0000 Subject: Add resource_config to specify CPU and menory Enhance K8s plugin used by DCAE Platform to specify CPU and memory Issue-ID: DCAEGEN2-1126 Change-Id: I2431b0b7f67f855122ed4494aa21cad0f99dcc37 Signed-off-by: Jason Luo --- k8s/ChangeLog.md | 4 ++++ k8s/k8s-node-type.yaml | 14 +++++++++++--- k8s/k8sclient/k8sclient.py | 23 +++++++++++++++++++---- k8s/k8splugin/tasks.py | 23 ++++++++++++++++++----- k8s/setup.py | 4 ++-- 5 files changed, 54 insertions(+), 14 deletions(-) diff --git a/k8s/ChangeLog.md b/k8s/ChangeLog.md index 70b5869..7c9a72c 100644 --- a/k8s/ChangeLog.md +++ b/k8s/ChangeLog.md @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [1.4.6] +* Support for specifying CPU and memory resources in a blueprint for a containerized component +* Changes the default time that the plugin will wait for a container to become ready from 300 seconds to 1800 seconds + ## [1.4.5] * DCAEGEN2-1086 update onap-dcae-dcaepolicy-lib version to avoid Consul stores under old service_component_name diff --git a/k8s/k8s-node-type.yaml b/k8s/k8s-node-type.yaml index a6f1559..f63f822 100644 --- a/k8s/k8s-node-type.yaml +++ b/k8s/k8s-node-type.yaml @@ -1,5 +1,5 @@ # ================================================================================ -# Copyright (c) 2017-2018 AT&T Intellectual Property. All rights reserved. +# Copyright (c) 2017-2019 AT&T Intellectual Property. All rights reserved. # ================================================================================ # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,13 +19,13 @@ tosca_definitions_version: cloudify_dsl_1_3 imports: - - http://www.getcloudify.org/spec/cloudify/3.4/types.yaml + - http://www.getcloudify.org/spec/cloudify/4.2/types.yaml plugins: k8s: executor: 'central_deployment_agent' package_name: k8splugin - package_version: 1.4.5 + package_version: 1.4.6 data_types: @@ -119,6 +119,14 @@ node_types: like healthcheck definitions for the Docker component. Health checks are optional. + resource_config: + default: {} + description: > + This is used to specify the cpu and memory request and limit for container. + Please specify "requests" property and/or a "limits" property, with subproproperties + for cpu and memory. (https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/) + + log_info: type: dcae.types.LoggingInfo description: > diff --git a/k8s/k8sclient/k8sclient.py b/k8s/k8sclient/k8sclient.py index 806b41e..84ca84f 100644 --- a/k8s/k8sclient/k8sclient.py +++ b/k8s/k8sclient/k8sclient.py @@ -1,7 +1,7 @@ # ============LICENSE_START======================================================= # org.onap.dcae # ================================================================================ -# Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. +# Copyright (c) 2019 AT&T Intellectual Property. All rights reserved. # ================================================================================ # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -121,7 +121,17 @@ def _create_probe(hc, port, use_tls=False): ) return probe -def _create_container_object(name, image, always_pull, use_tls=False, env={}, container_ports=[], volume_mounts = [], readiness = None): +def _create_resources(resources=None): + if resources is not None: + resources_obj = client.V1ResourceRequirements( + limits = resources.get("limits"), + requests = resources.get("requests") + ) + return resources_obj + else: + return None + +def _create_container_object(name, image, always_pull, use_tls=False, env={}, container_ports=[], volume_mounts = [], resources = None, readiness = None): # Set up environment variables # Copy any passed in environment variables env_vars = [client.V1EnvVar(name=k, value=env[k]) for k in env.keys()] @@ -139,6 +149,10 @@ def _create_container_object(name, image, always_pull, use_tls=False, env={}, co (hc_port, proto) = container_ports[0] probe = _create_probe(readiness, hc_port, use_tls) + if resources: + resources_obj = _create_resources(resources) + else: + resources_obj = None # Define container for pod return client.V1Container( name=name, @@ -147,6 +161,7 @@ def _create_container_object(name, image, always_pull, use_tls=False, env={}, co env=env_vars, ports=[client.V1ContainerPort(container_port=p, protocol=proto) for (p, proto) in container_ports], volume_mounts = volume_mounts, + resources = resources_obj, readiness_probe = probe ) @@ -326,7 +341,7 @@ def _execute_command_in_pod(namespace, pod_name, command): return {"pod" : pod_name, "output" : output} -def deploy(namespace, component_name, image, replicas, always_pull, k8sconfig, **kwargs): +def deploy(namespace, component_name, image, replicas, always_pull, k8sconfig, resources, **kwargs): ''' This will create a k8s Deployment and, if needed, one or two k8s Services. (We are being opinionated in our use of k8s... this code decides what k8s abstractions and features to use. @@ -445,7 +460,7 @@ def deploy(namespace, component_name, image, replicas, always_pull, k8sconfig, * # Create the container for the component # Make it the first container in the pod - containers.insert(0, _create_container_object(component_name, image, always_pull, use_tls, kwargs.get("env", {}), container_ports, volume_mounts, kwargs["readiness"])) + containers.insert(0, _create_container_object(component_name, image, always_pull, use_tls, kwargs.get("env", {}), container_ports, volume_mounts, resources, kwargs["readiness"])) # Build the k8s Deployment object labels = kwargs.get("labels", {}) diff --git a/k8s/k8splugin/tasks.py b/k8s/k8splugin/tasks.py index ba71bd9..7f91513 100644 --- a/k8s/k8splugin/tasks.py +++ b/k8s/k8splugin/tasks.py @@ -1,7 +1,7 @@ # ============LICENSE_START======================================================= # org.onap.dcae # ================================================================================ -# Copyright (c) 2017-2018 AT&T Intellectual Property. All rights reserved. +# Copyright (c) 2017-2019 AT&T Intellectual Property. All rights reserved. # ================================================================================ # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -42,6 +42,7 @@ plugin_conf = configure.configure() CONSUL_HOST = plugin_conf.get("consul_host") CONSUL_INTERNAL_NAME = plugin_conf.get("consul_dns_name") DCAE_NAMESPACE = plugin_conf.get("namespace") +DEFAULT_MAX_WAIT = plugin_conf.get("max_wait", 1800) # Used to construct delivery urls for data router subscribers. Data router in FTL # requires https but this author believes that ONAP is to be defaulted to http. @@ -52,6 +53,7 @@ SERVICE_COMPONENT_NAME = "service_component_name" CONTAINER_ID = "container_id" APPLICATION_CONFIG = "application_config" K8S_DEPLOYMENT = "k8s_deployment" +RESOURCE_KW = "resource_config" # Utility methods @@ -97,6 +99,12 @@ def _done_for_create(**kwargs): ctx.logger.info("Done setting up: {0}".format(name)) return kwargs +def _get_resources(**kwargs): + if kwargs is not None: + ctx.logger.debug("{0}: {1}".format(RESOURCE_KW, kwargs.get(RESOURCE_KW))) + return kwargs.get(RESOURCE_KW) + ctx.logger.info("set resources to None") + return None @merge_inputs_for_create @monkeypatch_loggers @@ -287,12 +295,14 @@ def _create_and_start_container(container_name, image, **kwargs): ctx.logger.info("Starting k8s deployment for {}, image: {}, env: {}, kwargs: {}".format(container_name, image, env, kwargs)) ctx.logger.info("Passing k8sconfig: {}".format(plugin_conf)) replicas = kwargs.get("replicas", 1) + resource_config = _get_resources(**kwargs) _,dep = k8sclient.deploy(DCAE_NAMESPACE, container_name, image, replicas = replicas, always_pull=kwargs.get("always_pull_image", False), k8sconfig=plugin_conf, + resources = resource_config, volumes=kwargs.get("volumes",[]), ports=kwargs.get("ports",[]), msb_list=kwargs.get("msb_list"), @@ -387,6 +397,7 @@ def _create_and_start_component(**kwargs): "log_info": kwargs.get("log_info", {}), "tls_info": kwargs.get("tls_info", {}), "labels": kwargs.get("labels", {}), + "resource_config": kwargs.get("resource_config",{}), "readiness": kwargs.get("readiness",{})} _create_and_start_container(service_component_name, image, **sub_kwargs) @@ -396,7 +407,7 @@ def _verify_component(**kwargs): """Verify deployment is ready""" service_component_name = kwargs[SERVICE_COMPONENT_NAME] - max_wait = kwargs.get("max_wait", 300) + max_wait = kwargs.get("max_wait", DEFAULT_MAX_WAIT) ctx.logger.info("Waiting up to {0} secs for {1} to become ready".format(max_wait, service_component_name)) if _verify_k8s_deployment(service_component_name, max_wait): @@ -492,6 +503,8 @@ def create_and_start_container_for_platforms(**kwargs): # Capture node properties image = ctx.node.properties["image"] docker_config = ctx.node.properties.get("docker_config", {}) + resource_config = ctx.node.properties.get("resource_config", {}) + kwargs["resource_config"] = resource_config if "healthcheck" in docker_config: kwargs["readiness"] = docker_config["healthcheck"] if "dns_name" in ctx.node.properties: @@ -543,7 +556,7 @@ def create_and_start_container_for_platforms(**kwargs): # Verify that the k8s deployment is ready - max_wait = kwargs.get("max_wait", 300) + max_wait = kwargs.get("max_wait", DEFAULT_MAX_WAIT) ctx.logger.info("Waiting up to {0} secs for {1} to become ready".format(max_wait, service_component_name)) if _verify_k8s_deployment(service_component_name, max_wait): @@ -595,7 +608,7 @@ def scale(replicas, **kwargs): ctx.instance.runtime_properties["replicas"] = replicas # Verify that the scaling took place as expected - max_wait = kwargs.get("max_wait", 300) + max_wait = kwargs.get("max_wait", DEFAULT_MAX_WAIT) ctx.logger.info("Waiting up to {0} secs for {1} to scale and become ready".format(max_wait, service_component_name)) if _verify_k8s_deployment(service_component_name, max_wait): ctx.logger.info("Scaling complete: {0} from {1} to {2} replica(s)".format(service_component_name, current_replicas, replicas)) @@ -618,7 +631,7 @@ def update_image(image, **kwargs): ctx.instance.runtime_properties["image"] = image # Verify that the update took place as expected - max_wait = kwargs.get("max_wait", 300) + max_wait = kwargs.get("max_wait", DEFAULT_MAX_WAIT) ctx.logger.info("Waiting up to {0} secs for {1} to be updated and become ready".format(max_wait, service_component_name)) if _verify_k8s_deployment(service_component_name, max_wait): ctx.logger.info("Update complete: {0} from {1} to {2}".format(service_component_name, current_image, image)) diff --git a/k8s/setup.py b/k8s/setup.py index 9b7f2bb..36fbe05 100644 --- a/k8s/setup.py +++ b/k8s/setup.py @@ -1,7 +1,7 @@ # ============LICENSE_START======================================================= # org.onap.dcae # ================================================================================ -# Copyright (c) 2017-2018 AT&T Intellectual Property. All rights reserved. +# Copyright (c) 2017-2019 AT&T Intellectual Property. All rights reserved. # ================================================================================ # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -23,7 +23,7 @@ from setuptools import setup setup( name='k8splugin', description='Cloudify plugin for containerized components deployed using Kubernetes', - version="1.4.5", + version="1.4.6", author='J. F. Lucas, Michael Hwang, Tommy Carpenter', packages=['k8splugin','k8sclient','msb','configure'], zip_safe=False, -- cgit 1.2.3-korg