commit ff59f6f2fd6e459d2924f7947f2456c504ef76e3 Author: Eric Yang Date: Wed Jan 2 18:35:47 2019 -0500 YARN-9003. Add support for multi-homed docker container. Contributed by Eric Yang 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/DockerLinuxContainerRuntime.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/DockerLinuxContainerRuntime.java index 4970c7c..a0f9897 100644 --- 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/DockerLinuxContainerRuntime.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/DockerLinuxContainerRuntime.java @@ -503,9 +503,27 @@ private void checkDockerVolumeCreated( private void validateContainerNetworkType(String network) throws ContainerExecutionException { + boolean allow = true; + // Check for single network if (allowedNetworks.contains(network)) { return; } + // Check for multiple networks + if (network.contains(",")) { + String[] networks = network.split(","); + if (networks.length > 2) { + String msg = "Container Network exceeded maximum allowed limits."; + throw new ContainerExecutionException(msg); + } + for (String net : networks) { + if (!allowedNetworks.contains(net)) { + allow=false; + } + } + if (allow) { + return; + } + } String msg = "Disallowed network: '" + network + "' specified. Allowed networks: are " + allowedNetworks diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/utils/docker-util.c b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/utils/docker-util.c index ced7424..58854aa 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/utils/docker-util.c +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/utils/docker-util.c @@ -932,13 +932,64 @@ free_and_exit: return ret; } +static int check_network_config(const struct configuration *command_config, + const struct configuration *conf) { + int i = 0, valid = 0, ret = 0; + char *docker_binary = get_docker_binary(command_config); + char *docker_network_command = NULL; + char *network_type = (char*) malloc(128); + char **network_names = get_configuration_values_delimiter("net", DOCKER_COMMAND_FILE_SECTION, command_config, ","); + if (network_names != NULL) { + for (i = 0; network_names[i] != NULL; i++) { + docker_network_command = make_string("%s network inspect %s --format='{{.Driver}}'", docker_binary, network_names[i]); + FILE* docker_network = popen(docker_network_command, "r"); + ret = fscanf(docker_network, "%s", network_type); + if (pclose (docker_network) != 0 || ret <= 0) { + fprintf (ERRORFILE, "Could not inspect docker network to get type %s.\n", docker_network_command); + ret = INVALID_DOCKER_NETWORK; + } + if (i == 0 && strcasecmp(network_type, "bridge") == 0) { + valid = 1; + } + if (i > 0 && (strcasecmp(network_type, "bridge") == 0 || + strcasecmp(network_type, "host") == 0)) { + valid = 0; + } + } + } + if (i == 0 || i == 1) { + // No network or one network is always allowed. + ret = 0; + } else if (i == 2 && valid == 1) { + // More than one network specified, and + // first network is bridge network type. + // configuration is allowed. + ret = 0; + } else { + ret = INVALID_DOCKER_NETWORK; + } +cleanup: + free(docker_binary); + free(docker_network_command); + free(network_type); + free_values(network_names); + return ret; +} + static int set_network(const struct configuration *command_config, const struct configuration *conf, args *args) { int ret = 0; + ret = check_network_config(command_config, conf); + if (ret != 0) { + fprintf(ERRORFILE, "Invalid network configuration %d.\n", ret); + ret = INVALID_DOCKER_NETWORK; + return ret; + } + ret = add_param_to_command_if_allowed(command_config, conf, "net", "docker.allowed.networks", "--net=", - 0, 0, args); + 1, 0, args); if (ret != 0) { fprintf(ERRORFILE, "Could not find requested network in allowed networks\n"); ret = INVALID_DOCKER_NETWORK; @@ -948,31 +999,36 @@ static int set_network(const struct configuration *command_config, } static int add_ports_mapping_to_command(const struct configuration *command_config, args *args) { - int i = 0, ret = 0; + int i = 0, ret = 0, expose_port = 0; char *network_type = (char*) malloc(128); char *docker_network_command = NULL; char *docker_binary = get_docker_binary(command_config); - char *network_name = get_configuration_value("net", DOCKER_COMMAND_FILE_SECTION, command_config); + char **network_names = get_configuration_values_delimiter("net", DOCKER_COMMAND_FILE_SECTION, command_config, ","); char **ports_mapping_values = get_configuration_values_delimiter("ports-mapping", DOCKER_COMMAND_FILE_SECTION, command_config, ","); - if (network_name != NULL) { - docker_network_command = make_string("%s network inspect %s --format='{{.Driver}}'", docker_binary, network_name); - FILE* docker_network = popen(docker_network_command, "r"); - ret = fscanf(docker_network, "%s", network_type); - if (pclose (docker_network) != 0 || ret <= 0) { - fprintf (ERRORFILE, "Could not inspect docker network to get type %s.\n", docker_network_command); - goto cleanup; - } - // other network type exit successfully without ports mapping - if (strcasecmp(network_type, "bridge") != 0) { + if (network_names != NULL) { + for (i = 0; network_names[i] != NULL; i++) { + docker_network_command = make_string("%s network inspect %s --format='{{.Driver}}'", docker_binary, network_names[i]); + FILE* docker_network = popen(docker_network_command, "r"); + ret = fscanf(docker_network, "%s", network_type); + if (pclose (docker_network) != 0 || ret <= 0) { + fprintf (ERRORFILE, "Could not inspect docker network to get type %s.\n", docker_network_command); + goto cleanup; + } ret = 0; - goto cleanup; + // other network type exit successfully without ports mapping + if (strcasecmp(network_type, "bridge") == 0) { + expose_port = 1; + } } - // add -P when not configure ports mapping - if (ports_mapping_values == NULL) { - ret = add_to_args(args, "-P"); - if (ret != 0) { - ret = BUFFER_TOO_SMALL; + if (expose_port != 0) { + // add -P when not configure ports mapping + if (ports_mapping_values == NULL) { + ret = add_to_args(args, "-P"); + if (ret != 0) { + ret = BUFFER_TOO_SMALL; + } } + goto cleanup; } } // add -p when configure ports mapping @@ -999,8 +1055,8 @@ static int add_ports_mapping_to_command(const struct configuration *command_conf cleanup: free(network_type); free(docker_binary); - free(network_name); free(docker_network_command); + free_values(network_names); free_values(ports_mapping_values); return ret; } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/test/utils/test_docker_util.cc b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/test/utils/test_docker_util.cc index 66e987e..6075fe5 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/test/utils/test_docker_util.cc +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/test/utils/test_docker_util.cc @@ -458,12 +458,12 @@ namespace ContainerExecutor { struct configuration container_cfg; struct args buff = ARGS_INITIAL_VALUE; int ret = 0; - std::string container_executor_cfg_contents = "[docker]\n docker.allowed.networks=sdn1,bridge"; + std::string container_executor_cfg_contents = "[docker]\n docker.allowed.networks=none,bridge"; std::vector > file_cmd_vec; file_cmd_vec.push_back(std::make_pair( "[docker-command-execution]\n docker-command=run\n net=bridge", "--net=bridge")); file_cmd_vec.push_back(std::make_pair( - "[docker-command-execution]\n docker-command=run\n net=sdn1", "--net=sdn1")); + "[docker-command-execution]\n docker-command=run\n net=bridge,none", "--net=bridge --net=none")); file_cmd_vec.push_back(std::make_pair( "[docker-command-execution]\n docker-command=run", "")); write_container_executor_cfg(container_executor_cfg_contents); diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/DockerContainers.md b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/DockerContainers.md index 5620299..959965f 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/DockerContainers.md +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/DockerContainers.md @@ -364,7 +364,7 @@ environment variables in the application's environment: | `YARN_CONTAINER_RUNTIME_TYPE` | Determines whether an application will be launched in a Docker container. If the value is "docker", the application will be launched in a Docker container. Otherwise a regular process tree container will be used. | | `YARN_CONTAINER_RUNTIME_DOCKER_IMAGE` | Names which image will be used to launch the Docker container. Any image name that could be passed to the Docker client's run command may be used. The image name may include a repo prefix. | | `YARN_CONTAINER_RUNTIME_DOCKER_RUN_OVERRIDE_DISABLE` | Controls whether the Docker container's default command is overridden. When set to true, the Docker container's command will be "bash _path\_to\_launch\_script_". When unset or set to false, the Docker container's default command is used. | -| `YARN_CONTAINER_RUNTIME_DOCKER_CONTAINER_NETWORK` | Sets the network type to be used by the Docker container. It must be a valid value as determined by the yarn.nodemanager.runtime.linux.docker.allowed-container-networks property. | +| `YARN_CONTAINER_RUNTIME_DOCKER_CONTAINER_NETWORK` | A comma-separated network names, sets the network to be used by the Docker container. It must be a valid value as determined by the yarn.nodemanager.runtime.linux.docker.allowed-container-networks (yarn-site.xml) and docker.allowed.networks (container-executor.cfg) property. If more than one network is specified, the first network must be bridge network type. Other configuration are not supported by Docker. | | `YARN_CONTAINER_RUNTIME_DOCKER_PORTS_MAPPING` | Allows a user to specify ports mapping for the bridge network Docker container. The value of the environment variable should be a comma-separated list of ports mapping. It's the same to "-p" option for the Docker run command. If the value is empty, "-P" will be added. | | `YARN_CONTAINER_RUNTIME_DOCKER_CONTAINER_PID_NAMESPACE` | Controls which PID namespace will be used by the Docker container. By default, each Docker container has its own PID namespace. To share the namespace of the host, the yarn.nodemanager.runtime.linux.docker.host-pid-namespace.allowed property must be set to true. If the host PID namespace is allowed and this environment variable is set to host, the Docker container will share the host's PID namespace. No other value is allowed. | | `YARN_CONTAINER_RUNTIME_DOCKER_RUN_PRIVILEGED_CONTAINER` | Controls whether the Docker container is a privileged container. In order to use privileged containers, the yarn.nodemanager.runtime.linux.docker.privileged-containers.allowed property must be set to true, and the application owner must appear in the value of the yarn.nodemanager.runtime.linux.docker.privileged-containers.acl property. If this environment variable is set to true, a privileged Docker container will be used if allowed. No other value is allowed, so the environment variable should be left unset rather than setting it to false. | diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/yarn-service/Configurations.md b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/yarn-service/Configurations.md index 524cfb9..14cf2ef 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/yarn-service/Configurations.md +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/yarn-service/Configurations.md @@ -149,7 +149,7 @@ Component-level service AM configuration properties can be specified either in t |yarn.service.container-health-threshold.init-delay-secs | The amount of initial time the health check monitor waits before the first check kicks in. It gives a lead time for the service containers to come up for the first time. The default is 600 secs (10 mins). There is one component-level configuration property that is set differently in the `yarn-site.xml` file than it is in the service specification. -To select the docker network type that will be used for docker containers, `docker.network` may be set in the service `Configuration` `properties` or the component `Configuration` `properties`. +To select the docker network names that will be used for docker containers, `docker.network` may be set in the service `Configuration` `properties` or the component `Configuration` `properties`. Network names can be a comma-separated values. The system-wide default for the docker network type (for both YARN service containers and all other application containers) is set via the `yarn.nodemanager.runtime.linux.docker.default-container-network` property in the `yarn-site.xml` file. ### Component-level readiness check properties