高级运维学习(十一)块存储、文件系统存储和对象存储的实现

块存储基础

  • 块设备存取数据时,可以一次存取很多。字符设备只能是字符流
[root@ceph1 ~]# ll /dev/sda
brw-rw---- 1 root disk 8, 0 Dec 12 13:15 /dev/sda
# b表示block,块设备

[root@ceph1 ~]# ll /dev/tty
crw-rw-rw- 1 root tty 5, 0 Dec 12 13:31 /dev/tty
# c表示character,字符设备
  • 块存储,就是可以提供像硬盘一样的设备。使用块存储的节点,第一次连接块设备,需要对块设备进行分区、格式化,然后挂载使用。
  • ceph中的块设备叫做rbd,是rados block device的简写,表示ceph的块设备。rados是Reliable, Autonomic Distributed Object Store的简写,意思是“可靠、自主的分布式对象存储”。
  • Ceph块设备采用精简配置,可调整大小,并将数据存储在多个OSD上。
  • ceph提供存储时,需要使用存储池。为了给客户端提供存储资源,需要创建名为存储池的容器。存储池类似于逻辑卷管理中的卷组。卷组中包含很多硬盘和分区;存储池中包含各节点上的硬盘。
  • 查看基础存储池信息
# 查看存储池。默认有一个名为.mgr的存储池,编号为1
[ceph: root@ceph1 /]# ceph osd lspools
1 .mgr

# 查看存储详细使用情况
[ceph: root@ceph1 /]# ceph df
--- RAW STORAGE ---
CLASS     SIZE    AVAIL     USED  RAW USED  %RAW USED
hdd    180 GiB  180 GiB  187 MiB   187 MiB       0.10
TOTAL  180 GiB  180 GiB  187 MiB   187 MiB       0.10
 
--- POOLS ---
POOL  ID  PGS   STORED  OBJECTS     USED  %USED  MAX AVAIL
.mgr   1    1  449 KiB        2  449 KiB      0     57 GiB

# 查看.mgr存储池的副本数量
[ceph: root@ceph1 /]# ceph osd pool get .mgr size
size: 3
  • 在ceph中,操作块存储的命令也是rbd。
  • 创建存储池。如果不指定操作哪一个存储池,rbd命令将会操作名为rbd的存储池。该存储池不存在,需要自己创建。
# 不指定存储池名字执行查看操作。提示名为rbd的存储池不存在
[ceph: root@ceph1 /]# rbd ls
rbd: error opening default pool 'rbd'
Ensure that the default pool has been created or specify an alternate pool name.
rbd: listing images failed: (2) No such file or directory

存储池

  • 创建存储池是需要指定存储池中PG的数量。
  • Placement Group简称PG,可翻译为归置组。
  • PG只是一个组而已,用于把存储的对象分组管理。
  • 将数据放入集群时,对象被映射到pg,而这些pg被映射到OSD。这减少了我们需要跟踪的每个对象的元数据的数量以及我们需要运行的进程的数量。
  • 创建并使用存储池
# 1. 创建名为rbd的存储池
[ceph: root@ceph1 /]# ceph osd pool create rbd 100
pool 'rbd' created

# 2. 设置rbd存储池的应用类型是rbd。还可以是rgw或cephfs
# 语法:ceph osd pool application enable <pool-name> <app-name>
[ceph: root@ceph1 /]# ceph osd pool application enable rbd rbd

# 3. 查看
[ceph: root@ceph1 /]# ceph osd pool ls 
.mgr
rbd
[ceph: root@ceph1 /]# ceph df
--- RAW STORAGE ---
CLASS     SIZE    AVAIL     USED  RAW USED  %RAW USED
hdd    180 GiB  180 GiB  191 MiB   191 MiB       0.10
TOTAL  180 GiB  180 GiB  191 MiB   191 MiB       0.10
 
--- POOLS ---
POOL  ID  PGS   STORED  OBJECTS     USED  %USED  MAX AVAIL
.mgr   1    1  897 KiB        2  2.6 MiB      0     57 GiB
rbd    2   99      0 B        0      0 B      0     57 GiB

# 4. 执行命令。不指定存储池,默认操作名为rbd的存储池。
[ceph: root@ceph1 /]# rbd ls   # 无输出内容,也不会报错

镜像

  • 在存储池中划分空间提供给客户端作为硬盘使用。
  • 划分出来的空间,术语叫做镜像。
# 1. 查看rbd存储池中有哪些镜像
[ceph: root@ceph1 /]# rbd ls

# 2. 创建名为img1的镜像,大小10GB
[ceph: root@ceph1 /]# rbd create img1 --size 10G

# 3. 查看存储池中有哪些镜像
[ceph: root@ceph1 /]# rbd ls
img1

# 4. 查看镜像详情
[ceph: root@ceph1 /]# rbd info img1
rbd image 'img1':
        size 10 GiB in 2560 objects
...略...

# 5. 扩容。容量只是承诺大小,并不会立即分配全部空间,所以值可以超过总容量。
[ceph: root@ceph1 /]# rbd resize img1 --size 200G
Resizing image: 100% complete...done.
[ceph: root@ceph1 /]# rbd info img1
rbd image 'img1':
        size 200 GiB in 51200 objects
...略...

# 6. 删除镜像
[ceph: root@ceph1 /]# rbd rm img1
Removing image: 100% complete...done.

ceph客户端

  • 客户端使用ceph块存储需要解决的问题:

    • 怎么用?装软件
    • ceph集群在哪?通过配置文件说明集群地址
    • 权限。keyring文件
# 1. 拷贝/linux-soft/s2/zzg/ceph_soft/cephclient-rpm/目录内所有rpm包到pubserver的/var/ftp/rpms目录
# 2. 更新yum仓库
[root@pubserver ~]# createrepo /var/ftp/rpms/

# 3. 安装ceph客户端软件
[root@client1 ~]# yum install -y ceph-common

# 4. 将ceph1上的配置文件和密钥keyring文件拷贝给客户端
[root@ceph1 ceph_soft]# scp /etc/ceph/ceph.client.admin.keyring /etc/ceph/ceph.conf 192.168.88.10:/etc/ceph/

# 5. 在客户端验证是否可以操作ceph
[root@client1 ~]# rbd create img1 --size 10G
[root@client1 ~]# rbd ls
img1
[root@client1 ~]# rbd info img1
rbd image 'img1':
        size 10 GiB in 2560 objects
...略...

# 6. 将ceph镜像映射为本地硬盘
[root@client1 ~]# rbd map img1
/dev/rbd0    # rbd为固定名称,0是编号

# 7. 查看
[root@client1 ~]# lsblk
NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
sda      8:0    0   60G  0 disk 
└─sda1   8:1    0   60G  0 part /
sr0     11:0    1 10.5G  0 rom  
rbd0   253:0    0   10G  0 disk   # rbd0来自于ceph镜像

[root@client1 ~]# rbd showmapped  # 镜像img1映射为了本地硬盘rbd0
id  pool  namespace  image  snap  device   
0   rbd              img1   -     /dev/rbd0

# 8. 应用
[root@client1 ~]# mkdir /data
[root@client1 ~]# mkfs.xfs /dev/rbd0
[root@client1 ~]# mount /dev/rbd0 /data/
[root@client1 ~]# df -h /data/
Filesystem      Size  Used Avail Use% Mounted on
/dev/rbd0        10G  105M  9.9G   2% /data
[root@client1 ~]# cp /etc/hosts /data/
[root@client1 ~]# ls /data/
hosts
  • 删除
# 查看img1的状态
[root@client1 ~]# rbd status img1

# 按以下步骤删除img1
[root@client1 ~]# umount /dev/rbd0
[root@client1 ~]# rbd unmap img1
[root@client1 ~]# rbd rm img1
Removing image: 100% complete...done.

快照

  • 快照可以保存某一时间点时的状态数据
  • 快照是映像在特定时间点的只读逻辑副本
  • 希望回到以前的一个状态,可以恢复快照
  • 使用镜像、快照综合示例
# 1. 在rbd存储池中创建10GB的镜像,名为img1
[root@client1 ~]# rbd --help   # 查看子命令
[root@client1 ~]# rbd help create  # 查看子命令create的帮助
[root@client1 ~]# rbd create img1 --size 10G
[root@client1 ~]# rbd list
img1
[root@client1 ~]# rbd info img1
rbd image 'img1':
        size 10 GiB in 2560 objects
        order 22 (4 MiB objects)
        snapshot_count: 0
        id: fa91208bfdaf
        block_name_prefix: rbd_data.fa91208bfdaf
        format: 2
        features: layering, exclusive-lock, object-map, fast-diff, deep-flatten
        op_features: 
        flags: 
        create_timestamp: Sat Dec 17 10:44:17 2022
        access_timestamp: Sat Dec 17 10:44:17 2022
        modify_timestamp: Sat Dec 17 10:44:17 2022


# 2. 在客户端使用镜像img1,将其挂载到/mnt
[root@client1 ~]# rbd list
img1
[root@client1 ~]# rbd map img1
/dev/rbd0
[root@client1 ~]# mkfs.xfs /dev/rbd0
[root@client1 ~]# mount /dev/rbd0 /mnt/
[root@client1 ~]# rbd showmapped
id  pool  namespace  image  snap  device   
0   rbd              img1   -     /dev/rbd0
[root@client1 ~]# df -h /mnt/
Filesystem      Size  Used Avail Use% Mounted on
/dev/rbd0        10G  105M  9.9G   2% /mnt


# 3. 向/mnt中写入数据
[root@client1 ~]# cp /etc/hosts /mnt/
[root@client1 ~]# cp /etc/passwd /mnt/
[root@client1 ~]# ls /mnt/
hosts  passwd

# 4. 创建img1的快照,名为img1-sn1
[root@client1 ~]# rbd snap create img1 --snap img1-sn1
Creating snap: 100% complete...done.
[root@client1 ~]# rbd snap ls img1
SNAPID  NAME      SIZE    PROTECTED  TIMESTAMP               
     4  img1-sn1  10 GiB             Sat Dec 17 10:46:07 2022

# 5. 删除/mnt/中的数据
[root@client1 ~]# rm -f /mnt/*

# 6. 通过快照还原数据
[root@client1 ~]# umount /mnt/
[root@client1 ~]# rbd unmap /dev/rbd0
[root@client1 ~]# rbd help snap rollback   # 查看子命令帮助
# 回滚img1到快照img1-sn1
[root@client1 ~]# rbd snap rollback img1 --snap img1-sn1
# 重新挂载
[root@client1 ~]# rbd map img1
/dev/rbd0
[root@client1 ~]# mount /dev/rbd0 /mnt/
[root@client1 ~]# ls /mnt/   # 数据还原完成
hosts  passwd
  • 保护快照,防止删除
[root@client1 ~]# rbd help snap protect
# 保护镜像img1的快照img1-sn1
[root@client1 ~]# rbd snap protect img1 --snap img1-sn1
[root@client1 ~]# rbd snap rm img1 --snap img1-sn1   # 不能删
  • 删除操作
# 1. 取消对快照的保护
[root@client1 ~]# rbd snap unprotect img1 --snap img1-sn1

# 2. 删除快照
[root@client1 ~]# rbd snap rm img1 --snap img1-sn1

# 3. 卸载块设备
[root@client1 ~]# umount /dev/rbd0

# 4. 取消映射
[root@client1 ~]# rbd unmap img1

# 5. 删除镜像
[root@client1 ~]# rbd rm img1

快照克隆

  • 不能将一个镜像同时挂载到多个节点,如果这样操作,将会损坏数据
  • 如果希望不同的节点,拥有完全相同的数据盘,可以使用克隆技术
  • 克隆是基于快照的,不能直接对镜像克隆
  • 快照必须是受保护的快照,才能克隆
  • 克隆流程

  • 给多个客户端生成数据相同的数据盘
# 1. 创建名为img2的镜像,大小10GB
[root@client1 ~]# rbd create img2 --size 10G

# 2. 向镜像中写入数据
[root@client1 ~]# rbd map img2
/dev/rbd0
[root@client1 ~]# mkfs.xfs /dev/rbd0
[root@client1 ~]# mount /dev/rbd0 /mnt/
[root@client1 ~]# for i in {1..20}
> do
> echo "Hello World $i" > /mnt/file$i.txt
> done
[root@client1 ~]# ls /mnt/
file10.txt  file15.txt  file1.txt   file5.txt
file11.txt  file16.txt  file20.txt  file6.txt
file12.txt  file17.txt  file2.txt   file7.txt
file13.txt  file18.txt  file3.txt   file8.txt
file14.txt  file19.txt  file4.txt   file9.txt

# 3. 卸载镜像
[root@client1 ~]# umount /mnt/
[root@client1 ~]# rbd unmap img2

# 4. 为img2创建名为img2-sn1快照
[root@client1 ~]# rbd snap create img2 --snap img2-sn1

# 5. 保护img2-sn1快照
[root@client1 ~]# rbd snap protect img2 --snap img2-sn1

# 6. 通过受保护的快照img2-sn1创建克隆镜像
[root@client1 ~]# rbd clone img2 --snap img2-sn1 img2-sn1-1
[root@client1 ~]# rbd clone img2 --snap img2-sn1 img2-sn1-2
# 7. 查看创建出来的、克隆的镜像
[root@client1 ~]# rbd ls
img2
img2-sn1-1
img2-sn1-2

# 8. 不同的客户端挂载不同的克隆镜像,看到的是相同的数据
[root@client1 ~]# rbd map img2-sn1-1
/dev/rbd0
[root@client1 ~]# mkdir /data
[root@client1 ~]# mount /dev/rbd0 /data
[root@client1 ~]# ls /data
file10.txt  file15.txt  file1.txt   file5.txt
file11.txt  file16.txt  file20.txt  file6.txt
file12.txt  file17.txt  file2.txt   file7.txt
file13.txt  file18.txt  file3.txt   file8.txt
file14.txt  file19.txt  file4.txt   file9.txt

[root@ceph1 ~]# yum install -y ceph-common
[root@ceph1 ~]# rbd map img2-sn1-2
/dev/rbd0
[root@ceph1 ~]# mkdir /data
[root@ceph1 ~]# mount /dev/rbd0 /data/
[root@ceph1 ~]# ls /data/
file10.txt  file15.txt  file1.txt   file5.txt
file11.txt  file16.txt  file20.txt  file6.txt
file12.txt  file17.txt  file2.txt   file7.txt
file13.txt  file18.txt  file3.txt   file8.txt
file14.txt  file19.txt  file4.txt   file9.txt
  • 查询镜像和快照
# 查看快照信息
[root@client1 ~]# rbd info img2 --snap img2-sn1
rbd image 'img2':
        size 10 GiB in 2560 objects
        order 22 (4 MiB objects)
        snapshot_count: 1
        id: d46eed84bb61
        block_name_prefix: rbd_data.d46eed84bb61
        format: 2
        features: layering, exclusive-lock, object-map, fast-diff, deep-flatten
        op_features: 
        flags: 
        create_timestamp: Sat Dec 17 10:58:05 2022
        access_timestamp: Sat Dec 17 10:58:05 2022
        modify_timestamp: Sat Dec 17 10:58:05 2022
        protected: True    # 受保护

# 查看克隆的快照
[root@client1 ~]# rbd info img2-sn1-2
rbd image 'img2-sn1-2':
        size 10 GiB in 2560 objects
        order 22 (4 MiB objects)
        snapshot_count: 0
        id: d48fe3d6559e
        block_name_prefix: rbd_data.d48fe3d6559e
        format: 2
        features: layering, exclusive-lock, object-map, fast-diff, deep-flatten
        op_features: 
        flags: 
        create_timestamp: Sat Dec 17 10:59:53 2022
        access_timestamp: Sat Dec 17 10:59:53 2022
        modify_timestamp: Sat Dec 17 10:59:53 2022
        parent: rbd/img2@img2-sn1  # 父对象是rbd池中img2镜像的img2-sn1快照
        overlap: 10 GiB
  • 合并父子镜像

    • img2-sn1-2是基于img2的快照克隆来的,不能独立使用。
    • 如果父镜像删除了,子镜像也无法使用。
    • 将父镜像内容合并到子镜像中,子镜像就可以独立使用了。
# 把img2的数据合并到子镜像img2-sn1-2中
[root@client1 ~]# rbd flatten img2-sn1-2
# 查看状态,它就没有父镜像了
[root@client1 ~]# rbd info img2-sn1-2
rbd image 'img2-sn1-2':
        size 10 GiB in 2560 objects
        order 22 (4 MiB objects)
        snapshot_count: 0
        id: d48fe3d6559e
        block_name_prefix: rbd_data.d48fe3d6559e
        format: 2
        features: layering, exclusive-lock, object-map, fast-diff, deep-flatten
        op_features: 
        flags: 
        create_timestamp: Sat Dec 17 10:59:53 2022
        access_timestamp: Sat Dec 17 10:59:53 2022
        modify_timestamp: Sat Dec 17 10:59:53 2022

# 删除父镜像,如果镜像正在被使用,则先取消
[root@client1 ~]# umount /data/
[root@client1 ~]# rbd unmap img2-sn1-1
# 1. 删除镜像img2-sn1-1
[root@client1 ~]# rbd rm img2-sn1-1
# 2. 取消img2-sn1的保护
[root@client1 ~]# rbd snap unprotect img2 --snap img2-sn1
# 3. 删除img2-sn1快照
[root@client1 ~]# rbd snap rm img2 --snap img2-sn1
# 4. 删除img2
[root@client1 ~]# rbd rm img2

# 因为img2-sn1-2已经是独立的镜像了,所以它还可以使用
# ceph1上的镜像没有受到影响
[root@ceph1 ~]# cat /data/file1.txt 
Hello World 1

开机自动挂载

# 1. 准备镜像
[root@client1 ~]# rbd create img1 --size 10G
[root@client1 ~]# rbd map img1
/dev/rbd0
[root@client1 ~]# mkfs.xfs /dev/rbd0

# 2. 设置开机自动挂载
[root@client1 ~]# vim /etc/ceph/rbdmap  # 指定要挂载的镜像及用户名、密钥
rbd/img1        id=admin,keyring=/etc/ceph/ceph.client.admin.keyring
[root@client1 ~]# vim /etc/fstab  # 追加
/dev/rbd/rbd/img1   /data   xfs    noauto  0  0
# noauto的意思是,等rbdmap服务启动后,再执行挂载

# 3. 启动rbdmap服务
[root@client1 ~]# systemctl enable rbdmap --now

# 4. reboot后查看结果
[root@client1 ~]# df -h /data/
Filesystem      Size  Used Avail Use% Mounted on
/dev/rbd0        10G  105M  9.9G   2% /data

ceph文件系统

  • 文件系统:相当于是组织数据存储的方式。
  • 格式化时,就是在为存储创建文件系统。
  • Linux对ceph有很好的支持,可以把ceph文件系统直接挂载到本地。
  • 要想实现文件系统的数据存储方式,需要有MDS组件

使用MDS

  • 元数据就是描述数据的属性。如属主、属组、权限等。

  • ceph文件系统中,数据和元数据是分开存储的

  • 新建存储池

    • 归置组PG:存储池包含PG。PG是一个容器,用于存储数据。
    • 为了管理方便,将数量众多的数据放到不同的PG中管理,而不是直接把所有的数据扁平化存放。
    • 通常一个存储池中创建100个PG。
  • 创建ceph文件系统

# 1. 新建一个名为data1的存储池,目的是存储数据,有100个PG
[root@client1 ~]# ceph osd pool create data01 100

# 2. 新建一个名为metadata1的存储池,目的是存储元数据
[root@client1 ~]# ceph osd pool create metadata01 100

# 3. 创建名为myfs1的cephfs,数据保存到data1中,元数据保存到metadata1中
[root@client1 ~]# ceph fs new myfs01 metadata01 data01

# 4. 查看存储池
[root@client1 ~]# ceph osd lspools
1 .mgr
2 rbd
3 data01
4 metadata01
[root@client1 ~]# ceph df
--- RAW STORAGE ---
CLASS     SIZE    AVAIL     USED  RAW USED  %RAW USED
hdd    180 GiB  180 GiB  206 MiB   206 MiB       0.11
TOTAL  180 GiB  180 GiB  206 MiB   206 MiB       0.11
 
--- POOLS ---
POOL        ID  PGS   STORED  OBJECTS     USED  %USED  MAX AVAIL
.mgr         1    1  449 KiB        2  1.3 MiB      0     57 GiB
rbd          2   32  7.1 MiB       43   22 MiB   0.01     57 GiB
data01       3   94      0 B        0      0 B      0     57 GiB
metadata01   4   94      0 B        0      0 B      0     57 GiB
    
# 5. 查看文件系统
[root@client1 ~]# ceph fs ls
name: myfs01, metadata pool: metadata01, data pools: [data01 ]

# 6. 启动MDS服务
[root@client1 ~]# ceph orch apply mds myfs01 --placement="2 ceph1 ceph2"

# 7. 查看部署结果
[root@client1 ~]# ceph -s
  cluster:
    id:     a4b69ab4-79dd-11ed-ae7b-000c2953b002
    health: HEALTH_OK
 
  services:
    mon: 3 daemons, quorum ceph1,ceph3,ceph2 (age 92m)
    mgr: ceph1.gmqorm(active, since 92m), standbys: ceph3.giqaph
    mds: 1/1 daemons up, 1 standby  # mds服务信息
    osd: 9 osds: 9 up (since 92m), 9 in (since 4d)
...略...
  • 客户端使用cephfs
# 挂载文件系统需要密码。查看密码
[root@client1 ~]# cat /etc/ceph/ceph.client.admin.keyring 
[client.admin]
    key = AQBmhINh1IZjHBAAvgk8m/FhyLiH4DCCrnrdPQ==

# -t 指定文件系统类型。-o是选项,提供用户名和密码
[root@client1 ~]# mkdir /mydata
[root@client1 ~]# mount.ceph 192.168.88.13:/ /mydata -o name=admin,secret=AQC5u5ZjnTA1ERAAruLAI8F1W1nyOgxZSx0UXw== 
[root@client1 ~]# df -h /mydata/
Filesystem       Size  Used Avail Use% Mounted on
192.168.88.13:/   57G     0   57G   0% /mydata

对象存储

配置服务器端

  • 需要专门的客户端访问
  • 键值对存储方式
  • 对象存储需要rgw组件
  • 安装部署
# 1. 在ceph1/ceph2上部署rgw服务,名为myrgw
[root@client1 ~]# ceph orch apply rgw myrgw --placement="2 ceph1 ceph2" --port 8080
 
[root@client1 ~]# ceph -s
  cluster:
    id:     a4b69ab4-79dd-11ed-ae7b-000c2953b002
    health: HEALTH_OK
 
  services:
    mon: 3 daemons, quorum ceph1,ceph3,ceph2 (age 101m)
    mgr: ceph1.gmqorm(active, since 6h), standbys: ceph3.giqaph
    mds: 1/1 daemons up, 1 standby
    osd: 9 osds: 9 up (since 6h), 9 in (since 5d); 1 remapped pgs
    rgw: 2 daemons active (2 hosts, 1 zones)  # rgw信息
...略...

配置客户端

  • ceph对象存储提供了一个与亚马逊S3(Amazon Simple Storage Service)兼容的接口
  • 在S3中,对象被存储在一个称作桶(bucket)的器皿中。这就好像是本地文件存储在目录中一样。
# 1. 安装amazon S3 cli工具(客户端工具)
[root@client1 ~]# yum install -y awscli

# 2. 在ceph中创建一个用户
[root@client1 ~]# radosgw-admin user create --uid=testuser --display-name="Test User" --email=test@tedu.cn --access-key=12345 --secret=67890

# 3. 初始化客户端
[root@client1 ~]# aws configure --profile=ceph
AWS Access Key ID [None]: 12345
AWS Secret Access Key [None]: 67890
Default region name [None]:   # 回车
Default output format [None]: # 回车

# 4. 创建名为testbucket的bucket,用于存储数据
[root@client1 ~]# vim /etc/hosts  # 添加以下内容
192.168.88.11 ceph1
192.168.88.12 ceph2
192.168.88.13 ceph3
[root@client1 ~]# aws --profile=ceph --endpoint=http://ceph1:8080 s3 mb s3://testbucket

# 5. 上传文件
[root@client1 ~]# aws --profile=ceph --endpoint=http://ceph1:8080 --acl=public-read-write s3 cp /etc/hosts s3://testbucket/hosts.txt

# 6. 查看bucket中的数据
[root@client1 ~]# aws --profile=ceph --endpoint=http://ceph1:8080 s3 ls s3://testbucket
2022-12-17 17:05:58        241 hosts.txt

# 7. 下载数据
[root@client1 ~]# wget -O zhuji http://ceph1:8080/testbucket/hosts.txt

访问Dashborad

  • 通过浏览器访问https://192.168.88.11:8443,用户名为admin,密码是安装时指定的123456。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值