Архитектура и разработка в YARN

LinuxBeginner
Практиковаться сейчас

Введение

В Hadoop версии 2.0 и более поздних появилась новая схема управления ресурсами YARN. Она помогает эффективнее использовать ресурсы кластера, обеспечивает централизованное управление ими и позволяет совместно использовать данные. В этом разделе, опираясь на уже созданный псевдораспределённый кластер Hadoop, вы изучите архитектуру и принципы работы YARN, его конфигурацию, а также методы разработки и мониторинга приложений.

Для выполнения этой лабораторной работы необходимы базовые знания Java.

В шагах по разработке приведён полный исходный код Java. Вставьте каждый файл целиком в редактор, а затем разберитесь, как клиент отправляет приложение и как ApplicationMaster запрашивает и запускает контейнер для задачи.

Архитектура и компоненты YARN

На этом этапе изучите архитектуру YARN и назначение его компонентов.

YARN появился в Hadoop 0.23 как часть MapReduce 2.0 (MRv2) и изменил подход к управлению ресурсами и планированию заданий в кластерах Hadoop:

  • Разделение функций JobTracker: MRv2 разделяет функции JobTracker между отдельными службами: ResourceManager управляет ресурсами, а ApplicationMaster планирует задания и отслеживает их выполнение.
  • Единый ResourceManager: каждому приложению соответствует свой ApplicationMaster. Приложением может быть задание MapReduce или DAG, описывающий задание.
  • Среда выполнения и обработки данных: ResourceManager, Slave и NodeManager образуют среду, в которой ResourceManager управляет ресурсами всех приложений.
  • Компоненты ResourceManager: Scheduler выделяет ресурсы с учётом ограничений, например ёмкости и очередей, а ApplicationsManager обрабатывает отправку заданий и запуск ApplicationMaster.
  • Выделение ресурсов: требования к ресурсам задаются контейнерами, в описании которых указываются такие ресурсы, как память, процессор, диск и сеть.
  • Роль NodeManager: NodeManager отслеживает использование ресурсов контейнерами и передаёт сведения ResourceManager и Scheduler.
  • Задачи ApplicationMaster: ApplicationMaster согласует выделение контейнеров с Scheduler, отслеживает их состояние и контролирует ход выполнения.

Связи между компонентами показаны на рисунке:

Диаграмма компонентов архитектуры YARN

YARN сохраняет совместимость API с предыдущими версиями, поэтому запущенные задания MapReduce можно перенести без сложных изменений. Чтобы эффективно управлять ресурсами и планировать задания в кластере Hadoop, важно понимать архитектуру YARN и назначение его компонентов.

Запуск служб Hadoop

На этом этапе запустите службы Hadoop, необходимые для работы приложения YARN.

Прежде чем изучать параметры конфигурации и разработку приложений YARN, запустите службы Hadoop. После этого они будут доступны для работы.

Сначала дважды щёлкните значок терминала Xfce на рабочем столе. В открывшемся терминале выполните команду, чтобы переключиться на пользователя hadoop:

su - hadoop

Подсказка: пароль пользователя hadoop — hadoop.

После переключения запустите службы, связанные с Hadoop, в том числе HDFS и YARN.

Выполните в терминале следующие команды:

/home/hadoop/hadoop/sbin/start-dfs.sh
/home/hadoop/hadoop/sbin/start-yarn.sh

После запуска можете выполнить команду jps, чтобы проверить, работают ли соответствующие службы.

hadoop:~$ jps
3378 NodeManager
3028 SecondaryNameNode
3717 Jps
2791 DataNode
2648 NameNode
3240 ResourceManager

Подготовка файла конфигурации

На этом этапе изучите один из основных файлов конфигурации Hadoop — yarn-site.xml — и выясните, какие параметры кластера YARN можно в нём задавать.

Чтобы случайно не изменить исходный файл конфигурации, скопируйте его в другой каталог, а затем откройте копию.

Сначала создайте каталог для файла конфигурации, выполнив в терминале команду:

mkdir /home/hadoop/hadoop_conf

Затем скопируйте основной файл конфигурации YARN yarn-site.xml из каталога установки в созданный каталог.

Выполните в терминале команду:

cp /home/hadoop/hadoop/etc/hadoop/yarn-site.xml /home/hadoop/hadoop_conf/yarn-site.xml

Откройте файл в редакторе vim и просмотрите его содержимое:

vim /home/hadoop/hadoop_conf/yarn-site.xml

Принцип работы файла конфигурации

На этом этапе рассмотрите параметры конфигурации, которые использует запущенный кластер Hadoop.

В среде YARN важны две роли: ResourceManager и NodeManager. Поэтому параметры в файле относятся к настройкам этих компонентов.

В файле можно задавать много параметров, но по умолчанию пользовательские настройки в нём отсутствуют. Например, в открытом сейчас файле указана только настройка aux-services, заданная ранее при настройке псевдораспределённого кластера Hadoop. Это показано на примере ниже:

hadoop:~$ cat /home/hadoop/hadoop/etc/hadoop/mapred-site.xml

...
<configuration>
    <property>
        <name>mapreduce.framework.name</name>
        <value>yarn</value>
    </property>
</configuration>

Этот параметр задаёт службы, которые должны запускаться на NodeManager и от которых он зависит. Указанное нами значение — mapreduce_shuffle. Оно означает, что в YARN нужно запустить службу, необходимую программам MapReduce.

Значит ли это, что параметры, которых нет в файле, не работают? Не совсем. Если параметры конфигурации явно не указаны в файле, среда YARN в Hadoop считывает значения по умолчанию из внутренних файлов. Все параметры, явно заданные в yarn-site.xml, переопределяют значения по умолчанию. Это позволяет адаптировать Hadoop к различным сценариям использования.

Параметры конфигурации ResourceManager

Правильная настройка ResourceManager в файле yarn-site.xml необходима для эффективного управления ресурсами и выполнения заданий в кластере Hadoop. Ниже перечислены основные связанные с ним параметры:

  • yarn.resourcemanager.address: адрес, по которому клиенты отправляют приложения и останавливают их. Порт по умолчанию — 8032.
  • yarn.resourcemanager.scheduler.address: адрес, по которому ApplicationMaster запрашивает и освобождает ресурсы. Порт по умолчанию — 8030.
  • yarn.resourcemanager.resource-tracker.address: адрес, по которому NodeManager отправляет сигналы проверки связи и получает задания. Порт по умолчанию — 8031.
  • yarn.resourcemanager.admin.address: адрес для команд администрирования. Порт по умолчанию — 8033.
  • yarn.resourcemanager.webapp.address: адрес веб-интерфейса для просмотра сведений о кластере. Порт по умолчанию — 8088.
  • yarn.resourcemanager.scheduler.class: полное имя основного класса планировщика, например FIFO, CapacityScheduler или FairScheduler.
  • Настройки потоков:
    • yarn.resourcemanager.resource-tracker.client.thread-count
    • yarn.resourcemanager.scheduler.client.thread-count
  • Выделение ресурсов:
    • yarn.scheduler.minimum-allocation-mb
    • yarn.scheduler.maximum-allocation-mb
    • yarn.scheduler.minimum-allocation-vcores
    • yarn.scheduler.maximum-allocation-vcores
  • Управление NodeManager:
    • yarn.resourcemanager.nodes.exclude-path
    • yarn.resourcemanager.nodes.include-path
  • Настройка сигналов проверки связи:
    • yarn.resourcemanager.nodemanagers.heartbeat-interval-ms

Эти параметры позволяют настраивать поведение ResourceManager, выделение ресурсов, обработку потоков, управление NodeManager и интервалы между сигналами проверки связи в кластере Hadoop. Знание этих настроек помогает предотвращать проблемы и поддерживать стабильную работу кластера.

Параметры конфигурации NodeManager

Настройки NodeManager в файле yarn-site.xml важны для эффективного управления ресурсами и задачами кластера Hadoop. Ниже перечислены основные связанные с ним параметры:

  • yarn.nodemanager.resource.memory-mb: общий объём физической памяти, доступный NodeManager. Это значение не меняется во время работы YARN.
  • yarn.nodemanager.vmem-pmem-ratio: соотношение выделяемой виртуальной и физической памяти. По умолчанию — 2.1.
  • yarn.nodemanager.resource.cpu-vcores: общее число виртуальных процессоров, доступных NodeManager. Значение по умолчанию — 8.
  • yarn.nodemanager.local-dirs: путь к каталогам для хранения промежуточных результатов на NodeManager. Можно указать несколько каталогов.
  • yarn.nodemanager.log-dirs: путь к каталогам журналов NodeManager. Можно указать несколько каталогов.
  • yarn.nodemanager.log.retain-seconds: максимальное время хранения журналов NodeManager. По умолчанию — 10800 секунд (3 часа).

Эти параметры позволяют настраивать выделение ресурсов, управление памятью, пути к каталогам и срок хранения журналов, чтобы NodeManager эффективнее использовал ресурсы кластера Hadoop. Знание этих настроек помогает поддерживать стабильную работу кластера и эффективно выполнять задачи.

Справочные материалы по параметрам и значениям по умолчанию

Чтобы ознакомиться со всеми доступными параметрами YARN и других распространённых компонентов Hadoop, изучите файлы конфигурации по умолчанию, предоставленные Apache Hadoop. Вот ссылки на них:

В этих файлах подробно описаны параметры конфигурации и их назначение. Изучив их, вы лучше поймёте роль каждого параметра в архитектуре Hadoop.

Закончив изучение настроек Hadoop, закройте редактор vim.

Создание каталогов и файлов проекта

На этом этапе создайте исходные файлы приложения. Вы соберёте минимальное, но полноценное приложение YARN: клиент отправит ApplicationMaster, который запросит один контейнер для задачи, выводящей приветствие.

Сначала создайте каталог проекта. Выполните в терминале команду:

mkdir /home/hadoop/yarn_app

Затем создайте в каталоге проекта два файла с исходным кодом.

Сначала создайте файл Client.java с помощью команды touch:

touch /home/hadoop/yarn_app/Client.java

Затем создайте файл ApplicationMaster.java:

touch /home/hadoop/yarn_app/ApplicationMaster.java
hadoop:~$ tree /home/hadoop/yarn_app/
/home/hadoop/yarn_app/
├── ApplicationMaster.java
└── Client.java

0 directories, 2 files

Написание кода клиента

На этом этапе напишите полный код клиента, который отправляет приложение. Продолжайте работать от имени пользователя hadoop. Откройте файл с исходным кодом:

vim /home/hadoop/yarn_app/Client.java

Полностью замените содержимое файла приведённым ниже кодом, включая объявление пакета и импорты. В Vim введите :set paste и нажмите Enter, а затем нажмите i, чтобы перейти в режим вставки. Вставьте файл целиком и дождитесь, пока появятся все строки. Затем нажмите Esc, введите :wq и сохраните файл, выйдя из редактора.

package com.labex.yarn.app;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.yarn.api.ApplicationConstants;
import org.apache.hadoop.yarn.api.records.*;
import org.apache.hadoop.yarn.client.api.YarnClient;
import org.apache.hadoop.yarn.client.api.YarnClientApplication;
import org.apache.hadoop.yarn.conf.YarnConfiguration;
import org.apache.hadoop.yarn.util.ConverterUtils;

public class Client {
    public static void main(String[] args) throws Exception {
        if (args.length != 1) {
            throw new IllegalArgumentException("Usage: Client /absolute/path/to/yarn-app.jar");
        }
        YarnConfiguration conf = new YarnConfiguration();
        YarnClient client = YarnClient.createYarnClient();
        client.init(conf);
        client.start();
        try {
            YarnClientApplication application = client.createApplication();
            ApplicationSubmissionContext context = application.getApplicationSubmissionContext();
            ApplicationId id = context.getApplicationId();
            FileSystem fs = FileSystem.get(conf);
            Path destination = new Path(fs.getHomeDirectory(), "yarn-app/" + id + "/app.jar");
            fs.mkdirs(destination.getParent());
            fs.copyFromLocalFile(new Path(args[0]), destination);
            FileStatus status = fs.getFileStatus(destination);
            LocalResource jar = LocalResource.newInstance(
                ConverterUtils.getYarnUrlFromPath(fs.makeQualified(destination)),
                LocalResourceType.FILE, LocalResourceVisibility.APPLICATION,
                status.getLen(), status.getModificationTime());
            Map<String, LocalResource> resources = new HashMap<>();
            resources.put("app.jar", jar);
            Map<String, String> environment = new HashMap<>();
            // The single-node lab uses the same Hadoop installation in every container.
            environment.put("CLASSPATH", "./app.jar:" + System.getProperty("java.class.path"));
            String command = ApplicationConstants.Environment.JAVA_HOME.$$()
                + "/bin/java -Xmx128m com.labex.yarn.app.ApplicationMaster"
                + " 1>" + ApplicationConstants.LOG_DIR_EXPANSION_VAR + "/stdout"
                + " 2>" + ApplicationConstants.LOG_DIR_EXPANSION_VAR + "/stderr";
            ContainerLaunchContext launch = ContainerLaunchContext.newInstance(
                resources, environment, Collections.singletonList(command), null, null, null);
            context.setApplicationName("LabEx YARN Hello");
            context.setQueue("default");
            context.setResource(Resource.newInstance(256, 1));
            context.setAMContainerSpec(launch);
            client.submitApplication(context);
            System.out.println("Application ID: " + id);
            long deadline = System.currentTimeMillis() + 180000;
            while (System.currentTimeMillis() < deadline) {
                ApplicationReport report = client.getApplicationReport(id);
                YarnApplicationState state = report.getYarnApplicationState();
                if (state == YarnApplicationState.FINISHED
                    || state == YarnApplicationState.FAILED
                    || state == YarnApplicationState.KILLED) {
                    System.out.println("Final status: " + report.getFinalApplicationStatus());
                    if (report.getFinalApplicationStatus() != FinalApplicationStatus.SUCCEEDED) {
                        throw new IllegalStateException(report.getDiagnostics());
                    }
                    return;
                }
                Thread.sleep(1000);
            }
            client.killApplication(id);
            throw new IllegalStateException("Application timed out after three minutes");
        } finally {
            client.stop();
        }
    }
}

YarnClient подключается к ResourceManager и получает идентификатор приложения. Клиент копирует JAR-файл в HDFS и объявляет его локальным ресурсом app.jar, чтобы YARN мог поместить его в рабочий каталог ApplicationMaster. Контекст запуска задаёт classpath и команду Java, а контекст отправки приложения — очередь и ресурсы контейнера.

Classpath использует установленные библиотеки Hadoop, общие для контейнеров в этом одновузловом лабораторном кластере. Загружать зависимости с помощью Maven или Gradle не нужно. Этот пример предназначен для кластера лабораторной работы, в котором не используется Kerberos.

После отправки клиент проверяет отчёт приложения, пока оно не перейдёт в конечное состояние, и проверяет итоговый статус. Метод killApplication вызывается, только если приложение не завершилось за три минуты. В заключительном шаге мы скомпилируем и запустим оба класса.

Написание кода ApplicationMaster

На этом этапе напишите полный код ApplicationMaster. Он зарегистрируется в ResourceManager, запросит один контейнер, запустит в нём команду приветствия через NodeManager и сообщит результат выполнения задачи.

Откройте файл от имени пользователя hadoop:

vim /home/hadoop/yarn_app/ApplicationMaster.java

Полностью замените содержимое файла приведённым ниже кодом. Введите :set paste и нажмите Enter, а затем нажмите i. Вставьте файл целиком и дождитесь, пока появятся все строки. Затем нажмите Esc, введите :wq и сохраните файл, выйдя из редактора.

package com.labex.yarn.app;

import java.util.Collections;
import org.apache.hadoop.yarn.api.ApplicationConstants;
import org.apache.hadoop.yarn.api.protocolrecords.AllocateResponse;
import org.apache.hadoop.yarn.api.records.*;
import org.apache.hadoop.yarn.client.api.AMRMClient;
import org.apache.hadoop.yarn.client.api.NMClient;
import org.apache.hadoop.yarn.conf.YarnConfiguration;

public class ApplicationMaster {
    public static void main(String[] args) throws Exception {
        YarnConfiguration conf = new YarnConfiguration();
        AMRMClient<AMRMClient.ContainerRequest> rm = AMRMClient.createAMRMClient();
        NMClient nm = NMClient.createNMClient();
        rm.init(conf);
        nm.init(conf);
        rm.start();
        nm.start();
        try {
            rm.registerApplicationMaster("", 0, "");
            AMRMClient.ContainerRequest request = new AMRMClient.ContainerRequest(
                Resource.newInstance(256, 1), null, null, Priority.newInstance(0));
            rm.addContainerRequest(request);
            boolean launched = false;
            long deadline = System.currentTimeMillis() + 120000;
            while (System.currentTimeMillis() < deadline) {
                // Each allocate call also sends a heartbeat to the ResourceManager.
                AllocateResponse response = rm.allocate(launched ? 0.5f : 0.0f);
                for (Container container : response.getAllocatedContainers()) {
                    if (launched) {
                        rm.releaseAssignedContainer(container.getId());
                        continue;
                    }
                    rm.removeContainerRequest(request);
                    String command = "/bin/echo Hello-from-YARN"
                        + " 1>" + ApplicationConstants.LOG_DIR_EXPANSION_VAR + "/stdout"
                        + " 2>" + ApplicationConstants.LOG_DIR_EXPANSION_VAR + "/stderr";
                    ContainerLaunchContext launch = ContainerLaunchContext.newInstance(
                        Collections.emptyMap(), Collections.emptyMap(),
                        Collections.singletonList(command), null, null, null);
                    nm.startContainer(container, launch);
                    launched = true;
                }
                for (ContainerStatus status : response.getCompletedContainersStatuses()) {
                    if (status.getExitStatus() != 0) {
                        throw new IllegalStateException(status.getDiagnostics());
                    }
                    rm.unregisterApplicationMaster(FinalApplicationStatus.SUCCEEDED,
                        "Hello container completed", "");
                    return;
                }
                Thread.sleep(1000);
            }
            rm.unregisterApplicationMaster(FinalApplicationStatus.FAILED,
                "No successful container within two minutes", "");
            throw new IllegalStateException("Container timed out");
        } finally {
            nm.stop();
            rm.stop();
        }
    }
}

AMRMClient отвечает за регистрацию и запрос ресурсов. Каждый вызов allocate отправляет сигнал проверки связи и возвращает сведения о вновь выделенных контейнерах и статусах завершённых контейнеров. NMClient запускает команду в выделенном контейнере. В этом примере используется синхронный цикл опроса, поэтому не нужны неописанные классы обратного вызова или вспомогательные методы.

Задача выводит Hello-from-YARN в журнал своего контейнера. Если задача завершается с кодом 0, ApplicationMaster снимается с регистрации со статусом SUCCEEDED. После этого клиент выводит итоговый статус. ResourceManager может округлить запрошенные 256 МБ в большую сторону до минимального объёма выделения. Пока ApplicationMaster ожидает контейнер, цикл продолжает отправлять сигналы проверки связи.

Процесс запуска приложения

На этом этапе скомпилируйте и запустите два созданных Java-класса, а затем просмотрите сведения о приложении в веб-интерфейсе ResourceManager. Продолжайте работу в терминале от имени пользователя hadoop.

Компиляция и запуск приложения

Перейдите в каталог с исходными файлами:

cd /home/hadoop/yarn_app

Создайте каталог для результатов компиляции:

mkdir -p classes

Скомпилируйте оба исходных файла, используя библиотеки Hadoop, уже установленные в виртуальной машине. Параметр --release 8 создаёт классы, совместимые со средой выполнения Hadoop на Java 8, даже если по умолчанию используется более новая версия компилятора:

javac --release 8 -cp "$(/home/hadoop/hadoop/bin/hadoop classpath --glob)" -d classes Client.java ApplicationMaster.java

Предупреждение об устаревшем API можно не учитывать. Ошибок компиляции быть не должно. Упакуйте скомпилированные классы:

jar cf yarn-app.jar -C classes .

Отправьте приложение с помощью клиента и сохраните его вывод. Последний аргумент — JAR-файл, который клиент загрузит в HDFS для ApplicationMaster:

set -o pipefail
/home/hadoop/hadoop/bin/yarn jar /home/hadoop/yarn_app/yarn-app.jar com.labex.yarn.app.Client /home/hadoop/yarn_app/yarn-app.jar | tee /home/hadoop/yarn_app/application.log

Дождитесь завершения приложения. Среди сообщений Hadoop ожидайте увидеть:

Application ID: application_<timestamp>_<sequence>
Final status: SUCCEEDED

Идентификатор меняется при каждом запуске. Приветствие записывается в журнал контейнера задачи, а клиент выводит идентификатор приложения и итоговый статус. В этом запуске используются созданные вами клиент и ApplicationMaster, а не отдельный заранее собранный пример MapReduce.

Просмотр результатов выполнения приложения

Откройте Firefox на рабочем столе и перейдите по адресу:

http://localhost:8088

Найдите в списке приложений LabEx YARN Hello, используя идентификатор приложения, выведенный в терминале. Состояние приложения должно быть FINISHED, а итоговый статус — SUCCEEDED. Нажмите на идентификатор приложения, чтобы просмотреть подробную информацию. ResourceManager отслеживает приложение, ApplicationMaster управляет его контейнером задачи, а NodeManager выполняет эту задачу.

Итоги

Эта лабораторная работа продолжает изучение псевдораспределённого кластера Hadoop: вы познакомились с архитектурой и принципами работы YARN, его конфигурацией, а также методами разработки и мониторинга приложений. В курсе приведено много фрагментов кода и файлов конфигурации — внимательно изучите их.