标签:避免 ref tier attr hub 自定义 管理程序 生物 targe
一、架构
Ceph在一个统一的系统中独特地提供对象,块和文件存储。 Ceph高度可靠,易于管理且免费。 Ceph的强大功能可以改变您公司的IT基础架构以及管理大量数据的能力。 Ceph提供了非凡的可扩展性 - 成千上万的客户端访问数PB到数十亿的数据。 Ceph节点利用商用硬件和智能守护进程,Ceph存储集群可容纳大量节点,这些节点相互通信以动态地复制和重新分配数据。
二、RADOS-ceph存储集群
Ceph提供基于RADOS的无限可扩展Ceph存储集群,您可以在RADOS中阅读 - 一个可扩展,可靠的存储服务,用于PB级存储集群。(https://ceph.com/wp-content/uploads/2016/08/weil-rados-pdsw07.pdf)
Ceph存储集群由两种类型的守护进程组成:
Ceph Monitor维护集群映射的主副本。 Ceph监视器集群可确保监视器守护程序失败时的高可用性。 存储群集客户端从Ceph Monitor检索群集映射的副本。
Ceph OSD守护进程检查自己的状态和其他OSD的状态,并向监视器报告。
存储集群客户端和每个Ceph OSD守护程序使用CRUSH算法有效地计算有关数据位置的信息,而不必依赖于中央查找表。 Ceph的高级功能包括通过librados为Ceph存储集群提供本机接口,以及在librados之上构建的许多服务接口。
三、存储数据过程
Ceph存储集群从Ceph客户端接收数据 - 无论是通过Ceph块设备,Ceph对象存储,Ceph文件系统还是使用librados创建的自定义实现 - 它都将数据存储为对象。 每个对象对应于文件系统中的文件,该文件存储在对象存储设备上。 Ceph OSD Daemons处理存储磁盘上的读/写操作。
Ceph OSD守护进程将所有数据作为对象存储在平面命名空间中(例如,没有目录层次结构)。 对象具有标识符,二进制数据和由一组名称/值对组成的元数据。 语义完全取决于Ceph客户端。 例如,CephFS使用元数据来存储文件属性,例如文件所有者,创建日期,上次修改日期等。
注意:对象ID在整个群集中是唯一的,而不仅仅是本地文件系统。
四、可扩展性和高可用性
在传统架构中,客户端与集中式组件(例如,网关,代理,API,外观等)通信,该集中式组件充当复杂子系统的单个入口点。 这对性能和可扩展性施加了限制,同时引入单点故障(即,如果集中式组件发生故障,整个系统也会出现故障)。
Ceph消除了集中式网关,使客户端能够直接与Ceph OSD Daemons进行交互。 Ceph OSD Daemons在其他Ceph节点上创建对象副本,以确保数据安全性和高可用性。 Ceph还使用一组监视器来确保高可用性。 为了消除集中化,Ceph使用一种名为CRUSH的算法。
1.CRUSH 介绍
Ceph客户端和Ceph OSD守护进程都使用CRUSH算法有效地计算有关对象位置的信息,而不必依赖于中央查找表。 与旧方法相比,CRUSH提供了更好的数据管理机制,并通过将工作干净地分发到群集中的所有客户端和OSD守护进程来实现大规模扩展。 CRUSH使用智能数据复制来确保弹性,这更适合超大规模存储。 以下部分提供了有关CRUSH如何工作的其他详细信息。 有关CRUSH的详细讨论,请参阅CRUSH - 受控,可扩展,分散的复制数据放置(https://ceph.com/wp-content/uploads/2016/08/weil-crush-sc06.pdf)。
2.集群map
Ceph依赖于Ceph客户端和Ceph OSD守护进程,他们了解集群拓扑结构,其中包括5个映射,统称为“集群映射”:
监视器映射:包含集群 fsid,每个监视器的位置,名称地址和端口。它还指示当前时间点,创建映射的时间以及上次更改的时间。要查看监视器映射,请执行ceph mon dump。
OSD映射:包含集群fsid,创建映射的时间和上次修改映射的时间,包括池列表,副本大小,PG数量,OSD列表及其状态(例如up,in)。要查看OSD映射,请执行ceph osd dump。
PG 映射:包含PG版本,时间戳,最后一个OSD映射时间点,完整比率以及每个放置组的详细信息,例如PG ID,Up Set,Acting Set,PG的状态(例如,active + clean),以及每个池的数据使用情况统计信息。
CRUSH 映射:包含存储设备的列表,故障域层次结构(例如,设备,主机,机架,行,房间等),以及用于在存储数据时遍历层次结构的规则。要查看CRUSH映射,请执行ceph osd getcrushmap -o {filename};然后,通过执行crushtool -d {comp-crushmap-filename} -o {decomp-crushmap-filename}对其进行反编译。您可以在文本编辑器中或使用cat查看反编译的映射。
MDS 映射:包含当前MDS映射时间点,创建映射的时间以及上次更改时间。它还包含用于存储元数据的池,元数据服务器列表以及启动和运行的元数据服务器。要查看MDS映射,请执行ceph fs dump。
每个映射都保持其运行状态变化的迭代历史。 Ceph监视器维护集群映射的主副本,包括集群成员,状态,更改以及Ceph存储集群的整体运行状况。
3.高可用监视器
在Ceph客户端可以读取或写入数据之前,他们必须联系Ceph Monitor以获取群集映射的最新副本。 Ceph存储集群可以使用单个监视器进行操作; 然而,这引入了单点故障(即,如果监视器发生故障,Ceph客户端无法读取或写入数据)。
为了增加可靠性和容错能力,Ceph支持一组监视器。 在监视器群集中,延迟和其他故障可能导致一个或多个监视器落后于群集的当前状态。 因此,Ceph必须在各种监视器实例之间就集群状态达成一致。 Ceph总是使用大多数监视器(例如,1,2:3,3:5,4:6等)和Paxos算法在监视器之间建立关于群集当前状态的一致意见。
有关配置监视器的详细信息,请参阅“监视器配置参考”(https://docs.ceph.com/docs/master/rados/configuration/mon-config-ref)。
4.高可用性认证
为了识别用户并防止中间人攻击,Ceph提供了cephx身份验证系统来验证用户和守护进程。
Cephx使用共享密钥进行身份验证,这意味着客户端和监控集群都拥有客户端密钥的副本。认证协议使得双方能够彼此证明他们具有密钥的副本而不实际揭示密钥。这提供了相互身份验证,这意味着群集确保用户拥有密钥,并且用户确信群集具有密钥的副本。
Ceph的一个关键可扩展性功能是避免Ceph对象存储的集中接口,这意味着Ceph客户端必须能够直接与OSD交互。为了保护数据,Ceph提供了cephx身份验证系统,该系统对运行Ceph客户端的用户进行身份验证。 cephx协议以类似于Kerberos的行为运行。
用户/ actor调用Ceph客户端来联系监视器。与Kerberos不同,每个监视器都可以对用户进行身份验证并分发密钥,因此使用cephx时没有单点故障或瓶颈。监视器返回一个类似于Kerberos票证的验证数据结构,该票证包含用于获取Ceph服务的会话密钥。此会话密钥本身使用用户的永久密钥加密,因此只有用户可以从Ceph监视器请求服务。然后,客户端使用会话密钥从监视器请求其所需的服务,并且监视器向客户端提供将向客户端验证实际处理数据的OSD的票证。 Ceph监视器和OSD共享一个秘密,因此客户端可以使用监视器提供的票证与群集中的任何OSD或元数据服务器。与Kerberos一样,cephx票证到期,因此攻击者无法使用过期获得的过期票证或会话密钥。这种形式的身份验证将阻止访问通信介质的攻击者在其他用户的身份下创建虚假消息或者更改其他用户的合法消息,只要用户的密钥在到期之前未泄露即可。
注意:client.admin用户必须以安全的方式向用户提供用户标识和密钥。
要通过监视器进行身份验证,客户端会将用户名传递给监视器,监视器会生成会话密钥并使用与用户名关联的密钥对其进行加密。 然后,监视器将加密的票证发送回客户端。 然后,客户端用共享密钥解密有效载荷以检索会话密钥。 会话密钥标识当前会话的用户。 然后,客户端代表由会话密钥签名的用户请求票证。 监视器生成票证,使用用户的密钥对其进行加密,然后将其发送回客户端。 客户端对票证进行解密,并使用它对整个群集中的OSD和元数据服务器的请求进行签名。
cephx协议验证客户端计算机和Ceph服务器之间正在进行的通信。 在初始身份验证之后,客户端和服务器之间发送的每条消息都使用监视器,OSD和元数据服务器可以使用其共享密钥验证的故障单进行签名。
此身份验证提供的保护是在Ceph客户端和Ceph服务器主机之间。 身份验证不会超出Ceph客户端。 如果用户从远程主机访问Ceph客户端,则Ceph身份验证不会应用于用户主机和客户端主机之间的连接。
有关配置详细信息,请参阅Cephx配置指南(https://docs.ceph.com/docs/master/rados/configuration/auth-config-ref)。 有关用户管理的详细信息,请参阅用户管理(https://docs.ceph.com/docs/master/rados/operations/user-management)
5.SMART DAEMONS可以实现超级高效
在许多集群体系结构中,集群成员资格的主要目的是使集中式接口知道它可以访问哪些节点。 然后,集中式接口通过双重调度向客户端提供服务 - 这是PB级到EB级的巨大瓶颈。
Ceph消除了瓶颈:Ceph的OSD守护进程和Ceph客户端是群集感知的。 与Ceph客户端一样,每个Ceph OSD守护进程都知道集群中的其他Ceph OSD守护进程。 这使得Ceph OSD守护进程能够与其他Ceph OSD守护进程和Ceph监视器直接交互。 此外,它使Ceph客户端能够直接与Ceph OSD守护进程交互。
Ceph客户端,Ceph监视器和Ceph OSD守护进程相互交互的能力意味着Ceph OSD守护进程可以利用Ceph节点的CPU和RAM轻松执行会使中央服务器陷入困境的任务。 利用此计算能力的能力带来几个主要好处:
直接OSD服务客户端:由于任何网络设备都可以支持并发连接数限制,因此集中式系统在高规模时具有较低的物理限制。通过使Ceph客户端直接联系Ceph OSD守护进程,Ceph可同时提高性能和总系统容量,同时消除单点故障。 Ceph客户端可以在需要时维护会话,并使用特定的Ceph OSD守护进程而不是集中式服务器。
(1) OSD成员资格和状态:Ceph OSD守护进程加入群集并报告其状态。在最低级别,Ceph OSD守护程序状态向上或向下,反映它是否正在运行并能够为Ceph客户端请求提供服务。如果Ceph OSD守护程序关闭并且在Ceph存储群集中,此状态可能表示Ceph OSD守护程序失败。如果Ceph OSD守护进程未运行(例如,它崩溃),Ceph OSD守护进程无法通知Ceph Monitor它已关闭。 OSD定期向Ceph监视器发送消息(MPGStats预发光,新的MOSDBeacon发光)。如果Ceph Monitor在一段可配置的时间后没有看到该消息,那么它会将OSD标记为关闭。但是,这种机制是一种故障保护。通常,Ceph OSD守护进程将确定相邻OSD是否已关闭并将其报告给Ceph Monitor。这确保了Ceph Monitors是轻量级的过程。有关其他详细信息,请参阅监视OSD和心跳。
(2) 数据清理:作为维护数据一致性和清洁度的一部分,Ceph OSD守护进程可以清理放置组中的对象。也就是说,Ceph OSD守护进程可以将一个放置组中的对象元数据与存储在其他OSD上的放置组中的副本进行比较。清理(??通常每天执行)可以捕获错误或文件系统错误。 Ceph OSD守护进程还通过逐位比较对象中的数据来执行更深层次的清理。深度擦洗(通常每周执行一次)发现驱动器上的坏扇区在轻度擦洗中不明显。有关配置清理的详细信息,请参阅数据清理。
(3) 复制:与Ceph客户端一样,Ceph OSD守护进程使用CRUSH算法,但Ceph OSD守护进程使用它来计算应存储对象副本的位置(以及重新平衡)。在典型的写入方案中,客户端使用CRUSH算法计算存储对象的位置,将对象映射到池和放置组,然后查看CRUSH映射以标识放置组的主OSD。
(4) 客户端将对象写入主OSD中的标识的放置组。然后,具有其自己的CRUSH映射副本的主OSD识别用于复制目的的二级和三级OSD,并将该对象复制到二级和三级OSD中的适当放置组(与另外的副本一样多的OSD),并响应客户端确认对象已成功存储后。
凭借执行数据复制的能力,Ceph OSD Daemons可以减轻Ceph客户的负担,同时确保高数据可用性和数据安全性。
6.动态集群管理
在可伸缩性和高可用性部分,我们解释了Ceph如何使用CRUSH,集群感知和智能守护进程来扩展和维护高可用性。 Ceph设计的关键是自主,自我修复和智能的Ceph OSD守护进程。 让我们深入了解CRUSH如何使现代云存储基础架构能够放置数据,重新平衡集群并动态恢复故障。
关于池
Ceph存储系统支持“池”的概念,“池”是用于存储对象的逻辑分区。
Ceph客户端从Ceph监视器检索集群映射,并将对象写入池。 池的大小或副本数量,CRUSH规则和放置组的数量决定了Ceph将如何放置数据。
池至少设置以下参数:
所有权/对象访问权限
放置组数量
CRUSH使用规则。
有关详细信息,请参阅设置池值(https://docs.ceph.com/docs/master/rados/operations/pools#set-pool-values)
映射PGS 到 OSDS
每个池都有许多放置组。 CRUSH动态地将PG映射到OSD。 当Ceph客户端存储对象时,CRUSH会将每个对象映射到一个放置组。
将对象映射到放置组会在Ceph OSD守护进程和Ceph客户端之间创建一个间接层。 Ceph存储集群必须能够在动态存储对象的地方增长(或收缩)和重新平衡。 如果Ceph客户端“知道”哪个Ceph OSD守护进程具有哪个对象,那么这将在Ceph客户端和Ceph OSD守护进程之间产生紧密耦合。 相反,CRUSH算法将每个对象映射到一个放置组,然后将每个放置组映射到一个或多个Ceph OSD守护进程。 这个间接层允许Ceph在新的Ceph OSD守护进程和基础OSD设备联机时动态重新平衡。 下图描述了CRUSH如何将对象映射到放置组,以及将放置组映射到OSD。
通过群集映射和CRUSH算法的副本,客户端可以精确计算在读取或写入特定对象时要使用的OSD。
当从擦除编码池中读取对象NYAN时,解码功能读取三个块:包含ABC的块1,包含GHI的块3和包含YXY的块4。 然后,它重建对象ABCDEFGHI的原始内容。 解码功能被通知组块2和5丢失(它们被称为“擦除”)。 由于OSD4不在,因此无法读取块5。 一旦读取了三个块,就可以调用解码功能:OSD2是最慢的,并且没有考虑其块。
OSD 1是主要的并且从客户端接收WRITE FULL,这意味着有效载荷将完全替换对象而不是覆盖其中的一部分。创建对象的版本2(v2)以覆盖版本1(v1)。 OSD 1将有效载荷编码为三个块:D1v2(即数据块编号1版本2)将在OSD 1上,在OSD 2上为D2v2,在OSD 3上为C1v2(即编码块编号1版本2)。每个块将被发送到目标OSD,包括主OSD,除了处理写操作和维护放置组日志的权威版本之外,还负责存储块。当OSD收到指示其写入块的消息时,它还会在放置组日志中创建新条目以反映更改。例如,一旦OSD 3存储C1v2,它就将条目1,2(即时期1,版本2)添加到其日志中。由于OSD以异步方式工作,因此某些块可能仍在运行(例如D2v2),而其他块则在磁盘上(例如C1v1和D1v1)被确认。
如果一切顺利,则在动作集中的每个OSD上确认块,并且日志的last_complete指针可以从1,1移动到1,2。
最后,可以删除用于存储先前版本对象的块的文件:OSD 1上的D1v1,OSD 2上的D2v1和OSD 3上的C1v1。
但事故发生了。 如果OSD 1在D2v2仍处于运行状态时关闭,则对象的版本2将被部分写入:OSD 3有一个块,但这还不足以恢复。 它丢失了两个块:D1v2和D2v2以及擦除编码参数K = 2,M = 1要求至少有两个块可用于重建第三个块。 OSD 4成为新的主要内容,并发现last_complete日志条目(即,此条目之前的所有对象在前一个动作集中的所有OSD上都可用)是1,1并且将成为新权威日志的头部。
在OSD 3上找到的日志条目1,2与OSD 4提供的新权威日志不同:它被丢弃并且包含C1v2块的文件被删除。 在擦除期间利用擦除编码库的解码功能重建D1v1块并将其存储在新的主OSD4上。
有关其他详细信息,请参阅缓存分层。(https://docs.ceph.com/docs/master/rados/operations/cache-tiering)
本地协议和库
现代应用程序需要具有异步通信功能的简单对象存储接口。 Ceph存储集群提供了一个具有异步通信功能的简单对象存储接口。 该接口提供对整个群集中对象的直接并行访问。
池操作
快照和写时复制克隆
读/写对象 - 创建或删除 - 整个对象或字节范围 - 追加或截断
创建/设置/获取/删除XATTR
创建/设置/获取/删除键/值对
复合运算和双重ack语义
对象类
对象手表/通知
客户端可以向对象注册持久兴趣并保持与主OSD的会话打开。 客户端可以向所有观察者发送通知消息和有效负载,并在观察者收到通知时接收通知。 这使客户端能够将任何对象用作同步/通信通道。
数据条带化
存储设备具有吞吐量限制,这会影响性能和可伸缩性。因此,存储系统通常支持在多个存储设备上条带化存储连续的信息 - 以提高吞吐量和性能。最常见的数据条带化形式来自RAID。与Ceph条带化最相似的RAID类型是RAID 0或“条带卷”。 Ceph的条带化提供了RAID 0条带化的吞吐量,n路RAID镜像的可靠性和更快的恢复。
如果您预期大图像大小,大型S3或Swift对象(例如视频)或大型CephFS目录,则可以通过在对象集内的多个对象上条带化客户端数据来看到相当大的读/写性能改进。当客户端将条带单元并行写入其对应的对象时,会发生显着的写入性能。由于对象被映射到不同的放置组并进一步映射到不同的OSD,因此每次写入以最大写入速度并行发生。对单个磁盘的写入将受到磁头移动(例如每次搜索6ms)和该一个设备的带宽(例如100MB / s)的限制。通过将该写入分布在多个对象(映射到不同的放置组和OSD)上,Ceph可以减少每个驱动器的搜索次数,并结合多个驱动器的吞吐量来实现更快的写入(或读取)速度。
注意条带化与对象副本无关。由于CRUSH跨OSD复制对象,因此条带会自动复制。
在下图中,客户端数据在包含4个对象的对象集(下图中的对象集1)中进行条带化,其中第一个条带单元是对象0中的条带单元0,第四个条带单元是条带单元3对象3.写入第四个条带后,客户端确定对象集是否已满。如果对象集未满,则客户端开始再次将条带写入第一个对象(下图中的对象0)。如果对象集已满,则客户端创建一个新对象集(下图中的对象集2),并开始写入新对象集中第一个对象中的第一个条带(条带单元16)(对象4中的对象集)下图)。
三个重要变量决定了Ceph条纹数据的方式:
对象大小:Ceph存储集群中的对象具有最大可配置大小(例如,2MB,4MB等)。对象大小应足够大以容纳许多条带单元,并且应该是条带单元的倍数。
条纹宽度:条纹具有可配置的单位大小(例如,64kb)。 Ceph客户端将它将写入对象的数据划分为大小相等的条带单元,最后一个条带单元除外。条带宽度应该是对象大小的一部分,以便对象可以包含许多条带单元。
条带计数:Ceph客户端在由条带计数确定的一系列对象上写入一系列条带单元。该系列对象称为对象集。在Ceph客户端写入对象集中的最后一个对象后,它将返回到对象集中的第一个对象。
重要信息在将群集投入生产之前,请测试条带化配置的性能。条带化数据并将其写入对象后,您无法更改这些条带化参数。
一旦Ceph客户端将条带数据条带化并将条带单元映射到对象,Ceph的CRUSH算法将对象映射到放置组,并将放置组映射到Ceph OSD守护进程,然后将对象作为文件存储在存储磁盘上。
CEPH客户端
Ceph客户端包括许多服务接口。 这些包括:
块设备:Ceph块设备(a.k.a.,RBD)服务提供可调整大小,精简配置的块设备,具有快照和克隆功能。 Ceph在整个集群中对块设备进行条带化以获得高性能。 Ceph支持内核对象(KO)和直接使用librbd的QEMU管理程序 - 避免虚拟化系统的内核对象开销。
对象存储:Ceph对象存储(a.k.a.,RGW)服务为RESTful API提供与Amazon S3和OpenStack Swift兼容的接口。
文件系统:Ceph文件系统(CephFS)服务提供符合POSIX标准的文件系统,可用于挂载或用作用户空间(FUSE)中的文件系统。
Ceph可以运行其他OSD,MDS和监视器实例,以实现可扩展性和高可用性。 下图描绘了高级体系结构。
CEPH对象存储
Ceph对象存储守护程序radosgw是一种FastCGI服务,它提供RESTful HTTP API来存储对象和元数据。它使用自己的数据格式在Ceph存储集群之上分层,并维护自己的用户数据库,身份验证和访问控制。 RADOS Gateway使用统一命名空间,这意味着您可以使用OpenStack与Swift兼容的API或与Amazon S3兼容的API。例如,您可以使用兼容S3的API将数据写入一个应用程序,然后使用与另一个应用程序兼容的Swift兼容API读取数据。
S3 / Swift对象和存储群集对象比较
Ceph的Object Storage使用术语object来描述它存储的数据。 S3和Swift对象与Ceph写入Ceph存储集群的对象不同。 Ceph对象存储对象映射到Ceph存储集群对象。 S3和Swift对象不一定以1:1的方式与存储在存储集群中的对象相对应。 S3或Swift对象可以映射到多个Ceph对象。
有关详细信息,请参阅Ceph对象存储(https://docs.ceph.com/docs/master/radosgw/)
CEPH BLOCK DEVICE
Ceph块设备在Ceph存储集群中的多个对象上对块设备映像进行条带化处理,其中每个对象都映射到一个放置组并进行分发,并且放置组分布在整个集群中的各个ceph-osd守护程序中。
重要条带允许RBD块设备比单个服务器执行得更好!
精简配置的快照Ceph块设备是虚拟化和云计算的有吸引力的选择。在虚拟机方案中,人们通常在QEMU / KVM中部署带有rbd网络存储驱动程序的Ceph块设备,其中主机使用librbd为来宾提供块设备服务。许多云计算堆栈使用libvirt与虚拟机管理程序集成。您可以将精简配置的Ceph块设备与QEMU和libvirt一起使用,以支持OpenStack和CloudStack以及其他解决方案。
虽然我们目前不向其他虚拟机管理程序提供librbd支持,但您也可以使用Ceph Block Device内核对象向客户端提供块设备。其他虚拟化技术(如Xen)可以访问Ceph Block Device内核对象。这是使用命令行工具rbd完成的。
CEPH FILESYSTEM
Ceph文件系统(CephFS)提供符合POSIX标准的文件系统作为一种服务,它位于基于对象的Ceph存储集群之上。 CephFS文件被映射到Ceph存储在Ceph存储集群中的对象。 Ceph客户端将CephFS文件系统挂载为内核对象或用户空间(FUSE)中的文件系统。
Ceph文件系统服务包括与Ceph存储集群一起部署的Ceph元数据服务器(MDS)。 MDS的目的是将所有文件系统元数据(目录,文件所有权,访问模式等)存储在元数据驻留在内存中的高可用性Ceph元数据服务器中。 MDS(一个名为ceph-mds的守护进程)的原因是,列出目录或更改目录(ls,cd)等简单的文件系统操作会对Ceph OSD守护进程造成不必要的负担。因此,将元数据与数据分离意味着Ceph文件系统可以提供高性能服务,而不会对Ceph存储集群造成负担。
CephFS将元数据与数据分离,将元数据存储在MDS中,并将文件数据存储在Ceph存储集群中的一个或多个对象中。 Ceph文件系统旨在实现POSIX兼容性。 ceph-mds可以作为单个进程运行,也可以分发到多个物理机器,以实现高可用性或可伸缩性。
高可用性:额外的ceph-mds实例可以处于待机状态,随时可以接管任何活动失败的ceph-mds的职责。这很容易,因为包括期刊在内的所有数据都存储在RADOS上。转换由ceph-mon自动触发。
可伸缩性:多个ceph-mds实例可以处于活动状态,它们会将目录树拆分为子树(以及单个忙目录的分片),从而有效地平衡所有活动服务器之间的负载。
备用和活动等的组合是可能的,例如,为缩放运行3个活动ceph-mds实例,以及为高可用性运行一个备用实例。
标签:避免 ref tier attr hub 自定义 管理程序 生物 targe
原文地址:https://www.cnblogs.com/flytor/p/11384518.html