http://ju.outofmemory.cn/entry/237491

Overview

最近一段时间都在搞集群迁移。最早公司的hadoop数据集群实在阿里云上的,机器不多,大概4台的样子,据说每个月要花7000多。从成本的角度,公司采购了4台2手服务器(E5-2420 v2 * 2+96G内存)在办公室自己搭数据集群。虽然说机房条件艰苦,没空调就算了,还有暖气呢,但是机器还是挺不错的,比阿里云32G的的机器强多了,4台大概2万,还不够阿里云烧3个月的,理论上只要能用3个月就已经很划算了。

硬件分配方面,因为磁盘不大,外加后续还有一些其他用途,所以有三台机器直接用的物理机,一台拿出来用esxi做虚拟机。用阿里云很蛋疼的是,要想通过内网访问还得走vpn,而且vpn不太稳定,也就1m左右的速度,和阿里云宣称的上传无限速差多了。而且还有一个蛋疼的问题是,从阿里云的机器访问办公网,由于没有那么多公网ip,所以只能在办公网搭一套vpn,那边需要访问的服务通过vpn接入进来。

之前公司的hadoop是用的 ambari搭的, cloudera的CDH和ambari的HDP都用过一阵子,个人感觉上HDP没有CDH稳定,而且CDH的管理程序易用性也好于HDP。而且cloudera的市场占有率也好于ambari,参考CentOS之于Red Hat,这种商业公司的开源社区产品在稳定性上应该是好于纯社区版的,尽管ambari后面也有 Hortonworks这家公司支撑,但是还是更倾向于用CDH。

既然连全家桶的都变了,自然每一个服务的版本也不可能完全对应兼容,话说回来,哪怕相同全家桶的不同版本,也无法保证兼容不是。

迁移相关

Hive导入/导出

Hive作为一个“数据库”,竟然没有一个逻辑备份工具,我也是醉了。虽然没看过Hive架构、源码,不过从最近的迁移工作中感觉Hive基本文件是存在HDFS上的,而HiveMetaStore(MySQL/PostgreSQL)存储一些元信息,而SQL查询就是编译成MapReduce在HDFS进行查询,所以Hive相对来说比较慢。基本上Hive在HDFS就是采用一定序列化方式的文本文件而已。尽管Hive wiki上有一篇关于怎么 导入/导出的方法的介绍,但是一点不好用,而且其实没那么麻烦。刚也说了,其实Hive基本数据就是存在HDFS上的,都是存在类似/somepath/hive/warehouse/dbname.db/tablename下的,而schema是存在MetaStore的,如果两边是相同的配置方式,其实只要把warehouse全部distcp到目标集群对应目录下,再把MetaStore给dump还原回去就好了。不过因为这次迁移是从HDP(用的MySQL)迁移到CDH(用的PostgreSQL),所以并没有直接还原MetaStore,而且用 show create table <table_name>; 打印出创建Schema的语句,然后在新的目标集群创建新表,然后再通过类似命令 load data inpath '/tmp/credit_apply/dt=2016-01-21' into table credit_apply partition (dt='2016-01-21'); 直接导入就好。这里写了一个Perl脚本来生成导入语句:

这个脚本用来导入本地文件系统数据文件,HDFS上也是类似的,只要稍微改下,去掉inpath前的local。提供的输入是文件完整路径列表,里面需要包含 dbname.db/tablename 这类信息。

#!/usr/bin/env perl

use strict;
use warnings; my %filter = (); # ./xiaomai_report.db/zhuanti_site/dt=2015-12-27/000000_0
# load data inpath '/tmp/credit_apply/dt=2016-01-12' into table credit_apply partition (dt='2016-01-12');
while (defined (my $line = <STDIN>)) {
chomp $line;
if ($line =~ m/\/([^\/]+)\.db\/([^\/]+)\/(\w\w)=(\d{4}-*\d\d-*\d\d)\/(?:([^=]+)=([^\/]+)\/)*/) {
my $key = "$1\t$2\t$3\t$4";
if (defined $5 and defined $6) {
$key = "$key\t$5\t$6";
}
unless (exists $filter{$key}) {
if (defined $5 and defined $6 and $5 ne '') {
print STDOUT "load data local inpath '/var/lib/hive/data/$1.db/$2/$3=$4/$5=$6' OVERWRITE into table $1.$2 ";
print STDOUT "partition($3='$4',$5='$6');\n";
} else {
print STDOUT "load data local inpath '/var/lib/hive/data/$1.db/$2/$3=$4' OVERWRITE into table $1.$2 ";
print STDOUT "partition($3='$4');\n";
}
$filter{$key}++;
}
}
}

HBase(Phoenix)导入/导出

相较于Hive的傻大粗的迁移方式,HBase就人性化多了,提供了各种小工具方便 导入/导出

$ bin/hbase org.apache.hadoop.hbase.mapreduce.Export [ [ []]]

可以看看手册,不仅仅有导入导出工具,还有一些诸如RowCount的小工具(虽然RowCount并没有输出结果……),不过坑的是导出好像不能导出表结构,还得自己手动创建,也是蛋疼,好在HBase其实只是知道CF就行了。最坑的是Phoenix,它是有schema的,但是Phoenix坑爹的是它没有命令查看怎么创建的schema,你说你这不是坑爹么……

CDH安装Apache Phoenix

值得注意的是,不像HDP,CDH全家桶里并不包含Phoenix,你得单点。好在 Cloudera Labs提供了发行版以便尽可能简单的安装使用。那篇文件里介绍的比较详细,怕Cloudera挂了,所以这里留个备胎:

  1. 在 parcels里面添加对应的分支,5.4之前的5.x对应1.1版本,而5.5是对应1.2版本,因为HBase/Spark版本和Phoenix有很大关系,所以还是要找对应的版本,不过好在即使添加错了,安装时也会告诉你错了,就是浪费时间罢了。。。。
  2. 在ClouderaManager里安装
  3. 在HBase的Configuration里修改 hbase-site.xml ,添加:
<property>
<name>hbase.regionserver.wal.codec</name>
<value>org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec</value>
</property>
  1. 之后重启HBase就可以支持Phoenix了,其实就是添加Phoenix的jar包。

另外,虽然Phoenix吹的挺牛逼,但是对于复杂查询,速度还是特别慢,默认的timeout是1分钟,很多查询远远不够,可以在hbase-site.xml 里添加配置:

<!-- 很多文章说是改这个,而且HDP的配置也是加的这个,但是我简单看了一下Phoenix的源码,这个值默认是10分钟,下面keepAlive是1分钟,所以应该调大下面的值,两个都调也可以
<property>
<name>phoenix.query.timeoutMs</name>
<value>180000</value>
</property>
-->
<property>
<name>phoenix.query.keepAliveMs</name>
<value>180000</value>
</property>

Hive添加额外的Jar包

公司之前的Hive用了 Hive-JSON-Serde用于让Hive支持JSON,他们用的时候一般要在脚本前面加上 add jar /path/to/jar ,但是这样多蛋疼,让Hive启动时带着多方便。实际上Hive提供了一个环境变量 HIVE_AUX_JARS_PATH 用来引用额外的jar包,而CM的Hive配置里有Hive Auxiliary JARs Directory这一项,在相关机器上创建一个目录,加到这个环境变量里即可。

Spark直接引入额外Jar包(Phoenix集成)

因为公司用了Phoenix,而Spark默认是不加在相关Driver的,但是每次启动spark-shell/spark-submit时都要带上 --jars 参数又有点蛋疼,所以还是在CM的Spark配置里找到spark-env.sh的配置,加上:

SPARK_DIST_CLASSPATH="$SPARK_DIST_CLASSPATH:/opt/cloudera/parcels/CLABS_PHOENIX/lib/phoenix/lib/*"

修改sqoop的job信息

公司定时导数据用的sqoop,这是一个RDBMS/Hive之间互导数据的工具,据说sqoop2已经是一个服务了,但是我们用的还是sqoop1代,看起来像是一个普通工具,修改它的任务信息其实也挺简单的,直接修改 ~/.sqoop/metastore.db.script 就行,看起来它是一个sql脚本,但是却是每次运行sqoop都重新执行一遍,大概用的一个mem数据库,不知道可不可以配置成持久化数据库,反正现在弱爆了,本来java启动就慢,每次启动再重构一遍数据表也是蛋疼。不过好处就是改了方便,直接改文本文件就行。

踩过的坑

终于进入了喜大普奔的踩坑环节,因为hadoop全家桶的各个服务实际上都是独立的社区,所以难免会有兼容性问题,所以这里就变成了各种坑让人踩。像CDH/HDP这种全家桶一般都是有过相关兼容性测试的,所以其实还好,如果自己from scratch搭建一套,估计问题更多,下面说几个比较典型的吧。

phoenix-spark不支持Spark 1.5

CDH5.5直接把Spark升级到了1.5版本,而5.4版还是在用1.3,真是挺激进。而phoenix-spark的依赖是Spark1.4,1.5有一些内部结构的变动导致了一些不兼容的问题,详见番号 PHOENIX-2287,这是我实实在在遇到的一个问题,就是用spark-sql读取phoenix产生的问题,抛出异常:

java.lang.ClassCastException: org.apache.spark.sql.catalyst.expressions.GenericMutableRow cannot be cast to org.apache.spark.sql.Row

找到了官方JIRA上对应的CASE( PHOENIX-2287),看到在phoenix-spark的4.6.0和4.5.3版本已经修复了这个问题,遗憾的是Cloudera Labs提供的phoenix版本正好是4.5.2,并且他们还没提供更新的版本。这个问题直接影响到我们的一个服务的功能了,所以只能自己动手修复。好在开源大法好,cloudera早就将他们的Phoenix的发行版的源码开源了,可以去它的 github上clone出来。并且自己下载2287上面的patch,用 git apply 功能打上补丁自己build一个phoenix-spark的jar出来替换掉原先的。需要注意的是,一定要checkout出自己CDH对应的的分支,5.5对应是1.2。替换了jar包之后重启HBase就可以了。

Hue用sqlite性能问题

刚装好CDH时Hue总遇到提示 DatabaseError: database is locked,而且执行查询经常看不见结果,需要过一会点Recent Queries才能看结果。搜了一下原来是Hue默认用的是sqlite,但是在多用户场景下会存在性能问题,大概是个大锁,所以需要修改一下让它使用pgsql或者mysql。直接在CM的Hue的配置里搜索database相关的就能改了,还是挺方便的。可以参考 reference3

CDH5.5中Spark-Hive对于\t分隔符的兼容性问题

这是遇到的一个比较诡异的问题,google上不知道怎么表达好,也没找到类似的按理,但确实是一个可以复现的问题。之前公司有一些Hive表是用 \t 分隔字段的,但是导入到新的集群之后,在spark中查询结果,返回的字段全是NULL,将分隔符改成 \u0001之后或者没有partition的表就没有任何问题,猜想是一个spark1.5和hive1.1之间兼容性的问题吧,反正spark1.5的兼容性已经见怪不怪了。这个问题我在 spark-user邮件组里面咨询过,不知道是我表达不好,还是怎样,反正没人鸟我,这个邮件组点击率和回复率都挺低,社区不给力啊。

下面说一下复现方法:

  • 在hive中创建 \t 分隔的表,并导入 \t 分隔的文本文件
$ cat /var/lib/hive/dataimport/mendian/target/test.txt
test
xxxx
# in hive
hive> create table `tmp.test_d`(`id` int, `name` string) PARTITIONED BY (`dt` string) ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t';
hive> load data local inpath '/var/lib/hive/dataimport/mendian/target/test.txt' OVERWRITE into table tmp.test_d partition(dt='2016-01-25');
hive> select * from tmp.test_d;
test 2016-01-25
xxxx 2016-01-25
  • 在spark中查看数据,可以看到除partition字段以外,值全是null
scala> sqlContext.sql("select * from tmp.test_d").collect
res9: Array[org.apache.spark.sql.Row] = Array([null,null,2016-01-25], [null,null,2016-01-25])

目前解决办法只能是不用 \t ,全部用 \u0001 

Conclusion

这次迁移数据还是踩了一些坑的,主要是兼容性方面的问题,所以做集群迁移时,保险期间还是选用相同版本的比较靠谱。总体来说,这两周还是收获颇丰的,至少快速的熟悉了一把hadoop相关的一些工具,对hive/hbase/sqoop/phoenix/spark有了一些新的了解。之前在百度时,基本只用过hadoop streaming,那时候还没有这么多上层工具。现在虽然说对这些工具只是一个了解熟悉,远没有精通,但至少丰富了自己的技术栈,以后遇到问题时可以选择的解决办法又多了一些。

Reference

  1. Apache Phoenix Joins Cloudera Labs
  2. DatabaseError: database is locked
  3. Using an External Database for Hue Using the Command Line

【转】最近搞Hadoop集群迁移踩的坑杂记的更多相关文章

  1. 海豚调度5月Meetup:6个月重构大数据平台,帮你避开调度升级改造/集群迁移踩过的坑

    当今许多企业都有着技术架构的DataOps程度不够.二次开发成本高.迁移成本高.集群部署混乱等情况,团队在技术选型之后发现并不适合自己的需求,但是迁移成本和难度又比较大,甚至前团队还留下了不少坑,企业 ...

  2. (转)hadoop 集群间数据迁移

    hadoop集群之间有时候需要将数据进行迁移,如将一些保存的过期文档放置在一个小集群中进行保存. 使用的是社区提供的功能,distcp.用法非常简单: hadoop distcp hdfs://nn1 ...

  3. hadoop集群zookeeper迁移

    1. zookeeper作用 ZooKeepr在Hadoop中的应用主要有: 1.1 HDFS中NameNode的HA和YARN中ResourceManager的HA. 1.2 存储RMStateSt ...

  4. Hadoop跨集群迁移数据(整理版)

    1. 什么是DistCp DistCp(分布式拷贝)是用于大规模集群内部和集群之间拷贝的工具.它使用Map/Reduce实现文件分发,错误处理和恢复,以及报告生成.它把文件和目录的列表作为map任务的 ...

  5. 【Big Data】HADOOP集群的配置(一)

    Hadoop集群的配置(一) 摘要: hadoop集群配置系列文档,是笔者在实验室真机环境实验后整理而得.以便随后工作所需,做以知识整理,另则与博客园朋友分享实验成果,因为笔者在学习初期,也遇到不少问 ...

  6. 基于Docker快速搭建多节点Hadoop集群--已验证

    Docker最核心的特性之一,就是能够将任何应用包括Hadoop打包到Docker镜像中.这篇教程介绍了利用Docker在单机上快速搭建多节点 Hadoop集群的详细步骤.作者在发现目前的Hadoop ...

  7. Hadoop集群datanode磁盘不均衡的解决方案

    一.引言: Hadoop的HDFS集群非常容易出现机器与机器之间磁盘利用率不平衡的情况,比如集群中添加新的数据节点,节点与节点之间磁盘大小不一样等等.当hdfs出现不平衡状况的时候,将引发很多问题,比 ...

  8. [转]大数据hadoop集群硬件选择

      问题导读 1.哪些情况会遇到io受限制? 2.哪些情况会遇到cpu受限制? 3.如何选择机器配置类型? 4.为数据节点/任务追踪器提供的推荐哪些规格? 随着Apache Hadoop的起步,云客户 ...

  9. hadoop 集群部署ganglia 监控服务与nagios 报警服务

      1. 部署ganglia 服务   ganglia 涉及到的组件:     数据监测节点(gmond):这个部件装在需要监测的节点上,用于收集本节点的运行情况,并将这些统计信息传送到gmetad, ...

随机推荐

  1. Mac Mysql mysql_secure_installation Error: Access denied for user 'root'@'localhost' (using password: YES)

    mysql由brew安装, 期间好像自动更新了一次 然后再次执行mysql_secure_installation, 输入root密码后报错, 重装mysql还是不行 Error: Access de ...

  2. Effective C++ -----条款31:将文件间的编译依存关系降至最低

    支持“编译依存性最小化”的一般构想是:相依于声明式,不要相依于定义式.基于此构想的两个手段是Handle classes 和 Interface classes. 程序库头文件应该以“完全且仅有声明式 ...

  3. ASM:《X86汇编语言-从实模式到保护模式》第13章:保护模式下内核的加载,程序的动态加载和执行

    ★PART1:32位保护模式下内核简易模型 1. 内核的结构,功能和加载 每个内核的主引导程序都会有所不同,因为内核都会有不同的结构.有时候主引导程序的一些段和内核段是可以共用的(事实上加载完内核以后 ...

  4. codeforces 557B. Pasha and Tea 解题报告

    题目链接:http://codeforces.com/problemset/problem/557/B 题目意思:有 2n 个茶杯,规定第 i 个茶杯最多只能装 ai 毫升的水.现在给出 w 毫升的水 ...

  5. HTTP协议与HTML表单(再谈GET与POST的区别)

    HTTP的GET/POST方式有何区别?这是一个老生常谈的问题,但老生常谈的问题往往有一些让人误解的结论.本文将带您浅尝HTTP协议,在了 解HTTP协议的同时将会展示许多被人们忽视的内容.在掌握了H ...

  6. jsp通过s:hidden传值给后台,后台数据出现了若干逗号问题

    <s:iterator value="rpActionVO.page.result" id="list" status="st"> ...

  7. 编写一个程序,求s=1+(1+2)+(1+2+3)+…+(1+2+3+…+n)的值

    编写一个程序,求s=1+(1+2)+(1+2+3)+…+(1+2+3+…+n)的值 1 #import <Foundation/Foundation.h>  2   3 int main( ...

  8. css3圣诞雪景球开源

    css3圣诞雪景球开源 <!DOCTYPE html><html lang="en"><head> <meta charset=" ...

  9. kvm 下运行的 WINWS7磁盘空间不足 增加磁盘 实战(这个有问题,还未解决)

    创建一个新硬盘: [root@NB vhost]# qemu-img create -f qcow2 add_win_desk.img 5G Formatting encryption=off clu ...

  10. max virtual memory areas vm.max_map_count [65530] likely too low, increase to at least [262144]

    sh- /etc/sysctl.conf vm.max_map_count = #在/etc/sysctl.conf追加上面一条 #并执行命令: sysctl -p