在 Hadoop 中通过 Java 客户端访问 JuiceFS

JuiceFS 提供兼容 HDFS 的 Java 客户端来支持 Hadoop 生态中的各种应用。

与 HDFS 的区别

用户组

在用户组管理方面,JuiceFSHDFS 略有不同。Hadoop 中用户组信息是可配置的(hadoop.security.group.mapping),默认使用系统用户组信息(ShellBasedUnixGroupsMapping),此时:

HDFS 使用的是 NameNode 所在节点的用户组信息,可以通过管理命令(hdfs dfsadmin -refreshUserToGroupsMappings)来刷新缓存。

JuiceFS 默认使用的是每个客户端在创建时所在节点的用户组信息,无法刷新。当要修改用户组信息时,需要同步到所有部署 JuiceFS 的节点,并重启使用 JuiceFS 的服务

JuiceFS 还可以通过指定 group 文件来配置用户组信息,可以将 group 文件放置在 JuiceFS 上。 这样每个节点就都可以读取同一份 group 信息,当需要向用户组添加用户的时候,只需要修改此 group 文件,各个 JuiceFS 客户端会在 2 分钟内自动更新信息,无需重启服务。具体操作如下:

  1. 修改 core-site.xml,添加 juicefs.grouping = jfs://your-jfs-name/etc/group

  2. 在 JuiceFS 上创建 juicefs.grouping 所配置的文件

  3. 修改此文件,添加用户组信息,格式如下:

    1. groupname:username1,username2

内存使用

JuiceFS 客户端需要额外使用 300~500 MB 内存用来预读数据,提高性能。所以在使用 Spark 的时候,建议每个 executor 配置多个 CPU 的方式去使用。 因为每个 executor 使用一个 JuiceFS 客户端,一个 executor 内多个 task 可以共享这部分内存。

JuiceFS 客户端数据缓存

JuiceFS 客户端有读缓存功能,会异步地将所读取的数据块缓存在所配置的本地路径上。缓存的数据块是原始数据(未经过和加密)。 读数据时,如果缓存目录中有相应的数据块,就会直接从本地读取,以提高读取速度和降低时延,节省对象存储的 GET 请求产生的费用。

JuiceFS 将文件分成一个个对象块(默认 4MB)存储在对象存储上,对于文件的任何修改操作都是将新数据写入新增的对象块,原有块是不会有任何修改的。所以不用担心本地缓存数据的一致性问题,因为一旦文件被修改过了,JuiceFS 会从对象存储读取新的对象块,不会再读取文件中被覆盖的部分对应的数据块(之后会被淘汰掉)。

单机缓存

JuiceFS 默认的缓存策略是单机独享的,既同一个节点上的多个客户端可以共享本地目录中的缓存数据,各个节点之间是独立的。

通过 juicefs.cache-dir 设置缓存路径,支持多路径(用「:」隔开)以及通配符。 当将 juicefs.cache-dir 设置在内存盘上时,比如 /dev/shm,就可以使用内存作为缓存。

通过 juicefs.cache-size 以及 juicefs.free-space 来设置缓存大小和最少剩余空间。 juicefs.cache-size 的单位是 MB,限制的是本地所有磁盘使用量的和。juicefs.free-space 是每块磁盘剩余可用容量百分数。 这两个参数任意一个达到阈值,均会自动触发缓存淘汰,默认使用的是类似于 LRU 的 Random 2 策略,即随机选择两个数据块,淘汰最后访问时间更早的那个。

读一般有两种模式,连续读和随机读。对于连续读,一般需要较高的吞吐。对于随机读,一般需要较低的时延。 本地磁盘的性能有时反而吞吐比不上直接通过网络从对象存储读。 JuiceFS 为了更好地适配这种情况,增加了 juicefs.cache-full-block 参数,当设置为 false 的时候, 连续读虽然会将一整个对象块都会被读取下来,但并不会被缓存。 而随机读(例如读 Parquet 或者 ORC 文件的 footer)所读取的字节数比较小,不会读取整个对象块,此类读取就会被缓存。 充分地利用了本地磁盘低时延和网络高吞吐的优势。

Parquet 或者 ORC 格式文件的随机读通常会读很少的字节,当整个文件系统启用压缩后会导致即使读很少的部分也会将整个对象块 GET 下来,解压缩并缓存在本地,会降低缓存未建立前的性能。 所以在随机读多的场景,一般建议在创建 JuiceFS 文件系统时将文件压缩关闭,以提高随机读的性能。 在 JuiceFS 文件系统未开启压缩的情况下,JuiceFS 可以读取一个对象块的部分数据,降低了读的时延和带宽需求,提升了没有缓存的性能。 同时有后台线程会将对象块整体下载下来并缓存在本地,后台线程数由 juicefs.prefetch 控制,默认是 3。

为了使单机缓存能更好的被利用,JuiceFS 给每个 128MB 的逻辑文件块(同 HDFS 的 block)分配一个 BlockLocation 信息,Spark、MR、Presto 等计算引擎会利用此信息将任务调度到同一台机器上,读缓存就会生效。BlockLocation 是通过预先知道的机器列表算出来的,使用一致性哈希算法,减少机器上下线导致的缓存失效。JuiceFS 可以通过 YARN、Spark、Presto 等提供的接口获得计算节点列表,通过 juicefs.discover-nodes-url 参数设置。

多机分布式缓存

在单机缓存的基础上,JuiceFS 还可以将磁盘上的缓存数据提供给其他机器上的客户端访问,即多机分布式缓存。

分布式缓存通过设置参数 juicefs.cache-group(缓存组的名字)来启用,设置了相同名字的机器组成一个共享缓存组,组内的机器通过一致性哈希来决定缓存数据的节点。

当组内一个客户端读取数据的时候,会通过一致性哈希计算出应该由哪个节点去缓存数据,如果是其他节点,则会请求该节点,如果对方还没有该数据快的缓存,则去对象存储读取数据并将读取数据返回,同时将数据异步地缓存在本地。同一个文件的同一个位置计算出来的缓存节点是一样的,再次读取时,该节点已经缓存了数据,可以直接读缓存数据,避免对象存储的 GET 请求。

这种共享缓存模式,所有加入的 JuiceFS 客户端均会参与建立缓存组,比较适合 JuiceFS 客户端比较固定的情况,比如 Presto、Spark Thrift Server 等。

独立缓存集群

在多机分布式缓存方案中,每个 JuiceFS 客户端均参与缓存组的建立,如果遇到 JuiceFS 客户端不是常驻的情况(如 Spark on K8s),会导致缓存利用率很低。

因此 JuiceFS 提供了缓存集群方案,此方案将 JuiceFS 挂载客户端挂载在固定机器上,组成一个稳定的缓存组,形成缓存集群。在缓存集群上每个节点执行下面挂载命令:

  1. ./juicefs mount {JFS_NAME} /jfs --cache-dir=xxx --cache-size=xxx --cache-mode=0777 --cache-group=xxx

在使用 SDK 的时候,将 juicefs.cache-group 设置为和缓存集群一样的名字,同时将 juicefs.no-sharing 设置为 true,就可以使当前机器不参与构建缓存组,而只会从缓存集群读取数据并缓存在它的本地磁盘,但不会共享给其他机器。

准备工作

JuiceFS 客户端需要通过外网访问 JuiceFS 元数据服务,需要部署 NAT 等给 Hadoop 集群提供外网访问能力。

说明:JuiceFS 的元数据服务部署在与客户相同的公有云和相同区域中(可能是不同可用区),客户端是通过外网出口访问区内的服务。如果部署在客户内网,则无此要求。

自动安装 JuiceFS Java 客户端

挂载 JuiceFS

详见: 挂载 JuiceFS

Python 脚本安装

安装脚本:setup-hadoop.py,在 Cloudera Manager 节点、Ambari 节点或者公有云 EMR Master 节点上按下面步骤运行安装。

此脚本使用 Python 编写,支持 Python2 和 Python3。 可以自动下载并安装 juicefs-hadoop.jar ,并且自动安装部署到整个集群,同时也可以自动部署 JuiceFS 相关配置到 core-site.xml (仅支持 HDP 和 CDH )

使用说明

  1. python setup-hadoop.py COMMAND
  2. COMMAND could be:
  3. -h 显示帮助信息
  4. install 在本机安装 JuiceFS JAR
  5. install_all 在所有机器上安装 JuiceFS JAR 需要把脚本运行节点和其他节点的 root 用户 SSH 打通
  6. config 自动填写配置,目前支持 Ambari ClouderaManager
  7. deploy_config 自动配置下发到部署了客户端的机器,目前支持 Ambari ClouderaManager
  8. test 验证 JuiceFS JAR 是否真确安装
  9. test_all 验证所有机器 JuiceFS JAR 是否正确安装

使用步骤

  1. 安装 JAR 文件
  1. python setup-hadoop.py install

此操作会将 juicefs-hadoop.jar 下载到 /usr/local/lib 下, 并在 Hadoop 各个发行版下面对应组件的 lib/ 目录下建立软连接,具体放置目录会在日志中打出。

  1. 配置
  1. python setup-hadoop.py config

此操作会自动将必要配置项写入 HDFS 的 core-site.xml,并在日志中打印出来。

对于 CDH 和 HDP 环境,运行此命令,并按照提示输入管理员的账号密码。如果在这台机器上成功执行过 juicefs authjuicefs mount,会自动读取 /root/.juicefs/ 中的认证信息, 然后通过 RESTful API 自动将配置项写入 core-site.xml

此操作还会按照 jucefs.cache-dir 配置的目录在机器上创建缓存目录。

  1. 将 JAR 文件分发到整个集群
  • CDH 或者 HDP 环境,执行:

    1. python setup-hadoop.py install_all
  • 其他公有云 EMR 环境,执行:

    1. export NODE_LIST=node1,node2
    2. python setup-hadoop.py install_all
  • Apache 社区版自定义安装,执行:

    1. # 组件 classpath,用逗号隔开
    2. export EXTRA_PATH=$HADOOP_HOME/share/hadoop/common/lib,$SPARK_HOME/jars,$PRESTO_HOME/plugin/hive-hadoop2
    3. export NODE_LIST=node1,node2
    4. python setup-hadoop.py install_all

此操作将会把 juicefs-hadoop.jar/etc/juicefs/.juicefs 通过 scp 的方式安装部署到指定的节点上去。

  1. 全新安装或者升级需要重启的相关服务
  • 部署配置

    需要到 CDH 或者 HDP,或者其他 EMR 管理界面操作。

  • 无需重启组件

    • HDFS
    • HUE
    • ZooKeeper
    • YARN
  • 需要重启组件

    以下各组件服务,只有当用到 JuiceFS 时才需要重启。

    • Hive

      如果启用 HA,可以通过滚动重启,不影响使用

      • Hive Metastore Server
      • HiveServer2
    • Spark on Yarn

      如果只是使用 spark-sql -f 提交任务,则无需重启

      • ThriftServer
    • Spark Standalone

      • Master
      • Worker
    • Presto

      • Coordinator
      • Worker
    • Impala

      • Catalog Server
      • Daemon
    • HBase

      • Master
      • RegionServer
    • Flume

Ansible 安装

Ansible 配置模板:setup-hadoop.yml

Ansible hosts 配置

  1. [master] # 有外网的机器,需要和 slave 节点 ssh 打通
  2. master001
  3. [slave] # 除 master 节点外所有的大数据集群管理的节点
  4. slave001
  5. slave002

使用说明

  1. ansible setup-hadoop.yml \
  2. --extra-vars '{"jfs_name":"your-jfs-name", "jfs_version":"0.5", "hadoop":"your-hadoop-dist", "cache_dir":["/data01/jfscache","/data02/jfscache"]}'
  3. 参数:
  4. jfs_name JuiceFS 上面创建的文件系统
  5. jfs_version JuiceFS 客户端版本
  6. hadoop Hadoop 发行版,目前支持:cdh5,cdh6,hdp,emr-ali,emr-tencent,kmr,uhadoop
  7. cache_dir 本机磁盘缓存目录

手动安装 JuiceFS Java 客户端

  1. 下载最新的 juicefs-hadoop.jar
  2. 将下载的 JAR 文件路径加入到应用的 classpath 中,可通过 hadoop classpath 命令查看 classpath 路径。

很多应用有默认放置 JAR 文件的目录,放在那些目录中会被自动加入到 classpath 中。

JuiceFS Java 客户端参数配置

将以下配置参数加入到 Hadoop 配置文件 core-site.xml 中。

核心配置

配置项默认值描述
fs.jfs.implcom.juicefs.JuiceFileSystem指定 jfs:// 这个存储类型所使用的实现。
fs.AbstractFileSystem.jfs.implcom.juicefs.JuiceFSMR 等应用需要指定 AbstractFileSystem 的实现
juicefs.token访问 JuiceFS 的凭证,在控制台的设置页或者访问控制页可以看到。
juicefs.accesskey对象存储的访问ID(Access Key ID)。如果计算节点已经有访问对象存储的权限,则无需提供。
juicefs.secretkey对象存储的私钥 (Secret Access Key)。如果计算节点已经有访问对象存储的权限,则无需提供。

缓存配置

配置项默认值描述
juicefs.cache-dir本地缓存目录,可以指定多个文件夹,用冒号 : 分隔,也可以使用通配符(比如 * )。通常应用没有权限创建这些目录,需要手动创建并给予 0777 权限,便于多个应用共享缓存数据
juicefs.cache-size0磁盘缓存容量,单位 MB。如果配置多个目录,这是所有缓存目录的空间总和。
juicefs.cache-replica1磁盘缓存副本数。
juicefs.cache-group配置此项则使用 P2P cache,各个相同 group 之间的 JuiceFS 客户端可以共享缓存,推荐在主要使用 Spark 的环境使用此配置,因为 Spark 在单个任务处理多个小文件时,计算没有数据本地化。
juicefs.cache-full-blocktrue是否缓存连续读数据,在磁盘空间有限或者磁盘性能低下的时候,设置为 false。
juicefs.memory-size300预读最大使用内存量,单位 MB。
juicefs.auto-create-cache-dirtrue是否自动创建缓存目录。为 false 时,它会忽略不存在的缓存目录。
juicefs.free-space0.2最低剩余磁盘空间比例,当剩余磁盘空间低于这个比例时,它会清空缓存数据以释放空间,默认 20%。
juicefs.metacachetrue是否启用元数据缓存。
juicefs.discover-nodes-url指定发现集群计算节点列表的方式,每 10 分钟刷新一次。

YARN:yarn
Spark Standalone:http://spark-master:web-ui-port/json/
Spark ThriftServer:http://thrift-server:4040/api/v1/applications/
Presto:http://coordinator:discovery-uri-port/v1/service/presto/

其他配置

配置项默认值描述
juicefs.access-log访问日志的路径。需要所有应用都有写权限,可以配置为 /tmp/juicefs.access.log 。该文件会自动轮转,保留最近 7 个文件。
juicefs.debugfalse是否启用 Debug 级别的日志。
juicefs.max-uploads50单进程写入对象存储最大并发数
juicefs.superuserhdfs超级用户
juicefs.bucket指定存储桶名称
juicefs.upload-limit0单进程写入对象存储速度限制,单位 byte/s
juicefs.object-timeout5访问对象存储单次读请求的最长时间,单位 秒
juicefs.externalfalse是否使用公网域名访问对象存储
juicefs.rsaPrivKeyPath用于数据存储加密的 RSA 密钥路径
juicefs.rsaPassphase用于数据存储加密的 RSA 密钥密码
juicefs.file.checksumfalse用于 Hadoop DistCp 时是否验证 checksum
juicefs.grouping指定 group 文件放置位置,用来配置用户组和用户映射信息,推荐配置在 jfs://your-jfs-name/etc/group。文件内容格式:groupname:username1,username2

当使用多个 JuiceFS 文件系统时,上述所有配置项均可对单个文件系统指定,需要将文件系统名字 JFS_NAME 放在配置项的中间,比如:

  1. <property>
  2. <name>juicefs.{JFS_NAME}.token</name>
  3. <value>false</value>
  4. </property>

常见发行版配置方法

在通过 自动化脚本安装 Java 客户端 后,无需再进行下面的配置。

CDH

使用 Parcel 方式安装 JuiceFS

  1. 下载 ParcelCSD 文件

    将 CSD 文件放入 Cloudera Manager 节点的 /opt/cloudera/csd 目录,将 Parcel 文件解压并将内容放入 /opt/cloudera/parcel-repo 目录。

  2. 重启 Cloudera Manager

    1. service cloudera-scm-server restart
  3. 激活 Parcel

    打开 CDH 管理界面 → Hosts → Check for New Parcels → JUICEFS → Distribute → Active

  4. 添加服务

    打开 CDH 管理界面 → 集群名 → Add Service → JuiceFS → 选择安装机器 → 配置缓存目录(cache_dirs

  5. 部署 JAR 文件

    在 Hadoop 中通过 Java 客户端访问 JuiceFS - 图1

  6. 升级 JuiceFS

    如果需要升级,则下载新的 Parcel 文件,并进行第 3 步。

通过 Cloudera Manager 修改配置

  • Hadoop

    CDH 5.x

    通过 HDFS 服务界面修改 core-site.xml

    在 Hadoop 中通过 Java 客户端访问 JuiceFS - 图2

    常用配置

    1. fs.jfs.impl=com.juicefs.JuiceFileSystem
    2. fs.AbstractFileSystem.jfs.impl=com.juicefs.JuiceFS
    3. juicefs.cache-size=10240
    4. juicefs.cache-dir=xxxxxx
    5. juicefs.cache-group=yarn
    6. juicefs.discover-nodes-url=yarn
    7. juicefs.accesskey=xxxxxx
    8. juicefs.secretkey=xxxxxx
    9. juicefs.token=xxxxxx
    10. juicefs.access-log=/tmp/juicefs.access.log

    CDH 6.x

    除了上述 5.x 内容外。您还需通过 YARN 服务界面修改 mapreduce.application.classpath,增加以下配置

    1. $HADOOP_COMMON_HOME/lib/juicefs-hadoop.jar
  • HBase

    通过 HBase 服务界面修改 hbase-site.xml

    images/hbase-site.png

    配置:

    1. <property>
    2. <name>hbase.rootdir</name>
    3. <value>jfs://{JFS_NAME}/hbase</value>
    4. </property>
    5. <property>
    6. <name>hbase.wal.dir</name>
    7. <value>hdfs://your-hdfs-uri/hbase-wal</value>
    8. </property>

    通过 ZooKeeper 客户端删除 zookeeper.znode.parent 配置的 znode(默认 /hbase

    注意:此操作将会删除原有 HBase 上面的所有数据

  • Hive

    通过 Hive 服务界面修改 hive.metastore.warehouse.dir,可修改 Hive 建表默认位置(非必须)

    1. jfs://your-jfs-name/your-warehouse-dir
  • Impala

    通过 Impala 服务界面修改 Impala 命令行参数高级配置

    此参数可以使用 20/本地挂载磁盘数 来设置,修改此参数主要是为了增加 JuiceFS 的读取 IO 线程数。

    1. -num_io_threads_per_rotational_disk=4
  • Solr

    通过 Solr 服务界面修改 Solr 服务环境高级配置代码段

    1. hdfs_data_dir=jfs://your-jfs/solr

最后重启集群,让配置修改生效。

HDP

将 JuiceFS 集成到 Ambari

  1. 下载 HDP 安装文件

    下载文件解压后将内容放到 /var/lib/ambari-server/resources/stacks/HDP/{YOUR-HDP-VERSION}/services

  2. 重启 Ambari

    1. systemctl restart ambari-server
  3. 添加 JuiceFS 服务

    打开 Ambari 管理界面 → Services → Add Service → JuiceFS → 选择安装机器 → 配置 → deploy

    在配置步骤,主要是配置缓存目录(cache_dirs)和下载版本(download_url)。

    如果 Ambari 没有外网,可以直接将下载好的 JAR 包收到放到 share_download_dir 下,默认为 HDFS 的 /tmp 目录。

  4. 升级 JuiceFS

    修改 download_url 的版本号,保存并 Refresh configs。

    images/jfs-hdp.png

通过 Ambari 修改配置

  • Hadoop

    通过 HDFS 服务界面修改 core-site.xml,具体配置见 详细配置表

  • MapReduce2

    通过 MapReduce2 服务界面修改配置 mapreduce.application.classpath

    在末尾增加 :/usr/hdp/${hdp.version}/hadoop/lib/juicefs-hadoop.jar (变量无需替换)。

  • Hive

    通过 Hive 服务界面修改 hive.metastore.warehouse.dir,可修改 hive 建表默认位置(非必须):

    1. jfs://your-jfs-name/your-warehouse-dir

    如果配置了 Ranger 服务,则需要配置 HIVE 服务 ranger.plugin.hive.urlauth.filesystem.schemes,追加 jfs 支持

    1. ranger.plugin.hive.urlauth.filesystem.schemes=hdfs:,file:,wasb:,adl:,jfs:
  • Druid

    通过 Druid 界面修改目录地址(如无权限,需手动创建目录):

    1. "druid.storage.storageDirectory": "jfs://your-jfs-name/apps/druid/warehouse"
    2. "druid.indexer.logs.directory": "jfs://your-jfs-name/user/druid/logs"
  • HBase

    通过 HBase 服务界面修改一下参数

    1. hbase.rootdir=jfs://your-jfs-name/hbase
    2. hbase.wal.dir=hdfs://your-hdfs-uri/hbase-wal

    通过 ZooKeeper 客户端删除 zookeeper.znode.parent 配置的 znode(默认 /hbase

    注意:此操作将会删除原有 HBase 上面的所有数据

  • Sqoop

    使用 Sqoop 将数据导入 Hive 时,Sqoop 会首先把数据导入 target-dir,然后在通过 hive load 命令将数据加载到 Hive 表,所以使用 Sqoop 时,需要修改 target-dir

    1.4.6

    使用此版本的 Sqoop 时还需要修改 fs, 此参数修改默认文件系统,所以需要将 HDFS 上面的 mapreduce.tar.gz 复制到 JuiceFS 上的相同路径,默认目录在 HDFS /hdp/apps/${hdp.version}/mapreduce/mapreduce.tar.gz

    1. sqoop import \
    2. -fs jfs://your-jfs-name/ \
    3. --target-dir jfs://your-jfs-name/tmp/your-dir

    1.4.7

    1. sqoop import \
    2. --target-dir jfs://your-jfs-name/tmp/your-dir

最后,重启相应服务让配置修改生效。

阿里云 EMR

新建 EMR 集群集成 JuiceFS

  1. 在 EMR 软件配置界面配置高级设置

    在软件配置 → 高级设置 → 软件自定义配置填入以下配置,缓存配置按需修改。

    1. [
    2. {
    3. "ServiceName": "HDFS",
    4. "FileName": "core-site",
    5. "ConfigKey": "fs.jfs.impl",
    6. "ConfigValue": "com.juicefs.JuiceFileSystem"
    7. },
    8. {
    9. "ServiceName": "HDFS",
    10. "FileName": "core-site",
    11. "ConfigKey": "fs.AbstractFileSystem.jfs.impl",
    12. "ConfigValue": "com.juicefs.JuiceFS"
    13. },
    14. {
    15. "ServiceName": "HDFS",
    16. "FileName": "core-site",
    17. "ConfigKey": "juicefs.cache-group",
    18. "ConfigValue": "yarn"
    19. },
    20. {
    21. "ServiceName": "HDFS",
    22. "FileName": "core-site",
    23. "ConfigKey": "juicefs.cache-dir",
    24. "ConfigValue": "/mnt/disk*/jfs"
    25. },
    26. {
    27. "ServiceName": "HDFS",
    28. "FileName": "core-site",
    29. "ConfigKey": "juicefs.cache-size",
    30. "ConfigValue": "1000000"
    31. },
    32. {
    33. "ServiceName": "HDFS",
    34. "FileName": "core-site",
    35. "ConfigKey": "juicefs.discover-nodes-url",
    36. "ConfigValue": "yarn"
    37. },
    38. {
    39. "ServiceName": "HDFS",
    40. "FileName": "core-site",
    41. "ConfigKey": "juicefs.token",
    42. "ConfigValue": ""
    43. },
    44. {
    45. "ServiceName": "HDFS",
    46. "FileName": "core-site",
    47. "ConfigKey": "juicefs.conf-dir",
    48. "ConfigValue": "/etc/juicefs"
    49. },
    50. {
    51. "ServiceName": "HDFS",
    52. "FileName": "core-site",
    53. "ConfigKey": "juicefs.access-log",
    54. "ConfigValue": "/tmp/juicefs.access.log"
    55. }
    56. ]
  2. 在基础信息界面的高级设置里面添加引导操作

    下载 emr-boot.sh 脚本和 juicefs-hadoop.jar ,并上传到您的对象存储上。

    在 Hadoop 中通过 Java 客户端访问 JuiceFS - 图5

    「脚本位置」填写 emr-boot.sh 在 OSS 上面的地址,「参数」填写 juicefs-hadoop-{version}.jar 在 OSS 上面的地址。

    • 可以访问公网

      1. --jar oss://{bucket}/resources/juicefs-hadoop-4.5.4.jar --endpoint {endpoint}
      2. 或者
      3. --jar https://{bucket}.{endpoint}/resources/juicefs-hadoop-4.5.4.jar
    • 无外网连接(私有部署)

      由于 JuiceFS 需要联网下载部分必须配置文件来发行服务器的地址,因此在私有环境下(无外网连接),需要将此文件事先准备好。此配置文件可以在挂载 JuiceFS 的机器上面 /root/.juicefs 下面找到,文件名:{jfs-name}.conf

      将此配置文件同样上传到 OSS 上,然后在引导操作的参数里面填写下面配置:

      1. --jar oss://{bucket}/{jar-path}
      2. --conf-file oss://{bucket}/{conf-file-path}

已有 EMR 集群集成 JuiceFS

  • Hadoop

    通过 HDFS 服务界面修改 core-site.xml

    常用配置:

    1. fs.jfs.impl=com.juicefs.JuiceFileSystem
    2. fs.AbstractFileSystem.jfs.impl=com.juicefs.JuiceFS
    3. juicefs.cache-size=10240
    4. juicefs.cache-dir=xxxxxx
    5. juicefs.cache-group=yarn
    6. juicefs.discover-nodes-url=yarn
    7. juicefs.accesskey=xxxxxx
    8. juicefs.secretkey=xxxxxx
    9. juicefs.token=xxxxxx
    10. juicefs.access-log=/tmp/juicefs.access.log
  • Hive

    通过 Hive 服务界面修改 hive.metastore.warehouse.dir,可修改 Hive 建表默认位置(非必须):

    1. jfs://{jfs-name}/{warehouse-dir}

最后,重启相应服务让配置修改生效。

腾讯云 EMR

新建 EMR 集群集成 JuiceFS

  1. 在 EMR 可用区与软件配置界面配置高级设置

    images/emr-tx-conf.png

    在软件配置填入以下配置,缓存配置按需修改。

    1. [
    2. {
    3. "serviceName": "HDFS",
    4. "classification": "core-site.xml",
    5. "serviceVersion": "2.8.5",
    6. "properties": {
    7. "fs.jfs.impl": "com.juicefs.JuiceFileSystem",
    8. "fs.AbstractFileSystem.jfs.impl": "com.juicefs.JuiceFS",
    9. "juicefs.token": "",
    10. "juicefs.accesskey": "",
    11. "juicefs.secretkey": "",
    12. "juicefs.cache-group": "yarn",
    13. "juicefs.cache-dir": "/data*/jfs",
    14. "juicefs.cache-size": "10000000",
    15. "juicefs.discover-nodes-url": "yarn",
    16. "juicefs.superuser": "hadoop",
    17. "juicefs.conf-dir": "/etc/juicefs",
    18. "juicefs.access-log": "/tmp/juicefs.access.log"
    19. }
    20. }
    21. ]

    注意 serviceVersion 需要和 Hadoop 版本匹配

  2. 在基础信息界面的高级设置里面添加引导操作

    下载 emr-boot.sh 脚本和 juicefs-hadoop.jar ,并上传到您的对象存储上。

    images/emr-tx-boot.png

    对象存储需授权,引导操作的运行时机为「集群启动前」。

    「脚本位置」填写 emr-boot.sh 在 COS 上面的地址,「参数」填写 juicefs-hadoop-{version}.jar 在 COS 上面的地址。

    • 可以访问公网

      1. --jar cosn://{bucket}/{jar-path}
    • 无外网连接(私有部署)

      由于 JuiceFS 需要联网下载部分必须配置文件来发行服务器的地址,因此在私有环境下(无外网连接),需要将此文件事先准备好。此配置文件可以在挂载 JuiceFS 的机器上面 /root/.juicefs 下面找到,文件名:{jfs-name}.conf

      将此配置文件同样上传到 COS 上,然后在引导操作的参数里面填写下面配置:

      1. --jar cosn://{bucket}/{jar-path}
      2. --conf-file cosn://{bucket}/{conf-file-path}
  3. 启动集群

已有 EMR 集群集成 JuiceFS

需要授予集群 COS 访问权限

  • Hadoop

    通过 HDFS 服务界面修改 core-site.xml

    常用配置:

    1. fs.jfs.impl=com.juicefs.JuiceFileSystem
    2. fs.AbstractFileSystem.jfs.impl=com.juicefs.JuiceFS
    3. juicefs.cache-size=10240
    4. juicefs.cache-dir=xxxxxx
    5. juicefs.cache-group=yarn
    6. juicefs.discover-nodes-url=yarn
    7. juicefs.accesskey=xxxxxx
    8. juicefs.secretkey=xxxxxx
    9. juicefs.token=xxxxxx
    10. juicefs.access-log=/tmp/juicefs.access.log

    注意

    由于腾讯 EMR 默认使用 hadoop 用户启动 HDFS ,HDFS 的 superuser 为 hadoop。为了保持一致所以还需要指定 juicefs.superuser=hadoop

  • Hive

    通过 Hive 服务界面修改 hive.metastore.warehouse.dir,可修改 Hive 建表默认位置(非必须):

    1. jfs://{jfs-name}/{warehouse-dir}

最后,重启相应服务让配置修改生效。

金山 KMR

HDP 环境的配置方法

AWS EMR

新建 EMR 集群集成 JuiceFS

  1. 在 Software and Steps 界面填写配置

    images/aws-emr-conf.png

    • 基础配置

      1. [
      2. {
      3. "classification": "core-site",
      4. "properties": {
      5. "fs.jfs.impl": "com.juicefs.JuiceFileSystem",
      6. "fs.AbstractFileSystem.jfs.impl": "com.juicefs.JuiceFS",
      7. "juicefs.cache-size": "10240",
      8. "juicefs.access-log": "/tmp/juicefs.access.log",
      9. "juicefs.discover-nodes-url": "yarn",
      10. "juicefs.conf-dir": "/etc/juicefs",
      11. "juicefs.cache-full-block": "false",
      12. "juicefs.token": "",
      13. "juicefs.cache-group": "yarn",
      14. "juicefs.cache-dir": "/mnt*/jfs"
      15. }
      16. }
      17. ]
    • 使用 HBase on JuiceFS 配置。

      1. [
      2. {
      3. "classification": "core-site",
      4. "properties": {
      5. "fs.jfs.impl": "com.juicefs.JuiceFileSystem",
      6. "juicefs.cache-size": "10240",
      7. "juicefs.access-log": "/tmp/juicefs.access.log",
      8. "juicefs.discover-nodes-url": "yarn",
      9. "juicefs.conf-dir": "/etc/juicefs",
      10. "juicefs.cache-full-block": "false",
      11. "juicefs.token": "",
      12. "juicefs.cache-group": "yarn",
      13. "juicefs.free-space": "0.3",
      14. "juicefs.cache-dir": "/mnt*/jfs",
      15. "fs.AbstractFileSystem.jfs.impl": "com.juicefs.JuiceFS"
      16. }
      17. },
      18. {
      19. "classification": "hbase-site",
      20. "properties": {
      21. "hbase.rootdir": "jfs: //{name}/hbase"
      22. }
      23. }
      24. ]

    配置项 juicefs.cache-sizejuicefs.free-space 的缓存配置,需要按需配置。juicefs.token 需要到 JuiceFS 控制台获取。

  2. 在 General Cluster Settings 界面添加引导操作

    下载 emr-boot.sh 脚本和 juicefs-hadoop.jar ,并上传到您的对象存储上。

    images/aws-emr-boot.png

    「脚本位置」填写 emr-boot.sh 在 OSS 上面的地址,「参数」填写 juicefs-hadoop-{version}.jar 在 S3 上面的地址。

    • 可以访问公网

      1. --jar s3://{bucket}/resources/juicefs-hadoop-4.5.4.jar
    • 无外网连接(私有部署)

      由于 JuiceFS 需要联网下载部分必须配置文件来发行服务器的地址,因此在私有环境下(无外网连接),需要将此文件事先准备好。此配置文件可以在挂载 JuiceFS 的机器上面 /root/.juicefs 下面找到,文件名:{jfs-name}.conf

      将此配置文件同样上传到 S3 上,然后在引导操作的参数里面填写下面配置:

      1. --jar s3://{bucket}/{jar-path}
      2. --conf-file s3://{bucket}/{conf-file-path}

已有 EMR 集群集成 JuiceFS

在 Master 节点上按照 手动安装 Java 客户端 的方法配置 JAR 文件,然后通过 EMR 界面修改配置:

  • Hadoop

    通过 HDFS 服务界面修改 core-site.xml

    常用配置:

    1. fs.jfs.impl=com.juicefs.JuiceFileSystem
    2. fs.AbstractFileSystem.jfs.impl=com.juicefs.JuiceFS
    3. juicefs.cache-size=10240
    4. juicefs.cache-dir=xxxxxx
    5. juicefs.cache-group=yarn
    6. juicefs.discover-nodes-url=yarn
    7. juicefs.accesskey=xxxxxx
    8. juicefs.secretkey=xxxxxx
    9. juicefs.token=xxxxxx
    10. juicefs.access-log=/tmp/juicefs.access.log
  • HBase

    修改 hbase-site

    1. "hbase.rootdir": "jfs://your-jfs/hbase"

    修改 hbase

    1. "hbase.emr.storageMode": "jfs"

等 EMR 配置刷新后,重启相应服务。

UCloud UHadoop

在 Master 节点上按照 手动安装 Java 客户端 的方法配置 JAR 文件。

修改 core-site.xml,具体配置见 详细配置表

最后,重启集群让配置修改生效。

自定义组件

首先配置好 Hadoop 基础环境

Apache Spark

修改 conf/spark-env.sh,在前面增加以下配置:

  1. export HADOOP_HOME=YOUR_HADOOP_HOME
  2. export HADOOP_CONF_DIR=/etc/hadoop/conf
  3. export SPARK_DIST_CLASSPATH=`hadoop classpath`

修改 bin/config.sh,在前面增加以下配置:

  1. export HADOOP_HOME=YOUR_HADOOP_HOME
  2. export HADOOP_CONF_DIR=/etc/hadoop/conf
  3. export HADOOP_CLASSPATH=`hadoop classpath`

Presto

juicefs-hadoop.jar 放到 plugin/hive-hadoop2 目录

DataX

  • juicefs-hadoop.jar 放到 datax/plugin/writer/hdfswriter/libs 目录

  • 修改 DataX 配置文件

    1. "defaultFS": "jfs://your-jfs-name",
    2. "hadoopConfig": {
    3. "fs.jfs.impl": "com.juicefs.JuiceFileSystem",
    4. "fs.AbstractFileSystem.jfs.impl": "com.juicefs.JuiceFS",
    5. "juicefs.access-log": "/tmp/juicefs.access.log",
    6. "juicefs.token": "xxxxxxxxxxxxx",
    7. "juicefs.accesskey": "xxxxxxxxxxxxx",
    8. "juicefs.secretkey": "xxxxxxxxxxxxx"
    9. }

测试验证

Hadoop

  1. hadoop fs -ls jfs://${JFS_NAME}/
  2. hadoop fs -mkdir jfs://${JFS_NAME}/jfs-test
  3. hadoop fs -rm -r jfs://${JFS_NAME}/jfs-test

Hive、SparkSQL、Impala

  1. create table if not exists person(
  2. name string,
  3. age int
  4. )
  5. location 'jfs://${JFS_NAME}/tmp/person';
  6. insert into table person values('tom',25);
  7. insert overwrite table person select name, age from person;
  8. select name, age from person;
  9. drop table person;

HBase

  1. create 'test', 'cf'
  2. list 'test'
  3. put 'test', 'row1', 'cf:a', 'value1'
  4. scan 'test'
  5. get 'test', 'row1'
  6. disable 'test'
  7. drop 'test'

Flume

  1. jfs.sources =r1
  2. jfs.sources.r1.type = org.apache.flume.source.StressSource
  3. jfs.sources.r1.size = 10240
  4. jfs.sources.r1.maxTotalEvents=10
  5. jfs.sources.r1.batchSize=10
  6. jfs.sources.r1.channels = c1
  7. jfs.channels = c1
  8. jfs.channels.c1.type = memory
  9. jfs.channels.c1.capacity = 100
  10. jfs.channels.c1.transactionCapacity = 100
  11. jfs.sinks = k1
  12. jfs.sinks.k1.type = hdfs
  13. jfs.sinks.k1.channel = c1
  14. jfs.sinks.k1.hdfs.path =jfs://${JFS_NAME}/tmp/flume
  15. jfs.sinks.k1.hdfs.writeFormat= Text
  16. jfs.sinks.k1.hdfs.fileType= DataStream
  1. echo 'hello world' > /tmp/jfs_test
  2. hadoop fs -put /tmp/jfs_test jfs://${JFS_NAME}/tmp/
  3. rm -f /tmp/jfs_test
  4. ./bin/flink run -m yarn-cluster ./examples/batch/WordCount.jar --input jfs://${JFS_NAME}/tmp/jfs_test --output jfs://${JFS_NAME}/tmp/result

将数据迁移到 JuiceFS

Hive、Spark、Impala

Hive 等数据仓库表的数据可以在不同的文件系统,迁移数据时可以使用 distcp 将 HDFS 中的数据拷贝至 JuiceFS,然后用 alter table 更新表的数据位置即可。

同一张表的不同分区的数据也可以在不同的文件系统,可以修改写入数据的代码,在插入新的分区数据时使用 JuiceFS,然后再逐步将已有分区的数据使用 distcp 拷贝过来后再更新分区的数据位置。

HBase

环境准备

您需要准备一个相同配置的 HBase 集群。

离线迁移

原 HBase 集群中关闭所有表:

  1. bin/disable_all_tables.sh

新的 JuiceFS HBase 集群中,

  • 通过 distcp 导入数据

    1. sudo -u hbase hadoop distcp -Dmapreduce.map.memory.mb=1500 your_filesystem/hbase/data jfs://your_jfs/hbase/data
  • 删除 HBase 元数据

    1. sudo -u hbase hadoop fs -rm -r jfs://your_jfs/hbase/data/hbase
  • 修复元数据

    1. sudo -u hbase hbase hbck -fixMeta -fixAssignments

在线迁移

接下来的操作会将历史数据导入新的 JuiceFS HBase 集群,并且和原 HBase 集群保持同步,一旦数据同步成功后,可以将业务切换到新的 JuiceFS HBase 集群。

原 HBase 集群中,

  • 打开 replication

    1. add_peer '1', CLUSTER_KEY => "jfs_zookeeper:2181:/hbase",TABLE_CFS => { "your_table" => []}
    2. enable_table_replication 'your_table'

    此操作会在 JuiceFS HBase 集群自动创建表(包括 splits )

  • 关闭同步

    1. disable_peer("1")
  • 创建快照

    1. snapshot 'your_table','your_snapshot'

新的 JuiceFS HBase 集群中,

  • 导入快照

    1. sudo -u hbase hbase org.apache.hadoop.hbase.snapshot.ExportSnapshot \
    2. -snapshot your_snapshot \
    3. -copy-from s3://your_s3/hbase \
    4. -copy-to jfs://your_jfs/hbase \
    5. -mappers 1 \
    6. -bandwidth 20

    您可以根据需要调整 map 数和带宽(MB/s)

  • 恢复快照

    1. disable 'your_table'
    2. restore_snapshot 'your_snapshot'
    3. enable 'your_table'
  • 原 HBase 集群中打开同步:

    1. enable_peer("1")

最佳实践

Hadoop

Hadoop 生态组件一般通过 org.apache.hadoop.fs.FileSystem 类与 HDFS 对接,JuiceFS 同样也通过继承这个类来支持 Hadoop 生态的各个组件。

JuiceFS 部署完成后,就可以通过 hadoop fs 命令操作 JuiceFS:

  1. hadoop fs -ls jfs://{JFS_NAME}/

如果想省去协议部分,可以将 fs.defaultFS 设置为 JuiceFS 地址,在 core-site.xml 里面增加如下配置:

  1. <property>
  2. <name>fs.defaultFS</name>
  3. <value>jfs://{JFS_NAME}</value>
  4. </property>

注意:修改 defaultFS 会导致所有没有带协议的路径默认均指向 JuiceFS,可能会导致一些其他问题,建议先在测试环境中测试。

Hive

Apache Hive 是 Hadoop 生态里面分布式容错数据仓库系统。

Hive Metastore 用来存储管理元数据,对于文件系统而言,主要使用 location 信息存储表数据的具体路径。

  • 建库建表

    1. -- create database
    2. CREATE DATABASE ... database_name
    3. LOCATION 'jfs://{JFS_NAME}/path-to-database';
    4. -- create table
    5. CREATE TABLE ... table_name
    6. LOCATION 'jfs://{JFS_NAME}/path-to-table';

    Hive create 的表,默认会在 database 的 location 下。如果 database 的 location 已经在 JuiceFS 上,新建 table 默认也会在 JuiceFS 上。

  • 改库改表

    1. -- alter database
    2. ALTER DATABASE database_name
    3. SET LOCATION 'jfs://{JFS_NAME}/path-to-database';
    4. -- alter table
    5. ALTER TABLE table_name
    6. SET LOCATION 'jfs://{JFS_NAME}/path-to-table';
    7. -- alter partition
    8. ALTER TABLE table_name PARTITION(...)
    9. SET LOCATION 'jfs://{JFS_NAME}/path-to-partition';

    Hive 支持使用多种文件系统存储数据。

    对于未分区表,整张表的数据必须是在同一个文件系统上。对于分区表,每个分区可以独立设置文件系统。

    如果想默认建库就在 JuiceFS 上,可以通过将 hive.metastore.warehouse.dir 修改为 JuiceFS 上的路径。

Spark

Spark 有多种运行模式,如 Standalone、YARN、K8s、Thrift Server 等。

由于 JuiceFS 的特殊的缓存架构设计,JuiceFS 需要保证客户端的机器 IP 尽量不变才能保证缓存的高效利用。 因此,Spark 各种运行模式对于 JuiceFS 的最主要区别就是 executor 进程是否常驻,executor 所在机器的 IP 是否经常变化。

在 executor 进程常驻时(Thrift Server),或者在 executor 所在的机器 IP 基本固定并且集群使用率比较高时(Spark on YARN,Standalone),使用「多机分布式缓存」方案 。juicefs.discover-nodes-url 参数需要做相应的设置。

在 executor 进程经常变化,并且机器 IP 也经常变化时(Spark on K8s),使用「独立缓存集群」方案。juicefs.discover-nodes-url 设置为 all

  • Spark shell

    1. scala> sc.textFile("jfs://{JFS_NAME}/path-to-input").count

HBase

HBase 是将数据存储在 HDFS 上的数据库。HBase 主要在 HDFS 存储两部分数据,WAL 文件以及 HFile。 写入数据时,会先写 WAL,然后在将数据写入 RegionServer 的 memstore,这样可以保证即使 RegionServer 异常退出时,已经提交的数据仍然可以通过 WAL 恢复。 当 RegionServer 里面的数据已经落入 HDFS 形成 HFile 后,WAL 文件会被删除,所以整体 WAL 文件对空间的使用量不会很大。

当客户端 commit 时,会调用 HDFS 的 hflush 方法来保证数据的持久化。

  • hflush 接口 JuiceFS 与 HDFS 实现的区别:

    JuiceFS 的 hflush 和 HDFS 的 hflush 有所不同。

    HDFS 是将数据提交到多台 datanode 的内存里就算成功,只要 datanode 不同时掉电,数据就不会丢失,因此时延比较低。

    JuiceFS 的 hflush 有多种模式,通过参数 juicefs.hflush 设置

    • writeback(默认)

      此模式下调用 hflush 接口只会将数据提交到本地磁盘(后台有线程会异步将数据上传到对象存储),但在客户端异常退出的情况下,可能由于部分数据没来得及上次对象存储而导致数据丢失。

    • sync

      此模式下调用 hflush 接口会将数据提交到对象存储。但由于是数据需要提交到对象存储,不可避免的有较高的时延,因此适合批量数据调用。

  • 参数设置

    在保证数据不丢失的前提下,JuiceFS 的 hflush 性能比较慢,因此仍然建议将 WAL 文件写入 HDFS,最终的 HFile 写入 JuiceFS。

    1. <property>
    2. <name>hbase.rootdir</name>
    3. <value>jfs://{JFS_NAME}/hbase</value>
    4. </property>
    5. <property>
    6. <name>hbase.wal.dir</name>
    7. <value>hdfs://{NAME_SPACE}/hbase-wal</value>
    8. </property>

Apache Flink 是一个框架和分布式处理引擎,用于在无边界和有边界数据流上进行有状态的计算。

在 Streaming 场景下,Apache Flink 使用 Streaming File Sink 从文件系统读写数据。 为了保证数据的可靠性,Flink 需要在 checkpoint 时使用 RollingPolicy

对于版本小于 2.7 的 Hadoop 版本,由于 HDFS 没有 truncate 功能,需要使用 OnCheckpointRollingPolicy,每次 checkpoint 都关闭并生成新的文件写入数据,会导致大量小文件。Hadoop2.7 及以上版本的 HDFS,可以使用 DefaultRollingPolicy,可以根据文件大小、时间以及空闲时间来滚动文件。

JuiceFS 实现了 truncate 功能,所以同样也可以使用 DefaultRollingPolicy

Flink 支持使用 plugin 的模式对接文件系统,可以只将相关的 JAR 文件放到 plugin 目录下。不过由于 JuiceFS 的 JAR 依赖于本地的 Hadoop 环境,需要将所依赖的所有 JAR 都放到 plugin 目录下,比较麻烦。目前仍然推荐将 juicefs-hadoop.jar 文件放到 lib 目录下。

  1. flink run -m yarn-cluster -p 1 examples/streaming/WordCount.jar --input jfs://{JFS_NAME}/path-to-file

Flume

JuiceFS 同样利用 HDFS sink 与 Flume 集成。hdfs.batchSize 表示多少消息调用一次 hflush 接口。

  • hflush 接口 JuiceFS 与 HDFS 实现的区别:

    JuiceFS 的 hflush 和 HDFS 的 hflush 有所不同。

    HDFS 是将数据提交到多台 DataNode 的内存里就算成功,只要 DataNode 不同时掉电,数据就不会丢失,因此时延比较低。

    JuiceFS 的 hflush 有多种模式,通过参数 juicefs.hflush 设置

    • writeback (默认)

      此模式下调用 hflush 接口只会将数据提交到本地磁盘(后台有线程会异步将数据上传到对象存储),但在客户端异常退出的情况下,可能由于部分数据没来得及上次对象存储而导致数据丢失。

    • sync

      此模式下调用 hflush 接口会将数据提交到对象存储。但由于是数据需要提交到对象存储,不可避免的有较高的时延,因此适合批量数据调用。

  • 参数设置

    为了保证数据不丢失,juicefs.hflush 设置为 sync 模式,同时应该将 hdfs.batchSize 调大,将每个批次的数量控制在消息总大小 4MB(默认每个对象块的大小)左右。

    另外在 hdfs.fileType 设置为 CompressedStream 时, 表示数据压缩。在极端情况下如果出现对象存储不可写入的情况,可能会导致整个压缩文件损坏,导致之前已经 hflush 的数据仍然不可读。 建议将 hdfs.fileType 设置为 DataStream,后续在通过 ETL 任务将数据压缩或者转格式。

Sqoop

使用 Sqoop 将数据导入 Hive 时,Sqoop 会首先把数据导入 target-dir,然后在通过 hive load 命令将数据加载到 Hive 表,所以使用 Sqoop 时,需要修改 target-dir

1.4.6

使用此版本的 Sqoop 时还需要修改 fs, 此参数修改默认文件系统,所以需要将 HDFS 上面的 mapreduce.tar.gz 复制到 JuiceFS 上的相同路径,默认目录在 HDFS /hdp/apps/${hdp.version}/mapreduce/mapreduce.tar.gz

  1. sqoop import \
  2. -fs jfs://{JFS_NAME}/ \
  3. --target-dir jfs://{JFS_NAME}/path-to-dir

1.4.7

  1. sqoop import \
  2. --target-dir jfs://{JFS_NAME}/path-to-dir