在Hadoop集群中使用S3(对象存储)文件系统

本文简要介绍了在Hadoop集群(包括Hadoop、Hive与Spark)中使用S3(对象存储)文件系统的方法与注意事项。

对象存储

S3(Simple Storage Service)是一种对象存储服务,具有可扩展性、数据可用性、安全性和性能等优势。S3兼容存储是一种存储解决方案,允许用户访问和管理它通过S3兼容接口存储的数据。

现有的主流S3存储服务:

Hadoop 配置步骤

依赖包位置

Hadoop自带S3依赖,位置如下:

1
2
$HADOOP_HOME/share/hadoop/tools/lib/hadoop-aws-3.1.3.jar
$HADOOP_HOME/share/hadoop/tools/lib/aws-java-sdk-bundle-1.11.271.jar

但是这些依赖包默认不在hadoop classpath下面。可以使用以下两种方法引入这两个包:

  1. hadoop-env.sh中加入export HADOOP_CLASSPATH=$HADOOP_CLASSPATH:$HADOOP_HOME/share/hadoop/tools/lib/*。更改完毕后可以使用hadoop classpath确定。
  2. 通过软链接:ln -s $HADOOP_HOME/share/hadoop/tools/lib/*aws* $HADOOP_HOME/share/hadoop/common/lib/

注意,Minio可能会出现hdfs dfs -ls空文件夹报错的情况,也有可能可以正常部署但是无法通过YARN提交任务。遇到这种情况可以考虑降低aws-java-sdk-bundle的版本为1.11.84,然后重启集群。经测试,Hadoop 3.1.3与3.2.0两个版本可以与aws-java-sdk-bundle-1.11.84兼容,Hadoop 3.3.x版本暂时无法兼容。更换aws-java-sdk-bundle包版本后需要分发到所有集群。

配置 core-site.xml

$HADOOP_HOME/etc/hadoop/core-site.xml加入如下内容(分发至所有机器):

core-site.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
<property>
<name>fs.s3a.access.key</name>
<value>[ak]</value>
</property>
<property>
<name>fs.s3a.secret.key</name>
<value>[sk]</value>
</property>
<property>
<name>fs.s3a.connection.ssl.enabled</name>
<value>false</value>
</property>
<property>
<name>fs.s3a.path.style.access</name>
<value>true</value>
</property>
<property>
<name>fs.s3a.endpoint</name>
<value>http://[endpoint]</value>
</property>
<property>
<name>fs.s3a.impl</name>
<value>org.apache.hadoop.fs.s3a.S3AFileSystem</value>
</property>
</configuration>

更改完毕并重启集群后,可以使用hdfs dfs -ls s3a://[bucket]/等命令操作S3中的文件。

注意,若想将默认文件系统改为S3,需要配置fs.defaultFS为S3中的一个存储桶。示例如下:

core-site.xml
1
2
3
4
<property>
<name>fs.defaultFS</name>
<value>s3a://[default-bucket]/</value>
</property>

注意,使用Ceph部署时,会出现hdfs dfs -ls出现listStatus报错的情况,该报错会显示com.amazonaws.services.s3.model.AmazonS3Exception: Invalid Argument。这是因为Ceph最多只能支持max-keys为1000,但在core-default.xml里该默认设置为5000。需要增加如下配置来解决:

core-site.xml
1
2
3
4
<property>
<name>fs.s3a.paging.maximum</name>
<value>1000</value>
</property>

创建S3存储桶

有些S3实现自带UI,对S3存储桶与文件的操作可以直接在UI上完成。也可以使用S3 Browser(仅限Windows)或者Rclones3cmd等命令行工具。这里简要演示s3cmd的使用方法:

1
2
3
yum install -y s3cmd
vim ~/.s3cfg # configure with following contents
s3cmd mb s3://[some-bucket]

其中 ~/.s3cfg 的内容如下(根据自己的情况配置):

1
2
3
4
5
6
7
8
9
host_base = [endpoint]
host_bucket = [endpoint]
bucket_location = us-east-1
use_https = False

access_key = [ak]
secret_key = [sk]

signature_v2 = False

其他配置

至此,已经可以使用HDFS Client查看和获取到S3的文件了。如需使用YARN和MapReduce,可以尝试增加如下配置:

hdfs-site.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
<property>
<name>dfs.namenode.datanode.registration.ip-hostname-check</name>
<value>false</value>
</property><property>
<name>dfs.permissions.enabled</name>
<value>false</value>
</property>
<property>
<name>dfs.namenode.rpc-bind-host</name>
<value>0.0.0.0</value>
</property>
<property>
<name>dfs.namenode.servicerpc-bind-host</name>
<value>0.0.0.0</value>
</property>
<property>
<name>dfs.namenode.http-bind-host</name>
<value>0.0.0.0</value>
</property>
<property>
<name>dfs.namenode.https-bind-host</name>
<value>0.0.0.0</value>
</property>
<property>
<name>dfs.client.use.datanode.hostname</name>
<value>false</value>
</property>
<property>
<name>dfs.datanode.use.datanode.hostname</name>
<value>false</value>
</property>
</configuration>

需要将下面yarn-site.xml里的yarn.resourcemanager.hostname改为对应ResourceManager的地址:

yarn-site.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
<property>
<name>yarn.resourcemanager.hostname</name>
<value>[yarn-rm-hostname]</value>
</property>
<property>
<name>yarn.resourcemanager.store.class</name>
<value>org.apache.hadoop.yarn.server.resourcemanager.recovery.FileSystemRMStateStore</value>
</property>
<property>
<name>yarn.log-aggregation-enable</name>
<value>true</value>
</property>
<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>
<property>
<name>mapred.map.output.compress.codec</name>
<value>org.apache.hadoop.io.compress.SnappyCodec</value>
</property>
<property>
<name>yarn.resourcemanager.scheduler.class</name>
<value>org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacityScheduler</value>
</property>
<property>
<name>mapreduce.map.output.compress</name>
<value>true</value>
</property>
<property>
<name>yarn.resourcemanager.recovery.enabled</name>
<value>true</value>
</property>
<property>
<name>yarn.nodemanager.vmem-check-enabled</name>
<value>false</value>
</property>
<property>
<name>yarn.resourcemanager.bind-host</name>
<value>0.0.0.0</value>
</property>
<property>
<name>yarn.nodemanager.bind-host</name>
<value>0.0.0.0</value>
</property>
<property>
<name>yarn.nodemanager.bind-host</name>
<value>0.0.0.0</value>
</property>
<property>
<name>yarn.timeline-service.bind-host</name>
<value>0.0.0.0</value>
</property>
<property>
<name>yarn.application.classpath</name>
<value>
/usr/local/hadoop/etc/hadoop,
/usr/local/hadoop/share/hadoop/common/*,
/usr/local/hadoop/share/hadoop/common/lib/*,
/usr/local/hadoop/share/hadoop/hdfs/*,
/usr/local/hadoop/share/hadoop/hdfs/lib/*,
/usr/local/hadoop/share/hadoop/mapreduce/*,
/usr/local/hadoop/share/hadoop/mapreduce/lib/*,
/usr/local/hadoop/share/hadoop/yarn/*,
/usr/local/hadoop/share/hadoop/yarn/lib/*
</value>
</property>
</configuration>
mapred-site.xml
1
2
3
4
5
6
7
8
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
</configuration>

配置完毕后,需要重启整个Hadoop集群。

Hive 配置步骤

依赖包配置

在Hive的安装目录下进行以下操作,增加依赖包:

1
2
mkdir $HIVE_HOME/auxlib
ln -s $HADOOP_HOME/share/hadoop/tools/lib/*aws* $HIVE_HOME/auxlib/

创建core-site.xml

$HIVE_HOME/conf目录下新建core-site.xml并加入如下配置:

core-site.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<configuration>
<property>
<name>fs.defaultFS</name>
<value>s3a://[default-bucket]</value>
</property>
<property>
<name>fs.s3a.access.key</name>
<value>[ak]</value>
</property>
<property>
<name>fs.s3a.secret.key</name>
<value>[sk]</value>
</property>
<property>
<name>fs.s3a.connection.ssl.enabled</name>
<value>false</value>
</property>
<property>
<name>fs.s3a.path.style.access</name>
<value>true</value>
</property>
<property>
<name>fs.s3a.endpoint</name>
<value>http://[endpoint]</value>
</property>
<property>
<name>fs.s3a.impl</name>
<value>org.apache.hadoop.fs.s3a.S3AFileSystem</value>
</property>
</configuration>

其中fs.defaultFS这一项依据$HADOOP_HOME/etc/hadoop/core-site.xml是否配置此项而定。

配置hive-env.sh

$HIVE_HOME/conf目录下运行cp hive-env.sh.template hive-env.sh后添加如下内容:

hive-env.sh
1
2
# Folder containing extra libraries required for hive compilation/execution can be controlled by:
export HIVE_AUX_JARS_PATH=$HIVE_HOME/auxlib

重启Hive服务

1
$HIVE_HOME/bin/hiveservices.sh restart

Spark 配置步骤

运行以下语句并分发至所有机器,随后重启Spark Standalone集群:

1
2
3
wget https://repo1.maven.org/maven2/com/google/guava/guava/27.0-jre/guava-27.0-jre.jar -P $SPARK_HOME/jars
ln -s $HADOOP_HOME/share/hadoop/tools/lib/*aws* $SPARK_HOME/jars/
mv $SPARK_HOME/jars/guava-14.0.1.jar $SPARK_HOME/jars/guava-14.0.1.jar.bak

如果将是将Spark的任务提交到YARN,则需要在spark-env.sh里配置YARN_CONF_DIR

spark-env.sh
1
YARN_CONF_DIR=$HADOOP_HOME/etc/hadoop