Skip to content

Commit f4115db

Browse files
committed
TEZ-4352: Add a web ui interface for TezChild
1 parent 7a56e9b commit f4115db

8 files changed

Lines changed: 283 additions & 3 deletions

File tree

tez-api/src/main/java/org/apache/tez/dag/api/TezConfiguration.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,27 @@ public TezConfiguration(boolean loadDefaults) {
305305
@ConfigurationProperty
306306
public static final String TEZ_MDC_CUSTOM_KEYS_CONF_PROPS = TEZ_MDC_CUSTOM_KEYS + ".conf.props";
307307

308+
/**
309+
* String value
310+
* Whether to start web ui service in task processes.
311+
*/
312+
@ConfigurationScope(Scope.AM)
313+
@ConfigurationProperty(type="boolean")
314+
public static final String TEZ_TASK_WEBSERVICE_ENABLE = TEZ_TASK_PREFIX
315+
+ "webservice.enable";
316+
public static final boolean TEZ_TASK_WEBSERVICE_ENABLE_DEFAULT = false;
317+
318+
/**
319+
* String value. Range of ports that the task container can use for the WebUIService. Leave blank
320+
* to use all possible ports. Expert level setting. It's hadoop standard range configuration.
321+
* For example 50051-55000
322+
*/
323+
@ConfigurationScope(Scope.AM)
324+
@ConfigurationProperty(type = "string")
325+
public static final String TEZ_TASK_WEBSERVICE_PORT_RANGE = TEZ_AM_PREFIX + "webservice.port-range";
326+
327+
public static final String TEZ_TASK_WEBSERVICE_PORT_RANGE_DEFAULT = "50051-55000";
328+
308329
/**
309330
* double value. Represents ratio of unique failed outputs / number of consumer
310331
* tasks. When this condition or value mentioned in {@link
@@ -2054,7 +2075,7 @@ public TezConfiguration(boolean loadDefaults) {
20542075
* For example 50000-50050,50100-50200
20552076
*/
20562077
@ConfigurationScope(Scope.AM)
2057-
@ConfigurationProperty(type="boolean")
2078+
@ConfigurationProperty(type="string")
20582079
public static final String TEZ_AM_WEBSERVICE_PORT_RANGE = TEZ_AM_PREFIX
20592080
+ "tez-ui.webservice.port-range";
20602081

tez-api/src/main/java/org/apache/tez/runtime/api/ExecutionContext.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,5 @@ public interface ExecutionContext {
2828
* Get the hostname on which the JVM is running.
2929
* @return the hostname
3030
*/
31-
public String getHostName();
31+
String getHostName();
3232
}

tez-runtime-internals/src/main/java/org/apache/tez/runtime/api/impl/ExecutionContextImpl.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
public class ExecutionContextImpl implements ExecutionContext {
2020

2121
private final String hostname;
22+
private String containerId = null;
2223

2324
public ExecutionContextImpl(String hostname) {
2425
this.hostname = hostname;
@@ -28,4 +29,13 @@ public ExecutionContextImpl(String hostname) {
2829
public String getHostName() {
2930
return hostname;
3031
}
32+
33+
public ExecutionContext containerId(String containerId) {
34+
this.containerId = containerId;
35+
return this;
36+
}
37+
38+
public String getContainerId() {
39+
return containerId;
40+
}
3141
}

tez-runtime-internals/src/main/java/org/apache/tez/runtime/task/TezChild.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
import org.apache.tez.runtime.api.impl.ExecutionContextImpl;
7474
import org.apache.tez.runtime.common.objectregistry.ObjectRegistryImpl;
7575
import org.apache.tez.runtime.internals.api.TaskReporterInterface;
76+
import org.apache.tez.runtime.web.TezChildWebUIService;
7677
import org.apache.tez.util.LoggingUtils;
7778

7879
import org.apache.tez.util.TezRuntimeShutdownHandler;
@@ -131,6 +132,8 @@ public class TezChild {
131132
private final TezExecutors sharedExecutor;
132133
private ThreadLocalMap mdcContext;
133134

135+
private TezChildWebUIService webUIService;
136+
134137
public TezChild(Configuration conf, String host, int port, String containerIdentifier,
135138
String tokenIdentifier, int appAttemptNumber, String workingDir, String[] localDirs,
136139
Map<String, String> serviceProviderEnvMap,
@@ -207,6 +210,9 @@ public TezTaskUmbilicalProtocol run() throws Exception {
207210
ownUmbilical = false;
208211
}
209212
TezCommonUtils.logCredentials(LOG, credentials, "tezChildInit");
213+
if (isWebUIServiceEnabled(conf)) {
214+
this.webUIService = new TezChildWebUIService(conf, executionContext).start();
215+
}
210216
}
211217

212218
public ContainerExecutionResult run() throws IOException, InterruptedException, TezException {
@@ -424,11 +430,19 @@ public void shutdown() {
424430
if (ownUmbilical) {
425431
RPC.stopProxy(umbilical);
426432
}
433+
if (webUIService != null) {
434+
webUIService.stop();
435+
}
427436
}
428437
TezRuntimeShutdownHandler.shutdown();
429438
LOG.info("TezChild shutdown finished");
430439
}
431440

441+
private boolean isWebUIServiceEnabled(Configuration conf) {
442+
return conf.getBoolean(TezConfiguration.TEZ_TASK_WEBSERVICE_ENABLE,
443+
TezConfiguration.TEZ_TASK_WEBSERVICE_ENABLE_DEFAULT);
444+
}
445+
432446
public static class ContainerExecutionResult {
433447
public static enum ExitStatus {
434448
SUCCESS(0),
@@ -545,7 +559,9 @@ public static void main(String[] args) throws IOException, InterruptedException,
545559

546560
TezChild tezChild = newTezChild(defaultConf, host, port, containerIdentifier,
547561
tokenIdentifier, attemptNumber, localDirs, System.getenv(Environment.PWD.name()),
548-
System.getenv(), pid, new ExecutionContextImpl(System.getenv(Environment.NM_HOST.name())),
562+
System.getenv(), pid,
563+
new ExecutionContextImpl(System.getenv(Environment.NM_HOST.name()))
564+
.containerId(System.getenv(Environment.CONTAINER_ID.name())),
549565
credentials, Runtime.getRuntime().maxMemory(), System
550566
.getenv(ApplicationConstants.Environment.USER.toString()), null, true, hadoopShim);
551567
ContainerExecutionResult result = tezChild.run();
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package org.apache.tez.runtime.web;
20+
21+
import java.io.PrintWriter;
22+
23+
import org.apache.hadoop.yarn.webapp.Controller;
24+
import org.apache.hadoop.yarn.webapp.MimeType;
25+
import org.apache.hadoop.yarn.webapp.View;
26+
import org.apache.tez.runtime.api.impl.ExecutionContextImpl;
27+
28+
import com.google.inject.Inject;
29+
30+
public class TezChildWebController extends Controller {
31+
32+
@Inject
33+
public TezChildWebController(RequestContext requestContext) {
34+
super(requestContext);
35+
}
36+
37+
@Override
38+
public void index() {
39+
ui();
40+
}
41+
42+
public void ui() {
43+
render(StaticTezChildView.class);
44+
}
45+
46+
public static class StaticTezChildView extends View {
47+
@Inject
48+
private ExecutionContextImpl executionContext;
49+
50+
@Override
51+
public void render() {
52+
response().setContentType(MimeType.HTML);
53+
PrintWriter pw = writer();
54+
pw.write("<html><head><meta charset=\\\"utf-8\\\"><title>TezChild UI</title>");
55+
pw.write("</head><body>");
56+
pw.write(String.format("<h1>TezChild UI</h1> <h2>%s, %s</h2> %s :: %s :: %s", executionContext.getHostName(),
57+
executionContext.getContainerId(), getLink("jmx"), getLink("conf"), getLink("stacks")));
58+
pw.write("</body></html>");
59+
pw.flush();
60+
}
61+
62+
private String getLink(String path) {
63+
return "<a href=\"/" + path + "\">" + path + "</a>";
64+
}
65+
}
66+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.apache.tez.runtime.web;
19+
20+
import java.net.InetSocketAddress;
21+
22+
import org.apache.hadoop.conf.Configuration;
23+
import org.apache.hadoop.net.NetUtils;
24+
import org.apache.hadoop.yarn.webapp.WebApp;
25+
import org.apache.hadoop.yarn.webapp.WebApps;
26+
import org.apache.tez.common.web.ServletToControllerAdapters.ConfServletController;
27+
import org.apache.tez.common.web.ServletToControllerAdapters.JMXJsonServletController;
28+
import org.apache.tez.common.web.ServletToControllerAdapters.StackServletController;
29+
import org.apache.tez.dag.api.TezConfiguration;
30+
import org.apache.tez.dag.api.TezUncheckedException;
31+
import org.apache.tez.runtime.api.ExecutionContext;
32+
import org.apache.tez.runtime.api.impl.ExecutionContextImpl;
33+
import org.slf4j.Logger;
34+
import org.slf4j.LoggerFactory;
35+
36+
public class TezChildWebUIService {
37+
private static final Logger LOG = LoggerFactory.getLogger(TezChildWebUIService.class);
38+
39+
private Configuration conf;
40+
private ExecutionContext executionContext;
41+
private TezChildWebApp tezChildWebApp;
42+
private WebApp webApp;
43+
private String baseUrl = ""; //url without paths, like http://host:port
44+
45+
public TezChildWebUIService(Configuration conf, ExecutionContext executionContext) {
46+
this.tezChildWebApp = new TezChildWebApp(executionContext);
47+
this.conf = conf;
48+
this.executionContext = executionContext;
49+
}
50+
51+
public TezChildWebUIService start() {
52+
try {
53+
if (conf.get(TezConfiguration.TEZ_TASK_WEBSERVICE_PORT_RANGE) == null) {
54+
conf.set(TezConfiguration.TEZ_TASK_WEBSERVICE_PORT_RANGE,
55+
TezConfiguration.TEZ_TASK_WEBSERVICE_PORT_RANGE_DEFAULT);
56+
LOG.info(
57+
"Using default port range for WebUIService: " + conf.get(TezConfiguration.TEZ_TASK_WEBSERVICE_PORT_RANGE));
58+
}
59+
this.webApp = WebApps.$for(this.tezChildWebApp).with(conf)
60+
.withPortRange(conf, TezConfiguration.TEZ_TASK_WEBSERVICE_PORT_RANGE).start(this.tezChildWebApp);
61+
InetSocketAddress address = webApp.getListenerAddress();
62+
if (address != null) {
63+
String hostname = executionContext.getHostName();
64+
InetSocketAddress bindAddress = NetUtils.createSocketAddrForHost(hostname, address.getPort());
65+
final int port = address.getPort();
66+
if (bindAddress.getAddress() != null && bindAddress.getAddress().getCanonicalHostName() != null) {
67+
hostname = bindAddress.getAddress().getCanonicalHostName();
68+
} else {
69+
LOG.warn("Failed to resolve canonical hostname for " + hostname);
70+
}
71+
baseUrl = String.format("http://%s:%d", hostname, port);
72+
LOG.info("Instantiated TezChild WebUIService at " + baseUrl + "/ui");
73+
}
74+
} catch (Exception e) {
75+
LOG.error("TezChild WebUIService failed to start.", e);
76+
throw new TezUncheckedException(e);
77+
}
78+
return this;
79+
}
80+
81+
public void stop() {
82+
if (this.webApp != null) {
83+
LOG.debug("Stopping WebApp");
84+
this.webApp.stop();
85+
}
86+
}
87+
88+
private static class TezChildWebApp extends WebApp {
89+
private ExecutionContext executionContext;
90+
91+
TezChildWebApp(ExecutionContext executionContext) {
92+
this.executionContext = executionContext;
93+
}
94+
95+
@Override
96+
public void setup() {
97+
bind(ExecutionContextImpl.class).toInstance((ExecutionContextImpl) executionContext);
98+
route("/", TezChildWebController.class, "ui");
99+
route("/ui", TezChildWebController.class, "ui");
100+
route("/jmx", JMXJsonServletController.class);
101+
route("/conf", ConfServletController.class);
102+
route("/stacks", StackServletController.class);
103+
}
104+
}
105+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
@InterfaceAudience.Private
19+
package org.apache.tez.runtime.web;
20+
import org.apache.hadoop.classification.InterfaceAudience;

tez-tests/src/test/java/org/apache/tez/test/TestAM.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,48 @@ public void testAMWebUIService() throws TezException, IOException, InterruptedEx
140140
tezSession.stop();
141141
}
142142

143+
@Test(timeout = 60000)
144+
public void testTaskWebUIService() throws TezException, IOException, InterruptedException {
145+
SleepProcessorConfig spConf = new SleepProcessorConfig(1);
146+
147+
DAG dag = DAG.create("TezSleepProcessor");
148+
Vertex vertex = Vertex.create("SleepVertex",
149+
ProcessorDescriptor.create(SleepProcessor.class.getName()).setUserPayload(spConf.toUserPayload()), 1,
150+
Resource.newInstance(1024, 1));
151+
dag.addVertex(vertex);
152+
153+
TezConfiguration tezConf = new TezConfiguration(tezCluster.getConfig());
154+
tezConf.setBoolean(TezConfiguration.TEZ_TASK_WEBSERVICE_ENABLE, true);
155+
String tezTaskWebUIServicePort = "50051";
156+
tezConf.set(TezConfiguration.TEZ_TASK_WEBSERVICE_PORT_RANGE, tezTaskWebUIServicePort);
157+
158+
TezClient tezSession = TezClient.create("TezSleepProcessor", tezConf, false);
159+
tezSession.start();
160+
161+
DAGClient dagClient = tezSession.submitDAG(dag);
162+
163+
DAGStatus dagStatus = dagClient.getDAGStatus(null);
164+
while (!dagStatus.isCompleted()) {
165+
Thread.sleep(500L);
166+
dagStatus = dagClient.getDAGStatus(null);
167+
}
168+
169+
// host: this is a unit test, we can assume that task container runs on the same host as the am
170+
// port: we expect it to be what we configured
171+
String amWebUIAddress = dagClient.getWebUIAddress();
172+
URL amWebUIAddressUrl = new URL(amWebUIAddress);
173+
URL taskWebUIAddress = new URL(amWebUIAddressUrl.getProtocol(), amWebUIAddressUrl.getHost(),
174+
Integer.parseInt(tezTaskWebUIServicePort), "");
175+
176+
LOG.info("TezTask webUI address: " + taskWebUIAddress);
177+
178+
checkAddress(taskWebUIAddress + "/jmx");
179+
checkAddress(taskWebUIAddress + "/conf");
180+
checkAddress(taskWebUIAddress + "/stacks");
181+
182+
tezSession.stop();
183+
}
184+
143185
private void checkAddress(String url) {
144186
checkAddress(url, 200);
145187
}

0 commit comments

Comments
 (0)