# 六、集群内部

到目前为止，我们已经在高层次上检查了主节点和工作节点。现在，我们将更仔细地研究 Hadoop 集群，以便更好地了解如何设置机器，并了解它们如何协同工作。

Hadoop 集群是基础设施的动态部分。如果您有需要大数据解决方案的分析问题，您的数据收集将持续进行，并且您的群集需要定期增长，以满足不断增长的存储需求。此外，当丰富分析的好处开始实现时，许多公司会加快其大数据计划，将更多数据存储更长时间，并要求不同类型的分析。

Hadoop 具有内在的可扩展性和丰富的可配置性，这意味着您可以调整集群以满足不断变化的需求，并继续从单个平台存储和处理所有数据。

在最初设计 Hadoop 集群时，您需要对数据需求有所了解，以便调配正确数量的硬件。

作为绝对的最低要求，用于生产工作负载的 Hadoop 集群需要一个主节点作为 HDFS 名称节点和纱资源管理器，并且需要三个从节点作为 HDFS 数据节点和纱节点管理器。图 16 显示了 Hadoop 的最小部署，其中所有节点都位于单个机架上。

![nodes](img/00020.jpeg)

 16:最小的 Hadoop 集群

主服务器和从服务器的硬件要求在以下方面有所不同:

*   主节点是一个关键组件。它需要可靠，所以它应该有硬件 RAID 来存储，即使它不需要大量的磁盘空间。作为名称节点，它维护 HDFS 文件命名空间(存储在群集上的所有文件块的位置和元数据)，为了提高性能，它将这些信息保存在内存中。作为资源管理器，它是所有应用主机和客户端连接的中心联系点。名称节点和资源管理器服务具有多种功能，但 CPU 能力不是主要因素。
*   工作节点并不重要，因为 Hadoop 的架构考虑到了故障。作为 HDFS 数据节点，工作人员应该有大量的磁盘存储，并且不需要 RAID(HDFS 使用循环方法进行磁盘分配，对于典型的 Hadoop 工作负载，该方法的基准速度快于 RAID-0)。作为纱节点管理人员，工作人员应该有大量的中央处理器核心和大量的内存供应。如果您计划只运行 MapReduce 应用，每个任务将需要分配一个 CPU 内核和 1 GB 内存，您可以使用这些数据来粗略计算集群的容量。

服务器规格不断提高，但在撰写本文时，适合用作 Hadoop 从机的典型商用服务器将具有 12-16 个 CPU 内核、48-128 GB RAM 和最多 12 个磁盘，每个磁盘最多有 4 TB 的存储空间。合理的预算应该可以让您满足于拥有 48 GB 内存和 36 TB 存储的 16 核机器。

使用三个相同规格的从机，您将拥有 36 TB 的存储容量(因为 HDFS 复制三次，每个数据块将在每个服务器上复制)和大约 36 个并发任务容器的计算容量(假设运行 Hadoop 服务需要 25%的 CPU 开销，每个节点上留下 12 个内核供纱分配)。

如果你习惯于与普通的应用服务器甚至数据库服务器打交道，那么这个规范听起来可能并不简单，但就大数据而言，这是一个相当有限的设置。在不到一年的时间里，每天将有足够的存储空间来收集 100 GB 的数据，但计算方面更加有限。

假设平均每小时有 1.6 GB 的数据，那么每小时的数据就有 14 个文件块。您将使用集群的全部并发任务能力，只需处理三个小时的数据。如果您想在一个月的数据上运行一个作业(即 3 TB 分布在 24，000 个文件块中，每个文件块 128 MB)，假设每个任务需要 60 秒，您的作业将最大化集群 11 个小时。分析全年的数据需要五天以上的时间。

一个最小的集群实际上只是一个起点，如果您正在评估 Hadoop，这是一个相当小的投资。通常，一个功能性的小型集群包含大约 10 个工作节点。对于单个主服务器来说，这是一个很好的管理量，除此之外，您还需要扩展您的主服务器服务，我们将很快看到这一点。

小型 Hadoop 集群可能在多个机架上有服务器，通常在两层网络基础架构中，服务器在机架内有一个快速网络连接，并有一个到网络其余部分的上行链路交换机。图 17 显示了跨三个机架的 10 个工作节点的小型集群设置。

![2-layer](img/00021.jpeg)

 17:一个小型 Hadoop 集群

添加更多的从属服务器可以为您提供额外的存储和计算能力，但这并不像两者的线性增长那么简单。如果我们将复制因子保持在默认值 3，则添加三个服务器会通过新节点的组合存储来增加我们的存储容量。如果我们在最小的集群中添加三个节点，我们的容量将增加一倍，达到 72 TB，而扩展到九个服务器则提供 108 TB 的总存储。

同样，我们的九服务器集群现在有 144 个内核，因此它可以同时执行 108 个任务。不幸的是，这并不意味着我们分析一个月数据的工作将花费三分之一的时间，并在 11/3=3.5 小时内完成。由于复制因子为 3，并且有 9 台服务器，纱将任务分配给具有数据局部性的节点的机会较小，这意味着随着节点通过网络读取数据，平均任务时间将会增加。

我们无法预测丢失局部性的确切影响，因为这取决于集群正在运行的其他作业以及调度程序的工作效率，但是如果我们保守地猜测平均作业时间将从 60 秒增加到 75 秒，那么长达一个月的作业将需要 4.6 小时。我们的基础设施增加了两倍，但底线工作持续时间已降至最初时间的 40%，而不是 30%。

在野外，Hadoop 部署有数千个数据节点来存储和处理千兆字节的数据。Hadoop 2 中 HDFS 和纱的重新架构的主要驱动力之一解决了这个扩展问题，目标是集群规模的限制应该移动到非常大的规模。

从从属端来看，非常大的 Hadoop 集群有大量的数据节点。您可以将存储和计算功能分开，这样您的 HDFS 数据节点在一组机器上运行，而您的纱节点管理器在另一组机器上运行，但是您在分布中获得的东西将在数据局部性中丢失，所以这不是一个常见的模式(除了在云托管集群中，我们将在第 8 章中看到)。

从主端来看，大型集群看起来非常不同，因为它们确实会分离并联合存储角色。如果有数十亿个文件块，一个名称节点不能在内存中保存整个文件系统命名空间。如果您以每小时 1 TB 的速度存储数据，则每周会增加一百万个文件块，并且会很快达到内存限制。HDFS 允许联合，这意味着多个名称节点各自存储命名空间的一部分。

目前，纱不支持联合资源管理器。一个高性能的资源管理器可以支持大约 4，000 个节点管理器，这是目前 Hadoop 的实际限制(正在讨论支持联邦资源管理器的设计，您可以在 Apache JIRA 票证[YEAR-2915](https://issues.apache.org/jira/browse/YARN-2915)上遵循)。

Hadoop 高可用性和联盟超出了这本书的范围，但它是一条很好走的路。大型集群的主要区别是对共享状态和通知的快速子系统的需求，通常由 Apache Zookeeper 填充。

更大的集群不仅能提供更多的存储和计算，还能提供更高的可靠性，因为它们为 HDFS 和纱提供了更多的机器，以便在发生故障时使用。

Hadoop 建立在硬件将会失败，Hadoop 的工作将会在失败面前继续的预期之上。故障在 HDFS 数据节点中更为常见，因为它们数量更多，故障点也更多—一台具有 12 个旋转磁盘的服务器肯定会在某个时候出现故障。事实上，有了足够大的集群，日常故障就成了必然。

数据节点可能有故障磁盘，它们可能失去网络连接，或者服务器可能完全故障，但是 Hadoop 有一个简单的机制来解决所有这些问题——数据节点定期向名称节点发送消息，名称节点监视它们并对故障做出响应。

数据节点发送两种类型的消息——心跳消息和块报告，心跳消息确认节点已启动并能够通信，块报告中节点发送其所有磁盘上存储的每个块的列表。如图 18 所示，这些机制足以让 HDFS 保持可靠性。

![hdfs-messages](img/00022.jpeg)

18:HDFS 的心跳和块报告消息

如果来自数据节点的心跳消息停止，名称节点将假定数据节点脱机，并将其标记为不可用。文件系统命名空间中该节点的任何条目都会被删除，这可能意味着文件不再具有所需数量的数据块副本。在这种情况下，名称节点将指示其他数据节点复制不可用节点上的所有数据块，并且当复制完成时，文件系统将恢复完整复制。

使用默认配置，节点中的单个磁盘故障将导致数据节点服务关闭，从而触发名称节点的另一次复制。您可以将 HDFS 配置为容忍数据节点中的一个或多个磁盘故障，这是一种合理的方法，前提是您有适当的监控来在磁盘出现故障时向您发出警报。

HDFS 努力保持您的数据可用，但它不会自动优化集群中的空间使用。如果故障节点返回到群集，或者添加了新节点，节点之间的文件数据块会不平衡，返回的节点可能表示数据块的副本比您需要的多，而新节点开始是空的，这意味着它们没有任务的数据局部性。

Hadoop 具有在不停机的情况下跨集群重新平衡数据的功能，因此客户端请求仍然可以得到服务。它的设计不是一个侵入性的过程——它不是计算密集型的，尽管重新平衡通常需要很长时间。

您可以运行命令`hadoop balancer`开始跨集群重新平衡数据，但是，为了节省集群容量，Hadoop 在决定数据块应该移动到哪里时使用运行平衡器的机器的计算能力，这意味着您应该在能够快速访问集群的快速机器上调用它。即便如此，重新平衡数万亿字节的数据也需要几天时间。

正如 HDFS 监控数据可用性和补救任何丢失的块一样，纱监控作业进度，并可以解决丢失任务和失败作业的问题。当任务运行时，它们由应用主节点负责，它们发送心跳消息的方式与数据节点向名称节点发送心跳的方式相同。

如果任务意外失败，或者心跳停止，应用主机会将任务标记为失败，并请求资源管理器(理想情况下，资源管理器运行在与任务失败的节点不同的节点上)提供另一个容器来重复失败的任务。

纱让你配置多少重试允许映射和减少任务。默认值允许四次任务重试，但是如果任何一个任务失败四次，应用将被标记为失败。这也是可配置的，这意味着您可以设置纱，以允许一定百分比的任务失败，同时保持作业运行。

尽管是资源管理器的责任，运行的应用也发送心跳消息。如果应用主机以失败状态结束或停止发送心跳，资源管理器将结束应用，并使用新的应用主机重试。应用的重试次数也是可配置的，这意味着纱允许大量的调整，以确保短暂的故障不会阻止作业完成，坏的作业(可能是由于代码或数据的缺陷)不会阻塞集群。

我们已经看到了 Hadoop 行为可配置的几个方面，我们可以调整大量开关来改变集群的特性。

Hadoop 使用每个节点上的 XML 文件进行配置。配置文件位于 Hadoop 安装文件夹中的`/etc/hadoop`目录中(通常用`HADOOP_HOME`环境变量引用，例如`hadooop-succinctly` Docker 映像中的`$HADOOP_HOME`)。通常，我们在四个文件中配置设置，我们只需要指定我们希望覆盖默认值的设置。

这里要介绍的选项太多了，但我们将看看主配置文件和`hadoop-succinctly`容器中使用的具体设置。默认设置通常适用于基本的开发环境，这意味着有些设置您总是会覆盖。

这些是核心的、Hadoop 范围的设置，由 HDFS、纱、网络服务器和应用编程接口使用。相关版本的默认配置在 Hadoop 文档中，对于 2.7.3，这些是 [core-default.xml 设置](https://hadoop.apache.org/docs/r2.7.2/hadoop-project-dist/hadoop-common/core-default.xml)。

该文件配置关键设置，如 Hadoop 使用的安全性和默认文件系统(Hadoop 可以使用 HDFS 以外的文件系统，但只有 HDFS 提供了数据局部性的性能提升)。默认值是操作系统的文件系统，这意味着我们需要覆盖它才能使用 HDFS。代码清单 33 显示了 Docker 容器的设置。

 33:在核心站点中指定文件系统

```java
  <configuration>
      <property>

  <name>fs.defaultFS</name>

  <value>hdfs://localhost:9000</value>
      </property>
  </configuration>

```

这是整个`core-site.xml`，这意味着我们只指定了一个属性——所有其他设置都来自默认文件。我们指定`fs.defaultFS`，这是 Hadoop 的默认文件系统，它是根据提供者的基本 URI 来指定的——在本例中，是本地主机(即 Docker 容器)的端口 9000 上的 HDFS 路径。

此文件用于配置 HDFS 的属性的站点特定覆盖。可用的属性和默认值列在 [hdfs-default.xml 文档](https://hadoop.apache.org/docs/r2.7.2/hadoop-project-dist/hadoop-hdfs/hdfs-default.xml)中。

HDFS 配置属性允许您指定服务应该侦听的端口、数据传输是否应该加密以及文件缓存的行为。对于 Docker 容器，我们只覆盖一个属性，如代码清单 34 所示。

 34:在 hdfs-site.xml 中指定复制因子

```java
  <configuration>
      <property>

  <name>dfs.replication</name>

  <value>1</value>
      </property>
  </configuration>

```

`dfs.replication`属性指定 HDFS 文件块的默认复制因子。这里的默认因子是 3，但是我们的容器只运行一个数据节点服务。当 Hadoop 启动时，它会使用特殊的安全模式来检查文件系统的一致性。如果它期望三个数据块副本，但只有一个数据节点报告，那么 Hadoop 不会离开安全模式，集群实际上是只读的。

将复制因子指定为 1 允许容器上的伪分布式集群使用单个数据节点进程正确运行。

此文件配置 MapReduce 作业的行为。在 Hadoop 的 v1 版本中，这个文件配置了 MapReduce 引擎的行为以及实际的作业，但是对于纱，运行时行为存在于一个单独的文件中。默认属性值在 [mapred-default.xml 文档](https://hadoop.apache.org/docs/r2.7.2/hadoop-mapreduce-client/hadoop-mapreduce-client-core/mapred-default.xml)中指定。

在 mapred-site.xml 中，我们可以指定一些值，例如每个作业使用的默认缩减器数量、是否应该压缩从 mappers 输出的数据，以及对映射键执行的排序类型。在 Docker 设置中，我们已经覆盖了一个属性，如代码清单 35 所示。

 35:在 mapred-site.xml 中指定 MapReduce 引擎

```java
  <configuration>
      <property>

  <name>mapreduce.framework.name</name>

  <value>yarn</value>
      </property>
  </configuration>

```

这将指定用于 MapReduce 作业的引擎。因为纱在 Hadoop v2 中引入了一个非常不同的计算模型，所以默认情况下使用原始的 MapReduce v1 引擎，这样当旧集群升级时，它们就不会不知不觉地切换到一个潜在不兼容的新引擎。

我们要看的最后一个文件，纱-sit.xml，配置了纱的行为。像往常一样，可用的属性和默认设置列在 [yarn-default.xml 文档](https://hadoop.apache.org/docs/r2.7.2/hadoop-yarn/hadoop-yarn-common/yarn-default.xml)中。

我们可以在这里配置很多低级设置，比如应用主设备发送到资源管理器的心跳间隔，要使用的调度器类型，以及纱将允许的最小和最大 CPU 内核和内存分配请求。Docker 容器在这里指定了两个属性，如代码清单 36 所示。

 36:在纱线站点中指定洗牌服务

```java
  <configuration>
      <property>

  <name>yarn.nodemanager.aux-services</name>

  <value>mapreduce_shuffle</value>
      </property>
      <property>
              <name>yarn.nodemanager.aux-services.mapreduce.shuffle.class</name>
              <value>org.apache.hadoop.mapred.ShuffleHandler</value>
        </property>
  </configuration>

```

在 MapReduce v1 和 there 之间，在将来自 mappers 的输出发送到 Reduce 之前，对其进行排序的方式发生了变化。键的排序是洗牌阶段，在 MapReduce v1 中，洗牌实现是固定的。纱允许一个可插入的洗牌框架，定义为在节点管理器上运行的辅助服务，在这个配置中，我们指定标准的`ShuffleHandler`类作为`mapred_shuffle`服务的实现。

在本章中，我们更仔细地研究了 Hadoop 集群的细节。我们研究了主节点和从节点的硬件要求，以及 Hadoop 在不同大小的集群中的表现。因为 Hadoop 集群能够优雅地扩展，所以确定新实施规模的一个好方法是估计数据捕获率，并为已知时期内具有足够容量的集群做好规划。

我们看到了 Hadoop 节点如何相互通信，发送心跳和状态报告，我们还看到了这如何提高集群的可靠性。对于 HDFS，数据节点的丢失会触发其余节点之间的额外数据复制。对于纱，节点管理器的丢失意味着在该节点上运行的任务和应用将在集群上被重新调度。

最后，我们查看了 Hadoop 中的配置框架，并看到了一些我们可以覆盖的属性。Hadoop 操作中的所有关键决策点都有我们可以指定的设置，这意味着我们可以调整我们的集群，以最有效地满足我们的需求。

部署和配置一个 Hadoop 集群并不是一件简单的任务，有许多商业产品将 Hadoop 包装在定制和支持的包中。接下来，我们将看看一些流行的 Hadoop 发行版。