背景
HDFS通过把文件系统元数据全部加载到数据节点Namenode内存中,给客户端提供了低延迟的元数据访问。
由于元数据需要全部加载到内存,所以一个HDFS集群能支持的最大文件数,受Java堆内存的限制,上限大概是4亿~5亿个文件。所以HDFS适合大量大文件[几百兆字节(MB)以上]的集群,如果集群中有非常多的小文件,HDFS的元数据访问性能会受到影响。虽然可以通过各种Federation技术来扩展集群的节点规模,但单个HDFS集群仍然没法很好地解决小文件的限制。
什么是Ozone?
官方:Documentation for Apache Ozone
Ozone是一个大数据场景分布式存储,支持百亿到千亿级对象和文件。Ozone提供兼容S3 的对象功能,和兼容Hadoop File System(HCFS)的文件功能,同时通过CSI驱动接入Kubernets生态。Ozone定位于混合云场景存储支持,是面向数据湖的下一代大数据存储系统
- 支持S3协议以及HDFS RPC协议
- 专门为Hadoop设计的可扩展的分布式对象存储系统,具有易扩展、冗余存储的特点
- Hive、Spark、Yarn/MR 的应用无需修改任何代码就可以运行
- 可以通过命令行直接使用也有java客户端接口,而且接口支持RPC和REST
相比HDFS,Ozone调整:
- 元数据存储:拆分NameNode,引入第三方存储。将NameNode元数据管理功能拆分成OM(Ozone Manager,用于存储文件的元数据)和SCM(Storage Container Manager,用于存储Container的元数据)。另外,元数据不再是all in memory的,我们引入了RocksDB替代原先的内存存储。通过引入持久化和元数据管理的分层,进而提升了原数据的上限。
- 增加Container抽象:引入Container作为一组block的抽象逻辑,心跳汇报以Container为单位,降低汇报量,也减少SCM的元数据存储量。
- 增加对象语义:支持文件协议和S3协议。Ozone的对象路径分为三层,分别为Volume、Bucket和Object (key)。如果需要访问某个Object,可以通过OM地址,找到对应的volume、对应的bucket,再取到对应的key进行访问(http://hostname/volume/bucket/key)。
基本概念
1)Ozone的对象层次
2)概念集合
- Volume【卷】
- 只有管理员能够创建、删除,类似账号的概念,一般给某个团队或者组织创建一个Volume
- Ozone的namespace由多个volume组成
- volume及相关的元数据可以用过shard的方式水平扩展
- Bucket【存储桶】
- Ozone中的最顶层容器,有唯一标识
- 类似于目录的概念,但是只有一层
- Bucket下存放对象
- Object【对象】
- 基本数据单元,每个 Object 都有唯一的键(Key),通过键可以唯一标识一个 Object
- Object 可以是用户上传的文件、数据块或其他类似数据。
- 一个Bucket可以包含几十亿个object
- Key【键】
- 唯一标识Object的
字符串
或者字节数组
- 由Bucket和Object名称组成
- 唯一标识Object的
- SCM【storage Container Manager】
- Ozone 存储系统的核心组件之一,负责管理物理存储介质(如磁盘)上的存储容器(Storage Container)
- SCM 负责分配和监控存储容器的使用,跟踪对象的位置、副本和版本信息等。
- ACLS【访问控制列表】
- 帮助管理存储资源、限制用户和群组的权限,防止数据泄露和滥用
- ACLS:授予用户对于Object的权限
- BCLS:控制用户或者群组对于Bucket的读、写、执行等权限
- RBAC:支持基于角色的访问控制
- 支持通过Ranger管理权限
- 帮助管理存储资源、限制用户和群组的权限,防止数据泄露和滥用
技术架构
1)架构1
Ozone技术构架分为三个部分:
- Ozone Manager,统一的元数据管理
- Storage Container Manager,数据块分配和数据节点管理
- Datanode,数据节点,数据的最终存放处
类比HDFS的构架,可以看到原来的Namenode的功能,现在由Ozone Manager和Storage Container Manage分别进行管理。对象元数据空间和数据分布分开管理,有利于两者的独立按需扩展,避免之前Namenode单节点的压力。
HDFS 中的 NameNode HA 需要依赖 ZooKeeper 作为协调和控制中心,来实现集群节点间的状态协调、领导者选举等功能。相比之下,Ozone 的设计则更加简单和轻量级,将对象元数据和数据存储分开,去除了对 ZooKeeper 的依赖,从而提高了 Ozone 的可扩展性和性能。
2)架构2
Ozone Manager
-
Ozone Manager是管理Ozone的命名空间,提供所有的Volume(卷)、Bucket(存储桶)和Key(键)的新建、更新和删除操作。它存储了Ozone的元数据信息,这些元数据信息包括Volumes、Buckets和Keys,底层通过RATIS(实现了RAFT协议)扩展元数据的副本数来实现元数据的HA。
-
Ozone Manager只和Ozone Client和Storage Container Manager通信,并不直接和Datanode通信。Ozone Manager将命名空间的元数据存储在RocksDB中,避免了HDFS中需要将所有元数据都保留在内存,从而经常会受到小文件问题的困扰。
-
RocksDB是Facebook基于LevelDB开发的一个本地Key-Value存储引擎,尤其对于SSD有很多的优化和改进,提供高吞吐量的读写操作。
Storage Container Manager(SCM)
-
类似HDFS中的Block Manager,管理Container,写Pipelines和Datanode,为Ozone Manager提供Block和Container的操作和信息。
-
SCM也监听Datanode发来的心跳信息,作为Datanode Manager的角色,保证和维护集群所需的数据冗余级别。
-
SCM和Ozone Client之间没有通信。
Block、Container和Pipeline
1) Datanode Container内部结构
- Block是数据块对象,真实存储用户的数据,Container中的一条记录是一个Block的信息,每个Block在Container里面有且仅有一条记录。
- Ozone中,数据是以Container为粒度进行副本复制的。
- SCM中目前支持2种Pipeline方式:
- 单Datanode节点组成的Standalone读Pipeline
- 三个Datanode节点组成的Apache RATIS写Pipeline。
- Container有2种状态:OPEN和CLOSED,当一个Container是OPEN状态时,可以往里面写入新的Block。当一个Container达到它预定的大小时(默认5GB),它从OPEN状态转换成CLOSED状态。一个Closed Container是不可修改的。
2)RATIS写Pipeline
Ozone使用Popeline的方式写入Container,是复制的数据流,基于Apache Ratis的数据复制。
HDFS数据复制图:
DataNode
Datanode是Ozone的数据节点,以Container为基本存储单元维护每个Container内部的数据映射关系,并定时向SCM发送心跳节点、汇报节点的信息、管理Container的信息和Pipeline的信息。当一个Container大小超过预定大小的90%时或者写操作失败时,Datanode会发送Container Close命令给SCM,把Container的状态从OPEN转变成CLOSED。或者当Pipeline出错时,发送Pipeline Close命令给SCM,把Pipeline从OPEN状态转为CLOSED状态。
Recon
Recon 充当 Ozone 的管理和监视控制台。它提供了 Ozone 的鸟瞰图,并通过基于 REST 的 API 和丰富的网页用户界面(Web UI)展示了集群的当前状态,从而帮助用户解决任何问题。
在较高的层次上,Recon 收集和汇总来自 Ozone Manager(OM)、Storage Container Manager(SCM)和数据节点(DN)的元数据,并充当中央管理和监视控制台。Ozone 管理员可以使用 Recon 查询系统的当前状态,而不会使 OM 或 SCM 过载。
Recon 维护多个数据库,以支持批处理,更快的查询和持久化聚合信息。它维护 OM DB 和 SCM DB 的本地副本,以及用于持久存储聚合信息的 SQL 数据库。
Recon 还与 Prometheus 集成,提供一个 HTTP 端点来查询 Prometheus 的 Ozone 指标,并在网页用户界面(Web UI)中显示一些关键时间点的指标
分层结构
Ozone分层结构使得Ozone Manager、Storage Container Manager和Datanode可按需独立扩展。对于Ozone提供的语义,也是分层管理的。
对象写入
当Ozone Client(客户端)需要创建并且写入一个新对象时,客户端需要和Ozone Manager和Datanode直接打交道
1)Ozone客户端链接Ozone Manager,提供需要创建的对象信息,包括对象的名称、数据的大小、备份数和其他用户自定义的对象属性。
2)Ozone Manager收到Ozone客户端的请求后,和SCM通信,请求SCM寻找能够容纳数据的处于OPEN状态的Container,然后在找到的Container中分配足够数量的Block。
3)SCM将新对象数据将要写入的Container、Block和Container所在的Pipeline的三个Datanode的信息列表返回给Ozone Manager。
4)Ozone Manager将收到SCM返回的信息,返回给客户端。
5)客户端得到Datanode列表信息之后,和第一个Datanode(Raft Pipeline Leader)建立通信,将数据写入Datanode的Container。
6)客户端完成数据写入后,连接Ozone Manager,确认数据已经更新完成,Ozone Manager更新对象的元数据,记录对象数据所在的Container和Block的信息。至此,新的对象创建完成。之后,其他的客户端就可以访问这个对象了。
对象读取
类似于HDFS文件读取
1)Ozone Client(客户端)和Ozone Manager通信,制定要读取的对象Key (/volume/bucket/key)。
2)Ozone Manager在元数据库中查找对应的对象,返回对象数据所在的Container和Block信息,包括Container所在的Datanode列表信息给Ozone Client(客户端)。
3)Ozone支持Data locality。如果Ozone Client(客户端)运行在集群中的某个节点上,Ozone Manager会返回按照网络拓扑距离排序的Datanode列表。Ozone Client(客户端)可以选择第一个Datanode节点(本地节点),也是离Client(客户端)最近的节点来读取数据,节省数据读取的网络传输时间。
删除数据
OZONE安全
1)Ranger
Apache Ranger 从2.0版本开始支持Ozone鉴权。但由于在2.0中存在一些bug,更推荐使用Apache Ranger 2.1及以后版本。
2)kerberos
Ozone 集群的安全依赖于 Kerberos。过去 HDFS 支持在隔离的安全网络中运行,因此可以不进行安全化的集群部署。
Ozone 在这方面与 HDFS 保持一致,但不久之后将 默认启用安全机制
K8S集成
K8S通过标准的CSI实现容器的持久化,目前很多的存储都有现成的CSI,如NFS,GFS,CEPH等。但是HDFS目前为止没有官方的CSI实现,Apache基于HDFS封装的新的分布式文件系统Ozone可以有HDFS文件系统的属性,同时提供了官方CSI
部署
非HA部署
配置:ozone/hadoop-hdds/common/src/main/resources/ozone-default.xml at master · apache/ozone · GitHub
块删除:https://developer.baidu.com/article/detail.html?id=1977478
1)组件布局
主机 | 角色 |
---|---|
192.168.2.75 | SCM,DN |
192.168.2.77 | OM,DN |
192.168.2.46 | DN |
2)下载安装包并解压
cd /opt
wget https://dlcdn.apache.org/ozone/1.3.0/ozone-1.3.0.tar.gz
tar -zxvf ozone-1.3.0.tar.gz
3)设置JAVA_HOME
vi ozone-env.sh
export JAVA_HOME=/srv/dstore/1.0.0.0/jdk1.8.0_211
4)设置配置文件
- SCM服务,不需要依赖其它服务,直接自身启动起来即可,为最底层服务。
- OM服务,需要依赖SCM服务,要配置SCM的通信地址。
- Datanode节点服务,需要依赖SCM,OM服务,二者通信地址都得配上
vi ozone-site.xml
<configuration>
<property>
<name>ozone.metadata.dirs</name>
<value>/srv/data01/ozone/metadata</value>
</property>
<property>
<name>ozone.om.http-address</name>
<value>sun77:19874</value>
</property>
<property>
<name>ozone.om.https-address</name>
<value>sun77:19875</value>
</property>
<property>
<name>ozone.om.address</name>
<value>sun77</value>
</property>
<property>
<name>ozone.om.ratis.port</name>
<value>19873</value>
</property>
<property>
<name>ozone.om.db.dirs</name>
<value>/srv/data01/ozone/om</value>
</property>
<property>
<name>ozone.om.ratis.storage.dir</name>
<value>/srv/data01/ozone/om/ratis</value>
</property>
<property>
<name>ozone.om.ratis.snapshot.dir</name>
<value>/srv/data01/ozone/om/ratis</value>
</property>
<property>
<name>ozone.scm.http-address</name>
<value>sun75:19876</value>
</property>
<property>
<name>ozone.scm.https-address</name>
<value>sun75:19877</value>
</property>
<property>
<name>ozone.scm.client.address</name>
<value>sun75:19860</value>
</property>
<property>
<name>ozone.scm.ratis.port</name>
<value>19894</value>
</property>
<property>
<name>ozone.scm.datanode.port</name>
<value>19861</value>
</property>
<property>
<name>ozone.scm.block.client.port</name>
<value>19863</value>
</property>
<property>
<name>ozone.scm.names</name>
<value>sun75</value>
</property>
<property>
<name>ozone.scm.db.dirs</name>
<value>/srv/data01/ozone/scm</value>
</property>
<property>
<name>ozone.scm.ha.ratis.storage.dir</name>
<value>/srv/data01/ozone/scm/ratis</value>
</property>
<property>
<name>ozone.scm.ha.ratis.snapshot.dir</name>
<value>/srv/data01/ozone/scm/ratis</value>
</property>
<property>
<name>hdds.rest.http-address</name>
<value>0.0.0.0:19880</value>
</property>
<property>
<name>hdds.datanode.http-address</name>
<value>0.0.0.0:19882</value>
</property>
<property>
<name>hdds.datanode.https-address</name>
<value>0.0.0.0:19883</value>
</property>
<property>
<name>dfs.container.ratis.server.port</name>
<value>19856</value>
</property>
<property>
<name>dfs.container.ratis.admin.port</name>
<value>19857</value>
</property>
<property>
<name>dfs.container.ratis.ipc</name>
<value>19858</value>
</property>
<property>
<name>dfs.container.ipc</name>
<value>19859</value>
</property>
<property>
<name>hdds.metadata.dir</name>
<value>/srv/data01/ozone/datanode</value>
</property>
<property>
<name>hdds.datanode.dir</name>
<value>/srv/data01/ozone/datanode</value>
</property>
<property>
<name>dfs.container.ratis.datanode.storage.dir</name>
<value>/srv/data01/ozone/datanode/ratis</value>
</property>
<property>
<name>ozone.recon.http-address</name>
<value>sun75:19888</value>
</property>
<property>
<name>ozone.recon.https-address</name>
<value>sun75:19889</value>
</property>
<property>
<name>ozone.recon.address</name>
<value>sun75:19891</value>
</property>
<property>
<name>ozone.recon.datanode.address</name>
<value>sun75:19891</value>
</property>
<property>
<name>ozone.recon.db.dir</name>
<value>/srv/data01/ozone/recon</value>
</property>
<property>
<name>ozone.recon.om.db.dir</name>
<value>/srv/data01/ozone/recon/om</value>
</property>
<property>
<name>ozone.recon.scm.db.dirs</name>
<value>/srv/data01/ozone/recon/scm</value>
</property>
<property>
<name>ozone.s3g.http-address</name>
<value>sun75:19878</value>
</property>
</configuration>
5)启动集群
在启动 Ozone 集群之前,需要依次初始化 SCM 和 OM。
ozone scm --init
这条命令会使 SCM 创建集群 ID 并初始化它的状态。 init
命令和 Namenode 的 format
命令类似,只需要执行一次,SCM 就可以在磁盘上准备好正常运行所需的数据结构。
ozone --daemon start scm
SCM 启动之后,我们就可以创建对象存储空间,命令如下:
ozone om --init
OM 初始化完成之后,就可以启动 OM 服务了:
ozone --daemon start om
此时 Ozone 的命名服务 OM 和 块服务 SCM 都已运行。
注意: 如果 SCM 未启动,om --init
命令会失败,同样,如果磁盘上的元数据缺失,SCM 也无法启动,所以请确保 scm --init
和 om --init
两条命令都成功执行了。
接下来启动 Datanode,在每个 Datanode 上运行下面的命令:
ozone --daemon start datanode
现在 SCM、OM 和所有的 Datanode 都已启动并运行。
6)访问webui
OM: 192.168.2.77:9874
SCM: 192.168.2.75:9876
HA部署
Ozone Manager 的高可用
一个 Ozone Manager 使用 RocksDB 在本地持久化元数据(卷、桶和键)。 Ozone Manager 的高可用版本在功能上完全一致,只是所有的数据都借助 RAFT 共识算法复制到 Ozone Manager 的 follower 实例上。
客户端连接到 Ozone Manager 上,而 Ozone Manager 负责处理请求并且安排复制。当请求复制到所有的 follower 上后,leader 就可以给客户端回包了。
SCM高可用
当原始 SCM 失效时, 新的 SCM 不能被引导并添加到 HA 节点中。
尚未支持从非 HA 安全集群升级到 HA 安全集群。
⚠️ 注意 ⚠️
SCM HA 目前仅支持新初始化的集群。
SCM HA 必须在 Ozone 服务首次启动前开启。
角色分配
主机 | 角色 |
---|---|
xxx33 | SCM、OM、DataNode、S3g、Recon |
xxx80 | SCM、DataNode |
xxx72 | OM、DataNode |
配置ozone-site.xml
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
<property>
<name>ozone.metadata.dirs</name>
<value>/srv/data01/ozone/metadata</value>
</property>
<!--SCM HA开启-->
<property>
<name>ozone.scm.ratis.enable</name>
<value>true</value>
</property>
<property>
<name>ozone.scm.service.ids</name>
<value>cluster</value>
</property>
<property>
<name>ozone.scm.nodes.cluster</name>
<value>scm1,scm2</value>
</property>
<property>
<name>ozone.scm.address.cluster.scm1</name>
<value>xxx33</value>
</property>
<property>
<name>ozone.scm.address.cluster.scm2</name>
<value>xxx80</value>
</property>
<property>
<name>ozone.scm.primordial.node.id</name>
<value>scm1</value>
</property>
<property>
<config>ozone.security.enable</config>
<value>true</value>
</property>
<property>
<config>hdds.grpc.tls.enabled</config>
<value>true</value>
</property>
<!--OM HA开启-->
<property>
<name>ozone.om.ratis.enable</name>
<value>true</value>
</property>
<property>
<name>ozone.om.service.ids</name>
<value>cluster</value>
</property>
<property>
<name>ozone.om.nodes.cluster</name>
<value>om1,om2</value>
</property>
<property>
<name>ozone.om.address.cluster.om1</name>
<value>xxx33</value>
</property>
<property>
<name>ozone.om.address.cluster.om2</name>
<value>xxx72</value>
</property>
<property>
<name>ozone.om.http-address</name>
<value>0.0.0.0:19874</value>
</property>
<property>
<name>ozone.om.https-address</name>
<value>0.0.0.0:19875</value>
</property>
<property>
<name>ozone.om.address</name>
<value>0.0.0.0:9862</value>
</property>
<property>
<name>ozone.om.ratis.port</name>
<value>19873</value>
</property>
<property>
<name>ozone.om.db.dirs</name>
<value>/srv/data01/ozone/om</value>
</property>
<property>
<name>ozone.om.ratis.storage.dir</name>
<value>/srv/data01/ozone/om/ratis</value>
</property>
<property>
<name>ozone.om.ratis.snapshot.dir</name>
<value>/srv/data01/ozone/om/ratis</value>
</property>
<property>
<name>ozone.scm.http-address</name>
<value>0.0.0.0:19876</value>
</property>
<property>
<name>ozone.scm.https-address</name>
<value>0.0.0.0:19877</value>
</property>
<property>
<name>ozone.scm.client.address</name>
<value>0.0.0.0:19860</value>
</property>
<property>
<name>ozone.scm.ratis.port</name>
<value>19894</value>
</property>
<property>
<name>ozone.scm.datanode.port</name>
<value>19861</value>
</property>
<property>
<name>ozone.scm.block.client.port</name>
<value>19863</value>
</property>
<property>
<name>ozone.scm.names</name>
<value>scm1.scm2</value>
</property>
<property>
<name>ozone.scm.db.dirs</name>
<value>/srv/data01/ozone/scm</value>
</property>
<property>
<name>ozone.scm.ha.ratis.storage.dir</name>
<value>/srv/data01/ozone/scm/ratis</value>
</property>
<property>
<name>ozone.scm.ha.ratis.snapshot.dir</name>
<value>/srv/data01/ozone/scm/ratis</value>
</property>
<property>
<name>hdds.rest.http-address</name>
<value>0.0.0.0:19880</value>
</property>
<property>
<name>hdds.datanode.http-address</name>
<value>0.0.0.0:19882</value>
</property>
<property>
<name>hdds.datanode.https-address</name>
<value>0.0.0.0:19883</value>
</property>
<property>
<name>dfs.container.ratis.server.port</name>
<value>19856</value>
</property>
<property>
<name>dfs.container.ratis.admin.port</name>
<value>19857</value>
</property>
<property>
<name>dfs.container.ratis.ipc</name>
<value>19858</value>
</property>
<property>
<name>dfs.container.ipc</name>
<value>19859</value>
</property>
<property>
<name>hdds.metadata.dir</name>
<value>/srv/data01/ozone/datanode</value>
</property>
<property>
<name>hdds.datanode.dir</name>
<value>/srv/data01/ozone/datanode</value>
</property>
<property>
<name>dfs.container.ratis.datanode.storage.dir</name>
<value>/srv/data01/ozone/datanode/ratis</value>
</property>
<property>
<name>ozone.recon.http-address</name>
<value>xxx33:19888</value>
</property>
<property>
<name>ozone.recon.https-address</name>
<value>xxx33:19889</value>
</property>
<property>
<name>ozone.recon.address</name>
<value>xxx33:19891</value>
</property>
<property>
<name>ozone.recon.datanode.address</name>
<value>xxx33:19891</value>
</property>
<property>
<name>ozone.recon.db.dir</name>
<value>/srv/data01/ozone/recon</value>
</property>
<property>
<name>ozone.recon.om.db.dir</name>
<value>/srv/data01/ozone/recon/om</value>
</property>
<property>
<name>ozone.recon.scm.db.dirs</name>
<value>/srv/data01/ozone/recon/scm</value>
</property>
<property>
<name>ozone.s3g.default.bucket.layout</name>
<value>OBJECT_STORE</value>
<description>默认存储桶的布局</description>
</property>
<property>
<name>ozone.s3g.volume.name</name>
<value>s3v</value>
<description>卷的名称</description>
</property>
<property>
<name>ozone.s3g.http-address</name>
<value>0.0.0.0:9878</value>
<description>HTTP服务的地址和端口</description>
</property>
<property>
<name>ozone.s3g.http-bind-host</name>
<value>0.0.0.0</value>
<description>HTTP服务绑定的主机地址</description>
</property>
<property>
<name>ozone.s3g.http.enabled</name>
<value>true</value>
<description>是否启用HTTP服务</description>
</property>
<property>
<name>ozone.s3g.https-address</name>
<value></value>
<description>HTTPS服务的地址和端口</description>
</property>
<property>
<name>ozone.s3g.https-bind-host</name>
<value></value>
<description>HTTPS服务绑定的主机地址</description>
</property>
<property>
<name>ozone.s3g.list-keys.shallow.enabled</name>
<value>true</value>
<description>是否启用浅层列出键(shallow list keys)功能</description>
</property>
<property>
<name>ozone.s3g.secret.http.enabled</name>
<value>false</value>
<description>是否启用HTTP密钥(secret)认证</description>
</property>
<property>
<name>ozone.s3g.secret.http.auth.type</name>
<value>kerberos</value>
<description>HTTP密钥认证的类型</description>
</property>
<property>
<name>ozone.s3g.http.auth.type</name>
<value>simple</value>
<description>HTTP认证的类型</description>
</property>
<property>
<name>ozone.s3g.client.buffer.size</name>
<value>4KB</value>
<description>客户端缓冲区的大小</description>
</property>
<property>
<name>ozone.block.deleting.container.limit.per.interval</name>
<value>20</value>
</property>
<property>
<name>ozone.scm.container.size</name>
<value>10GB</value>
</property>
<property>
<name>ozone.scm.block.size</name>
<value>256MB</value>
</property>
<property>
<name>ozone.block.deleting.service.interval</name>
<value>1s</value>
</property>
<property>
<name>ozone.scm.block.deletion.max.retry</name>
<value>4090</value>
</property>
<property>
<name>ozone.scm.chunk.size</name>
<value>10MB</value>
</property>
<property>
<name>ozone.server.default.replication</name>
<value>1</value>
</property>
<property>
<name>ozone.server.default.replication.type</name>
<value>RATIS</value>
</property>
<property>
<name>ozone.block.deleting.limit.per.task</name>
<value>2000</value>
</property>
<property>
<name>ozone.block.deleting.service.timeout</name>
<value>400000ms</value>
</property>
<property>
<name>ozone.block.deleting.service.workers</name>
<value>10</value>
</property>
</configuration>
启动方式
所有SCM节点执行
ozone scm --init
ozone scm --bootstrap
ozone --daemon start scm
所有OM节点执行(前提是SCM已经启动)
ozone om --init
ozone -daemon start om
所有节点启动
ozone --daemon start datanode
最后启动Recon、S3g
ozone --daemon start recon
ozone --daemon start s3g
S3G适配
Ozone中关于s3g的配置如下
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property>
<name>ozone.s3g.default.bucket.layout</name>
<value>OBJECT_STORE</value>
<description>默认存储桶的布局</description>
</property>
<property>
<name>ozone.s3g.volume.name</name>
<value>s3v</value>
<description>卷的名称</description>
</property>
<property>
<name>ozone.s3g.domain.name</name>
<value></value>
<description>指定Ozone S3网关的域名列表。如果有多个域名,则应以逗号分隔。这个参数仅在遵循虚拟主机样式模式时才需要</description>
</property>
<property>
<name>ozone.s3g.http-address</name>
<value>0.0.0.0:9878</value>
<description>HTTP服务的地址和端口</description>
</property>
<property>
<name>ozone.s3g.http-bind-host</name>
<value>0.0.0.0</value>
<description>HTTP服务绑定的主机地址</description>
</property>
<property>
<name>ozone.s3g.http.enabled</name>
<value>true</value>
<description>是否启用HTTP服务</description>
</property>
<property>
<name>ozone.s3g.https-address</name>
<value></value>
<description>HTTPS服务的地址和端口</description>
</property>
<property>
<name>ozone.s3g.https-bind-host</name>
<value></value>
<description>HTTPS服务绑定的主机地址</description>
</property>
<property>
<name>ozone.s3g.list-keys.shallow.enabled</name>
<value>true</value>
<description>是否启用浅层列出键(shallow list keys)功能</description>
</property>
<property>
<name>ozone.s3g.secret.http.enabled</name>
<value>false</value>
<description>是否启用HTTP密钥(secret)认证</description>
</property>
<property>
<name>ozone.s3g.secret.http.auth.type</name>
<value>kerberos</value>
<description>HTTP密钥认证的类型</description>
</property>
<property>
<name>ozone.s3g.http.auth.type</name>
<value>simple</value>
<description>HTTP认证的类型</description>
</property>
<property>
<name>ozone.s3g.client.buffer.size</name>
<value>4KB</value>
<description>客户端缓冲区的大小</description>
</property>
<!----------Kerberos相关---------->
<property>
<name>ozone.s3g.kerberos.keytab.file</name>
<value>/etc/security/keytabs/s3g.keytab</value>
<description>Kerberos密钥表文件的路径</description>
</property>
<property>
<name>ozone.s3g.kerberos.principal</name>
<value>s3g/_HOST@REALM</value>
<description>Kerberos身份验证的主体(principal)</description>
</property>
<property>
<name>ozone.s3g.http.auth.kerberos.principal</name>
<value>HTTP/_HOST@REALM</value>
<description>Kerberos身份验证的HTTP服务主体(principal)</description>
</property>
<property>
<name>ozone.s3g.http.auth.kerberos.keytab</name>
<value>/etc/security/keytabs/HTTP.keytab</value>
<description>Kerberos身份验证的密钥表(keytab)文件路径</description>
</property>
</configuration>
启动
ozone --daemon start s3g
使用jps可以查看到启了一个Gateway
的进程,具体操作可以看下面的命令
基本命令
Ozone的对象存储使用方式和亚马逊的S3存储十分类似,也是基于Volume,Bucket,Key的存储模式。
所以在存储key文件对象前,我们需要建好Volume和Bucket。
首先是volume的创建
./ozone sh volume create /sunvolume --quota=1GB -u=sun # 创建一个卷路径为:/sunvolume 配额:1G 所有者为sun的卷
查看volume
[root@sun77 bin]# ./ozone sh volume list -u=sun
[ {
"metadata" : { },
"name" : "sunvolume",
"admin" : "root",
"owner" : "sun",
"quotaInBytes" : 1073741824,
"quotaInNamespace" : -1,
"usedNamespace" : 0,
"creationTime" : "2023-09-11T02:36:56.820Z",
"modificationTime" : "2023-09-11T02:36:56.820Z",
"acls" : [ {
"type" : "USER",
"name" : "sun",
"aclScope" : "ACCESS",
"aclList" : [ "ALL" ]
} ],
"refCount" : 0
创建bucket
ozone sh bucket create /sunvolume/sunbucket
查看bucket
[root@sun77 bin]# ./ozone sh bucket list /sunvolume/
[ {
"metadata" : { },
"volumeName" : "sunvolume",
"name" : "sunbucket",
"storageType" : "DISK",
"versioning" : false,
"usedBytes" : 0,
"usedNamespace" : 0,
"creationTime" : "2023-09-11T02:40:12.074Z",
"modificationTime" : "2023-09-11T02:40:12.074Z",
"quotaInBytes" : -1,
"quotaInNamespace" : -1,
"bucketLayout" : "LEGACY",
"owner" : "root",
"link" : false
} ]
key文件上传
ozone sh key put /sunvolume/sunbucket/test.txt test.txt # 首先得创建文件
测试
<property>
<name>ozone.scm.pipeline.creation.auto.factor.one</name>
<value>true</value>
</property>
s3客户端测试
启动s3g
ozone --daemon start s3g
yum -y install epel-release
yum install python-pip
yum -y install s3cmd
cat >/root/.s3cfg<<EOF
[default]
access_key = E10OE6MVMMD8SQ9ELRUN
secret_key = aU8LST1vo9SMIENb08ONm3hJpx9ORJ8bW9SQ68rw
host_base = sun75:19878
host_bucket = sun75:19878/%(bucket)
use_https = False
EOF
access_key和secret_key是在本地创建S3用户时获得,host_base是S3服务所使用的ip地址(包括端口 号),host_bucket为S3用户下的一个bucket(可在配置之后再创建,但该字段不能为空)
列举所有 Buckets
s3cmd ls
创建 bucket,且 bucket 名称是唯一的,不能重复。
s3cmd mb s3://my-bucket-name
删除空 bucket
s3cmd rb s3://my-bucket-name
列举 Bucket 中的内容
s3cmd ls s3://my-bucket-name
上传 file.txt 到某个 bucket,
s3cmd put file.txt s3://my-bucket-name/file.txt
上传并将权限设置为所有人可读
s3cmd put --acl-public file.txt s3://my-bucket-name/file.txt
批量上传文件
s3cmd put ./* s3://my-bucket-name/
下载文件
s3cmd get s3://my-bucket-name/file.txt file.txt
批量下载
s3cmd get s3://my-bucket-name/* ./
删除文件
s3cmd del s3://my-bucket-name/file.txt
来获得对应的bucket所占用的空间大小
s3cmd du -H s3://my-bucket-name
设置S3 bucket的Public权限
s3cmd setacl s3://myexamplebucket.calvium.com/ --acl-public --recursive
aws cli测试
下载客户端安装包
wget https://link.zhihu.com/?target=https%3A//s3.amazonaws.com/aws-cli/awscli-bundle.zip
yum -y install unzip
unzip awscli-bundle.zip
安装
[root@xxx33 awscli-bundle]# ./install
Unsupported Python version detected: Python 2.7
To continue using this installer you must use Python 3.7 or later.
For more information see the following blog post: https://aws.amazon.com/blogs/developer/announcing-end-of-support-for-python-2-7-in-aws-sdk-for-python-and-aws-cli-v1/
报错,不支持Python2.7,本机正好安装了Python3.10,所以可以带上Python路径
[root@xxx33 awscli-bundle]# /srv/dstore/1.0.0.0/base-env-centos/python/bin/python3.10 ./install -i /usr/local/aws -b /usr/local/bin/aws
Running cmd: /srv/dstore/1.0.0.0/base-env-centos/python/bin/python3.10 -m venv /usr/local/aws
Running cmd: /usr/local/aws/bin/python -m pip install --no-binary :all: --no-cache-dir --no-index --find-links file://. setuptools_scm-3.3.3.tar.gz
Running cmd: /usr/local/aws/bin/python -m pip install --no-binary :all: --no-cache-dir --no-index --find-links file://. wheel-0.33.6.tar.gz
Running cmd: /usr/local/aws/bin/python -m pip install --no-binary :all: --no-build-isolation --no-cache-dir --no-index --find-links file:///root/awscli-bundle/packages awscli-1.30.0.tar.gz
You can now run: /usr/local/bin/aws --version
Note: AWS CLI version 2, the latest major version of the AWS CLI, is now stable and recommended for general use. For more information, see the AWS CLI version 2 installation instructions at: https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html
# 检查版本
[root@xxx33 awscli-bundle]# aws --version
aws-cli/1.30.0 Python/3.10.6 Linux/3.10.0-957.el7.x86_64 botocore/1.32.0
接下来开始配置访问端端点信息,可以配置环境变量或者修改配置
第一种方式,添加环境变量
如果s3g服务没启动安全模式(Kerberos),则设置任何秘钥都可以,如下
export AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
export AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
export AWS_DEFAULT_REGION=us-west-2
第二种方式,修改配置文件
vi ~/.aws/credentials
[default]
aws_access_key_id = aasas
aws_secret_access_key = asas
aws_default_region = us-west-2
或者使用aws命令配置
aws configure
[root@xxx33 .aws]# aws configure
AWS Access Key ID [****************asas]: xxxxx
AWS Secret Access Key [****************asas]: xxxx
Default region name [None]:
Default output format [None]:
[root@xxx33 .aws]#
配置好后,开始测试
通过指定自定义的 –endpoint 选项,aws
命令行接口可以在 Ozone S3 上使用
aws s3api --endpoint http://localhost:9878 create-bucket --bucket buckettest
或者
aws s3 ls --endpoint http://localhost:9878 s3://buckettest
[root@xxx33 .aws]# aws s3api --endpoint http://192.168.2.177:9878 create-bucket --bucket buckettest
{
"Location": "http://192.168.2.177:9878/buckettest"
}
aws命令配置 --endpoint
[root@xxx33 .aws]# aws configure set default.s3.endpoint_url http://192.168.2.177:9878
[root@xxx33 .aws]# cat ~/.aws/config
[default]
s3 =
endpoint_url = http://192.168.2.177:9878
[root@xxx33 .aws]#
虽然配置了,但是测试报错,遗留问题
[root@xxx33 .aws]# aws s3 ls s3://buckettest
An error occurred (InvalidAccessKeyId) when calling the ListObjectsV2 operation: The AWS Access Key Id you provided does not exist in our records.
[root@xxx33 .aws]#
FS文件测试
# 创建文件夹
hdfs dfs -mkdir -p /sun/test1
# 查看
[root@sun46 ~]# hdfs dfs -ls o3fs://bucket.volume/sun
2023-09-20 17:27:37,187 INFO conf.Configuration: Removed undeclared tags:
Found 1 items
drwxrwxrwx - root root 0 2023-09-20 17:26 o3fs://bucket.volume/sun/test1
# 上传文件
hdfs dfs -put test.txt o3fs://bucket.volume/sun/test1
# 查看
[root@sun46 ~]# hdfs dfs -cat test.txt o3fs://bucket.volume/sun/test1/test.txt
2023-09-20 17:29:31,733 INFO conf.Configuration: Removed undeclared tags:
cat: `test.txt': No such file or directory
2023-09-20 17:29:32,687 INFO impl.MetricsConfig: loaded properties from hadoop-metrics2.properties
2023-09-20 17:29:32,744 INFO impl.MetricsSystemImpl: Scheduled Metric snapshot period at 10 second(s).
2023-09-20 17:29:32,744 INFO impl.MetricsSystemImpl: XceiverClientMetrics metrics system started
hello,world
Yarn测试
首先停止HDFS集群
修改原集群的配置
<property>
<name>fs.AbstractFileSystem.o3fs.impl</name>
<value>org.apache.hadoop.fs.ozone.OzFs</value>
</property>
<property>
<name>fs.defaultFS</name>
<value>o3fs://bucket.volume</value>
</property>
export HADOOP_CLASSPATH=/opt/ozone/share/ozone/lib/ozone-filesystem-hadoop3-*.jar:$HADOOP_CLASSPATH
ln -s /opt/ozone-1.3.0/etc/hadoop/ozone-site.xml /srv/dstore/1.0.0.0/hdfs/etc/hadoop/
重启Yarn HA 集群
sbin/stop-yarn.sh
sbin/start-yarn.sh
测试任务执行是否正常
hadoop jar ./share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.1.jar wordcount 10 100
查看文件以及webui
Hive测试
插入数据
insert into table student values(1,'along');
查看文件系统
[root@sun46 ~]# hdfs dfs -ls o3fs://bucket.volume/data/hive/warehouse
2023-09-20 17:42:32,916 INFO conf.Configuration: Removed undeclared tags:
Found 2 items
drwxrwxrwx - root root 0 2023-09-20 17:16 o3fs://bucket.volume/data/hive/warehouse/student
drwxrwxrwx - root root 0 2023-09-20 17:16 o3fs://bucket.volume/data/hive/warehouse/students
[root@sun46 ~]#
Flink测试
开启会话
yarn-session.sh -nm test -d
如图所示正常启动,WebUI:http://sun77:18081
Spark测试
启动过程中,报错
Caused by: java.lang.NoClassDefFoundError: [Lorg/apache/hadoop/security/token/DelegationTokenIssuer;
,暂未解决。
代码测试
Java Ozone
package com.ozone.demo;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.ozone.client.*;
import org.apache.hadoop.ozone.client.io.OzoneOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.concurrent.TimeUnit;
public class OzoneDemo {
static long startTime = 0;
static long endTime = 0;
static int fileCounter = 0;
/**
* 读取文件内容并返回字节数组
*
* @param path 文件路径
* @return 文件内容的字节数组,若读取失败则返回 null
*/
public static byte[] readFile(String path) {
try {
FileInputStream in = new FileInputStream(new File(path));
byte[] data = new byte[in.available()];
in.read(data);
in.close();
return data;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 上传指定文件到 Ozone
*
* @param client 客户端对象
* @param bucket 桶对象
* @param keyName 键名称
* @param filePath 文件路径
* @param chunkSize 每次写入的 Chunk 大小
* @throws IOException 如果上传过程中出现异常,则抛出 IOException
*/
public static void uploadFile(OzoneClient client, OzoneBucket bucket, String keyName, String filePath, int chunkSize) throws IOException {
// 读取文件内容
byte[] data = readFile(filePath);
if (data == null) {
System.out.println("文件 " + filePath + " 读取失败");
return;
}
// 创建输出流并写入数据
OzoneOutputStream stream = bucket.createKey(keyName, data.length);
int offset = 0;
while (offset < data.length) {
int length = Math.min(chunkSize, data.length - offset);
stream.write(data, offset, length);
offset += length;
}
// 关闭输出流
stream.close();
// 统计上传的文件数
fileCounter++;
}
/**
* 上传指定目录中的所有文件到 Ozone
*
* @param client 客户端对象
* @param bucket 桶对象
* @param sourceFolder 要上传的本地目录路径
* @param targetFolder 桶中的目标路径
* @param chunkSize 每次写入的 Chunk 大小
* @throws IOException 如果上传过程中出现异常,则抛出 IOException
*/
public static void uploadFolder(OzoneClient client, OzoneBucket bucket, String sourceFolder, String targetFolder, int chunkSize) throws IOException {
File folder = new File(sourceFolder);
if (!folder.exists()) {
System.out.println("目录 " + sourceFolder + " 不存在");
return;
}
// 获取目录下的所有文件和子目录
File[] files = folder.listFiles();
for (File file : files) {
String fileName = file.getName();
if (file.isDirectory()) {
// 处理子目录并递归调用 uploadFolder 方法
String subDir = targetFolder + "/" + fileName;
System.out.println("正在上传子目录:" + subDir);
uploadFolder(client, bucket, file.getAbsolutePath(), subDir, chunkSize);
} else {
// 上传文件
String filePath = file.getAbsolutePath();
String keyName = targetFolder + "/" + fileName;
System.out.println("正在上传文件:" + keyName);
uploadFile(client, bucket, keyName, filePath, chunkSize);
}
}
}
public static void deletebucket(OzoneClient client, String volume, String bucket) throws IOException {
ObjectStore store = client.getObjectStore();
OzoneVolume volumes = store.getVolume(volume);
if (volumes == null) {
System.out.println("卷" + volume + "为空");
return;
}
OzoneBucket ozoneBucket = volumes.getBucket(bucket);
if (ozoneBucket == null) {
System.out.println("桶" + bucket + "不存在");
return;
}
Iterator<? extends OzoneKey> keys = ozoneBucket.listKeys("");
while (keys.hasNext()) {
OzoneKey key = keys.next();
ozoneBucket.deleteKey(key.getName());
System.out.println("删除键:" + key.getName());
}
}
public static void deleteAllVolume(OzoneClient client) throws IOException {
ObjectStore store = client.getObjectStore();
Iterator<? extends OzoneVolume> listVolumes = store.listVolumes("");
while (listVolumes.hasNext()) {
OzoneVolume volume = listVolumes.next();
Iterator<? extends OzoneBucket> buckets = volume.listBuckets("");
while (buckets.hasNext()) {
OzoneBucket next = buckets.next();
Iterator<? extends OzoneKey> keys = next.listKeys("");
while (keys.hasNext()) {
OzoneKey key = keys.next();
next.deleteKey(key.getName());
System.out.println("删除键:" + key.getName());
}
volume.deleteBucket(next.getName());
System.out.println("成功删除桶:" + next.getName());
}
store.deleteVolume(volume.getName());
System.out.println("成功删除卷:" + volume.getName());
}
client.close();
}
public static void deleteVolumeByName(OzoneClient client, String volume) throws IOException {
ObjectStore store = client.getObjectStore();
OzoneVolume volumes = store.getVolume(volume);
if (volumes == null) {
System.out.println("卷" + volume + "为空");
return;
}
Iterator<? extends OzoneBucket> buckets = volumes.listBuckets("");
while (buckets.hasNext()) {
OzoneBucket next = buckets.next();
Iterator<? extends OzoneKey> keys = next.listKeys("");
while (keys.hasNext()) {
OzoneKey key = keys.next();
next.deleteKey(key.getName());
System.out.println("删除键:" + key.getName());
}
}
store.deleteVolume(volume);
System.out.println("成功删除卷:" + volume);
client.close();
}
public static void uploadPathinFIle(String sourceFolder, OzoneClient ozClient, String volumeName, String bucket) throws IOException {
// "I:\\学习资料\\路飞-爬虫开发+APP逆向超级大神班\\03--爬虫3期:爬虫&逆向3期【完结】";
int chunkSize = 1048576; // 每次写入的 Chunk 大小
// 获取 ObjectStore 引用
ObjectStore objectStore = ozClient.getObjectStore();
// 获取或创建卷
OzoneVolume assets = null;
try {
assets = objectStore.getVolume(volumeName);
} catch (Exception e) {
System.out.println("卷" + volumeName + "不存在");
}
if (assets == null) {
objectStore.createVolume(volumeName);
assets = objectStore.getVolume(volumeName);
}
OzoneBucket video = null;
// 获取或创建桶
try {
video = assets.getBucket(bucket);
} catch (Exception e) {
System.out.println("桶不存在");
}
if (video == null) {
assets.createBucket(bucket);
video = assets.getBucket(bucket);
}
// 记录开始时间
startTime = System.nanoTime();
System.out.println("开始时间:" + startTime);
// 上传目录中的所有文件
uploadFolder(ozClient, video, sourceFolder, bucket, chunkSize);
// 记录结束时间
endTime = System.nanoTime();
System.out.println("结束时间:" + endTime);
// 显示统计信息
System.out.println(">>>>>>>上传完成,共上传了 " + fileCounter + " 个文件,耗时 " + TimeUnit.NANOSECONDS.toSeconds(endTime - startTime) + " 秒。");
//关闭客户端连接
ozClient.close();
}
public static void queryAll(OzoneClient client) throws IOException {
ObjectStore store = client.getObjectStore();
Iterator<? extends OzoneVolume> volumes = store.listVolumes("");
while (volumes.hasNext()) {
OzoneVolume volume = volumes.next();
System.out.println("卷:" + volume.getName());
Iterator<? extends OzoneBucket> buckets = volume.listBuckets("");
while (buckets.hasNext()) {
OzoneBucket bucket = buckets.next();
System.out.println("\t桶:" + bucket.getName());
Iterator<? extends OzoneKey> keys = bucket.listKeys("");
while (keys.hasNext()) {
OzoneKey key = keys.next();
System.out.println("\t\tkey: " + key.getName());
}
}
}
}
public static void queryvolumes(OzoneClient client) throws IOException {
ObjectStore objectStore = client.getObjectStore();
Iterator<? extends OzoneVolume> iterator = objectStore.listVolumes("");
System.out.println("============================");
while (iterator.hasNext()) {
OzoneVolume volume = iterator.next();
System.out.println("Volume: " + volume.getName());
}
System.out.println("============================");
}
public static void main(String[] args) throws IOException {
OzoneConfiguration conf = new OzoneConfiguration();
conf.set("ozone.om.address", "192.168.2.177:9862");
OzoneClient ozClient = null;
try {
ozClient = OzoneClientFactory.getRpcClient(conf);
} catch (IOException e) {
e.printStackTrace();
System.out.println("连接异常");
return;
}
ObjectStore objectStore = ozClient.getObjectStore();
//删除key
//deletebucket(ozClient, "sun-volume", "sun-bucket");
//上传文件
//uploadPathinFIle("I:\\学习资料\\路飞-爬虫开发+APP逆向超级大神班\\03--爬虫3期:爬虫&逆向3期【完结】", ozClient, "sun-volume1", "sun-bucket");
//列出所有的key
//queryAll(ozClient);
//queryvolumes(ozClient);
//deleteVolumeByName(ozClient, "myvolume");
deleteAllVolume(ozClient);
}
}
AWS Java
package com.ozone.controller;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import com.amazonaws.AmazonClientException;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.ClientConfiguration;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.*;
public class AWSClient {
static AmazonS3 s3;
private static void init() throws Exception {
AWSCredentials credentials = new BasicAWSCredentials("access_key",
"secret_key");
ClientConfiguration configuration = new ClientConfiguration();
configuration.setUseExpectContinue(false);
String endPoint = "http://192.168.2.177:9878";
AwsClientBuilder.EndpointConfiguration endpointConfiguration = new AwsClientBuilder.EndpointConfiguration(
endPoint, null);
s3 = AmazonS3ClientBuilder.standard().withEndpointConfiguration(endpointConfiguration)
.withClientConfiguration(configuration).withCredentials(new AWSStaticCredentialsProvider(credentials))
//.withChunkedEncodingDisabled(true)
.withPathStyleAccessEnabled(true).build();
}
public static void deleteObject(String bucket, String object) {
try {
s3.deleteObject(bucket, object);
} catch (AmazonServiceException e) {
System.out.println("status code:" + e.getStatusCode());
} catch (AmazonClientException e2) {
System.out.println("status code:" + e2.getMessage());
}
}
public static void putObject(String bucket, String object) {
try {
PutObjectRequest request = new PutObjectRequest(bucket, object,
new File("C:\\Users\\C\\Desktop\\files\\testfile.png"));
s3.putObject(request);
} catch (AmazonServiceException e) {
System.out.println("status code:" + e.getStatusCode());
} catch (AmazonClientException e2) {
System.out.println("status code:" + e2.getMessage());
}
}
public static void getObject(String bucket, String object) {
try {
GetObjectRequest request = new GetObjectRequest(bucket, object, null);
System.out.println(object.toString());
S3Object result = s3.getObject(request);
S3ObjectInputStream s3is = result.getObjectContent();
FileOutputStream fos = new FileOutputStream(new File("C:\\Users\\C\\Desktop\\files\\" + object));
byte[] read_buf = new byte[1024 * 34];
int read_len = 0;
while ((read_len = s3is.read(read_buf)) > 0) {
fos.write(read_buf, 0, read_len);
}
s3is.close();
fos.close();
} catch (AmazonServiceException e) {
System.err.println(e.getErrorMessage());
} catch (FileNotFoundException e) {
System.err.println(e.getMessage());
} catch (IOException e) {
System.err.println(e.getMessage());
}
}
public static void listObjects(String bucket) {
try {
ListObjectsV2Request request = new ListObjectsV2Request();
request.setBucketName(bucket);
ListObjectsV2Result result = s3.listObjectsV2(request);
List<String> commonPrefix = result.getCommonPrefixes();
for (int i = 0; i < commonPrefix.size(); i++) {
System.out.println("commonPrefix:" + commonPrefix.get(i));
}
List<S3ObjectSummary> objectList = result.getObjectSummaries();
for (int i = 0; i < objectList.size(); i++) {
System.out.println("key:" + objectList.get(i).getKey());
}
} catch (AmazonServiceException e) {
System.out.println("status code:" + e.getStatusCode());
} catch (AmazonClientException e2) {
System.out.println("status code:" + e2.getMessage());
}
}
public static void putBucket(String bucket) {
try {
s3.createBucket(bucket);
} catch (AmazonServiceException e) {
System.err.println(e.getStatusCode());
System.err.println(e.getErrorCode());
System.err.println(e.getErrorMessage());
}
}
//运行主函数
public static void main(String[] args) throws Exception {
String bucketName = "mybucket";
String keyName = "example.png";
//初始化连接
init();
//创建桶
putBucket(bucketName);
// //添加对象
// putObject(bucketName, keyName);
// //获取对象
// getObject(bucketName, keyName);
// //删除对象
// deleteObject(bucketName, keyName);
// //枚举对象列表
// listObjects(bucketName);
}
}
问题
一、org.apache.hadoop.fs.UnsupportedFileSystemException: No FileSystem for scheme "o3fs"
无法加载ozone的客户端包,解决方案是直接将包cp到hadoop库下,然后重启
cp ozone-filesystem-hadoop3* /srv/dstore/1.0.0.0/hdfs/share/hadoop/common/lib/
二、Tue Sep 19 10:34:29 +0800 2023] Application is added to the scheduler and is not yet activated. Skipping AM assignment as cluster resource is empty. Details : AM Partition = <DEFAULT_PARTITION>; AM Resource Request = <memory:2048, vCores:1>; Queue Resource Limit for AM = <memory:0, vCores:0>; User AM Resource Limit of the queue = <memory:0, vCores:0>; Queue AM Resource Usage = <memory:1024, vCores:1>;
导致job任务提交后,webui一直显示accept状态,无法运行,检查yarn集群nodemanage状态
root@sun46 hadoop]# yarn node -all -list
WARNING: YARN_LOG_DIR has been replaced by HADOOP_LOG_DIR. Using value of YARN_LOG_DIR.
WARNING: YARN_PID_DIR has been replaced by HADOOP_PID_DIR. Using value of YARN_PID_DIR.
2023-09-19 15:00:43,303 INFO client.ConfiguredRMFailoverProxyProvider: Failing over to rm2
Total Nodes:3
Node-Id Node-State Node-Http-Address Number-of-Running-Containers
sun75:34809 UNHEALTHY sun75:8042 0
sun46:34042 UNHEALTHY sun46:8042 0
sun77:36783 UNHEALTHY sun77:8042 0
发现节点状态不健康,查看详细信息
[root@sun46 hadoop]# yarn node -status sun75:34809
WARNING: YARN_LOG_DIR has been replaced by HADOOP_LOG_DIR. Using value of YARN_LOG_DIR.
WARNING: YARN_PID_DIR has been replaced by HADOOP_PID_DIR. Using value of YARN_PID_DIR.
2023-09-19 15:01:40,479 INFO client.ConfiguredRMFailoverProxyProvider: Failing over to rm2
2023-09-19 15:01:40,549 INFO conf.Configuration: resource-types.xml not found
2023-09-19 15:01:40,549 INFO resource.ResourceUtils: Unable to find 'resource-types.xml'.
Node Report :
Node-Id : sun75:34809
Rack : /default-rack
Node-State : UNHEALTHY
Node-Http-Address : sun75:8042
Last-Health-Update : Tue 19/Sep/23 03:00:25:279CST
Health-Report : 1/1 log-dirs usable space is below configured utilization percentage/no more usable space [ /srv/dstore/1.0.0.0/nfs/sun/sun75/yarn/userlogs : used space above threshold of 90.0% ]
Containers : 0
Memory-Used : 0MB
Memory-Capacity : 8192MB
CPU-Used : 0 vcores
CPU-Capacity : 8 vcores
Node-Labels :
Resource Utilization by Node : PMem:9942 MB, VMem:9942 MB, VCores:0.15328224
Resource Utilization by Containers : PMem:0 MB, VMem:0 MB, VCores:0.0
发现磁盘空间超过90%,释放空间,状态为RUNNING即可
三、启动spark-shell
Caused by: java.lang.NoClassDefFoundError: [Lorg/apache/hadoop/security/token/DelegationTokenIssuer;
参考
- https://mp.weixin.qq.com/s/s0ZdvEGcWO8QcFQBqccT7Q
- https://www.bilibili.com/video/BV1Yr4y1H7hz
- https://mp.weixin.qq.com/s/8nTjfEbet64mkL3wvNRRAA
- https://blog.51cto.com/u_15127680/4324197
- (https://blog.csdn.net/Androidlushangderen/article/details/106456427)
- [HDFS-11922]臭氧: KSM: 垃圾收集已删除的块 - ASF JIRA (apache.org)
- Ozone 介绍 - 简书 (jianshu.com)
- https://docs.aws.amazon.com/zh_cn/cli/latest/userguide/cli-configure-envvars.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?