flink yarn 核心入口方法
flink yarn
集群模式运行的job
,通过flink cli
提交任务,对应的类为org.apache.flink.yarn.cli.FlinkYarnSessionCli
。在FlinkYarnSessionCli
对象内会创建org.apache.flink.yarn.YarnClusterDescriptor
对象,此对象封装了创建flink yarn session
逻辑。
org.apache.flink.yarn.YarnClusterDescriptor#deploySessionCluster
方法包含创建/获取flink yarn
集群,以及启动task manager
的流程。所以最需要关注的就是YarnClusterDescriptor deploySessionCluster
方法。
创建 ApplicationMaster 提交任务
deploySessionCluster
代码如下,getYarnSessionClusterEntrypoint
方法指定了flink yarn
集群启动入口类YarnSessionClusterEntrypoint
,ApplicationMaster
在启动申请到container
资源时,会启动调用入口类main
方法,在这里也就是启动flink
集群。(具体可以继续往下看)
@Override
public ClusterClientProvider<ApplicationId> deploySessionCluster(ClusterSpecification clusterSpecification) throws ClusterDeploymentException {
try {
return deployInternal(
clusterSpecification,
"Flink session cluster",
getYarnSessionClusterEntrypoint(),
null,
false);
} catch (Exception e) {
throw new ClusterDeploymentException("Couldn't deploy Yarn session cluster", e);
}
}
protected String getYarnSessionClusterEntrypoint() {
return YarnSessionClusterEntrypoint.class.getName();
}
deployInternal
方法会创建ApplicationMaster
和JobManager
。下面的代码里通过yarn client
创建YarnClientApplication
对象,校验yarn
资源是否能够满足当前需求,最后通过startAppMaster
启动ApplicationMaster
,startAppMaster
配置ApplicationMaster
启动时的context
,说简单点就是告诉它你要怎么启动,启动的时候运行什么东西。
private ClusterClientProvider<ApplicationId> deployInternal(
ClusterSpecification clusterSpecification,
String applicationName,
String yarnClusterEntrypoint,
@Nullable JobGraph jobGraph,
boolean detached) throws Exception {
...
isReadyForDeployment(clusterSpecification);
// ------------------ Check if the specified queue exists --------------------
checkYarnQueues(yarnClient);
// ------------------ Check if the YARN ClusterClient has the requested resources --------------
// Create application via yarnClient
final YarnClientApplication yarnApplication = yarnClient.createApplication();
final GetNewApplicationResponse appResponse = yarnApplication.getNewApplicationResponse();
...
final ClusterSpecification validClusterSpecification;
try {
validClusterSpecification = validateClusterResources(
clusterSpecification,
yarnMinAllocationMB,
maxRes,
freeClusterMem);
} catch (YarnDeploymentException yde) {
failSessionDuringDeployment(yarnClient, yarnApplication);
throw yde;
}
...
flinkConfiguration.setString(ClusterEntrypoint.EXECUTION_MODE, executionMode.toString());
ApplicationReport report = startAppMaster(
flinkConfiguration,
applicationName,
yarnClusterEntrypoint,
jobGraph,
yarnClient,
yarnApplication,
validClusterSpecification);
...
}
startAppMaster
方法特别长,还是不帖代码了,主要流程
- 构建
hdfs FileSystem client
。 flink job
的jar
文件以及相关的依赖文件上传到hdfs
,记录文件位置。- 记录启动
flink job
需要的所有classpath
,最后会设置ApplicationMaster
环境变量内,key
为_FLINK_CLASSPATH
。 setupApplicationMasterContainer
方法设置ApplicationMaster Container
,这一步在配置container
启动命令。这一步会用到本文上面讲到的getYarnSessionClusterEntrypoint
方法获取的Application Container
启动入口类`YarnSessionClusterEntrypoint。指定了启动入口类时的启动参数等配置。- 完善
Application Context
内容,将第4
步的配置写入context
,以及相关的环境变量,yarn queue
等。 - 最后通过
Yarn client
提交Application Context
,死循环等待ApplicationMaster
启动完成。
任务被成功提交启动后,通过ApplicationMaster
的各种回调启动flink
任务,具体看下一节,
运行 flink job
在Yarn Application
各个阶段,都会有对应的回调通知。
flinl
通过cli
向yarn
提交任务后,入口类YarnSessionClusterEntrypoint
启动最终落到YarnResourceManager
对象,这个类实现了org.apache.hadoop.yarn.client.api.async.AMRMClientAsync.CallbackHandler
接口,能够在Yarn Application Master
创建的各个阶段,管理flink on yarn
任务。例如在yarn container
被分配完成的时候,会启动运行 TaskExecutor
@Override
public void onContainersAllocated(List<Container> containers) {
runAsync(() -> {
...
final List<Container> requiredContainers = containers.subList(0, numAcceptedContainers);
...
// 在 container 运行 TaskExecutor
requiredContainers.forEach(this::startTaskExecutorInContainer);
// if we are waiting for no further containers, we can go to the
// regular heartbeat interval
if (numPendingContainerRequests <= 0) {
resourceManagerClient.setHeartbeatInterval(yarnHeartbeatIntervalMillis);
}
});
}
在startTaskExecutorInContainer
方法内createTaskExecutorLaunchContext
初始化yarn container
运行的context
,这个方法内容比较多,简略的说就是构建了ContainerLaunchContext
对象,该对象用来描述启动container
的context
。具体可以自己阅读org.apache.flink.yarn.Utils#createTaskExecutorContext
方法。这个最重要的就是将YarnTaskExecutorRunner.class
作为container
启动类。
最后启动container
, task manager
的启动逻辑都被封装在YarnTaskExecutorRunner.class
类,这个已经涉及到task manager
启动处理过程,不在本篇展开。
private void startTaskExecutorInContainer(Container container) {
final String containerIdStr = container.getId().toString();
final ResourceID resourceId = new ResourceID(containerIdStr);
workerNodeMap.put(resourceId, new YarnWorkerNode(container));
try {
// Context information used to start a TaskExecutor Java process
// 构建 task manager 运行的 context
ContainerLaunchContext taskExecutorLaunchContext = createTaskExecutorLaunchContext(
containerIdStr,
container.getNodeId().getHost());
// 启动 container
nodeManagerClient.startContainerAsync(container, taskExecutorLaunchContext);
} catch (Throwable t) {
releaseFailedContainerAndRequestNewContainerIfRequired(container.getId(), t);
}
}
private ContainerLaunchContext createTaskExecutorLaunchContext(
String containerId,
String host) throws Exception {
...
// 构建 container context
ContainerLaunchContext taskExecutorLaunchContext = Utils.createTaskExecutorContext(
flinkConfig,
yarnConfig,
env,
taskManagerParameters,
taskManagerDynamicProperties,
currDir,
YarnTaskExecutorRunner.class,
log);
// set a special environment variable to uniquely identify this container
taskExecutorLaunchContext.getEnvironment()
.put(ENV_FLINK_CONTAINER_ID, containerId);
taskExecutorLaunchContext.getEnvironment()
.put(ENV_FLINK_NODE_ID, host);
return taskExecutorLaunchContext;
}
至此flink on yarn
提交任务启动方式基本已经搞清楚,主要通过在flink yarn
集群初始化各个阶段回调构建不同的对象,完成任务到yarn
的提交。任务本身的jar
会被放在hdfs
,job
运行时一定是通过自定义classloader
去hdfs
加载类,完成任务的运行。