YARN-9184. Add a system flag to allow update to latest docker images.
Contributed by Zhaohui Xin
This commit is contained in:
parent
7806403842
commit
3dc2523266
@ -1936,6 +1936,10 @@ public static boolean isAclEnabled(Configuration conf) {
|
|||||||
public static final String NM_DOCKER_IMAGE_NAME =
|
public static final String NM_DOCKER_IMAGE_NAME =
|
||||||
DOCKER_CONTAINER_RUNTIME_PREFIX + "image-name";
|
DOCKER_CONTAINER_RUNTIME_PREFIX + "image-name";
|
||||||
|
|
||||||
|
/** Default option to decide whether to pull the latest image or not. **/
|
||||||
|
public static final String NM_DOCKER_IMAGE_UPDATE =
|
||||||
|
DOCKER_CONTAINER_RUNTIME_PREFIX + "image-update";
|
||||||
|
|
||||||
/** Capabilities allowed (and added by default) for docker containers. **/
|
/** Capabilities allowed (and added by default) for docker containers. **/
|
||||||
public static final String NM_DOCKER_CONTAINER_CAPABILITIES =
|
public static final String NM_DOCKER_CONTAINER_CAPABILITIES =
|
||||||
DOCKER_CONTAINER_RUNTIME_PREFIX + "capabilities";
|
DOCKER_CONTAINER_RUNTIME_PREFIX + "capabilities";
|
||||||
|
@ -1761,6 +1761,13 @@
|
|||||||
<value></value>
|
<value></value>
|
||||||
</property>
|
</property>
|
||||||
|
|
||||||
|
<property>
|
||||||
|
<description>Default option to decide whether to pull the latest image
|
||||||
|
or not.</description>
|
||||||
|
<name>yarn.nodemanager.runtime.linux.docker.image-update</name>
|
||||||
|
<value>false</value>
|
||||||
|
</property>
|
||||||
|
|
||||||
<property>
|
<property>
|
||||||
<description>This configuration setting determines if privileged docker
|
<description>This configuration setting determines if privileged docker
|
||||||
containers are allowed on this cluster. Privileged containers are granted
|
containers are allowed on this cluster. Privileged containers are granted
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker.DockerCommandExecutor;
|
import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker.DockerCommandExecutor;
|
||||||
import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker.DockerExecCommand;
|
import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker.DockerExecCommand;
|
||||||
import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker.DockerKillCommand;
|
import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker.DockerKillCommand;
|
||||||
|
import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker.DockerPullCommand;
|
||||||
import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker.DockerRmCommand;
|
import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker.DockerRmCommand;
|
||||||
import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker.DockerStartCommand;
|
import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker.DockerStartCommand;
|
||||||
import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker.DockerVolumeCommand;
|
import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker.DockerVolumeCommand;
|
||||||
@ -272,6 +273,7 @@ public class DockerLinuxContainerRuntime implements LinuxContainerRuntime {
|
|||||||
private Map<String, CsiAdaptorProtocol> csiClients = new HashMap<>();
|
private Map<String, CsiAdaptorProtocol> csiClients = new HashMap<>();
|
||||||
private PrivilegedOperationExecutor privilegedOperationExecutor;
|
private PrivilegedOperationExecutor privilegedOperationExecutor;
|
||||||
private String defaultImageName;
|
private String defaultImageName;
|
||||||
|
private Boolean defaultImageUpdate;
|
||||||
private Set<String> allowedNetworks = new HashSet<>();
|
private Set<String> allowedNetworks = new HashSet<>();
|
||||||
private String defaultNetwork;
|
private String defaultNetwork;
|
||||||
private CGroupsHandler cGroupsHandler;
|
private CGroupsHandler cGroupsHandler;
|
||||||
@ -352,6 +354,8 @@ public void initialize(Configuration conf, Context nmContext)
|
|||||||
defaultTmpfsMounts.clear();
|
defaultTmpfsMounts.clear();
|
||||||
defaultImageName = conf.getTrimmed(
|
defaultImageName = conf.getTrimmed(
|
||||||
YarnConfiguration.NM_DOCKER_IMAGE_NAME, "");
|
YarnConfiguration.NM_DOCKER_IMAGE_NAME, "");
|
||||||
|
defaultImageUpdate = conf.getBoolean(
|
||||||
|
YarnConfiguration.NM_DOCKER_IMAGE_UPDATE, false);
|
||||||
allowedNetworks.addAll(Arrays.asList(
|
allowedNetworks.addAll(Arrays.asList(
|
||||||
conf.getTrimmedStrings(
|
conf.getTrimmedStrings(
|
||||||
YarnConfiguration.NM_DOCKER_ALLOWED_CONTAINER_NETWORKS,
|
YarnConfiguration.NM_DOCKER_ALLOWED_CONTAINER_NETWORKS,
|
||||||
@ -802,6 +806,7 @@ public void launchContainer(ContainerRuntimeContext ctx)
|
|||||||
throws ContainerExecutionException {
|
throws ContainerExecutionException {
|
||||||
Container container = ctx.getContainer();
|
Container container = ctx.getContainer();
|
||||||
ContainerId containerId = container.getContainerId();
|
ContainerId containerId = container.getContainerId();
|
||||||
|
String containerIdStr = containerId.toString();
|
||||||
Map<String, String> environment = container.getLaunchContext()
|
Map<String, String> environment = container.getLaunchContext()
|
||||||
.getEnvironment();
|
.getEnvironment();
|
||||||
String imageName = environment.get(ENV_DOCKER_CONTAINER_IMAGE);
|
String imageName = environment.get(ENV_DOCKER_CONTAINER_IMAGE);
|
||||||
@ -822,7 +827,10 @@ public void launchContainer(ContainerRuntimeContext ctx)
|
|||||||
|
|
||||||
validateImageName(imageName);
|
validateImageName(imageName);
|
||||||
|
|
||||||
String containerIdStr = containerId.toString();
|
if (defaultImageUpdate) {
|
||||||
|
pullImageFromRemote(containerIdStr, imageName);
|
||||||
|
}
|
||||||
|
|
||||||
String runAsUser = ctx.getExecutionAttribute(RUN_AS_USER);
|
String runAsUser = ctx.getExecutionAttribute(RUN_AS_USER);
|
||||||
String dockerRunAsUser = runAsUser;
|
String dockerRunAsUser = runAsUser;
|
||||||
Path containerWorkDir = ctx.getExecutionAttribute(CONTAINER_WORK_DIR);
|
Path containerWorkDir = ctx.getExecutionAttribute(CONTAINER_WORK_DIR);
|
||||||
@ -1379,6 +1387,25 @@ public static void validateImageName(String imageName)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void pullImageFromRemote(String containerIdStr, String imageName)
|
||||||
|
throws ContainerExecutionException {
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
DockerPullCommand dockerPullCommand = new DockerPullCommand(imageName);
|
||||||
|
LOG.debug("now pulling docker image." + " image name: " + imageName + ","
|
||||||
|
+ " container: " + containerIdStr);
|
||||||
|
|
||||||
|
DockerCommandExecutor.executeDockerCommand(dockerPullCommand,
|
||||||
|
containerIdStr, null,
|
||||||
|
privilegedOperationExecutor, false, nmContext);
|
||||||
|
|
||||||
|
long end = System.currentTimeMillis();
|
||||||
|
long pullImageTimeMs = end - start;
|
||||||
|
LOG.debug("pull docker image done with "
|
||||||
|
+ String.valueOf(pullImageTimeMs) + "ms spent."
|
||||||
|
+ " image name: " + imageName + ","
|
||||||
|
+ " container: " + containerIdStr);
|
||||||
|
}
|
||||||
|
|
||||||
private void executeLivelinessCheck(ContainerRuntimeContext ctx)
|
private void executeLivelinessCheck(ContainerRuntimeContext ctx)
|
||||||
throws ContainerExecutionException {
|
throws ContainerExecutionException {
|
||||||
String procFs = ctx.getExecutionAttribute(PROCFS);
|
String procFs = ctx.getExecutionAttribute(PROCFS);
|
||||||
|
@ -533,6 +533,121 @@ public void testDockerContainerLaunchWithDefaultImage()
|
|||||||
dockerCommands.get(counter));
|
dockerCommands.get(counter));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDockerContainerLaunchWithoutDefaultImageUpdate()
|
||||||
|
throws ContainerExecutionException, PrivilegedOperationException,
|
||||||
|
IOException {
|
||||||
|
DockerLinuxContainerRuntime runtime = new DockerLinuxContainerRuntime(
|
||||||
|
mockExecutor, mockCGroupsHandler);
|
||||||
|
conf.setBoolean(YarnConfiguration.NM_DOCKER_IMAGE_UPDATE, false);
|
||||||
|
|
||||||
|
runtime.initialize(conf, nmContext);
|
||||||
|
runtime.launchContainer(builder.build());
|
||||||
|
List<String> dockerCommands = readDockerCommands();
|
||||||
|
Assert.assertEquals(false,
|
||||||
|
conf.getBoolean(YarnConfiguration.NM_DOCKER_IMAGE_UPDATE, false));
|
||||||
|
|
||||||
|
int expected = 13;
|
||||||
|
int counter = 0;
|
||||||
|
Assert.assertEquals(expected, dockerCommands.size());
|
||||||
|
Assert.assertEquals("[docker-command-execution]",
|
||||||
|
dockerCommands.get(counter++));
|
||||||
|
Assert.assertEquals(" cap-add=SYS_CHROOT,NET_BIND_SERVICE",
|
||||||
|
dockerCommands.get(counter++));
|
||||||
|
Assert.assertEquals(" cap-drop=ALL", dockerCommands.get(counter++));
|
||||||
|
Assert.assertEquals(" detach=true", dockerCommands.get(counter++));
|
||||||
|
Assert.assertEquals(" docker-command=run", dockerCommands.get(counter++));
|
||||||
|
Assert.assertEquals(" group-add=" + String.join(",", groups),
|
||||||
|
dockerCommands.get(counter++));
|
||||||
|
Assert
|
||||||
|
.assertEquals(" image=busybox:latest", dockerCommands.get(counter++));
|
||||||
|
Assert.assertEquals(
|
||||||
|
" launch-command=bash,/test_container_work_dir/launch_container.sh",
|
||||||
|
dockerCommands.get(counter++));
|
||||||
|
Assert.assertEquals(" mounts="
|
||||||
|
+ "/test_container_log_dir:/test_container_log_dir:rw,"
|
||||||
|
+ "/test_application_local_dir:/test_application_local_dir:rw,"
|
||||||
|
+ "/test_filecache_dir:/test_filecache_dir:ro,"
|
||||||
|
+ "/test_user_filecache_dir:/test_user_filecache_dir:ro",
|
||||||
|
dockerCommands.get(counter++));
|
||||||
|
Assert.assertEquals(
|
||||||
|
" name=container_e11_1518975676334_14532816_01_000001",
|
||||||
|
dockerCommands.get(counter++));
|
||||||
|
Assert.assertEquals(" net=host", dockerCommands.get(counter++));
|
||||||
|
Assert.assertEquals(" user=" + uidGidPair, dockerCommands.get(counter++));
|
||||||
|
Assert.assertEquals(" workdir=/test_container_work_dir",
|
||||||
|
dockerCommands.get(counter));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDockerContainerLaunchWithDefaultImageUpdate()
|
||||||
|
throws ContainerExecutionException, PrivilegedOperationException,
|
||||||
|
IOException {
|
||||||
|
DockerLinuxContainerRuntime runtime = new DockerLinuxContainerRuntime(
|
||||||
|
mockExecutor, mockCGroupsHandler);
|
||||||
|
conf.setBoolean(YarnConfiguration.NM_DOCKER_IMAGE_UPDATE, true);
|
||||||
|
|
||||||
|
runtime.initialize(conf, nmContext);
|
||||||
|
runtime.launchContainer(builder.build());
|
||||||
|
|
||||||
|
ArgumentCaptor<PrivilegedOperation> opCaptor = ArgumentCaptor.forClass(
|
||||||
|
PrivilegedOperation.class);
|
||||||
|
|
||||||
|
//Two invocations expected.
|
||||||
|
verify(mockExecutor, times(2))
|
||||||
|
.executePrivilegedOperation(any(), opCaptor.capture(), any(),
|
||||||
|
any(), anyBoolean(), anyBoolean());
|
||||||
|
|
||||||
|
List<PrivilegedOperation> allCaptures = opCaptor.getAllValues();
|
||||||
|
|
||||||
|
// pull image from remote hub firstly
|
||||||
|
PrivilegedOperation op = allCaptures.get(0);
|
||||||
|
Assert.assertEquals(PrivilegedOperation.OperationType
|
||||||
|
.RUN_DOCKER_CMD, op.getOperationType());
|
||||||
|
|
||||||
|
File commandFile = new File(StringUtils.join(",", op.getArguments()));
|
||||||
|
FileInputStream fileInputStream = new FileInputStream(commandFile);
|
||||||
|
String fileContent = new String(IOUtils.toByteArray(fileInputStream));
|
||||||
|
Assert.assertEquals("[docker-command-execution]\n"
|
||||||
|
+ " docker-command=pull\n"
|
||||||
|
+ " image=busybox:latest\n", fileContent);
|
||||||
|
fileInputStream.close();
|
||||||
|
|
||||||
|
// launch docker container
|
||||||
|
List<String> dockerCommands = readDockerCommands(2);
|
||||||
|
|
||||||
|
int expected = 13;
|
||||||
|
int counter = 0;
|
||||||
|
Assert.assertEquals(expected, dockerCommands.size());
|
||||||
|
Assert.assertEquals("[docker-command-execution]",
|
||||||
|
dockerCommands.get(counter++));
|
||||||
|
Assert.assertEquals(" cap-add=SYS_CHROOT,NET_BIND_SERVICE",
|
||||||
|
dockerCommands.get(counter++));
|
||||||
|
Assert.assertEquals(" cap-drop=ALL", dockerCommands.get(counter++));
|
||||||
|
Assert.assertEquals(" detach=true", dockerCommands.get(counter++));
|
||||||
|
Assert.assertEquals(" docker-command=run", dockerCommands.get(counter++));
|
||||||
|
Assert.assertEquals(" group-add=" + String.join(",", groups),
|
||||||
|
dockerCommands.get(counter++));
|
||||||
|
Assert
|
||||||
|
.assertEquals(" image=busybox:latest", dockerCommands.get(counter++));
|
||||||
|
Assert.assertEquals(
|
||||||
|
" launch-command=bash,/test_container_work_dir/launch_container.sh",
|
||||||
|
dockerCommands.get(counter++));
|
||||||
|
Assert.assertEquals(" mounts="
|
||||||
|
+ "/test_container_log_dir:/test_container_log_dir:rw,"
|
||||||
|
+ "/test_application_local_dir:/test_application_local_dir:rw,"
|
||||||
|
+ "/test_filecache_dir:/test_filecache_dir:ro,"
|
||||||
|
+ "/test_user_filecache_dir:/test_user_filecache_dir:ro",
|
||||||
|
dockerCommands.get(counter++));
|
||||||
|
Assert.assertEquals(
|
||||||
|
" name=container_e11_1518975676334_14532816_01_000001",
|
||||||
|
dockerCommands.get(counter++));
|
||||||
|
Assert.assertEquals(" net=host", dockerCommands.get(counter++));
|
||||||
|
Assert.assertEquals(" user=" + uidGidPair, dockerCommands.get(counter++));
|
||||||
|
Assert.assertEquals(" workdir=/test_container_work_dir",
|
||||||
|
dockerCommands.get(counter));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testContainerLaunchWithUserRemapping()
|
public void testContainerLaunchWithUserRemapping()
|
||||||
throws ContainerExecutionException, PrivilegedOperationException,
|
throws ContainerExecutionException, PrivilegedOperationException,
|
||||||
|
@ -131,6 +131,15 @@ The following properties should be set in yarn-site.xml:
|
|||||||
</description>
|
</description>
|
||||||
</property>
|
</property>
|
||||||
|
|
||||||
|
<property>
|
||||||
|
<name>yarn.nodemanager.runtime.linux.docker.image-update</name>
|
||||||
|
<value>false</value>
|
||||||
|
<description>
|
||||||
|
Optional. Default option to decide whether to pull the latest image
|
||||||
|
or not.
|
||||||
|
</description>
|
||||||
|
</property>
|
||||||
|
|
||||||
<property>
|
<property>
|
||||||
<name>yarn.nodemanager.runtime.linux.docker.allowed-container-networks</name>
|
<name>yarn.nodemanager.runtime.linux.docker.allowed-container-networks</name>
|
||||||
<value>host,none,bridge</value>
|
<value>host,none,bridge</value>
|
||||||
|
Loading…
Reference in New Issue
Block a user