• Ceph Luminous 12 添加/删除/更换 OSD 详解


    1. OSD 介绍

    OSD 的记录存储于以下四处位置:

    • 配置文件:ceph.conf(可不写 OSD 记录)
    • mon 节点的 map:osdmapcrushmap
    • ceph auth 认证信息

    以下所有操作均来自官方文档。

    2. 手动操作

    2.1 手动删除 OSD

    官方文档链接:REMOVING OSDS (MANUAL)

    2.1.1 把 OSD 踢出集群

    删除 OSD 前,它通常是 up 且 in 的,要先把它踢出集群,触发 Ceph 迁移。

    ceph osd out {osd-num}
    
    • 1

    此时 OSD 状态将变为 up 且 out , Ceph 会开始重新均衡集群、把 PG 迁出此 OSD 。

    ceph -w
    
    • 1

    使用此命令看到 PG 状态从 active+clean 变为 active, some degraded objects 、迁移完成后最终回到 active+clean 状态。( Ctrl-c 中止)。

    2.1.2 停止 OSD

    停止 OSD 进程。

    ssh {osd-host}
    sudo systemctl stop ceph-osd@{osd-num}
    
    • 1
    • 2

    此时 OSD 状态变为 down 且 out。

    2.1.3 删除 OSD

    此步骤依次把一个 OSD 移出 crushmap、删除 ceph auth 中此 OSD 的密钥、移出 osdmap、删除 ceph.conf 中对应条目。如果主机有多个硬盘,每个硬盘对应的 OSD 都得重复此步骤。不同的 Ceph 版本操作命令略有不同:

    2.1.3.1 Ceph Luminous 及之后版本操作
    1. 一步到位移出三处记录,移出 crushmap、删除 ceph auth 记录、移出 osdmap
    ceph osd purge {id} --yes-i-really-mean-it
    
    • 1
    1. 如果 ceph.conf 有此 osd 记录,则删除此记录后同步配置 。
    2.1.3.2 Ceph 其他版本操作
    1. 移出 crushmap
    ceph osd crush remove {name}
    
    • 1
    1. 删除 ceph auth 记录
    ceph auth del osd.{osd-num}
    
    • 1
    1. 移出 osdmap
    ceph osd rm {osd-num}
    #for example
    ceph osd rm 1
    
    • 1
    • 2
    • 3
    1. 删除 ceph.conf 中的记录(如果有)

    2.2 手动添加 OSD(方法一)

    官方文档链接:ADDING AN OSD (MANUAL)
    手动添加 OSD 的步骤与手动删除 OSD 的步骤正好相反,首先创建挂载目录、把挂载硬盘,然后把 OSD 加入 osdmap,在 ceph auth 中添加密钥,最后加入 crushmap

    1. 创建 OSD 编号,后续步骤会用到此编号。此命令会在 osdmap 中生成新的 osd 记录。但此 osd 状态为 down 且 out。且不属于任何 host 下。
    ceph osd create [{uuid} [{id}]]
    
    • 1

    Warning 一般来说,不建议指定 {id} 。因为 ID 是按照数组分配的,跳过一些依然会浪费内存;尤其是跳过太多、或者集群很大时,会更明显。若未指定 {id} ,将用最小可用数字。

    创建 OSD 编号举例如下:

    [root@SYSOPS00081857 ~]# ceph osd create
    3
    [root@SYSOPS00081857 ~]# ceph osd tree
    ID CLASS WEIGHT  TYPE NAME               STATUS REWEIGHT PRI-AFF
    -1       0.32639 root default
    -3       0.10880     host SYSOPS00081857
     0   hdd 0.10880         osd.0               up  1.00000 1.00000
    -7       0.10880     host SYSOPS00081858
     1   hdd 0.10880         osd.1               up  1.00000 1.00000
    -5       0.10880     host SYSOPS00081859
     2   hdd 0.10880         osd.2               up  1.00000 1.00000
     3             0 osd.3                     down        0 1.00000
    [root@SYSOPS00081857 ~]# ceph osd dump | grep osd.3    #查看 osdmap 中的 osd.3 记录
    osd.3 down out weight 0 up_from 0 up_thru 0 down_at 0 last_clean_interval [0,0) - - - - exists,new
    [root@SYSOPS00081857 ~]#
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    1. 在新 OSD 所在节点创建默认目录,其中 {ceph} 为集群名,默认为 ceph{osd-number} 为第一步生成的 OSD 编号。
    ssh {new-osd-host}
    sudo mkdir /var/lib/ceph/osd/{ceph}-{osd-number}
    
    • 1
    • 2
    1. 如果准备用于 OSD 的是单独的而非系统盘,先把它挂载到刚创建的目录下:
    ssh {new-osd-host}
    sudo mkfs -t {fstype} /dev/{drive}
    sudo mount -o user_xattr /dev/{hdd} /var/lib/ceph/osd/ceph-{osd-number}
    
    • 1
    • 2
    • 3
    1. 初始化 OSD 数据目录。至此该 OSD 彻底加入 osdmap。状态应该变为 down 且 in。运行 ceph-osd 时目录必须是空的。
    ssh {new-osd-host}
    ceph-osd -i {osd-num} --mkfs --mkkey
    
    • 1
    • 2
    1. ceph auth 中添加密钥, ceph-{osd-num} 路径里的 ceph 值应该是 $cluster-$id ,如果你的集群名字不是 ceph ,那就用改过的名字。
    ceph auth add osd.{osd-num} osd 'allow *' mon 'allow rwx' -i /var/lib/ceph/osd/ceph-{osd-num}/keyring
    
    • 1
    1. 把 OSD 加入 crushmap,这样它才开始收数据。需要选择合适的 bucket 加入。
    ceph osd crush add {id-or-name} {weight}  [{bucket-type}={bucket-name} ...]
    
    • 1
    1. 启动 OSD 服务,此时 OSD 状态变成 up 且 in。

    2.3 手动添加 OSD(方法二)

    与方法一思路一致,使用命令有差异。官方文档中有两处记录了手动添加的方法。
    官方文档另一处的链接:LONG FORM

    1. 复制 osd key 到对应节点
    scp ceph.bootstrap-osd.keyring node-1859:/var/lib/ceph/bootstrap-osd/ceph.keyring
    
    • 1
    1. ssh 到对应节点后,直接执行以下命令
    UUID=$(uuidgen)
    OSD_SECRET=$(ceph-authtool --gen-print-key)
    
    • 1
    • 2
    ID=$(echo "{\"cephx_secret\": \"$OSD_SECRET\"}" | \
       ceph osd new $UUID -i - \
       -n client.bootstrap-osd -k /var/lib/ceph/bootstrap-osd/ceph.keyring)
    
    • 1
    • 2
    • 3
    mkdir /var/lib/ceph/osd/ceph-$ID
    
    • 1
    如果是磁盘,则需要运行以下命令:
    mkfs.xfs /dev/{DEV}
    mount /dev/{DEV} /var/lib/ceph/osd/ceph-$ID
    
    • 1
    • 2
    • 3
    ceph-authtool --create-keyring /var/lib/ceph/osd/ceph-$ID/keyring \
         --name osd.$ID --add-key $OSD_SECRET
    
    • 1
    • 2
    ceph-osd -i $ID --mkfs --osd-uuid $UUID
    
    • 1
    chown -R ceph:ceph /var/lib/ceph/osd/ceph-$ID
    
    • 1
    systemctl enable ceph-osd@$ID
    systemctl start ceph-osd@$ID
    
    • 1
    • 2

    3. ceph-deploy 操作

    3.1 使用 ceph-deploy 添加 OSD

    官方文档链接:ADD/REMOVE OSDS
    CEPH-DISK – CEPH 的硬盘管理工具

    ceph-deploy osd create HOST:DISK[:JOURNAL] [HOST:DISK[:JOURNAL]...]
    
    • 1

    此命令可一次完成所有操作,其分别调用了 prepareactivate 子命令。如下:

    ceph-deploy osd prepare HOST:DISK[:JOURNAL] [HOST:DISK[:JOURNAL]...]
    ceph-deploy osd activate HOST:DISK[:JOURNAL] [HOST:DISK[:JOURNAL]...]
    
    • 1
    • 2

    这两个命令调用的是 ceph-disk 对应的 prepareactivate 子命令。
    prepare 创建 bootstrap-osd 密钥(如果需要),创建 GPT 分区、给分区打上 Ceph 风格的 uuid 标记、创建文件系统、把此文件系统标记为已就绪、使用日志磁盘的整个分区并新增一分区。
    activate 激活 Ceph OSD 。先把此卷挂载到一临时位置,分配 OSD id(若有必要),重挂载到正确位置 /var/lib/ceph/osd/ c l u s t e r − cluster- clusterid 并启动 ceph-osd 。

    4. ceph-disk 操作

    从 ceph 13 版本开始,ceph-diskceph-volume 取代。故此方法只能在 ceph 13 之前版本使用。如果 osd 磁盘使用了 lvm 管理,建议使用 ceph-volume 进行操作

    4.1 使用 ceph-disk 删除 OSD(待验证)

    官方文档链接:DESTROY

    ceph-disk destroy [PATH]
    
    • 1

    销毁 Ceph OSD 。它会从集群、 crushmap 删除 OSD ,并回收 OSD ID 。它只能销毁处于 down 状态的 OSD 。[PATH] 是块设备或目录的路径。

    这个子命令还支持 --zap 选项。 --zap 选项会销毁分区表和硬盘内容。

    ceph-disk destroy [PATH] [--zap]
    
    • 1

    除了路径,你也可以指定 OSD 的 ID ,用 --destroy-by-id 选项。

    ceph-disk destroy --destroy-by-id [OSD-ID]
    
    • 1

    杀死、擦除、销毁磁盘的分区表和内容。可由 ceph-deploy 调用。这里的 [DEV] 是块设备路径。

    ceph-disk zap [DEV]
    
    • 1

    4.2 使用 ceph-disk 更换 OSD (待验证)

    官方文档链接:REPLACING AN OSD
    磁盘故障后如果需要进行更换,与移除 OSD 不同,此操作下的 OSD id 和 crushmap 保持不变。

    1. destroy 原来的 OSD
    ceph osd destroy {id} --yes-i-really-mean-it
    
    • 1
    1. zap 新 OSD 的磁盘
    ceph-disk zap /dev/sdX
    
    • 1
    1. 使用原 OSD 的 id 来 prepare 新磁盘
    ceph-disk prepare --bluestore /dev/sdX  --osd-id {id} --osd-uuid `uuidgen`
    
    • 1
    1. 激活新 OSD
    ceph-disk activate /dev/sdX1
    
    • 1
    1. 启动 OSD 服务
    systemctl start ceph-osd@{osd-num}
    
    • 1

    5. ceph-volume 操作

    从 Ceph 12 版本开始引入 ceph-volume 代替 ceph-disk。故此方法只能在 Ceph 12 及之后版本使用。
    新版本的 OSD 磁盘管理官方建议使用了 lvm (官方文档说明),因此只能使用 ceph-volume 命令

    5.1 使用 ceph-volume 删除 OSD

    官方文档链接:REMOVING DEVICES

    5.2 使用 ceph-volume 更换 OSD

    官方文档链接:REPLACING AN OSD
    参考文档链接:Luminous下删除和新建OSD的正确姿势

    1. 确认待更换 OSD 的服务可以安全停止,如果此 OSD 的服务已经停止,则可以跳过第一步:
    ceph osd ok-to-stop osd.{id}
    
    • 1

    如果可以安全停止,则会有如下提示:

    [root@node1 ~]# ceph osd ok-to-stop osd.15
    OSD(s) 15 are ok to stop without reducing availability, provided there are no other concurrent failures or interventions. 0 PGs are likely to be degraded (but remain available) as a result.
    
    • 1
    • 2

    登录故障 OSD 所在的节点,停止对应 OSD 的服务:

    systemctl stop ceph-osd@{id}.service
    
    • 1
    1. 确认此 OSD 可以安全 destroy:
    while ! ceph osd safe-to-destroy osd.{id} ; do sleep 10 ; done
    
    • 1

    如果可以安全 destroy,则会有如下提示:

    [root@node1 ~]# while ! ceph osd safe-to-destroy osd.15 ; do sleep 10 ; done     
    OSD(s) 15 are safe to destroy without reducing data durability.
    
    • 1
    • 2
    1. Destroy 对应的 OSD,此操作只会删除 ceph auth 中的认证信息 :
    ceph osd destroy {id} --yes-i-really-mean-it
    
    • 1

    此时此 OSD 的状态变为 destroyed,但仍然占用着 OSD id,示例如下:

    [root@node1 ~]# ceph osd destroy 15  --yes-i-really-mean-it
    destroyed osd.15
    [root@node1 ~]# ceph osd tree
    ID  CLASS WEIGHT    TYPE NAME               STATUS    REWEIGHT PRI-AFF 
     -1       270.07278 root default                                       
    ......
     -3        54.01456     host node1                            
     10   hdd   5.36510         osd.10                 up  1.00000 1.00000 
     15   hdd   5.36510         osd.15          destroyed        0 1.00000 
     20   hdd   5.36510         osd.20                 up  1.00000 1.00000 
     25   hdd   5.36510         osd.25                 up  1.00000 1.00000 
    ......
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    1. 如果待更换的磁盘之前使用过,需要先进行 zap 操作。如果是全新磁盘,则可以跳过此步骤。zap 操作可以清除对应的磁盘或者分区:
    ceph-volume lvm zap /dev/sdX --destroy
    
    • 1

    备注:添加 --destroy 参数,可以彻底删除 lvm 和磁盘分区信息,将磁盘还原成裸盘状态,详细介绍见官网链接:REMOVING DEVICES

    1. 如果使用了 filestore,并且 datajournal 在同一块磁盘上,则按照集群之前的磁盘分区方式,对新磁盘进行格式化并分区,示例如下:
    # 首先创建了一个 100GB 的 journal 分区,然后将剩余空间全部划分给 data 分区
    [root@node1 ~]# parted -s /dev/sdn "mklabel gpt" 
    [root@node1 ~]# parted -s /dev/sdn "mkpart primary 1 100g" 
    [root@node1 ~]# parted -s /dev/sdn "mkpart xfs 100g -1"
    [root@node1 ~]# partprobe
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1. 登录故障 OSD 所在节点,卸载对应 OSD 的挂载目录。如果使用了 filestore,且 journal 在单独的磁盘分区上,则需要记录下故障 OSD 此时正在使用的 journal 分区指向的分区信息,方便下一步添加磁盘时重新利用:
    umount /var/lib/ceph/osd/ceph-{id}
    
    • 1
    1. 使用之前的 OSD id 一步到位创建 OSD,--osd-id {id} 参数不能省略,否则不会使用老的 OSD id。在创建 OSD 之前,也可以先将老 OSD 挂载目录进行卸载,防止创建命令执行出错。
    # 使用 bluestore 格式化磁盘
    ceph-volume lvm create --osd-id {id} --data /dev/sdX [--crush-device-class type]
    # 使用 filestore 格式化磁盘,journal 选择磁盘分区
    ceph-volume lvm create --osd-id {id} --filestore --data /dev/sdX --journal /dev/sdY1 [--crush-device-class type]
    # 使用 filestore 格式化磁盘,journal 选择 lvm 卷
    ceph-volume lvm create --osd-id {id} --filestore --data /dev/sdX --journal VG_NAME/LV_NAME [--crush-device-class type]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    备注:
    ceph-volume 默认采用 bluestore,如果使用的是 filestore,则需要添加对应的参数,详细介绍见官网链接ceph-volume

    举例如下,可以看到此命令详细的执行过程:

    [root@node1 ~]# ceph-volume lvm create --osd-id 15 --filestore --data /dev/sdn2 --journal /dev/sdn1 --crush-device-class hdd 
    Running command: /bin/ceph-authtool --gen-print-key
    Running command: /bin/ceph --cluster ceph --name client.bootstrap-osd --keyring /var/lib/ceph/bootstrap-osd/ceph.keyring osd tree -f json
    Running command: /bin/ceph --cluster ceph --name client.bootstrap-osd --keyring /var/lib/ceph/bootstrap-osd/ceph.keyring -i - osd new 0a2320ad-f0aa-4550-a5c5-3cc73e021f23 15
    Running command: vgcreate --force --yes ceph-4eeeceac-d37a-4c66-abdc-400ed4f15762 /dev/sdn2
     stderr: /dev/ceph-a40f8c46-03ea-4546-8140-94c27866a2db/osd-data-0e29fbba-6b9a-48e7-a99b-de7c87c14cd2: read failed after 0 of 4096 at 0: Input/output error
      /dev/ceph-a40f8c46-03ea-4546-8140-94c27866a2db/osd-data-0e29fbba-6b9a-48e7-a99b-de7c87c14cd2: read failed after 0 of 4096 at 5901171752960: Input/output error
      /dev/ceph-a40f8c46-03ea-4546-8140-94c27866a2db/osd-data-0e29fbba-6b9a-48e7-a99b-de7c87c14cd2: read failed after 0 of 4096 at 5901171810304: Input/output error
      /dev/ceph-a40f8c46-03ea-4546-8140-94c27866a2db/osd-data-0e29fbba-6b9a-48e7-a99b-de7c87c14cd2: read failed after 0 of 4096 at 4096: Input/output error
     stdout: Physical volume "/dev/sdn2" successfully created.
     stdout: Volume group "ceph-4eeeceac-d37a-4c66-abdc-400ed4f15762" successfully created
    Running command: lvcreate --yes -l 100%FREE -n osd-data-0a2320ad-f0aa-4550-a5c5-3cc73e021f23 ceph-4eeeceac-d37a-4c66-abdc-400ed4f15762
     stdout: Logical volume "osd-data-0a2320ad-f0aa-4550-a5c5-3cc73e021f23" created.
    Running command: /bin/ceph-authtool --gen-print-key
    Running command: mkfs -t xfs -f -i size=2048 /dev/ceph-4eeeceac-d37a-4c66-abdc-400ed4f15762/osd-data-0a2320ad-f0aa-4550-a5c5-3cc73e021f23
     stdout: meta-data=/dev/ceph-4eeeceac-d37a-4c66-abdc-400ed4f15762/osd-data-0a2320ad-f0aa-4550-a5c5-3cc73e021f23 isize=2048   agcount=6, agsize=268435455 blks
             =                       sectsz=4096  attr=2, projid32bit=1
             =                       crc=1        finobt=0, sparse=0
    data     =                       bsize=4096   blocks=1440715776, imaxpct=5
             =                       sunit=0      swidth=0 blks
    naming   =version 2              bsize=4096   ascii-ci=0 ftype=1
    log      =internal log           bsize=4096   blocks=521728, version=2
             =                       sectsz=4096  sunit=1 blks, lazy-count=1
    realtime =none                   extsz=4096   blocks=0, rtextents=0
    Running command: mount -t xfs -o noatime,largeio,inode64,swalloc /dev/ceph-4eeeceac-d37a-4c66-abdc-400ed4f15762/osd-data-0a2320ad-f0aa-4550-a5c5-3cc73e021f23 /var/lib/ceph/osd/ceph-15
    Running command: restorecon /var/lib/ceph/osd/ceph-15
    Running command: chown -R ceph:ceph /dev/sdn1
    Running command: ln -s /dev/sdn1 /var/lib/ceph/osd/ceph-15/journal
    Running command: ceph --cluster ceph --name client.bootstrap-osd --keyring /var/lib/ceph/bootstrap-osd/ceph.keyring mon getmap -o /var/lib/ceph/osd/ceph-15/activate.monmap
     stderr: got monmap epoch 1
    Running command: chown -h ceph:ceph /var/lib/ceph/osd/ceph-15/journal
    Running command: chown -R ceph:ceph /dev/sdn1
    Running command: chown -R ceph:ceph /var/lib/ceph/osd/ceph-15/
    Running command: /bin/ceph-osd --cluster ceph --osd-objectstore filestore --mkfs -i 15 --monmap /var/lib/ceph/osd/ceph-15/activate.monmap --osd-data /var/lib/ceph/osd/ceph-15/ --osd-journal /var/lib/ceph/osd/ceph-15/journal --osd-uuid 0a2320ad-f0aa-4550-a5c5-3cc73e021f23 --setuser ceph --setgroup ceph
     stderr: 2019-07-22 20:35:03.257346 7fa414a5fd40 -1 journal check: ondisk fsid 00000000-0000-0000-0000-000000000000 doesn't match expected 0a2320ad-f0aa-4550-a5c5-3cc73e021f23, invalid (someone else's?) journal
     stderr: 2019-07-22 20:35:03.458202 7fa414a5fd40 -1 journal do_read_entry(4096): bad header magic
     stderr: 2019-07-22 20:35:03.458210 7fa414a5fd40 -1 journal do_read_entry(4096): bad header magic
     stderr: 2019-07-22 20:35:03.459389 7fa414a5fd40 -1 read_settings error reading settings: (2) No such file or directory
     stderr: 2019-07-22 20:35:03.777120 7fa414a5fd40 -1 created object store /var/lib/ceph/osd/ceph-15/ for osd.15 fsid 9548fe54-9f1a-4a3b-b620-00406d602ef3
    Running command: ceph-authtool /var/lib/ceph/osd/ceph-15/keyring --create-keyring --name osd.15 --add-key AQBnrTVdeRceAhAAoxAa9mcZ/3b7vTtBrd9MJA==
     stdout: creating /var/lib/ceph/osd/ceph-15/keyring
     stdout: added entity osd.15 auth auth(auid = 18446744073709551615 key=AQBnrTVdeRceAhAAoxAa9mcZ/3b7vTtBrd9MJA== with 0 caps)
    Running command: chown -R ceph:ceph /var/lib/ceph/osd/ceph-15/keyring
    --> ceph-volume lvm prepare successful for: /dev/sdn2
    Running command: chown -R ceph:ceph /var/lib/ceph/osd/ceph-15
    Running command: ln -snf /dev/sdn1 /var/lib/ceph/osd/ceph-15/journal
    Running command: chown -R ceph:ceph /dev/sdn1
    Running command: systemctl enable ceph-volume@lvm-15-0a2320ad-f0aa-4550-a5c5-3cc73e021f23
     stderr: Created symlink from /etc/systemd/system/multi-user.target.wants/ceph-volume@lvm-15-0a2320ad-f0aa-4550-a5c5-3cc73e021f23.service to /usr/lib/systemd/system/ceph-volume@.service.
    Running command: systemctl enable --runtime ceph-osd@15
     stderr: Created symlink from /run/systemd/system/ceph-osd.target.wants/ceph-osd@15.service to /usr/lib/systemd/system/ceph-osd@.service.
    Running command: systemctl start ceph-osd@15
    --> ceph-volume lvm activate successful for osd ID: 15
    --> ceph-volume lvm create successful for: /dev/sdn2
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    备注:
    ceph-volume lvm create 命令是调用两个子命令执行,官网介绍如下:
    Prepare the disk for replacement by using the previously destroyed OSD id:
    ceph-volume lvm prepare --osd-id {id} --data /dev/sdX
    And activate the OSD:
    ceph-volume lvm activate {id} {fsid}

    5.3 使用 ceph-volume 添加 OSD

    官网文档链接:ADDING OSDS

  • 相关阅读:
    SQLite库使用小结
    CSS 伪类选择器 last-child 和 last-of-type 的区别
    Rust swap
    SpringBoot 条件注解之:自定义条件注解
    营销科学中的边际ROI计量随笔记
    Javascript——处理字符串单引号
    EPPlus库的安装和使用 C# 中 Excel的导入和导出
    Python吴恩达深度学习作业20 -- 用LSTM网络创作一首爵士小歌
    高可用containerd搭建K8s集群【v1.25】
    Vue项目流程7,交易页面,提交订单,支付页面,利用element UI 以及 QRCode 完成微信支付,弹出框按钮的相关工作,个人中心以及子路由我的订单
  • 原文地址:https://blog.csdn.net/zzboat0422/article/details/95618592