HDFS的安全模式

时间:2024-11-15 22:47:22
什么是安全模式

     安全模式是HDFS所处的一种特殊状态,在这种状态下,文件系统只接受读数据请求,而不接受删除、修改等变更请求。在NameNode主节点启动时,HDFS首先进入安全模式,DataNode在启动的时候会向namenode汇报可用的block等状态,当整个系统达到安全标准时,HDFS自动离开安全模式。如果HDFS出于安全模式下,则文件block不能进行任何的副本复制操作,因此达到最小的副本数量要求是基于datanode启动时的状态来判定的,启动时不会再做任何复制(从而达到最小副本数量要求)
HDFS 安全模式的理解

安全模式是hadoop的一种保护机制,用于保证集群中的数据块的安全性。

当集群启动的时候,会首先进入安全模式。当系统处于安全模式时会检查数据块的完整性。假设我们设置的副本数(即参数)是5,那么在datanode上就应该有5个副本存在,假设只存在3个副本,那么比例就是3/5=0.6。在配置文件中定义了一个最小的副本的副本率0.999,如图

 

我们的副本率0.6明显小于0.99,因此系统会自动的复制副本到其他的dataNode,使得副本率不小于0.999.如果系统中有8个副本,超过我们设定的5个副本,那么系统也会删除多余的3个副本。

 

安全模式对我们有什么影响呢?

这时,不允许客户端进行任何修改文件的操作,包括上传文件,删除文件,重命名,创建文件夹等操作。比如创建文件时,在源代码中就有对安全模式的判断,如图所示

当我们在安全模式下进行修改文件操作时,会报如下错误,如图

 

正常情况下,安全模式会运行一段时间自动退出的,只需要我们稍等一会就行了,到底等多长时间呢,我们可以通过50070端口查看安全模式退出的剩余时间,如图

 

虽然不能进行修改文件的操作,但是可以浏览目录结构、查看文件内容的。

在命令行下是可以控制安全模式的进入、退出和查看的。

命令 hadoop fs -safemode get 查看安全模式状态

命令 hadoop fs -safemode enter  进入安全模式状态

命令 hadoop fs -safemode leave 离开安全模式

操作如图所示

 

 安全模式是hadoop的一种保护机制,在启动时,最好是等待集群自动退出,然后进行文件操作。
下面是namenode的一个日志片段:

这里写图片描述
安全模式相关的配置

系   统什么时候才离开安全模式,需要满足哪些条件?

当收到来自datanode的状态报告后,namenode根据配置,确定

1)可用的block占总数的比例、

2)可用的数据节点数量符合要求之后,离开安全模式。如果有必要,也可以通过命令强制离开安全模式。与安全模式相关的主要配置在文件中,主要有下面几个属性


    : 最小的文件block副本数量,默认为1.

    -pct: 副本数达到最小要求的block占系统总block数的百分比,当实际比例超过该配置后,才能离开安全模式(但是还需要其他条件也满足)。默认为0.999f,也就是说符合最小副本数要求的block占比超过99.9%时,并且其他条件也满足才能离开安全模式。如果为小于等于0,则不会等待任何副本达到要求即可离开。如果大于1,则永远处于安全模式。

    : 离开安全模式的最小可用(alive)datanode数量要求,默认为0.也就是即使所有datanode都不可用,仍然可以离开安全模式。

    : 当集群可用block比例,可用datanode都达到要求之后,如果在extension配置的时间段之后依然能满足要求,此时集群才离开安全模式。单位为毫秒,默认为1.也就是当满足条件并且能够维持1毫秒之后,离开安全模式。 这个配置主要是对集群的稳定程度做进一步的确认。避免达到要求后马上又不符合安全标准。

总结一下,要离开安全模式,需要满足以下条件:
1)达到副本数量要求的block比例满足要求;
2)可用的datanode节点数满足配置的数量要求;
3) 1、2 两个条件满足后维持的时间达到配置的要求。


这里写图片描述
相关的操作命令

Hadoop提供脚本用于对安全模式进行操作,主要命令为:

hadoop dfsadmin -safemode <command>


command的可用取值如下:
command     功能     备注
get     查看当前状态     
enter     进入安全模式     
leave     强制离开安全模式     
wait     一直等待直到安全模式结束     
相关源码

安全模式的相关操作封装在SafeMode接口中:

/** 安全模式相关操作 */
@
public interface SafeMode {
  /**
   *  检查进入或者退出安全模式的条件是否满足,
   *  如果满足,进入或退出安全模式。
   */
  public void checkSafeMode();

  /** 系统当前是否处于安全模式? */
  public boolean isInSafeMode();

  /**
   * 系统启动时是否自动进入安全模式?
   */
  public boolean isInStartupSafeMode();

  /** Check whether replication queues are being populated. */
  public boolean isPopulatingReplQueues();

  /**
   * 增加达到最小副本数要求的block数
   * @param replication current replication
   */
  public void incrementSafeBlockCount(int replication);

  /** 降低达到最小副本数要求的block数 */
  public void decrementSafeBlockCount(Block b);
}

 

 

HDFS的命名系统需要提供安全模式相关的操作,因此实现了改接口,层次结构如下:

这里写图片描述

Namesystem继承了SafeMode接口,FSNamesystem类实现了Namesystem,间接实现SafeMode定义的操作,也就是我们可以对命名系统进行安全模式相关的操作。

FSNamesystem内部实现了一个内部线程类SafeModeMonitor,周期性地检查是否应该离开安全模式:


  /**
   * 周期性地检测是否可以离开安全模式,逻辑封装在run方法中.
   * This thread starts when the threshold level is reached.
   *
   */
  class SafeModeMonitor implements Runnable {
    /** 两次检测间隔的毫秒数,即1秒 */
    private static final long recheckInterval = 1000;

    /**
     */
    @Override
    public void run() {
      // 系统运行时,循环检测
      while (fsRunning) {
        writeLock();
        try {
          // 没有安全模式相关信息,也就是不在安全模式
          if (safeMode == null) { // Not in safe mode.
            break;//线程退出
          }
          if (()) {
            // Leave safe mode.
            ();
            smmthread = null;
            // 离开安全模式后,线程退出
            break;
          }
        } finally {
          writeUnlock();
        }

        try {
          // 两次检测之间,线程休眠
          (recheckInterval);
        } catch (InterruptedException ie) {
          // Ignored
        }
      }
      // 当系统不在运行的时候,线程结束退出
      if (!fsRunning) {
        ("NameNode is being shutdown, exit SafeModeMonitor thread");
      }
    }
  }

这个内部类中出现的safeMode,其类型为SafeModeInfo,用于保存安全模式下的相关信息:

 private volatile SafeModeInfo safeMode;  // safe mode information

 

注意到这个变量为volatile,也就是说线程对该变量的任何修改完成后,其他线程立刻可以看到变化。OK,那么什么时候会创建这个对象呢,我们用safeMode= new SafeModeInfo搜索源代码,发现2个地方会实例化这个对象:一个实在FSNamesystem的构造函数中,另一处在FSNamesystem类的enterSafeMode方法中。我们先来看构造函数,其方法声明如下:

FSNamesystem(Configuration conf, FSImage fsImage, boolean ignoreRetryCache)
      throws IOException

这个构造函数使用给定的配置及文件镜像,创建一个文件命名系统。其中有一句如下:

= new SafeModeInfo(conf);

我们去看看SafeModeInfo这个构造函数:

 /**
     * Creates SafeModeInfo when the name node enters
     * automatic safe mode at startup.
     *  
     * @param conf configuration
     */
    private SafeModeInfo(Configuration conf) {
      // 这个配置就是我们前面提到的百分比配置
      = (DFS_NAMENODE_SAFEMODE_THRESHOLD_PCT_KEY,
          DFS_NAMENODE_SAFEMODE_THRESHOLD_PCT_DEFAULT);
      if(threshold > 1.0) {
        ("The threshold value should't be greater than 1, threshold: " + threshold);
      }
    // 这个也是我们前面提到的最小可用datanode数量配置
      = (
        DFS_NAMENODE_SAFEMODE_MIN_DATANODES_KEY,
        DFS_NAMENODE_SAFEMODE_MIN_DATANODES_DEFAULT);
      = (DFS_NAMENODE_SAFEMODE_EXTENSION_KEY, 0);
      = (DFS_NAMENODE_REPLICATION_MIN_KEY,
                                         DFS_NAMENODE_REPLICATION_MIN_DEFAULT);

      (DFS_NAMENODE_SAFEMODE_THRESHOLD_PCT_KEY + " = " + threshold);
      (DFS_NAMENODE_SAFEMODE_MIN_DATANODES_KEY + " = " + datanodeThreshold);
      (DFS_NAMENODE_SAFEMODE_EXTENSION_KEY + "     = " + extension);

      // default to safe mode threshold (., don't populate queues before leaving safe mode)
      =
        (DFS_NAMENODE_REPL_QUEUE_THRESHOLD_PCT_KEY,
                      (float) threshold);
      = 0;
      = 0;
    }

   

核心逻辑就是获取相关的配置,创建初始的SafeModeInfo。

看看另一个地方:

 /**
   * Enter safe mode. If resourcesLow is false, then we assume it is manual
   * 参数用于区分是因为资源不满足要求还是手动进入安全模式
   * @throws IOException
   */
  void enterSafeMode(boolean resourcesLow) throws IOException {
    writeLock();
    try {
      // Stop the secret manager, since rolling the master key would
      // try to write to the edit log
      stopSecretManager();

      // Ensure that any concurrent operations have been fully synced
      // before entering safe mode. This ensures that the FSImage
      // is entirely stable on disk as soon as we're in safe mode.
      boolean isEditlogOpenForWrite = getEditLog().isOpenForWrite();
      // Before Editlog is in OpenForWrite mode, editLogStream will be null. So,
      // logSyncAll call can be called only when Edlitlog is in OpenForWrite mode
      if (isEditlogOpenForWrite) {
        getEditLog().logSyncAll();
      }
      if (!isInSafeMode()) {
        safeMode = new SafeModeInfo(resourcesLow);
        return;
      }
      if (resourcesLow) {
        ();
      } else {
        ();
      }
      if (isEditlogOpenForWrite) {
        getEditLog().logSyncAll();
      }
      ("STATE* Safe mode is ON"
          + ());
    } finally {
      writeUnlock();
    }
  }

总结一下,SafeModeMonitor作为守护线程,在收到来自datanode的BlockReport状态报告之后,周期性地检测是否达到离开安全模式的条件,如果符合条件,则离开安全模式。