From 4d601c7ebfffb8ef88a24c401088106be7eba7a1 Mon Sep 17 00:00:00 2001 From: Angel Misevski Date: Fri, 17 Mar 2017 12:17:03 -0400 Subject: [PATCH] Add support for resource limits when running on Openshift Add resource limits to workspace Pods when running on OpenShift. The memory limit is normally obtained from the API request to create the workspace, however it can be overridden via the property `che.openshift.workspace.memory.override`. The cpu limit used is determined by the property `che.openshift.workspace.cpu.limit`. In both cases, the value of the property is passed directly to OpenShift, so any valid quantity is acceptable (e.g. 150Mi, 1Gi, 1024, etc). Signed-off-by: Angel Misevski --- .../WEB-INF/classes/codenvy/che.properties | 3 ++ .../openshift/client/OpenShiftConnector.java | 46 +++++++++++++++---- .../client/OpenShiftConnectorTest.java | 9 ++-- 3 files changed, 47 insertions(+), 11 deletions(-) diff --git a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/codenvy/che.properties b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/codenvy/che.properties index b7c1b752d43..0b96f0457c0 100644 --- a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/codenvy/che.properties +++ b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/codenvy/che.properties @@ -294,6 +294,9 @@ che.openshift.liveness.probe.delay=300 che.openshift.liveness.probe.timeout=1 che.openshift.workspaces.pvc.name=claim-che-workspace che.openshift.workspaces.pvc.quantity=10Gi +che.openshift.workspace.cpu.limit=1 +# Override memory limit used for openshift workspaces. String, e.g. 1300Mi +che.openshift.workspace.memory.override=NULL # Which implementation of DockerConnector to use in managing containers. In general, # the base implementation of DockerConnector is appropriate, but OpenShiftConnector diff --git a/plugins/plugin-docker/che-plugin-openshift-client/src/main/java/org/eclipse/che/plugin/openshift/client/OpenShiftConnector.java b/plugins/plugin-docker/che-plugin-openshift-client/src/main/java/org/eclipse/che/plugin/openshift/client/OpenShiftConnector.java index b518efa4247..d7599f148e7 100644 --- a/plugins/plugin-docker/che-plugin-openshift-client/src/main/java/org/eclipse/che/plugin/openshift/client/OpenShiftConnector.java +++ b/plugins/plugin-docker/che-plugin-openshift-client/src/main/java/org/eclipse/che/plugin/openshift/client/OpenShiftConnector.java @@ -170,6 +170,8 @@ public class OpenShiftConnector extends DockerConnector { private final String cheWorkspaceStorage; private final String cheWorkspaceProjectsStorage; private final String cheServerExternalAddress; + private final String cheWorkspaceCpuLimit; + private final String cheWorkspaceMemory; @Inject public OpenShiftConnector(DockerConnectorConfiguration connectorConfiguration, @@ -183,7 +185,10 @@ public OpenShiftConnector(DockerConnectorConfiguration connectorConfiguration, @Named("che.openshift.workspaces.pvc.name") String workspacesPersistentVolumeClaim, @Named("che.openshift.workspaces.pvc.quantity") String workspacesPvcQuantity, @Named("che.workspace.storage") String cheWorkspaceStorage, - @Named("che.workspace.projects.storage") String cheWorkspaceProjectsStorage) { + @Named("che.workspace.projects.storage") String cheWorkspaceProjectsStorage, + @Named("che.openshift.workspace.cpu.limit") String cheWorkspaceCpuLimit, + @Nullable @Named("che.openshift.workspace.memory.override") String cheWorkspaceMemory) { + super(connectorConfiguration, connectionFactory, authResolver, dockerApiVersionPathPrefixProvider); this.cheServerExternalAddress = cheServerExternalAddress; @@ -194,6 +199,8 @@ public OpenShiftConnector(DockerConnectorConfiguration connectorConfiguration, this.workspacesPvcQuantity = workspacesPvcQuantity; this.cheWorkspaceStorage = cheWorkspaceStorage; this.cheWorkspaceProjectsStorage = cheWorkspaceProjectsStorage; + this.cheWorkspaceCpuLimit = cheWorkspaceCpuLimit; + this.cheWorkspaceMemory = cheWorkspaceMemory; this.openShiftClient = new DefaultOpenShiftClient(); } @@ -252,6 +259,21 @@ public ContainerCreated createContainer(CreateContainerParams createContainerPar String[] volumes = createContainerParams.getContainerConfig().getHostConfig().getBinds(); Map additionalLabels = createContainerParams.getContainerConfig().getLabels(); + + String memoryLimit; + if (!isNullOrEmpty(cheWorkspaceMemory)) { + LOG.info("Che property 'che.openshift.workspace.memory.override' " + + "used to override workspace memory limit to {}.", cheWorkspaceMemory); + memoryLimit = cheWorkspaceMemory; + } else { + long memoryLimitBytes = createContainerParams.getContainerConfig().getHostConfig().getMemory(); + memoryLimit = Long.toString(memoryLimitBytes / 1048576) + "Mi"; + LOG.info("Creating workspace pod with memory limit of {}.", memoryLimit); + } + Map resourceLimits = new HashMap<>(); + resourceLimits.put("memory", new Quantity(memoryLimit)); + resourceLimits.put("cpu", new Quantity(cheWorkspaceCpuLimit)); + String containerID; try { createOpenShiftService(workspaceID, exposedPorts, additionalLabels); @@ -260,16 +282,18 @@ public ContainerCreated createContainer(CreateContainerParams createContainerPar containerName, exposedPorts, envVariables, - volumes); + volumes, + resourceLimits); containerID = waitAndRetrieveContainerID(deploymentName); if (containerID == null) { throw new OpenShiftException("Failed to get the ID of the container running in the OpenShift pod"); } - } catch (IOException e) { + } catch (IOException | KubernetesClientException e) { // Make sure we clean up deployment and service in case of an error -- otherwise Che can end up // in an inconsistent state. LOG.info("Error while creating Pod, removing deployment"); + LOG.info(e.getMessage()); String deploymentName = CHE_OPENSHIFT_RESOURCES_PREFIX + workspaceID; cleanUpWorkspaceResources(deploymentName); openShiftClient.resource(imageStreamTag).delete(); @@ -990,7 +1014,8 @@ private String createOpenShiftDeployment(String workspaceID, String sanitizedContainerName, Set exposedPorts, String[] envVariables, - String[] volumes) { + String[] volumes, + Map resourceLimits) { String deploymentName = CHE_OPENSHIFT_RESOURCES_PREFIX + workspaceID; LOG.info("Creating OpenShift deployment {}", deploymentName); @@ -1018,7 +1043,8 @@ private String createOpenShiftDeployment(String workspaceID, deploymentName, selector, command, - false); + false, + resourceLimits); try { waitAndRetrieveContainerID(deploymentName); @@ -1043,7 +1069,8 @@ private String createOpenShiftDeployment(String workspaceID, deploymentName, selector, null, - true); + true, + resourceLimits); LOG.info("OpenShift deployment {} created", deploymentName); return deployment.getMetadata().getName(); } @@ -1079,7 +1106,8 @@ private Deployment createOpenShiftDeploymentInternal(String workspaceID, Map selector, String[] command, - boolean withSubpath) { + boolean withSubpath, + Map resourceLimits) { Container container = new ContainerBuilder() .withName(sanitizedContainerName) @@ -1093,6 +1121,9 @@ private Deployment createOpenShiftDeploymentInternal(String workspaceID, .withLivenessProbe(getLivenessProbeFrom(exposedPorts)) .withCommand(command) .withVolumeMounts(getVolumeMountsFrom(volumes, workspaceID, withSubpath)) + .withNewResources() + .withLimits(resourceLimits) + .endResources() .build(); PodSpec podSpec = new PodSpecBuilder() @@ -1196,7 +1227,6 @@ private ContainerInfo createContainerInfo(Service svc, // HostConfig HostConfig hostConfig = new HostConfig(); hostConfig.setBinds(new String[0]); - hostConfig.setMemory(imageInfo.getConfig().getMemory()); // Env vars List imageEnv = Arrays.asList(imageContainerConfig.getEnv()); diff --git a/plugins/plugin-docker/che-plugin-openshift-client/src/test/java/org/eclipse/che/plugin/openshift/client/OpenShiftConnectorTest.java b/plugins/plugin-docker/che-plugin-openshift-client/src/test/java/org/eclipse/che/plugin/openshift/client/OpenShiftConnectorTest.java index cc917a61972..dcf1ebb902b 100644 --- a/plugins/plugin-docker/che-plugin-openshift-client/src/test/java/org/eclipse/che/plugin/openshift/client/OpenShiftConnectorTest.java +++ b/plugins/plugin-docker/che-plugin-openshift-client/src/test/java/org/eclipse/che/plugin/openshift/client/OpenShiftConnectorTest.java @@ -37,8 +37,9 @@ public class OpenShiftConnectorTest { private static final String OPENSHIFT_DEFAULT_WORKSPACE_QUANTITY = "10Gi"; private static final String OPENSHIFT_DEFAULT_WORKSPACE_STORAGE = "/data/workspaces"; private static final String OPENSHIFT_DEFAULT_WORKSPACE_PROJECTS_STORAGE = "/projects"; - private static final String CHE_DEFAULT_SERVER_EXTERNAL_ADDRESS = "che.openshift.mini"; - + private static final String CHE_DEFAULT_SERVER_EXTERNAL_ADDRESS = "che.openshift.mini"; + private static final String CHE_WORKSPACE_CPU_LIMIT = "1"; + @Mock private DockerConnectorConfiguration dockerConnectorConfiguration; @Mock @@ -73,7 +74,9 @@ public void shouldGetWorkspaceIDWhenAValidOneIsProvidedInCreateContainerParams() OPENSHIFT_DEFAULT_WORKSPACE_PERSISTENT_VOLUME_CLAIM, OPENSHIFT_DEFAULT_WORKSPACE_QUANTITY, OPENSHIFT_DEFAULT_WORKSPACE_STORAGE, - OPENSHIFT_DEFAULT_WORKSPACE_PROJECTS_STORAGE); + OPENSHIFT_DEFAULT_WORKSPACE_PROJECTS_STORAGE, + CHE_WORKSPACE_CPU_LIMIT, + null); String workspaceID = openShiftConnector.getCheWorkspaceId(createContainerParams); //Then