背景
在开发系统的时候,我们可能会有把数据备份到HDFS的需求。如果我们自身的系统是用Java开发的,那么直接用HDFS的Java API就可以了;而如果系统本身采用的是C++,那么使用HDFS的C接口就有些头疼了。主要原因在于HDFS的C接口是基于jni的,这意味着我们在部署的时候会不那么的直观。
在这种情况下,用Linux Fuse的功能将HDFS挂在到本地的文件系统下会是一个非常好的选择。在挂载成功后,我们就可以用原生的文件API来访问HDFS了。
此外,webhdfs和httpfs也提供了http的方式来访问hdfs,但我觉得从易用性的角度而言,fuse仍旧应该是最好的选择。
如何部署使用fuse
首先,我们在本地起一个hdfs服务,然后用fuse对其进行挂载。
先下载hadoop的预编译包。我选取的是2.8.4,大家可以按照自己的需求选择其他版本。
然后可以参考Apache Hadoop的官方教程,在本地起一个简单的HDFS服务。
如果想利用fuse挂在该hdfs服务,我们需要先编译hdfs fuse的代码:
- 下载hadoop的源代码包,为了和刚才选择的预编译包保持一致,我也选择了2.8.4
- 解压后,按照解压根目录下的BUILDING.txt来安装编译环境。主要包括jdk, maven, protobuf-compiler, fuse
- 编译
mvn clean package -Pnative -DskipTests
- 编译好之后,fuse所用到的几个文件散落在几个不同的地方,需要手动把他们放到一起,这样方便使用:
mkdir output
cp hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/fuse-dfs/fuse_dfs_wrapper.sh output
cp hadoop-hdfs-project/hadoop-hdfs-native-client/target/native/target/usr/local/lib/libhdfs.so* output
cp hadoop-hdfs-project/hadoop-hdfs-native-client/target/main/native/fuse-dfs/fuse_dfs output
- 把fuse编译好之后,就可以使用上面提到的fuse_dfs_wrapper.sh来进行挂载了,命令方式为:
./fuse_dfs_wrapper.sh dfs://127.0.0.1:9000 /mnt/hdfs
脚本的主要作用是设置一些jar包和动态链接库的加载路径,然后调用fuse_dfs的可执行程序。该脚本在使用上有几个点需要注意:
- 要把编译hadoop时生成的jar包都放到java的CLASSPATH中。更简单的做法,是把前面下载的预编译hadoop包中的jar包加进去。
- fuse_dfs的程序会依赖hdfs的C接口动态库,从而会间接依赖到libjvm.so;而后者位于jre的目录下。需要修改LD_LIBRARY_PATH,使得程序可以找到这些动态库。
- fuse_dfs在运行时,依赖于fuse内核模块及其相应的设备文件。最简单的检测方式是看系统中是否存在/dev/fuse的设备文件。如果不存在,请安装fuse:
apt-get install fuse libfuse-dev
- ./fuse_dfs_wrapper.sh在使用的时候可以通过加-d参数来打印调试信息。这种方法可以非常方便的用来排错:
./fuse_dfs_wrapper.sh dfs://127.0.0.1:9000 /mnt/hdfs -d
如果hdfs开启了kerberos身份认证
上面的例子中,HDFS的服务端用的是simple的认证模式。如果server端开启了kerberos的身份认证,则利用fuse的过程会稍微复杂一些:
- 在使用fuse_dfs_wrapper.sh之前,客户端需要使用kinit来向kerberos获取自己用户的credential。相应的,由于credential的ticket存在有效期,所以请使用另外的程序来定期更新ticket。
- 如果本地用户名和kerberos的用户名不一样,访问hdfs可能会依旧没有权限。这里建议本地建一个同名的用户来进行访问。例如你的kerberos的用户名是hdfs_tst/fqdn@realm, 那么请建立hdfs_tst的本地用户,并在该用户下执行所有操作。