本地環境提交flink on yarn作業

天氰色等烟雨發表於2024-11-29

本地環境提交flink on yarn作業

在使用雲廠商提供的flink job管理平臺時,透過介面操作提交flink任務到yarn上十分方便,那麼開發除錯時能否在本地環境直接提交flink任務到yarn呢?

開源的flink管理平臺 streampark 有提交flink on yarn作業的程式碼實現,可以參照 streampark 裡對應模組的程式碼實現本地環境下的flink on yarn作業的提交。

其中 streampark-flink-client-core 作為提交flink job的核心模組,這裡我們只關心flink on yarn作業的提交。

1、flink啟動指令碼

flink 1.14.4版本為例,flink安裝目錄的bin/目錄下的 flink指令碼 有詳細的任務提交步驟,其中最後一行為:

# Add HADOOP_CLASSPATH to allow the usage of Hadoop file systems
exec "${JAVA_RUN}" $JVM_ARGS $FLINK_ENV_JAVA_OPTS "${log_setting[@]}" -classpath "`manglePathList "$CC_CLASSPATH:$INTERNAL_HADOOP_CLASSPATHS"`" org.apache.flink.client.cli.CliFrontend "$@"

可知,flink任務仍以java命令方式提交,程式入口為:org.apache.flink.client.cli.CliFrontend

2、啟動程式main方法

根據引數提交作業,CliFrontend#main方法執行流程如下:

// 1. find the configuration directory - 獲取配置檔案目錄
final String configurationDirectory = getConfigurationDirectoryFromEnv();

// 2. load the global configuration - 載入配置引數
final Configuration configuration = GlobalConfiguration.loadConfiguration(configurationDirectory);

// 3. load the custom command lines - 載入命令列引數
final List<CustomCommandLine> customCommandLines = loadCustomCommandLines(configuration, configurationDirectory);

// 4. 建立CliFrontend的物件並呼叫CliFrontend#parseAndRun方法
final CliFrontend cli = new CliFrontend(configuration, customCommandLines);
SecurityUtils.install(new SecurityConfiguration(cli.configuration));
retCode = SecurityUtils.getInstalledContext().runSecured(() -> cli.parseAndRun(args));

3、CliFrontend#parseAndRun方法

CliFrontend#parseAndRun方法主要程式碼如下:

// check for action
...
// get action
String action = args[0];

// remove action from parameters
final String[] params = Arrays.copyOfRange(args, 1, args.length);

try {
    // do action
    switch (action) {
        case ACTION_RUN: // String ACTION_RUN = "run";
            run(params);
            return 0;
        case ACTION_RUN_APPLICATION: // String ACTION_RUN_APPLICATION = "run-application";
            runApplication(params);
            return 0;
        case ACTION_LIST: // String ACTION_INFO = "list";
            list(params);
            return 0;
        case ACTION_INFO: // String ACTION_LIST = "info";
            info(params);
            return 0;
        ... 省略後續步驟

可以看到CliFrontend#parseAndRun方法透過獲取命令列的第一個引數,匹配並執行指定方法。

假設我們以run-application命令啟動程式,則呼叫CliFrontend#runApplication方法,進入該方法。

4、CliFrontend#runApplication方法

直接展示關鍵程式碼,CliFrontend#runApplication方法透過傳入clusterClientServiceLoader引數來建立一個ApplicationDeployer物件,然後呼叫該物件的ApplicationDeployer#run方法,執行完成結束呼叫。

final ApplicationDeployer deployer = new ApplicationClusterDeployer(clusterClientServiceLoader);
...
deployer.run(effectiveConfiguration, applicationConfiguration);

那麼application命令提交任務到yarn的具體實現就在這裡了,點選run方法並進入具體實現方法ApplicationClusterDeployer#run。

public <ClusterID> void run(
    final Configuration configuration,
    final ApplicationConfiguration applicationConfiguration)
    throws Exception {
    checkNotNull(configuration);
    checkNotNull(applicationConfiguration);

    LOG.info("Submitting application in 'Application Mode'.");
   
    final ClusterClientFactory<ClusterID> clientFactory =
        clientServiceLoader.getClusterClientFactory(configuration);
    try (final ClusterDescriptor<ClusterID> clusterDescriptor =
         clientFactory.createClusterDescriptor(configuration)) {
        final ClusterSpecification clusterSpecification =
            clientFactory.getClusterSpecification(configuration);
        // 提交flink on yarn任務
        clusterDescriptor.deployApplicationCluster(
            clusterSpecification, applicationConfiguration);
    }
}

程式碼實現流程:

  1. 透過clientServiceLoader生成ClusterClientFactory客戶端工廠;
  2. 透過ClusterClientFactory物件建立 ClusterDescriptor 叢集描述符 及 ClusterSpecification 叢集規格物件;
  3. ClusterDescriptor#deployApplicationCluster為提交任務方法,跳轉到具體實現方法YarnClusterDescriptor#deployApplicationCluster;
  4. 進入後我們看到deployApplicationCluster方法及下面的deployJobCluster方法,二者都呼叫了YarnClusterDescriptor#deployInternal方法,以完成flink on yran任務提交;
  5. 透過引數描述也可以看出deployApplicationCluster對應的是application提交模式,deployJobCluster對應的是per-job提交模式;

總結:透過對run方法的梳理,可以確定step2是我們提交任務所需要建立的物件,YarnClusterDescriptor#deployInternal方法是實現提交需要呼叫方法;

5、YarnClusterDescriptor#deployInternal方法

進入該方法,前面是一些引數校檢及認證操作,然後透過 yarnClient 建立一個YarnClientApplication(這裡的yarnClient在哪裡生成?先不管,後面再看),後面進入startAppMaster方法,傳入flinkConfiguration、yarnClient、yarnApplication等引數,這裡應該會進行yarn任務的提交。

// Create application via yarnClient
final YarnClientApplication yarnApplication = yarnClient.createApplication();
...
    
ApplicationReport report =
                startAppMaster(
                        flinkConfiguration,
                        applicationName,
                        yarnClusterEntrypoint,
                        jobGraph,
                        yarnClient,
                        yarnApplication,
                        validClusterSpecification);

進入YarnClusterDescriptor#startAppMaster方法,方法實現較長,這裡模糊搜尋'submit'關鍵詞,定位到 yarnClient.submitApplication 方法執行任務的提交,就此flink on yarn任務正式開始提交到叢集中。

LOG.info("Submitting application master " + appId);
yarnClient.submitApplication(appContext);

6、yarnClient物件在哪裡生成?

梳理提交流程後,已知入口程式CliFrontend#main方法會載入flink的FLINK_CONF_DIR配置檔案目錄並裝載配置引數,而我在FLINK_CONF_DIR目錄的配置檔案中並沒有找到對應的yarn引數配置,那麼flink如何和yarn建立起聯絡呢?

仍需要從ApplicationClusterDeployer#run方法中建立的物件入手:

  • clientServiceLoader是由DefaultClusterClientServiceLoader生成,使用SPI機制動態載入其ClusterClientFactory客戶端工廠;

  • clientFactory.createClusterDescriptor(configuration) 生成ClusterDescriptor叢集描述符物件,呼叫的方法為 YarnClusterClientFactory#createClusterDescriptor,方法程式碼如下:

    public YarnClusterDescriptor createClusterDescriptor(Configuration configuration) {
        checkNotNull(configuration);
        // 讀取flink configuration的CONF_DIR獲取配置檔案目錄
        final String configurationDirectory = configuration.get(DeploymentOptionsInternal.CONF_DIR);
        // 設定日誌相關引數,如未設定日誌引數,預設配置檔案目錄下的"log4j.properties"或"logback.xml"檔案路徑為該引數值
        YarnLogConfigUtil.setLogConfigFileInConfig(configuration, configurationDirectory); 
        // 獲取YarnClient
        return getClusterDescriptor(configuration);
    }
    
    private YarnClusterDescriptor getClusterDescriptor(Configuration configuration) {
        // 建立YarnClient物件
        final YarnClient yarnClient = YarnClient.createYarnClient();
        // 獲取yarn叢集配置
        final YarnConfiguration yarnConfiguration = Utils.getYarnAndHadoopConfiguration(configuration);
        // 根據yarnConfiguration配置,初始化yarn客戶端並啟動建立連線
        yarnClient.init(yarnConfiguration);
        yarnClient.start();
        // 建立並返回ClusterDescriptor叢集描述符物件
        return new YarnClusterDescriptor(
            configuration,
            yarnConfiguration,
            yarnClient,
            YarnClientYarnClusterInformationRetriever.create(yarnClient),
            false);
    }
    

    點選進入Utils#getYarnAndHadoopConfiguration方法獲取yarn叢集配置,程式碼邏輯如下:

    public static YarnConfiguration getYarnAndHadoopConfiguration(
        org.apache.flink.configuration.Configuration flinkConfig) {
        // 從flink configuration配置中獲取對應的yarn配置引數(如未設定相關yarn引數,則不新增)
        final YarnConfiguration yarnConfig = getYarnConfiguration(flinkConfig);
        // 獲取系統環境變數"HADOOP_HOME"、"HADOOP_CONF_DIR",讀取環境變數下的配置檔案目錄並新增到yarnConfig中
        yarnConfig.addResource(HadoopUtils.getHadoopConfiguration(flinkConfig));
    
        return yarnConfig;
    }
    

    HadoopUtils#getHadoopConfiguration方法部分截圖:

由此確定flink與yarn叢集建立連線,需要在提交flink任務的系統上配置hadoop環境變數,這與安裝flink時需要配置環境變數完成與YARN叢集的對接操作描述一致。

實現思路:

由上分析可知,提交flink job需要flink配置檔案、hadoop環境變數,在本地環境下需要在專案中新增 flink-conf.yaml 配置檔案,沒有配置hadoop環境變數的話,可以自行新增 core-site.xml、hdfs-site.xml、yarn-site.xml 配置檔案到專案指定路徑中並建立YarnClient物件,或手動配置引數建立YarnClient物件。

剩下的就是將 streampark 的streampark-flink-client-core模組下的flink on yarn提交任務程式碼提取出來,透過閱讀程式碼發現提交flink任務還需要flink-dist_*.jar檔案,這是flink任務提交到yarn的前提條件之一。

實現流程:

建立自定義的任務提交客戶端,透過HdfsUtils將任務jar包及依賴lib/路徑上傳至指定hdfs檔案目錄中,呼叫提交客戶端的 doSubmit方法 提交任務到yarn叢集,doCancel方法 取消正在執行的flink任務。

Windows系統提交任務失敗的問題解決

由於windows系統和linux系統下是不同的路徑分隔符,導致windows下的本地環境提交flink on yarn作業失敗(windows下找不到主類:YarnJobClusterEntrypoint)。

需在專案下建立 org.apache.flink.yarn 包路徑,複製並修改 Utils 和 YarnClusterDescriptor 類檔案,在啟動時覆蓋原始碼類載入執行,可以解決windows下提交任務失敗的問題。

————————————————
[FLINK-17858] Yarn mode, windows and linux environment should be interlinked - ASF JIRA

專案倉庫地址:Cyanty/flink-job-publish-demo

相關文章