|
@@ -87,6 +87,8 @@ import org.apache.hadoop.yarn.util.Apps;
|
|
|
import org.apache.hadoop.yarn.util.AuxiliaryServiceHelper;
|
|
|
|
|
|
import com.google.common.annotations.VisibleForTesting;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
import org.apache.hadoop.yarn.util.ConverterUtils;
|
|
|
|
|
|
public class ContainerLaunch implements Callable<Integer> {
|
|
@@ -94,8 +96,13 @@ public class ContainerLaunch implements Callable<Integer> {
|
|
|
private static final Logger LOG =
|
|
|
LoggerFactory.getLogger(ContainerLaunch.class);
|
|
|
|
|
|
+ private static final String CONTAINER_PRE_LAUNCH_PREFIX = "prelaunch";
|
|
|
+ public static final String CONTAINER_PRE_LAUNCH_STDOUT = CONTAINER_PRE_LAUNCH_PREFIX + ".out";
|
|
|
+ public static final String CONTAINER_PRE_LAUNCH_STDERR = CONTAINER_PRE_LAUNCH_PREFIX + ".err";
|
|
|
+
|
|
|
public static final String CONTAINER_SCRIPT =
|
|
|
Shell.appendScriptExtension("launch_container");
|
|
|
+
|
|
|
public static final String FINAL_CONTAINER_TOKENS_FILE = "container_tokens";
|
|
|
|
|
|
private static final String PID_FILE_NAME_FMT = "%s.pid";
|
|
@@ -147,7 +154,7 @@ public class ContainerLaunch implements Callable<Integer> {
|
|
|
Path containerLogDir) {
|
|
|
var = var.replace(ApplicationConstants.LOG_DIR_EXPANSION_VAR,
|
|
|
containerLogDir.toString());
|
|
|
- var = var.replace(ApplicationConstants.CLASS_PATH_SEPARATOR,
|
|
|
+ var = var.replace(ApplicationConstants.CLASS_PATH_SEPARATOR,
|
|
|
File.pathSeparator);
|
|
|
|
|
|
// replace parameter expansion marker. e.g. {{VAR}} on Windows is replaced
|
|
@@ -371,7 +378,7 @@ public class ContainerLaunch implements Callable<Integer> {
|
|
|
String relativeContainerLogDir = ContainerLaunch
|
|
|
.getRelativeContainerLogDir(appIdStr, containerIdStr);
|
|
|
|
|
|
- for(String logDir : logDirs) {
|
|
|
+ for (String logDir : logDirs) {
|
|
|
containerLogDirs.add(logDir + Path.SEPARATOR + relativeContainerLogDir);
|
|
|
}
|
|
|
|
|
@@ -516,6 +523,7 @@ public class ContainerLaunch implements Callable<Integer> {
|
|
|
* Tries to tail and fetch TAIL_SIZE_IN_BYTES of data from the error log.
|
|
|
* ErrorLog filename is not fixed and depends upon app, hence file name
|
|
|
* pattern is used.
|
|
|
+ *
|
|
|
* @param containerID
|
|
|
* @param ret
|
|
|
* @param containerLogDir
|
|
@@ -524,20 +532,46 @@ public class ContainerLaunch implements Callable<Integer> {
|
|
|
@SuppressWarnings("unchecked")
|
|
|
protected void handleContainerExitWithFailure(ContainerId containerID,
|
|
|
int ret, Path containerLogDir, StringBuilder diagnosticInfo) {
|
|
|
- LOG.warn(diagnosticInfo.toString());
|
|
|
+ LOG.warn("Container launch failed : " + diagnosticInfo.toString());
|
|
|
+
|
|
|
+ FileSystem fileSystem = null;
|
|
|
+ long tailSizeInBytes =
|
|
|
+ conf.getLong(YarnConfiguration.NM_CONTAINER_STDERR_BYTES,
|
|
|
+ YarnConfiguration.DEFAULT_NM_CONTAINER_STDERR_BYTES);
|
|
|
+
|
|
|
+ // Append container prelaunch stderr to diagnostics
|
|
|
+ try {
|
|
|
+ fileSystem = FileSystem.getLocal(conf).getRaw();
|
|
|
+ FileStatus preLaunchErrorFileStatus = fileSystem
|
|
|
+ .getFileStatus(new Path(containerLogDir, ContainerLaunch.CONTAINER_PRE_LAUNCH_STDERR));
|
|
|
+
|
|
|
+ Path errorFile = preLaunchErrorFileStatus.getPath();
|
|
|
+ long fileSize = preLaunchErrorFileStatus.getLen();
|
|
|
+
|
|
|
+ diagnosticInfo.append("Error file: ")
|
|
|
+ .append(ContainerLaunch.CONTAINER_PRE_LAUNCH_STDERR).append(".\n");
|
|
|
+ ;
|
|
|
|
|
|
+ byte[] tailBuffer = tailFile(errorFile, fileSize, tailSizeInBytes);
|
|
|
+ diagnosticInfo.append("Last ").append(tailSizeInBytes)
|
|
|
+ .append(" bytes of ").append(errorFile.getName()).append(" :\n")
|
|
|
+ .append(new String(tailBuffer, StandardCharsets.UTF_8));
|
|
|
+ } catch (IOException e) {
|
|
|
+ LOG.error("Failed to get tail of the container's prelaunch error log file", e);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Append container stderr to diagnostics
|
|
|
String errorFileNamePattern =
|
|
|
conf.get(YarnConfiguration.NM_CONTAINER_STDERR_PATTERN,
|
|
|
YarnConfiguration.DEFAULT_NM_CONTAINER_STDERR_PATTERN);
|
|
|
- FSDataInputStream errorFileIS = null;
|
|
|
+
|
|
|
try {
|
|
|
- FileSystem fileSystem = FileSystem.getLocal(conf).getRaw();
|
|
|
+ if (fileSystem == null) {
|
|
|
+ fileSystem = FileSystem.getLocal(conf).getRaw();
|
|
|
+ }
|
|
|
FileStatus[] errorFileStatuses = fileSystem
|
|
|
.globStatus(new Path(containerLogDir, errorFileNamePattern));
|
|
|
if (errorFileStatuses != null && errorFileStatuses.length != 0) {
|
|
|
- long tailSizeInBytes =
|
|
|
- conf.getLong(YarnConfiguration.NM_CONTAINER_STDERR_BYTES,
|
|
|
- YarnConfiguration.DEFAULT_NM_CONTAINER_STDERR_BYTES);
|
|
|
Path errorFile = errorFileStatuses[0].getPath();
|
|
|
long fileSize = errorFileStatuses[0].getLen();
|
|
|
|
|
@@ -560,32 +594,40 @@ public class ContainerLaunch implements Callable<Integer> {
|
|
|
.append(StringUtils.join(", ", errorFileNames)).append(".\n");
|
|
|
}
|
|
|
|
|
|
- long startPosition =
|
|
|
- (fileSize < tailSizeInBytes) ? 0 : fileSize - tailSizeInBytes;
|
|
|
- int bufferSize =
|
|
|
- (int) ((fileSize < tailSizeInBytes) ? fileSize : tailSizeInBytes);
|
|
|
- byte[] tailBuffer = new byte[bufferSize];
|
|
|
- errorFileIS = fileSystem.open(errorFile);
|
|
|
- errorFileIS.readFully(startPosition, tailBuffer);
|
|
|
-
|
|
|
+ byte[] tailBuffer = tailFile(errorFile, fileSize, tailSizeInBytes);
|
|
|
String tailBufferMsg = new String(tailBuffer, StandardCharsets.UTF_8);
|
|
|
diagnosticInfo.append("Last ").append(tailSizeInBytes)
|
|
|
.append(" bytes of ").append(errorFile.getName()).append(" :\n")
|
|
|
.append(tailBufferMsg).append("\n")
|
|
|
.append(analysesErrorMsgOfContainerExitWithFailure(tailBufferMsg));
|
|
|
+
|
|
|
}
|
|
|
} catch (IOException e) {
|
|
|
LOG.error("Failed to get tail of the container's error log file", e);
|
|
|
- } finally {
|
|
|
- IOUtils.cleanupWithLogger(LOG, errorFileIS);
|
|
|
}
|
|
|
-
|
|
|
this.dispatcher.getEventHandler()
|
|
|
.handle(new ContainerExitEvent(containerID,
|
|
|
ContainerEventType.CONTAINER_EXITED_WITH_FAILURE, ret,
|
|
|
diagnosticInfo.toString()));
|
|
|
}
|
|
|
|
|
|
+ private byte[] tailFile(Path filePath, long fileSize, long tailSizeInBytes) throws IOException {
|
|
|
+ FSDataInputStream errorFileIS = null;
|
|
|
+ FileSystem fileSystem = FileSystem.getLocal(conf).getRaw();
|
|
|
+ try {
|
|
|
+ long startPosition =
|
|
|
+ (fileSize < tailSizeInBytes) ? 0 : fileSize - tailSizeInBytes;
|
|
|
+ int bufferSize =
|
|
|
+ (int) ((fileSize < tailSizeInBytes) ? fileSize : tailSizeInBytes);
|
|
|
+ byte[] tailBuffer = new byte[bufferSize];
|
|
|
+ errorFileIS = fileSystem.open(filePath);
|
|
|
+ errorFileIS.readFully(startPosition, tailBuffer);
|
|
|
+ return tailBuffer;
|
|
|
+ } finally {
|
|
|
+ IOUtils.cleanupWithLogger(LOG, errorFileIS);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
private String analysesErrorMsgOfContainerExitWithFailure(String errorMsg) {
|
|
|
StringBuilder analysis = new StringBuilder();
|
|
|
if (errorMsg.indexOf("Error: Could not find or load main class"
|
|
@@ -982,8 +1024,48 @@ public class ContainerLaunch implements Callable<Integer> {
|
|
|
|
|
|
public abstract void whitelistedEnv(String key, String value) throws IOException;
|
|
|
|
|
|
+ protected static final String ENV_PRELAUNCH_STDOUT = "PRELAUNCH_OUT";
|
|
|
+ protected static final String ENV_PRELAUNCH_STDERR = "PRELAUNCH_ERR";
|
|
|
+
|
|
|
+ private boolean redirectStdOut = false;
|
|
|
+ private boolean redirectStdErr = false;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Set stdout for the shell script
|
|
|
+ * @param stdoutDir stdout must be an absolute path
|
|
|
+ * @param stdOutFile stdout file name
|
|
|
+ * @throws IOException thrown when stdout path is not absolute
|
|
|
+ */
|
|
|
+ public final void stdout(Path stdoutDir, String stdOutFile) throws IOException {
|
|
|
+ if (!stdoutDir.isAbsolute()) {
|
|
|
+ throw new IOException("Stdout path must be absolute");
|
|
|
+ }
|
|
|
+ redirectStdOut = true;
|
|
|
+ setStdOut(new Path(stdoutDir, stdOutFile));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Set stderr for the shell script
|
|
|
+ * @param stderrDir stderr must be an absolute path
|
|
|
+ * @param stdErrFile stderr file name
|
|
|
+ * @throws IOException thrown when stderr path is not absolute
|
|
|
+ */
|
|
|
+ public final void stderr(Path stderrDir, String stdErrFile) throws IOException {
|
|
|
+ if (!stderrDir.isAbsolute()) {
|
|
|
+ throw new IOException("Stdout path must be absolute");
|
|
|
+ }
|
|
|
+ redirectStdErr = true;
|
|
|
+ setStdErr(new Path(stderrDir, stdErrFile));
|
|
|
+ }
|
|
|
+
|
|
|
+ protected abstract void setStdOut(Path stdout) throws IOException;
|
|
|
+
|
|
|
+ protected abstract void setStdErr(Path stdout) throws IOException;
|
|
|
+
|
|
|
public abstract void env(String key, String value) throws IOException;
|
|
|
|
|
|
+ public abstract void echo(String echoStr) throws IOException;
|
|
|
+
|
|
|
public final void symlink(Path src, Path dst) throws IOException {
|
|
|
if (!src.isAbsolute()) {
|
|
|
throw new IOException("Source must be absolute");
|
|
@@ -1028,13 +1110,21 @@ public class ContainerLaunch implements Callable<Integer> {
|
|
|
out.append(sb);
|
|
|
}
|
|
|
|
|
|
- protected final void line(String... command) {
|
|
|
+ protected final void buildCommand(String... command) {
|
|
|
for (String s : command) {
|
|
|
sb.append(s);
|
|
|
}
|
|
|
+ }
|
|
|
+
|
|
|
+ protected final void linebreak(String... command) {
|
|
|
sb.append(LINE_SEPARATOR);
|
|
|
}
|
|
|
|
|
|
+ protected final void line(String... command) {
|
|
|
+ buildCommand(command);
|
|
|
+ linebreak();
|
|
|
+ }
|
|
|
+
|
|
|
public void setExitOnFailure() {
|
|
|
// Dummy implementation
|
|
|
}
|
|
@@ -1042,19 +1132,27 @@ public class ContainerLaunch implements Callable<Integer> {
|
|
|
protected abstract void link(Path src, Path dst) throws IOException;
|
|
|
|
|
|
protected abstract void mkdir(Path path) throws IOException;
|
|
|
+
|
|
|
+ boolean doRedirectStdOut() {
|
|
|
+ return redirectStdOut;
|
|
|
+ }
|
|
|
+
|
|
|
+ boolean doRedirectStdErr() {
|
|
|
+ return redirectStdErr;
|
|
|
+ }
|
|
|
+
|
|
|
}
|
|
|
|
|
|
private static final class UnixShellScriptBuilder extends ShellScriptBuilder {
|
|
|
-
|
|
|
private void errorCheck() {
|
|
|
line("hadoop_shell_errorcode=$?");
|
|
|
- line("if [ $hadoop_shell_errorcode -ne 0 ]");
|
|
|
+ line("if [[ \"$hadoop_shell_errorcode\" -ne 0 ]]");
|
|
|
line("then");
|
|
|
line(" exit $hadoop_shell_errorcode");
|
|
|
line("fi");
|
|
|
}
|
|
|
|
|
|
- public UnixShellScriptBuilder(){
|
|
|
+ public UnixShellScriptBuilder() {
|
|
|
line("#!/bin/bash");
|
|
|
line();
|
|
|
}
|
|
@@ -1062,29 +1160,47 @@ public class ContainerLaunch implements Callable<Integer> {
|
|
|
@Override
|
|
|
public void command(List<String> command) {
|
|
|
line("exec /bin/bash -c \"", StringUtils.join(" ", command), "\"");
|
|
|
- errorCheck();
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public void whitelistedEnv(String key, String value) {
|
|
|
+ public void whitelistedEnv(String key, String value) throws IOException {
|
|
|
line("export ", key, "=${", key, ":-", "\"", value, "\"}");
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public void env(String key, String value) {
|
|
|
+ public void setStdOut(final Path stdout) throws IOException {
|
|
|
+ line("export ", ENV_PRELAUNCH_STDOUT, "=\"", stdout.toString(), "\"");
|
|
|
+ // tee is needed for DefaultContainerExecutor error propagation to stdout
|
|
|
+ // Close stdout of subprocess to prevent it from writing to the stdout file
|
|
|
+ line("exec >\"${" + ENV_PRELAUNCH_STDOUT + "}\"");
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void setStdErr(final Path stderr) throws IOException {
|
|
|
+ line("export ", ENV_PRELAUNCH_STDERR, "=\"", stderr.toString(), "\"");
|
|
|
+ // tee is needed for DefaultContainerExecutor error propagation to stderr
|
|
|
+ // Close stdout of subprocess to prevent it from writing to the stdout file
|
|
|
+ line("exec 2>\"${" + ENV_PRELAUNCH_STDERR + "}\"");
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void env(String key, String value) throws IOException {
|
|
|
line("export ", key, "=\"", value, "\"");
|
|
|
}
|
|
|
|
|
|
+ @Override
|
|
|
+ public void echo(final String echoStr) throws IOException {
|
|
|
+ line("echo \"" + echoStr + "\"");
|
|
|
+ }
|
|
|
+
|
|
|
@Override
|
|
|
protected void link(Path src, Path dst) throws IOException {
|
|
|
line("ln -sf \"", src.toUri().getPath(), "\" \"", dst.toString(), "\"");
|
|
|
- errorCheck();
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- protected void mkdir(Path path) {
|
|
|
+ protected void mkdir(Path path) throws IOException {
|
|
|
line("mkdir -p ", path.toString());
|
|
|
- errorCheck();
|
|
|
}
|
|
|
|
|
|
@Override
|
|
@@ -1152,12 +1268,27 @@ public class ContainerLaunch implements Callable<Integer> {
|
|
|
errorCheck();
|
|
|
}
|
|
|
|
|
|
+ //Dummy implementation
|
|
|
+ @Override
|
|
|
+ protected void setStdOut(final Path stdout) throws IOException {
|
|
|
+ }
|
|
|
+
|
|
|
+ //Dummy implementation
|
|
|
+ @Override
|
|
|
+ protected void setStdErr(final Path stderr) throws IOException {
|
|
|
+ }
|
|
|
+
|
|
|
@Override
|
|
|
public void env(String key, String value) throws IOException {
|
|
|
lineWithLenCheck("@set ", key, "=", value);
|
|
|
errorCheck();
|
|
|
}
|
|
|
|
|
|
+ @Override
|
|
|
+ public void echo(final String echoStr) throws IOException {
|
|
|
+ lineWithLenCheck("@echo \"", echoStr, "\"");
|
|
|
+ }
|
|
|
+
|
|
|
@Override
|
|
|
protected void link(Path src, Path dst) throws IOException {
|
|
|
File srcFile = new File(src.toUri().getPath());
|