背景介绍
NameNode是管理着整个Hadoop文件系统的命名空间,它维护着文件系统数以及文件树中所有的文件和目录。如果NameNode的元数据意外丢失,那么意味着整个HDFS的数据丢失不可恢复,导致整个数据资产丢失,这无疑是灾难性的。
问题场景
在Hadoop系统运维过程中或者hadoop的相关应用中,可能存在相关人员意外进行格式化NameNode的操作,此举将导致整个HDFS文件丢失不可恢复。
逻辑方案
方案设计
版本Hadoop2.x支持
版本Hadoop3.x(待验证)
- 修改Hadoop相关的命令,在Hadoop相关命令上禁止使用NameNode格式化命令。
- 修改所有hadoop及所有hadoop客户端的命令,在
${HADOOP_HOME}/bin/hadoop、${HADOOP_HOME}/bin/hdfs
文件中添加
if [[ $* =~ "-format" ]]; then
echo "禁止使用-format相关命令,如有例外请联系相关运维"
exit
fi
- 备注:只有存在namenode、journalnode的服务的hadoop客户端才能执行namenode的格式化操作,故必须确保这几台hadoop更改。
- 定时备份namenode、journal的数据,当放生意外被格式化数据时进行数据和服务恢复。
- 定时备份相关脚本样例
vim /opt/hadoop/sbin/hadoop-backup.sh
#!/bin/bash
#########################
# 定时备份namenode、journal数据
# 在namenode的节点执行
# 每小时执行一次
#########################
dt=`date +"%Y%m%d-%H%M"`
pre_dt=`date +"%Y%m%d" -d "-2day"`
pre_hour=`date +"%Y%m%d-%H" -d "-2hour"`
del_hour=`date +"%H" -d "-2hour"`
echo "current time" ${dt}
echo "three days ago" ${pre_dt}
echo "three hours ago" ${pre_hour}
echo "three hours ago" ${del_hour}
if [ ! -d ”/data/hadoop/hadoopbak“ ];then
mkdir -p /data/hadoop/hadoopbak
fi
#如果namenode数据量较大可以直接拷贝,无需压缩,定时备份间隔周期可以改为一天
# 压缩备份namenode、journal数据
cd /data/hadoop/dfs
cp -r name /data/hadoop/hadoopbak/${dt}_name
cp -r journal /data/hadoop/hadoopbak/${dt}_journal
#删除前2小时的namenode、journal数据
#### 00的不删 ####
if [ ${del_hour} != '00' ];then
#删除历史备份的namenode、journal数据
if [ -d /data/hadoop/hadoopbak/${pre_hour}*_name ]; then
rm -rf /data/hadoop/hadoopbak/${pre_hour}*_name
fi
if [ -d /data/hadoop/hadoopbak/${pre_hour}*_journal ]; then
rm -rf /data/hadoop/hadoopbak/${pre_hour}*_journal
fi
fi
# 删除历史备份的namenode、journal数据
if [ -d /data/hadoop/hadoopbak/${pre_dt}*_name ]; then
rm -rf /data/hadoop/hadoopbak/${pre_dt}*_name
fi
if [ -d /data/hadoop/hadoopbak/${pre_dt}*_journal ]; then
rm -rf /data/hadoop/hadoopbak/${pre_dt}*_journal
fi
- crontab 定时调度
0 */1 * * * sh /opt/hadoop/sbin/hadoop-backup.sh
方案优缺点
优点
- 方案1、方案2 可以防止用错命令、意外执行错误命令等场景
- 可以快速恢复数据,恢复服务
- 方案1+方案2 操作较简单,版本比较通用,整体方案易于落地
缺点
- 由于数据是实时写入变化的,数据备份与被格式化存在时间间隔,该间隔时间内数据的变化不可恢复。
- 方案1 不能防止恶意更改,恶意破坏数据等场景。
保障方案及数据、服务恢复测试
- 搭建测试hadoop集群
-
版本 2.7.3
-
机器规划
-
安装过程 略
- 落地方案一,修改所有hadoop的hadoop、hdfs命令
vim /opt/hadoop/bin/hadoop
vim /opt/hadoop/bin/hdfs
- 测试执行格式化命令
- 不断向hadoop集群写入数据
vi test.sh
#!/bin/bash
i=1
while [ $i -le 7200 ]
do
echo ${i} > $i.txt
hadoop fs -put $i.txt /
let i++
sleep 1s
done
sh test.sh
-
执行方案二,备份文件
sh /opt/hadoop/sbin/hadoop-backup.sh -
恢复命令,强制格式化。
hdfs namenode -format
此时测试的文件已经写入89.txt,因为namenode被格式化,namenode服务宕机,停止写入,且无法查看任何hdfs数据。
- 利用方案2的备份恢复数据、恢复服务。
- 停止所有journalnode
./opt/hadoop/sbin/hadoop-daemon.sh stop journalnode
#如果遇到不能停掉的服务,可直接kill掉
- 删除当前namenode、journalnode数据,将备份的namenode、journalnode数据分别拷贝到127、128、129三台主机的相应目录上
cd /data/hadoop/dfs/
rm -rf journal/ name/
mv /data/hadoop/hadoopbak/20210402-0923_journal.zip ./
mv /data/hadoop/hadoopbak/20210402-0923_journal.zip ./
unzip 20210402-0923_journal.zip
unzip 20210402-0923_journal.zip
# 127 128 129 三台服务器同样
- 分别在127、128、129上启动journalndoe
cd /opt/hadoop/sbin
./hadoop-daemon.sh start journalnode
- 启动namenode
cd /opt/hadoop/sbin
./hadoop-daemon.sh start namenode
如果遇到如下报错报错,说明在备份过程中namenode和journalnode之间存在同步数据丢失,首先修复一下备份的namenode数据
2021-04-02 10:27:04,789 INFO org.apache.hadoop.metrics2.impl.MetricsSystemImpl: NameNode metrics system stopped.
2021-04-02 10:27:04,790 INFO org.apache.hadoop.metrics2.impl.MetricsSystemImpl: NameNode metrics system shutdown complete.
2021-04-02 10:27:04,790 ERROR org.apache.hadoop.hdfs.server.namenode.NameNode: Failed to start namenode.
java.io.IOException: There appears to be a gap in the edit log. We expected txid 24666, but got txid 24690.
at org.apache.hadoop.hdfs.server.namenode.MetaRecoveryContext.editLogLoaderPrompt(MetaRecoveryContext.java:94)
at org.apache.hadoop.hdfs.server.namenode.FSEditLogLoader.loadEditRecords(FSEditLogLoader.java:215)
at org.apache.hadoop.hdfs.server.namenode.FSEditLogLoader.loadFSEdits(FSEditLogLoader.java:143)
at org.apache.hadoop.hdfs.server.namenode.FSImage.loadEdits(FSImage.java:843)
at org.apache.hadoop.hdfs.server.namenode.FSImage.loadFSImage(FSImage.java:698)
at org.apache.hadoop.hdfs.server.namenode.FSImage.recoverTransitionRead(FSImage.java:294)
at org.apache.hadoop.hdfs.server.namenode.FSNamesystem.loadFSImage(FSNamesystem.java:976)
at org.apache.hadoop.hdfs.server.namenode.FSNamesystem.loadFromDisk(FSNamesystem.java:681)
at org.apache.hadoop.hdfs.server.namenode.NameNode.loadNamesystem(NameNode.java:585)
at org.apache.hadoop.hdfs.server.namenode.NameNode.initialize(NameNode.java:645)
at org.apache.hadoop.hdfs.server.namenode.NameNode.<init>(NameNode.java:812)
at org.apache.hadoop.hdfs.server.namenode.NameNode.<init>(NameNode.java:796)
at org.apache.hadoop.hdfs.server.namenode.NameNode.createNameNode(NameNode.java:1493)
at org.apache.hadoop.hdfs.server.namenode.NameNode.main(NameNode.java:1559)
2021-04-02 10:27:04,792 INFO org.apache.hadoop.util.ExitUtil: Exiting with status 1
2021-04-02 10:27:04,794 INFO org.apache.hadoop.hdfs.server.namenode.NameNode: SHUTDOWN_MSG:
/************************************************************
SHUTDOWN_MSG: Shutting down NameNode at 192-168-72-127/192.168.72.127
修复备份的namenode数据
hadoop namenode -recover
# 一直选择y 或者 c 即可
分别重新启动127、128的namenode
cd /opt/hadoop/sbin
./hadoop-daemon.sh start namenode
- 验证恢复的数据和服务
查看历史数据
备注:历史数据只到41.txt,实际上截止到被格式化时已经上传到89.txt,从备份到格式化中间的数据丢失。
重新写入数据
sh test.sh
#
# 如果遇到如下报错,说明datanode没有完成对namenode的信息汇报,可以等待几分钟或者重启datanode
#
21/04/02 10:39:04 INFO hdfs.DFSClient: Excluding datanode DatanodeInfoWithStorage[192.168.72.127:50010,DS-1aa30491-c989-4bb4-8d08-16beced62f02,DISK]
21/04/02 10:39:04 INFO hdfs.DFSClient: Exception in createBlockOutputStream
java.io.EOFException: Premature EOF: no length prefix available
at org.apache.hadoop.hdfs.protocolPB.PBHelper.vintPrefixed(PBHelper.java:2282)
at org.apache.hadoop.hdfs.DFSOutputStream$DataStreamer.createBlockOutputStream(DFSOutputStream.java:1343)
at org.apache.hadoop.hdfs.DFSOutputStream$DataStreamer.nextBlockOutputStream(DFSOutputStream.java:1262)
at org.apache.hadoop.hdfs.DFSOutputStream$DataStreamer.run(DFSOutputStream.java:448)
21/04/02 10:39:04 INFO hdfs.DFSClient: Abandoning BP-270677476-192.168.72.127-1617247664550:blk_1073745608_4784
21/04/02 10:39:04 INFO hdfs.DFSClient: Excluding datanode DatanodeInfoWithStorage[192.168.72.128:50010,DS-d7a08bf0-e12e-4d2b-953f-5b67d469c571,DISK]
21/04/02 10:39:04 WARN hdfs.DFSClient: DataStreamer Exception
org.apache.hadoop.ipc.RemoteException(java.io.IOException): File /101.txt._COPYING_ could only be replicated to 0 nodes instead of minReplication (=1). There are 3 datanode(s) running and 3 node(s) are excluded in this operation.
##
###重启datanode
cd /opt/hadoop/sbin
./hadoop-daemon.sh stop datanode
./hadoop-daemon.sh start datanode
数据成功写入,说明hdfs数据写入功能已经完全恢复。
注:至此,namendoe从被格式化到数据、服务恢复已经完成。
运维上线步骤
- 更新修改所有hadoop及所有hadoop客户端的命令,在
${HADOOP_HOME}/bin/hadoop、${HADOOP_HOME}/bin/hdfs
文件中添加
## 备注:在 function print_usage() 方法下面添加。
if [[ $* =~ "-skipTrash" ]] || [[ $* =~ "-format" ]]; then
echo "禁止使用-skipTrash、-format 相关命令,如有例外请联系相关运维"
exit
fi
- 在namenode的机器上,编辑备份脚本
定时备份脚本,用户数组为hadoop
vim /opt/hadoop/sbin/hadoop-backup.sh
#!/bin/bash
#########################
# 定时备份namenode、journal数据
# 在namenode的节点执行
# 每小时执行一次
#########################
dt=`date +"%Y%m%d-%H%M"`
pre_dt=`date +"%Y%m%d" -d "-2day"`
pre_hour=`date +"%Y%m%d-%H" -d "-2hour"`
del_hour=`date +"%H" -d "-2hour"`
echo "current time" ${dt}
echo "three days ago" ${pre_dt}
echo "three hours ago" ${pre_hour}
echo "three hours ago" ${del_hour}
if [ ! -d ”/data/hadoop/hadoopbak“ ];then
mkdir -p /data/hadoop/hadoopbak
fi
# 压缩备份namenode、journal数据
#cd /data/hadoop/dfs
#zip -r /data/hadoop/hadoopbak/${dt}_name.zip name
#zip -r /data/hadoop/hadoopbak/${dt}_journal.zip journal
# 删除历史备份的namenode、journal数据
#if [ -f /data/hadoop/hadoopbak/${pre_dt}*_name.zip ]; then
# rm -rf /data/hadoop/hadoopbak/${pre_dt}*_name.zip
#fi
#if [ -f /data/hadoop/hadoopbak/${pre_dt}*_journal.zip ]; then
# rm -rf /data/hadoop/hadoopbak/${pre_dt}*_journal.zip
#fi
#如果namenode数据量较大可以直接拷贝,无需压缩,定时备份间隔周期可以改为一天
# 压缩备份namenode、journal数据
cd /data/hadoop/dfs
cp -r name /data/hadoop/hadoopbak/${dt}_name
cp -r journal /data/hadoop/hadoopbak/${dt}_journal
#删除前2小时的namenode、journal数据
#### 00的不删 ####
if [ ${del_hour} != '00' ];then
#删除历史备份的namenode、journal数据
if [ -d /data/hadoop/hadoopbak/${pre_hour}*_name ]; then
rm -rf /data/hadoop/hadoopbak/${pre_hour}*_name
fi
if [ -d /data/hadoop/hadoopbak/${pre_hour}*_journal ]; then
rm -rf /data/hadoop/hadoopbak/${pre_hour}*_journal
fi
fi
# 删除历史备份的namenode、journal数据
if [ -d /data/hadoop/hadoopbak/${pre_dt}*_name ]; then
rm -rf /data/hadoop/hadoopbak/${pre_dt}*_name
fi
if [ -d /data/hadoop/hadoopbak/${pre_dt}*_journal ]; then
rm -rf /data/hadoop/hadoopbak/${pre_dt}*_journal
fi
在hadoop用户下添加crontab 定时调度
0 */1 * * * sh /opt/hadoop/sbin/hadoop-backup.sh