diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/conf/YarnConfiguration.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/conf/YarnConfiguration.java index be4c0232035..974dcae42e1 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/conf/YarnConfiguration.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/conf/YarnConfiguration.java @@ -1238,6 +1238,24 @@ private static void addDeprecatedKeys() { /** Default list for users allowed to run privileged containers is empty. */ public static final String DEFAULT_NM_DOCKER_PRIVILEGED_CONTAINERS_ACL = ""; + /** The list of allowed images when launching container using the + * DockerContainerRuntime + */ + public static final String NM_DOCKER_ALLOWED_IMAGES = + DOCKER_CONTAINER_RUNTIME_PREFIX + "allowed-images"; + + /** The default allowed images to be used when using the + * DockerContainerRuntime + */ + public static final String[] NM_DOCKER_DEFAULT_ALLOWED_IMAGES = + {""}; + + /** The image used when launching container using the + * DockerContainerRuntime + */ + public static final String NM_DOCKER_IMAGE_NAME = + DOCKER_CONTAINER_RUNTIME_PREFIX + "image-name"; + /** The path to the Linux container executor.*/ public static final String NM_LINUX_CONTAINER_EXECUTOR_PATH = NM_PREFIX + "linux-container-executor.path"; diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/resources/yarn-default.xml b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/resources/yarn-default.xml index 881ca8e3590..98bdc94e9b8 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/resources/yarn-default.xml +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/resources/yarn-default.xml @@ -1493,6 +1493,13 @@ + + The list of allowed images when launching container using the + DockerContainerRuntime + yarn.nodemanager.runtime.linux.docker.allowed-images + + + This flag determines whether memory limit will be set for the Windows Job Object of the containers launched by the default container executor. diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/LinuxContainerExecutor.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/LinuxContainerExecutor.java index e39592b5289..9c5cb2443e3 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/LinuxContainerExecutor.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/LinuxContainerExecutor.java @@ -40,6 +40,7 @@ import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.resources.ResourceHandlerModule; import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.DelegatingLinuxContainerRuntime; import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntime; +import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.NodeSpecificLinuxContainerRuntime; import org.apache.hadoop.yarn.server.nodemanager.containermanager.localizer.ContainerLocalizer; import org.apache.hadoop.yarn.server.nodemanager.containermanager.runtime.ContainerExecutionException; import org.apache.hadoop.yarn.server.nodemanager.containermanager.runtime.ContainerRuntimeContext; @@ -199,7 +200,7 @@ public void init() throws IOException { try { if (linuxContainerRuntime == null) { - LinuxContainerRuntime runtime = new DelegatingLinuxContainerRuntime(); + LinuxContainerRuntime runtime = new NodeSpecificLinuxContainerRuntime(); runtime.initialize(conf); this.linuxContainerRuntime = runtime; diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/NodeSpecificLinuxContainerRuntime.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/NodeSpecificLinuxContainerRuntime.java new file mode 100644 index 00000000000..c49d3518e46 --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/NodeSpecificLinuxContainerRuntime.java @@ -0,0 +1,154 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime; + +import org.apache.hadoop.classification.InterfaceAudience; +import org.apache.hadoop.classification.InterfaceStability; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.yarn.conf.YarnConfiguration; +import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.privileged.PrivilegedOperationExecutor; +import org.apache.hadoop.yarn.server.nodemanager.containermanager.runtime.ContainerExecutionException; +import org.apache.hadoop.yarn.server.nodemanager.containermanager.runtime.ContainerRuntimeContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.util.Arrays; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +@InterfaceAudience.Private +@InterfaceStability.Unstable +public class NodeSpecificLinuxContainerRuntime implements LinuxContainerRuntime { + private static final Logger LOG = LoggerFactory.getLogger( + NodeSpecificLinuxContainerRuntime.class); + private boolean isDockerSupported; + private String defaultDockerImage; + private LinuxContainerRuntime linuxContainerRuntime; + private EnumSet allowedRuntimes = + EnumSet.noneOf(LinuxContainerRuntimeConstants.RuntimeType.class); + private Set allowedImages = new HashSet<>(); + + @Override + public void initialize(Configuration conf) + throws ContainerExecutionException { + String[] configuredRuntimes = conf.getTrimmedStrings( + YarnConfiguration.LINUX_CONTAINER_RUNTIME_ALLOWED_RUNTIMES, + YarnConfiguration.DEFAULT_LINUX_CONTAINER_RUNTIME_ALLOWED_RUNTIMES); + for (String configuredRuntime : configuredRuntimes) { + try { + allowedRuntimes.add( + LinuxContainerRuntimeConstants.RuntimeType.valueOf( + configuredRuntime.toUpperCase())); + } catch (IllegalArgumentException e) { + throw new ContainerExecutionException("Invalid runtime set in " + + YarnConfiguration.LINUX_CONTAINER_RUNTIME_ALLOWED_RUNTIMES + " : " + + configuredRuntime); + } + } + + PrivilegedOperationExecutor privilegedOperationExecutor = + PrivilegedOperationExecutor.getInstance(conf); + isDockerSupported = new File("/usr/bin/docker").exists(); + if(isDockerSupported && isRuntimeAllowed(LinuxContainerRuntimeConstants.RuntimeType.DOCKER)) { + linuxContainerRuntime = new DockerLinuxContainerRuntime( + privilegedOperationExecutor); + allowedImages.addAll(Arrays.asList( + conf.getStrings(YarnConfiguration.NM_DOCKER_ALLOWED_IMAGES, + YarnConfiguration.NM_DOCKER_DEFAULT_ALLOWED_IMAGES))); + defaultDockerImage = conf.get(YarnConfiguration.NM_DOCKER_IMAGE_NAME); + if(!allowedImages.contains(defaultDockerImage)) { + String message = "Default Docker Image: " + defaultDockerImage + + " is not in the set of allowed images: " + allowedImages + + ". Please check configuration"; + LOG.warn(message); + throw new ContainerExecutionException(message); + } + } else if (isRuntimeAllowed(LinuxContainerRuntimeConstants.RuntimeType.DEFAULT)) { + linuxContainerRuntime = new DefaultLinuxContainerRuntime( + privilegedOperationExecutor); + } + + if(linuxContainerRuntime == null) { + throw new ContainerExecutionException("linuxContainerRuntime not set. " + + "No allowed container runtimes available"); + } + LOG.info("Using container runtime: " + linuxContainerRuntime.getClass() + .getSimpleName()); + linuxContainerRuntime.initialize(conf); + } + + private LinuxContainerRuntime pickContainerRuntime() { + return linuxContainerRuntime; + } + + @Override + public void prepareContainer(ContainerRuntimeContext ctx) + throws ContainerExecutionException { + LinuxContainerRuntime runtime = pickContainerRuntime(); + runtime.prepareContainer(ctx); + } + + @Override + public void launchContainer(ContainerRuntimeContext ctx) + throws ContainerExecutionException { + LinuxContainerRuntime runtime = pickContainerRuntime(); + if(runtime.getClass() == DockerLinuxContainerRuntime.class) { + Map env = ctx.getContainer().getLaunchContext().getEnvironment(); + String dockerImage = env.get(DockerLinuxContainerRuntime.ENV_DOCKER_CONTAINER_IMAGE); + if (dockerImage != null && !dockerImage.isEmpty()) { + if(!allowedImages.contains(dockerImage)) { + throw new ContainerExecutionException( + "User-defined Docker Image: " + dockerImage + + " is not in the set of allowed images: " + allowedImages); + } + } else { + env.put(DockerLinuxContainerRuntime.ENV_DOCKER_CONTAINER_IMAGE, + defaultDockerImage); + } + } + runtime.launchContainer(ctx); + } + + @Override + public void signalContainer(ContainerRuntimeContext ctx) + throws ContainerExecutionException { + LinuxContainerRuntime runtime = pickContainerRuntime(); + runtime.signalContainer(ctx); + } + + @Override + public void reapContainer(ContainerRuntimeContext ctx) + throws ContainerExecutionException { + LinuxContainerRuntime runtime = pickContainerRuntime(); + runtime.reapContainer(ctx); + } + + @Override + public boolean useWhitelistEnv(Map env) { + return linuxContainerRuntime.useWhitelistEnv(env); + } + + boolean isRuntimeAllowed( + LinuxContainerRuntimeConstants.RuntimeType runtimeType) { + return allowedRuntimes.contains(runtimeType); + } +}