美文网首页
Crush 与 PG 分布

Crush 与 PG 分布

作者: DaemonXiao | 来源:发表于2021-10-20 09:03 被阅读0次

参考资料:
《Ceph 之 RADOS 设计原理与实现》
https://docs.ceph.com/en/latest/rados/operations/crush-map/
http://strugglesquirrel.com/2019/02/02/ceph%E8%BF%90%E7%BB%B4%E5%A4%A7%E5%AE%9D%E5%89%91%E4%B9%8B%E9%9B%86%E7%BE%A4osd%E4%B8%BAfull/
https://docs.ceph.com/en/latest/rados/operations/balancer/#modes

CRUSH

Crush 算法是用来计算对象分布在哪个 OSD 上的工具。其包含两个步骤:

  1. 计算出对象到 PG 的映射,使用哈希算法。如果 PG 数量不变,则此结果始终不变。

    Hash(oid) = pgid

  2. 计算出 PG 到 具体 OSD 的映射,一般使用 straw2 算法(《Ceph 设计原理与实现》Crush 章节有详细介绍)。此结果可以通过调节 OSD 的权重来改变。

    CRUSH(pgid) = OSDid

由此可见,对象被分布在一个个的 PG 当中,并且这个映射结果在 PG 数量不变的情况下始终保持稳定,这样通过管理 PG 的分布就相当于管理了整个集群中对象的分布。在 《Ceph 之Rados设计原理与实现》第一章中,详细介绍了PG分裂与扩容方式以及其高效简洁的原因。

编辑 CRUSH map

Crushmap 主要由两部分组成:cluster map 和 placement rule。前者描述整个集群设备的分布情况,后者规定了挑选 OSD 的步骤规则。

获取 CRUSH map

此命令获取的 CRUSH map 是经过编译的,需要经过反编译之后才能以文本的方式被编辑。

ceph osd getcrushmap -o {compiled-crushmap-filename}

[root@node-1 ~]# ceph osd getcrushmap -o crushmap
7
[root@node-1 ~]# ls -al crushmap 
-rw-r--r-- 1 root root 845 6月  22 10:31 crushmap

反编译 CRUSH map

将 getcrushmap 获取的 CRUSH map 转化为可编辑可阅读的文本。

crushtool -d {compiled-crushmap-filename} -o {decompiled-crushmap-file}

[root@node-1 ~]# crushtool -d crushmap -o crushmap.txt
[root@node-1 ~]# cat crushmap.txt 
# begin crush map
# 一般不更改
tunable choose_local_tries 0              # 已废弃,为做向后兼容设为0
tunable choose_local_fallback_tries 0     # 已废弃,为做向后兼容设为0
tunable choose_total_tries 50             # 选择 bucket 最大尝试次数,默认值 50
tunable chooseleaf_descend_once 1         # 已废弃,为做向后兼容设为1
tunable chooseleaf_vary_r 1               # 
tunable chooseleaf_stable 1               # 避免一些不必要的 pg 迁移
tunable straw_calc_version 1              # starw 算法版本,为向后兼容设为1
tunable allowed_bucket_algs 54            # 允许使用的 bucket 选择算法,54 代表 straw2 算法

# devices
# 每一个最末端的的物理设备(即 OSD),也叫叶子节点,一般无需手动设置。
# id 一般为大于等于0。与后面的 bucket id 不同。
device 0 osd.0 class hdd
device 1 osd.1 class hdd
device 2 osd.2 class hdd

# types
# 定义的 bucket 类型,可自行定制(增删 type 类型),编号必须为正整数。
type 0 osd
type 1 host
type 2 chassis
type 3 rack
type 4 row
type 5 pdu
type 6 pod
type 7 room
type 8 datacenter
type 9 zone
type 10 region
type 11 root

# buckets
# 所有的中间节点就叫做bucket,bucket可以是一些devices的集合也可以是低一级的buckets的集合, 根节点称为root是整个集群的入口, bucket的id必须是负数且唯一,一个bucket在crush map 实际存储位置是 buckets[-1-(bucket id)]。
host node-1 {
    id -3                        # do not change unnecessarily
    id -4 class hdd              # do not change unnecessarily
    # weight 0.010               # 此 bucket 权重,等于 item 权重之和
    alg straw2                   # 使用 straw2 算法
    hash 0                       # rjenkins1
    item osd.0 weight 0.010      # 此 bucket 含有的 OSD 及其权重,权重一般根据容量来定,如 1T 等于 1 权重
}
host node-2 {
    id -5       # do not change unnecessarily
    id -6 class hdd     # do not change unnecessarily
    # weight 0.010
    alg straw2
    hash 0  # rjenkins1
    item osd.1 weight 0.010
}
host node-3 {
    id -7       # do not change unnecessarily
    id -8 class hdd     # do not change unnecessarily
    # weight 0.010
    alg straw2
    hash 0  # rjenkins1
    item osd.2 weight 0.010
}
# 根 bucket ,至少有一个,是 placement rule 的入口。
root default {
    id -1       # do not change unnecessarily
    id -2 class hdd     # do not change unnecessarily
    # weight 0.029
    alg straw2
    hash 0  # rjenkins1
    item node-1 weight 0.010      # 此 bucket 含有3个子 bucket,每个权重0.01
    item node-2 weight 0.010
    item node-3 weight 0.010
}

# rules
# placement rule。注意:crushmap 只有一个,但可以定义多条 rule
rule replicated_rule {
    id 0                                   # id
    type replicated                        # 类型 [replicated|erasure]
    min_size 1                             # 如果池副本数小于这个数值,就不会应用这条rule
    max_size 10                            # 如果池副本数大于这个数值,就不会应用这条rule
    step take default                      # crush规则的入口,一般是类型为root的bucket
    step choose firstn 0 type osd          # 分为choose和chooseleaf两种,num代表选择的数量,type是预期的bucket类型。
    step emit                              # 输出结果
}

# end crush map

编译 CRUSH map

crushtool -c {decomplied-crush-map-filename} -o {complied-crush-map-filename}

模拟测试

可以对编译过的 CRUSH map 进行模拟测试。
min-x:最小输入值。输入对象名称模拟为[min, max]的数字。
max-x:最大输入值。
num-rep:副本数,输出 osd 个数等于副本数
ruleset:rule id。选择哪一条 replacement rule。

crushtool -i {complied-crush-map-filename} --test --min-x 0 --max-x 9 --num-rep 3 --ruleset 0 --show_mappings

[root@node-1 ~]# crushtool -i crushmap --test --min-x 0 --max-x 9 --num-rep 3 --ruleset 0 --show_mappings
CRUSH rule 0 x 0 [1,2,0]
CRUSH rule 0 x 1 [2,0,1]
CRUSH rule 0 x 2 [2,1,0]
CRUSH rule 0 x 3 [0,1,2]
CRUSH rule 0 x 4 [1,2,0]
CRUSH rule 0 x 5 [0,1,2]
CRUSH rule 0 x 6 [2,0,1]
CRUSH rule 0 x 7 [1,2,0]
CRUSH rule 0 x 8 [2,0,1]
CRUSH rule 0 x 9 [1,2,0]

或者仅统计结果分布。

crushtool -i {complied-crush-map-filename} --test --min-x 0 --max-x 10000 --num-rep 3 --ruleset 0 --show_utilization

[root@node-1 ~]# crushtool -i crushmap --test --min-x 0 --max-x 10000 --num-rep 3 --ruleset 0 --show_utilization
rule 0 (replicated_rule), x = 0..10000, numrep = 3..3
rule 0 (replicated_rule) num_rep 3 result size == 3:    10001/10001
  device 0:      stored : 10001  expected : 10001
  device 1:      stored : 10001  expected : 10001
  device 2:      stored : 10001  expected : 10001

注入集群

CRUSH map 需要注入集群后,才能生效.

ceph osd setcrushmap -i {complied-crushmap-filename}

[root@node-1 ~]# ceph osd setcrushmap -i crushmap
8

编写一个主副本在 SSD,其他副本在 HDD 的 CRUSH map

如何写出主副本始终分配到 SSD 设备,从副本分配到 HDD 设备呢?

首先要了解,主副本在 CRUSH map 中,实际上是第一个被计算出的 device,从副本是随后计算出的 device。只要保证每次第一个 emit 中输出的为 SSD 类型,就可以始终让主副本在 SSD 设备上。

接下来以一个例子介绍。

这里给出设备分布图,有三台主机,每台主机上有1个 SSD 和2个 HDD 类型的 OSD。

                                root
           _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ 
          |                       |                       |
        node-1                  node-2                  node-3
   _ _ _ _|_ _ _ _         _ _ _ _|_ _ _ _         _ _ _ _|_ _ _ _ 
  |       |       |       |       |       |       |       |       |
osd.0   osd.1   osd.2   osd.3   osd.4   osd.5   osd.6   osd.7   osd.8 
 SSD     HDD     HDD     SSD     HDD     HDD     SSD     HDD     HDD     

编写

首先修改 device。

# 修改 device
device 0 osd.0 class SSD
device 1 osd.1 class HDD
device 2 osd.2 class HDD
device 3 osd.3 class SSD
device 4 osd.4 class HDD
device 5 osd.5 class HDD
device 6 osd.6 class SSD
device 7 osd.7 class HDD
device 8 osd.8 class HDD

然后,修改cluester map,把 SSD 归为一组。考虑到物理隔离域,把 HDD 根据主机不同分为三组。

# buckets
# SSD 化为一组
host node-SSD {
    id -1       # do not change unnecessarily
    alg straw2
    hash 0  # rjenkins1
    item osd.0 weight 0.010         # 权重可以根据硬盘容量调整,这里认为
    item osd.3 weight 0.010
    item osd.6 weight 0.010
}
# HDD 根据主机化为三组
host node-1-HDD {
    id -2       # do not change unnecessarily
    alg straw2
    hash 0  # rjenkins1
    item osd.1 weight 0.1
    item osd.2 weight 0.1
}
host node-2-HDD {
    id -3       # do not change unnecessarily
    alg straw2
    hash 0  # rjenkins1
    item osd.4 weight 0.1
    item osd.5 weight 0.1
}
host node-3-HDD {
    id -4       # do not change unnecessarily
    alg straw2
    hash 0  # rjenkins1
    item osd.7 weight 0.1
    item osd.8 weight 0.1
}

接着,给出入口。这里有两个入口:SSD 节点和 HDD 节点。
注:root 和 host 并无本质区别,都属于 type。host 也可做为 入口 。

# root bucket
root root-SSD {
    id -5       # do not change unnecessarily
    alg straw2
    hash 0  # rjenkins1
    item node-SSD weight 0.030      # 注意了,权重等于 node-SSD 内三个 item 权重之和
}
root root-HDD {
    id -6       # do not change unnecessarily
    alg straw2
    hash 0  # rjenkins1
    item node-1-HDD weight 0.2      # 注意了,权重等于 node-x-HDD 内三个 item 权重之和
    item node-2-HDD weight 0.2 
    item node-3-HDD weight 0.2 
}

placement rule,策略应该注意先输出 SSD 在输出 HDD。

# rules
rule ssd-primary {
    id 1
    type replicated
    min_size 1
    max_size 10
    step take root-SSD
    step chooseleaf firstn 1 type host
    step emit
    step take root-HDD
    step chooseleaf firstn -1 type host         # -1 表示选出(副本数-1)个host,3副本的话,这里就是2个host
    step emit
}

测试

[root@node-1 ~]# vi mycrushmap.txt
[root@node-1 ~]# crushtool -c mycrushmap.txt -o mycrushmap
[root@node-1 ~]# crushtool -i mycrushmap --test --min-x 0 --max-x 9 --num-rep 3 --ruleset 1 --s
how_mappings
CRUSH rule 1 x 0 [0,7,1]
CRUSH rule 1 x 1 [3,2,5]
CRUSH rule 1 x 2 [0,8,4]
CRUSH rule 1 x 3 [0,8,4]
CRUSH rule 1 x 4 [3,1,8]
CRUSH rule 1 x 5 [3,7,4]
CRUSH rule 1 x 6 [6,7,4]
CRUSH rule 1 x 7 [6,1,8]
CRUSH rule 1 x 8 [6,2,5]
CRUSH rule 1 x 9 [6,8,1]

测试结果显示所有的主 OSD,即第一个 OSD 都为 SSD 。说明此 CRUSH map 符合预设。

附录

给出完整的编译后的 CRUSH map。

[root@node-1 ~]# vi mycrushmap.txt
device 0 osd.0 class SSD
device 1 osd.1 class HDD
device 2 osd.2 class HDD
device 3 osd.3 class SSD
device 4 osd.4 class HDD
device 5 osd.5 class HDD
device 6 osd.6 class SSD
device 7 osd.7 class HDD
device 8 osd.8 class HDD

type 0 osd
type 1 host
type 11 root

host node-SSD {
        id -1           # do not change unnecessarily
        alg straw2
        hash 0  # rjenkins1
        item osd.0 weight 0.010
        item osd.3 weight 0.010
        item osd.6 weight 0.010
}

host node-1-HDD {
        id -2           # do not change unnecessarily
        alg straw2
        hash 0  # rjenkins1
        item osd.1 weight 0.1
        item osd.2 weight 0.1
}
host node-2-HDD {
      id -3           # do not change unnecessarily
        alg straw2
        hash 0  # rjenkins1
        item osd.4 weight 0.1
        item osd.5 weight 0.1
}
host node-3-HDD {
        id -4           # do not change unnecessarily
        alg straw2
        hash 0  # rjenkins1
        item osd.7 weight 0.1
        item osd.8 weight 0.1
}

root root-SSD {
        id -5           # do not change unnecessarily
        alg straw2
        hash 0  # rjenkins1
        item node-SSD weight 0.030
}
root root-HDD {
        id -6           # do not change unnecessarily
        alg straw2
        hash 0  # rjenkins1
        item node-1-HDD weight 0.2
        item node-2-HDD weight 0.2
        item node-3-HDD weight 0.2
}
rule ssd-primary {
        id 1
        type replicated
        min_size 1
        max_size 10
        step take root-SSD
        step chooseleaf firstn 1 type host
        step emit
        step take root-HDD
        step chooseleaf firstn -1 type host     
        step emit
}

CRUSH 其他命令

查看 device map

将 devie map 树形结构以深度优先遍历的方式全部打印出来。

ceph osd tree

[root@node-1 ~]# ceph osd tree
ID CLASS WEIGHT  TYPE NAME       STATUS REWEIGHT PRI-AFF 
-1       0.03918 root default                            
-3       0.01959     host node-1                         
 0   hdd 0.00980         osd.0       up  1.00000 1.00000 
 3   hdd 0.00980         osd.3       up  1.00000 1.00000 
-5       0.00980     host node-2                         
 1   hdd 0.00980         osd.1       up  1.00000 1.00000 
-7       0.00980     host node-3                         
 2   hdd 0.00980         osd.2       up  1.00000 1.00000 

查看 placement rule

查看 CLUSH map 中有哪些 rule,以及每个 rule 的具体步骤。

# 查询所有的 rule
ceph osd crush rule ls

# 查询指定 rule 的具体步骤
ceph osd crush rule dump

[root@node-1 ~]# ceph osd crush rule ls
replicated_rule
[root@node-1 ~]# ceph osd crush rule dump
[
    {
        "rule_id": 0,
        "rule_name": "replicated_rule",
        "ruleset": 0,
        "type": 1,
        "min_size": 1,
        "max_size": 10,
        "steps": [
            {
                "op": "take",
                "item": -1,
                "item_name": "default"
            },
            {
                "op": "chooseleaf_firstn",
                "num": 0,
                "type": "host"
            },
            {
                "op": "emit"
            }
        ]
    }
]

数据重平衡

尽管 CRUSH 算法在设计上尽可能的注意到数据的平衡分布,但是在实际生产中,随着集群存储数据的不断增加和集群设备的变动,这种平衡将必定被打破。
针对这类问题,Ceph 提供了一系列的工具来使得数据重新分配,回到相对平衡的位置。

查看集群空间使用率

ceph df 可以大致查看集群的设备和存储池的空间使用情况。
RAW STORAGE类型下,可以看到 USED 比 RAW USED 小,USED 为用户实际数据存储量,RAW USED 为总的使用量(包括集群自生元数据)。
而 POOLS中,STORED是用户认为的使用量,USED 是集群实际的空间使用量,后者大约是前者的3倍。这是因为 Ceph 使用三副本存储数据,每一份数据要使用三份的存储空间。

[root@node-1 ceph-deploy]# ceph df
RAW STORAGE:
    类型       大小       可用空间   已用空间     总使用空间    总使用百分比
    CLASS     SIZE       AVAIL      USED        RAW USED     %RAW USED 
    hdd       40 GiB     35 GiB     1.3 GiB      5.3 GiB         13.17 
    TOTAL     40 GiB     35 GiB     1.3 GiB      5.3 GiB         13.17 
 
POOLS:
    池名称       id     数据量       对象数量    
    POOL         ID     STORED      OBJECTS     USED        %USED     MAX AVAIL 
    pool-1        1     376 MiB          95     1.1 GiB      3.30        11 GiB 
    rbd-pool      2      22 MiB          17      67 MiB      0.20        11 GiB 

ceph osd df tree 可以详细查看每个设备的空间使用情况。

VAR = 当前 OSD 使用率 / 集群平均空间使用率。
通过最后一行的 “MIN/MAX VAR: 0.88/1.07”,可以得知 VAR 最大和最小的偏移量,从而判断整个集群分布是否均衡。

# 深度优先遍历,输出 CRUSH map 中所有 bucket 以及 device 的详细信息
[root@node-1 ceph-deploy]# ceph osd df tree
          权重     再权重  大小   总使用量 数据总量       元数据总量 剩余量  使用率 比重  pg数 
ID CLASS WEIGHT  REWEIGHT SIZE   RAW USE DATA    OMAP    META     AVAIL   %USE  VAR  PGS STATUS TYPE NAME       
-1       0.03918        - 40 GiB 5.3 GiB 1.3 GiB 112 KiB  4.0 GiB  35 GiB 13.17 1.00   -        root default    
-3       0.01959        - 20 GiB 2.4 GiB 449 MiB  32 KiB  2.0 GiB  18 GiB 12.20 0.93   -            host node-1 
 0   hdd 0.00980  1.00000 10 GiB 1.3 GiB 282 MiB  32 KiB 1024 MiB 8.7 GiB 12.76 0.97  92     up         osd.0   
 3   hdd 0.00980  1.00000 10 GiB 1.2 GiB 167 MiB     0 B    1 GiB 8.8 GiB 11.64 0.88 100     up         osd.3   
-5       0.00980        - 10 GiB 1.4 GiB 424 MiB  48 KiB 1024 MiB 8.6 GiB 14.14 1.07   -            host node-2 
 1   hdd 0.00980  1.00000 10 GiB 1.4 GiB 424 MiB  48 KiB 1024 MiB 8.6 GiB 14.14 1.07 192     up         osd.1   
-7       0.00980        - 10 GiB 1.4 GiB 424 MiB  32 KiB 1024 MiB 8.6 GiB 14.14 1.07   -            host node-3 
 2   hdd 0.00980  1.00000 10 GiB 1.4 GiB 424 MiB  32 KiB 1024 MiB 8.6 GiB 14.14 1.07 192     up         osd.2   
                    TOTAL 40 GiB 5.3 GiB 1.3 GiB 113 KiB  4.0 GiB  35 GiB 13.17                                 
MIN/MAX VAR: 0.88/1.07  STDDEV: 1.05

# 仅输出 osd 的详细信息
[root@node-1 ~]# ceph osd df 
ID CLASS WEIGHT  REWEIGHT SIZE   RAW USE DATA    OMAP   META     AVAIL   %USE  VAR  PGS STATUS TYPE NAME       
-1       0.03918        - 40 GiB 5.3 GiB 1.3 GiB 96 KiB  4.0 GiB  35 GiB 13.18 1.00   -        root default    
-3       0.01959        - 20 GiB 2.4 GiB 452 MiB 64 KiB  2.0 GiB  18 GiB 12.21 0.93   -            host node-1 
 0   hdd 0.00980  1.00000 10 GiB 1.3 GiB 283 MiB 48 KiB 1024 MiB 8.7 GiB 12.77 0.97  92     up         osd.0   
 3   hdd 0.00980  1.00000 10 GiB 1.2 GiB 169 MiB 16 KiB 1024 MiB 8.8 GiB 11.65 0.88 100     up         osd.3   
-5       0.00980        - 10 GiB 1.4 GiB 425 MiB 16 KiB 1024 MiB 8.6 GiB 14.16 1.07   -            host node-2 
 1   hdd 0.00980  1.00000 10 GiB 1.4 GiB 425 MiB 16 KiB 1024 MiB 8.6 GiB 14.16 1.07 192     up         osd.1   
-7       0.00980        - 10 GiB 1.4 GiB 425 MiB 16 KiB 1024 MiB 8.6 GiB 14.16 1.07   -            host node-3 
 2   hdd 0.00980  1.00000 10 GiB 1.4 GiB 425 MiB 16 KiB 1024 MiB 8.6 GiB 14.16 1.07 192     up         osd.2   
                    TOTAL 40 GiB 5.3 GiB 1.3 GiB 97 KiB  4.0 GiB  35 GiB 13.18                                 
MIN/MAX VAR: 0.88/1.07  STDDEV: 1.05

Ceph 的三副本的空间利用率在实际中比 33% 还要小,因为还需要预留出一部分空间用于 Ceph 集群的正常工作,所以其空间利用率的均值大约在 23%【《Ceph rados 设计原理与实现》】。replicated 策略实在是太糟糕了,吃了一碗粉,却要付三碗的钱。

可以通过 mon osd full ratio 和 mon osd nearfull ratio 来设置集群空间使用量。mon osd full ratio 默认0.95,在达到95%的数据写入量后,此 OSD 将禁止读写数据(注意,读写都不行)。mon osd nearfull ratio 默认0.85,达到85%的空间使用率后,将产生警告。
需要注意的是这两个设置只适合写在配置文件中,用于一开始创建 Ceph 集群的时候。

[global]
        mon_osd_full_ratio = .80
        mon_osd_backfillfull_ratio = .75
        mon_osd_nearfull_ratio = .70

可以通过 ceph osd set-nearfull-ratio 和 set-full-ratio 命令来在运行过程中修改这两个比率值。

[root@node-1 ceph-deploy]# ceph osd set-full-ratio 0.98
osd set-full-ratio 0.98
[root@node-1 ceph-deploy]# ceph osd dump | grep full
full_ratio 0.98
backfillfull_ratio 0.9
nearfull_ratio 0.85

[root@node-1 ceph-deploy]# ceph osd set-nearfull-ratio 0.95
osd set-nearfull-ratio 0.95
[root@node-1 ceph-deploy]# ceph osd dump | grep full
full_ratio 0.98
backfillfull_ratio 0.9
nearfull_ratio 0.95
 
# 注意:在调整参数时,要注意和其他参数的限制关系。 
[root@node-1 ~]# ceph osd dump | grep full
full_ratio 0.98
backfillfull_ratio 0.9
nearfull_ratio 0.95
[root@node-1 ~]# ceph health detail
HEALTH_ERR full ratio(s) out of order
OSD_OUT_OF_ORDER_FULL full ratio(s) out of order
    backfillfull_ratio (0.9) < nearfull_ratio (0.95), increased
    osd_failsafe_full_ratio (0.97) < full_ratio (0.98), increased

此命令通常用于集群满状态后,临时修改配置文件,使得 OSD 可以读写,然后通过扩容、删除、重平衡等方式使得集群恢复健康状态。这里贴出一个相关的运维链接:

http://strugglesquirrel.com/2019/02/02/ceph%E8%BF%90%E7%BB%B4%E5%A4%A7%E5%AE%9D%E5%89%91%E4%B9%8B%E9%9B%86%E7%BE%A4osd%E4%B8%BAfull/

reweight

CRUSH 算法在理论上平衡效果很好,但实际生产环境情况复杂多变,因此它的平衡效果不尽如人意。而 reweight 就是专门为 CRUSH 设计的补偿措施,通过调整 reweight ,可以在 straw2 计算完成后,再进行一次过载测试,只有通过了过载测试,才算真正被选中。reweight 的值越大,通过测试的概率越高,最大值为1,默认值为1。并且,它还支持对已经分配的 pg 进行重新计算,如果发现 osd 映射结果发生了变化,会动态迁移到新的 osd,实现集群运行时的数据重平衡。

《Ceph rados 设计原理与实现》 中还提到了过载测试的另一个好处:“可以对 OSD 暂时失效和 OSD 永久被删除的场景进行区分。区分这两者的意义在于:如果 OSD 暂时失效,可以通过将其 reweight 调整为0,从而利用过载测试将其从候选条目中淘汰,进而将其承载的数据迁移至其他 OSD,这样后续该 OSD 正常回归时,将其 reweight 重新调整为原来的数值,即可使得原本属于该 OSD 的数据全部回归。而删除操作则会使得 OSD 在 cluster map 中的唯一编号发生改变,所以可能承载不同的数据。这样数据迁移量更大”。

可以使用下列命令,调整每个 OSD 的 reweight。reweight 在程序中会被放大10000倍,即[0-10000]。

ceph osd reweight <osd_num|osd id> <reweight[0.0-1.0]>

# 通过调整 osd.3 的reweight 把它的所有 pg 都迁移到别的 osd 上。
[root@node-1 ~]# ceph osd reweight osd.3 0
reweighted osd.3 to 0 (0)
[root@node-1 ~]# ceph osd df 
ID CLASS WEIGHT  REWEIGHT SIZE   RAW USE DATA    OMAP   META     AVAIL   %USE  VAR  PGS STATUS 
 0   hdd 0.00980  1.00000 10 GiB 1.3 GiB 319 MiB 48 KiB 1024 MiB 8.7 GiB 13.12 0.95 192     up 
 3   hdd 0.00980        0    0 B     0 B     0 B    0 B      0 B     0 B     0    0   0     up 
 1   hdd 0.00980  1.00000 10 GiB 1.4 GiB 425 MiB 16 KiB 1024 MiB 8.6 GiB 14.16 1.03 192     up 
 2   hdd 0.00980  1.00000 10 GiB 1.4 GiB 425 MiB 16 KiB 1024 MiB 8.6 GiB 14.16 1.03 192     up 
                    TOTAL 40 GiB 5.3 GiB 1.3 GiB 97 KiB  4.0 GiB  35 GiB 13.81                 
MIN/MAX VAR: 0.95/1.03  STDDEV: 0.49

# 通过调整 osd.3 reweight 回到原本数值,pg 又迁移回去
[root@node-1 ~]# ceph osd reweight osd.3 1
reweighted osd.3 to 1 (10000)
[root@node-1 ~]# ceph osd df 
ID CLASS WEIGHT  REWEIGHT SIZE   RAW USE DATA    OMAP   META     AVAIL   %USE  VAR  PGS STATUS 
 0   hdd 0.00980  1.00000 10 GiB 1.3 GiB 283 MiB 48 KiB 1024 MiB 8.7 GiB 12.80 0.97  92     up 
 3   hdd 0.00980  1.00000 10 GiB 1.2 GiB 169 MiB 16 KiB 1024 MiB 8.8 GiB 11.66 0.88 100     up 
 1   hdd 0.00980  1.00000 10 GiB 1.4 GiB 425 MiB 16 KiB 1024 MiB 8.6 GiB 14.16 1.07 192     up 
 2   hdd 0.00980  1.00000 10 GiB 1.4 GiB 425 MiB 16 KiB 1024 MiB 8.6 GiB 14.16 1.07 192     up 
                    TOTAL 40 GiB 5.3 GiB 1.3 GiB 97 KiB  4.0 GiB  35 GiB 13.19                 
MIN/MAX VAR: 0.88/1.07  STDDEV: 1.05

也可以批量调整,目前有两种模式:1. 按 OSD 当前空间使用率(reweight-by-utilization),2. 按 PG 在 OSD 之间的分布(reweight-by-pg)。可以在这两条命令前加上“test-”,测试执行该命令会产生的结果。

# 参数说明
# overload:当且仅当某个 OSD 的空间使用率大于等于集群平均空间使用率的 overload/100 时,调整reweight。取值范围:>100,默认120
# max_change:每次调整reweight 的最大幅度。取值范围:[0,1],默认0.05
# max_osds:每次最多调整 OSD 数量。默认4
# --no-increasing:表示不允许 reweitght 上调,只允许下调。注意 reweight范围 [0,1]
ceph osd <reweight-by-utilization|reweight-by-pg|test-reweight-by-utilization|test-reweight-by-pg> {overload} {max_change} {max_osds} {--no-increasing}

[root@node-1 ~]# ceph osd test-reweight-by-utilization 101
no change
moved 22 / 576 (3.81944%)
avg 144
stddev 48.0833 -> 42.9535 (expected baseline 10.3923)
min osd.0 with 92 -> 92 pgs (0.638889 -> 0.638889 * mean)
max osd.1 with 192 -> 182 pgs (1.33333 -> 1.26389 * mean)

oload 101
max_change 0.05
max_change_osds 4
average_utilization 0.1319
overload_utilization 0.1333
osd.2 weight 1.0000 -> 0.9500
osd.1 weight 1.0000 -> 0.9500


[root@node-1 ~]# ceph osd test-reweight-by-pg 101
no change
moved 22 / 576 (3.81944%)
avg 144
stddev 48.0833 -> 42.9535 (expected baseline 10.3923)
min osd.0 with 92 -> 92 pgs (0.638889 -> 0.638889 * mean)
max osd.1 with 192 -> 182 pgs (1.33333 -> 1.26389 * mean)

oload 101
max_change 0.05
max_change_osds 4
average_utilization 14699.6636
overload_utilization 14846.6602
osd.2 weight 1.0000 -> 0.9500
osd.1 weight 1.0000 -> 0.9500

需要注意的是,如果 OSD 的 reweight 已经为1了,那么他将不能调的更大,只能通过调小所有其他的 OSD 的 reweight 来凸显它的值变大。这很不方便,在调整过程中,也会导致集群迁移频繁。因此需要在刚刚建立好集群的时候,就适当调小 OSD 的 reweight,为以后的运维留下裕度空间。

weight set

straw2 算法中一个关键的参数是输入变量的权重,对于 Ceph 来说,每次 CRUSH 算法计算出的是一组 OSD(如果副本数为 3,则是 3 个 OSD),这一组 OSD 是有先后顺序的。因此,通过 CRUSH 选择不同位置的 OSD 时,每个 OSD 呈现的概率都应该有所不同。weight set 就是针对每个 OSD 在不同位置时,呈现出不同的权重特性。

支持两种模式:

  1. 兼容模式:和原本的 weight 相同,只有一个数字表示自身权重。
  2. 非兼容模式:weight-set 和具体的存储池绑定,可以为每个位置设置权重。
# 创建一个兼容模式的 weight set
ceph osd crush weight-set create-compat

# 单独调整每个 OSD 的权重
ceph osd crush weight-set reweight-compat {name} {weight}

# 删除兼容模式的 weight set
ceph osd crush weight-set rm-compat
# 创建非兼容模式的 weight set
# flat:效果和兼容模式相同,每个 OSD 的 weight-set 只有1个参数
# positional:根据副本个数以及副本当前所处的位置,为每个 OSD 设置一组权重。
ceph osd crush weight-set create <poolname> flat|positional

# 删除
ceph osd crush weight-set rm <poolname>

[root@node-1 ~]# ceph osd set-require-min-compat-client luminous
set require_min_compat_client to luminous
[root@node-1 ~]# ceph osd crush weight-set create pool-1 positional
[root@node-1 ~]# ceph osd crush weight-set reweight pool-1 osd.0 0.3 0.4 0.5
[root@node-1 ~]# ceph osd crush dump
...
            {
                "bucket_id": -4,
                "weight_set": [
                    [
                        0.29998779296875,
                        0.009796142578125
                    ],
                    [
                        0.399993896484375,
                        0.009796142578125
                    ],
                    [
                        0.5,
                        0.009796142578125
                    ]
                ]
            },

...

upmap

前面介绍的 reweight 和 weight-set 都只能提高或者降低 OSD 被选中的概率,而 upmap 可以直接选中某个 OSD 作为输出结果。默认 upmap 等于 activemap(计算出的结果)。

有两种替换方式:

  1. 指定某个 PG 的计算结果

    ceph osd pg-upmap <pgid><osdname (id|osd.id)> [<osdname (id|osd.id)>...]
    
    # 查看 pg 映射
    [root@node-1 ~]# ceph pg dump | awk '{print $1, $17}'
    ...
    PG_STAT UP_PRIMARY
    1.7f [2,3,1]
    1.7e [1,0,2]
    1.7d [3,2,1]
    1.7c [0,2,1]
    1.7b [1,0,2]
    ...
    
    # 设置的副本个数必须大于等于 pool min size
    [root@node-1 ~]# ceph osd pg-upmap 1.7f 1
    Error EINVAL: num of osds (1) < pool min size (2)
    [root@node-1 ~]# ceph osd pg-upmap 1.7f 0 0 0
    osd.0 already exists, osd.0 already exists, set 1.7f pg_upmap mapping to [0]
    [root@node-1 ~]# ceph pg dump | awk '{print $1,$17}' | grep 1.7f
    dumped all
    1.7f [0]
    
  2. 替换 PG 计算结果中的某个 OSD,需要同时指定源 OSD 和目标 OSD

    ceph osd pg-upmap-items <pgid><osdname (id|osd.id)> [<osdname (id|osd.id)>...]
    
    # 在实际操作过程中,发现 primer osd 无法替换,而且 upmap 会自动恢复成原本数组。
    [root@node-1 ~]# ceph osd pg-upmap-items 1.7f  3 0
    set 1.7f pg_upmap_items mapping to [3->0]
    [root@node-1 ~]# ceph pg dump | awk '{print $1,$17}' | grep 1.7f
    dumped all
    1.7f [2,0,1]
    # 过一会,再查看,发现变回原来数组,balancer自动调整
    [root@node-1 ~]# ceph pg dump | awk '{print $1,$17}' | grep 1.7f
    dumped all
    1.7f [2,3,1]
    

删除 upmap

ceph osd rm-pg-upmap <pgid>
ceph osd rm-pg-upmap-items <pgid>

balancer

balancer 是 Ceph 自动性的重平衡工具,其主要依赖 reweigth、weight set 和 upmap 工具实现。

检查状态

ceph balancer status

# 参数说明
# last_optimize_duration:上一次优化过程持续时间
# plans:优化任务
# mode:指执行计划时,默认选择的工具和手段,当前支持 crush-compat、upmap、none
# active:是否启用 balancer
# optimize_result:优化结果
# last_optimize_started:上一次优化时间
[root@node-1 ~]# ceph balancer status
{
    "last_optimize_duration": "0:00:00.000220", 
    "plans": [], 
    "mode": "none", 
    "active": true, 
    "optimize_result": "Please do \"ceph balancer mode\" to choose a valid mode first", 
    "last_optimize_started": "Wed Jun 23 15:58:21 2021"
}

开启|关闭

ceph balancer on
ceph balancer off

通过 target_max_misplaced_ratio 参数调整平衡操作时,每次 PG 移动的比率

ceph config set mgr target_max_misplaced_ratio .07   # 7%

设置 balancer 自动调整的间隔时间,默认 60s,注意斜杠不要省略,不要分成3条命令输入

ceph config set mgr mgr/balancer/sleep_interval 60

设置 balancer 一天中的开始和结束时间,避开正常业务的高峰时段,默认全天都进行,时间格式为 HHMM

ceph config set mgr mgr/balancer/begin_time 0000
ceph config set mgr mgr/balancer/end_time 2400

设置 balancer 一周中的开始和结束时间,默认一整周,注意 0 或 7 为周日,1为周一,以此类推

ceph config set mgr mgr/balancer/begin_weekday 0
ceph config set mgr mgr/balancer/end_weekday 7

设置 balancer 平衡操作针对的存储池

ceph config set mgr mgr/balancer/pool_ids 1,2,3

调整 mode

ceph balancer mode crush-compat
ceph balancer mode upmap
ceph balancer mode none       # 相当于关闭 balancer

生成一个 plan,注意:没有 plan 的 balancer 是不会执行任何操作的

ceph balancer optimize <plan> {<pools> [<pools>...]}

执行 plan

ceph balancer execute <plan-name>

查询所有的 plan

ceph balancer ls

删除 plan

ceph balancer rm <plan>

对当前集群的平衡状态进行评估,数字越小越好

ceph balancer eval {pool}

查看评估的详细信息

ceph balancer eval-verbose {pool}

对一个 plan 进行评估

ceph balancer eval <plan-name>

如何为集群创建一个 blancer 计划

一般不需要手动建立计划,开启 balancer 并选择 mode后,balancer 会定时自动的完成优化工作。

# 首先关闭 balancer
[root@node-1 ~]# ceph balancer off

# 为 balancer 选择一个模式
[root@node-1 ~]# ceph balancer mode upmap

# 实验的集群已经很平衡了,直接告诉我不能继续优化
[root@node-1 ~]# ceph balancer optimize myplan
Error EALREADY: Unable to find further optimization, or pool(s) pg_num is decreasing, or distribution is already perfect

# 手动调整 reweight,构建一个不平衡的集群,注意 0 是指停用,这里reweight 如若调整为0,集群依然是完美平衡。
[root@node-1 ~]# ceph osd reweight osd.3 0.1
reweighted osd.3 to 0.1 (1999)
[root@node-1 ~]# ceph osd df
ID CLASS WEIGHT  REWEIGHT SIZE   RAW USE DATA    OMAP   META     AVAIL   %USE  VAR  PGS STATUS 
 0   hdd 0.00980  1.00000 10 GiB 1.4 GiB 427 MiB 48 KiB 1024 MiB 8.6 GiB 14.18 1.07 179     up 
 3   hdd 0.00980  0.09999 10 GiB 1.0 GiB  33 MiB 16 KiB 1024 MiB 9.0 GiB 10.35 0.78  13     up 
 1   hdd 0.00980  1.00000 10 GiB 1.4 GiB 431 MiB 16 KiB 1024 MiB 8.6 GiB 14.22 1.07 192     up 
 2   hdd 0.00980  1.00000 10 GiB 1.4 GiB 431 MiB 16 KiB 1024 MiB 8.6 GiB 14.22 1.07 192     up 
                    TOTAL 40 GiB 5.3 GiB 1.3 GiB 97 KiB  4.0 GiB  35 GiB 13.24                 
MIN/MAX VAR: 0.78/1.07  STDDEV: 1.08

# 新建计划
[root@node-1 ~]# ceph balancer optimize myplan

# 评估计划
[root@node-1 ~]# ceph balancer eval myplan
plan myplan final score 0.017154 (lower is better)

# 查看集群当前分数,发现优化有一点点效果,我们这里选择执行计划
[root@node-1 ~]# ceph balancer eval 
current cluster score 0.017850 (lower is better)

# 执行计划
[root@node-1 ~]# ceph balancer execute myplan
[root@node-1 ~]# ceph balancer eval 
current cluster score 0.017154 (lower is better)
[root@node-1 ~]# ceph osd df
ID CLASS WEIGHT  REWEIGHT SIZE   RAW USE DATA    OMAP   META     AVAIL   %USE  VAR  PGS STATUS 
 0   hdd 0.00980  1.00000 10 GiB 1.4 GiB 408 MiB 48 KiB 1024 MiB 8.6 GiB 13.99 1.06 180     up 
 3   hdd 0.00980  0.09999 10 GiB 1.1 GiB  57 MiB 16 KiB 1024 MiB 8.9 GiB 10.59 0.80  12     up 
 1   hdd 0.00980  1.00000 10 GiB 1.4 GiB 432 MiB 16 KiB 1024 MiB 8.6 GiB 14.22 1.07 192     up 
 2   hdd 0.00980  1.00000 10 GiB 1.4 GiB 432 MiB 16 KiB 1024 MiB 8.6 GiB 14.22 1.07 192     up 
                    TOTAL 40 GiB 5.3 GiB 1.3 GiB 97 KiB  4.0 GiB  35 GiB 13.25                 
MIN/MAX VAR: 0.80/1.07  STDDEV: 1.00

PG 相关命令

获取 PG 详细信息

[root@node-1 ~]# ceph pg dump_json sum
dumped all
version 406
stamp 2021-06-24 09:24:46.705278
last_osdmap_epoch 0
last_pg_scan 0
PG_STAT OBJECTS MISSING_ON_PRIMARY DEGRADED MISPLACED UNFOUND BYTES    OMAP_BYTES* OMAP_KEYS* LOG DISK_LOG STATE        STATE_STAMP                VERSION REPORTED UP      UP_PRIMARY ACTING  ACTING_PRIMARY LAST_SCRUB SCRUB_STAMP                LAST_DEEP_SCRUB DEEP_SCRUB_STAMP           SNAPTRIMQ_LEN 
1.7f          0                  0        0         0       0        0           0          0   0        0 active+clean 2021-06-24 09:11:24.543912     0'0  275:687 [2,1,0]          2 [2,1,0]              2        0'0 2021-06-23 14:51:08.406526             0'0 2021-06-22 09:31:39.906375             0 
1.7e          1                  0        0         0       0  4194304           0          0   2        2 active+clean 2021-06-24 09:11:24.318962    50'2  275:606 [1,0,2]          1 [1,0,2]              1       50'2 2021-06-23 14:37:05.824343            50'2 2021-06-23 14:37:05.824343             0 
1.7d          0                  0        0         0       0        0           0          0   0        0 active+clean 2021-06-24 09:11:22.895867     0'0   275:36 [0,2,1]          0 [0,2,1]              0        0'0 2021-06-23 12:21:07.406368             0'0 2021-06-22 09:31:09.128962             0 
...

查询每个 OSD 上 PG 总数

[root@node-1 ~]# ceph pg dump osds
dumped osds
OSD_STAT USED    AVAIL   USED_RAW TOTAL  HB_PEERS PG_SUM PRIMARY_PG_SUM 
3         59 MiB 8.9 GiB  1.1 GiB 10 GiB  [0,1,2]     12              4 
2        433 MiB 8.6 GiB  1.4 GiB 10 GiB  [0,1,3]    192             75 
1        433 MiB 8.6 GiB  1.4 GiB 10 GiB  [0,2,3]    192             62 
0        409 MiB 8.6 GiB  1.4 GiB 10 GiB  [1,2,3]    180             51 
sum      1.3 GiB  35 GiB  5.3 GiB 40 GiB   

查询指定 OSD 上的 PG 的详细信息

[root@node-1 ~]# ceph pg ls-by-osd 0
PG   OBJECTS DEGRADED MISPLACED UNFOUND BYTES    OMAP_BYTES* OMAP_KEYS* LOG STATE        SINCE VERSION REPORTED UP        ACTING    SCRUB_STAMP                DEEP_SCRUB_STAMP           
1.1        0        0         0       0        0           0          0   0 active+clean   19m     0'0  275:573 [2,0,1]p2 [2,0,1]p2 2021-06-23 14:37:36.380990 2021-06-22 09:28:39.643688 
1.2        1        0         0       0  4194304           0          0   2 active+clean   19m    50'2  275:606 [1,0,2]p1 [1,0,2]p1 2021-06-23 14:44:23.268353 2021-06-23 14:44:23.268353 
1.3        0        0         0       0        0           0          0   0 active+clean   19m     0'0  275:472 [1,2,0]p1 [1,2,0]p1 2021-06-23 10:20:09.588889 2021-06-23 10:20:09.588889 
...

查询指定 pool 上的 PG 的详细信息

[root@node-1 ~]# ceph pg ls-by-pool pool-1
PG   OBJECTS DEGRADED MISPLACED UNFOUND BYTES    OMAP_BYTES* OMAP_KEYS* LOG STATE        SINCE VERSION REPORTED UP        ACTING    SCRUB_STAMP                DEEP_SCRUB_STAMP           
1.0        1        0         0       0  4194304           0          0   2 active+clean   22m    50'2  275:715 [1,2,3]p1 [1,2,3]p1 2021-06-23 12:56:25.914554 2021-06-22 09:29:38.155739 
1.1        0        0         0       0        0           0          0   0 active+clean   22m     0'0  275:573 [2,0,1]p2 [2,0,1]p2 2021-06-23 14:37:36.380990 2021-06-22 09:28:39.643688 
1.2        1        0         0       0  4194304           0          0   2 active+clean   22m    50'2  275:606 [1,0,2]p1 [1,0,2]p1 2021-06-23 14:44:23.268353 2021-06-23 14:44:23.268353 
1.3        0        0         0       0        0           0          0   0 active+clean   22m     0'0  275:472 [1,2,0]p1 [1,2,0]p1 2021-06-23 10:20:09.588889 2021-06-23 10:20:09.588889 
...

查询 PG 上对象数量

[root@node-1 ~]# ceph pg dump | awk '{print $1, $2}'
dumped all
version 760
stamp 2021-06-24
last_osdmap_epoch 0
last_pg_scan 0
PG_STAT OBJECTS
1.7f 0
1.7e 1
1.7d 0
1.7c 1
1.7b 2
1.7a 1
1.79 1
1.78 2
1.77 1

查询指定 OSD 上对象数量

[root@node-1 ~]# ceph pg ls-by-osd 0 | awk 'BEGIN{sum = 0;}{sum+=$2}END {print "objects: " sum}'
objects: 106

查询指定 pool 上对象数量

注意啦,这个是包含了副本对象的,实际对象可能只有 1/3 不到。

[root@node-1 ~]# ceph df
RAW STORAGE:
    CLASS     SIZE       AVAIL      USED        RAW USED     %RAW USED 
    hdd       40 GiB     35 GiB     1.3 GiB      5.3 GiB         13.26 
    TOTAL     40 GiB     35 GiB     1.3 GiB      5.3 GiB         13.26 
 
POOLS:
    POOL         ID     STORED      OBJECTS     USED        %USED     MAX AVAIL 
    pool-1        1     376 MiB          95     1.1 GiB      3.30        11 GiB 
    rbd-pool      2      22 MiB          17      67 MiB      0.20        11 GiB 

附录

 Monitor commands: 
 =================
pg cancel-force-backfill <pgid> [<pgid>...]             restore normal backfill priority of <pgid>
pg cancel-force-recovery <pgid> [<pgid>...]             restore normal recovery priority of <pgid>
pg debug unfound_objects_exist|degraded_pgs_exist       show debug info about pgs
pg deep-scrub <pgid>                                    start deep-scrub on <pgid>
pg dump {all|summary|sum|delta|pools|osds|pgs|pgs_      show human-readable versions of pg map (only 
 brief [all|summary|sum|delta|pools|osds|pgs|pgs_brief.  valid with plain)
 ..]}                                                   
pg dump_json {all|summary|sum|pools|osds|pgs [all|      show human-readable version of pg map in json only
 summary|sum|pools|osds|pgs...]}                        
pg dump_pools_json                                      show pg pools info in json only
pg dump_stuck {inactive|unclean|stale|undersized|       show information about stuck pgs
 degraded [inactive|unclean|stale|undersized|degraded.. 
 .]} {<int>}                                            
pg force-backfill <pgid> [<pgid>...]                    force backfill of <pgid> first
pg force-recovery <pgid> [<pgid>...]                    force recovery of <pgid> first
pg getmap                                               get binary pg map to -o/stdout
pg ls {<int>} {<states> [<states>...]}                  list pg with specific pool, osd, state
pg ls-by-osd <osdname (id|osd.id)> {<int>} {<states>    list pg on osd [osd]
 [<states>...]}                                         
pg ls-by-pool <poolstr> {<states> [<states>...]}        list pg with pool = [poolname]
pg ls-by-primary <osdname (id|osd.id)> {<int>}          list pg with primary = [osd]
 {<states> [<states>...]}                               
pg map <pgid>                                           show mapping of pg to osds
pg repair <pgid>                                        start repair on <pgid>
pg repeer <pgid>                                        force a PG to repeer
pg scrub <pgid>                                         start scrub on <pgid>
pg stat                                                 show placement group status.

相关文章

  • Crush 与 PG 分布

    参考资料:《Ceph 之 RADOS 设计原理与实现》https://docs.ceph.com/en/lates...

  • PGP与PG的关系

    PGP是PG的逻辑承载体,是CRUSH算法不可缺少的部分 在Ceph集群里,增加PG数量,PG到OSD的映射关系就...

  • k8s分布式存储-Ceph

    Ceph架构介绍 Ceph核心概念 RADOS Librados Crush Pool PG Object Poo...

  • CEPH CRUSH算法

    今天看CRUSH数据分布算法,想从头捋一遍,便于从宏观到细节地理解ceph的设计。 首先,CRUSH算法是什么?c...

  • 【学习】智能分布CRUSH

    CRUSH(Controlled Replication Under Scalable Hashing...

  • PG数量的预估

    PG数量的设置牵扯到数据分布的均匀性问题。 预设Ceph集群中的PG数至关重要,公式如下: (**结果必须舍入到最...

  • ceph分布式存储-数据不均衡调整

    1. 查看数据分布是否均衡 2. reweight-by-pg 按归置组分布情况调整 OSD 的权重 3. rew...

  • ceph crushtool模拟测试CRUSH分布

  • crush

    以前看刘瑜的《送你一颗子弹》,里面有一篇标题叫《crush》。have a crush 意为迷恋。与暗恋不同,...

  • -Crush与爱

    世间畅想:一个热爱写作的自创号,喜欢请大家关注,谢谢! 英文里有个词,叫crush。如果查字典,它会告诉你,这是“...

网友评论

      本文标题:Crush 与 PG 分布

      本文链接:https://www.haomeiwen.com/subject/nryaoltx.html