kubernetes配置glusterfs动态存储
GlusterFS分布式文件系统
一.简单介绍
分布式存储按其存储接口分为三种:文件存储、块存储、对象存储
- 文件存储
通常支持POSIX接口(如glusterFS,但GFS、HDFS是非POSIX接口的),可以像普通文件系统(如ext4)那样访问,但又比普通文件系统多了并行化访问的能力和冗余机制。主要的分布式存储系统有TFS、CephFS、GlusterFS和HDFS等。主要存储非结构化数据,如普通文件,图片,音视频等。
- 块存储
这种接口通常以QEMU Driver或者Kernel Module的方式存在,主要通过qemu或iscsi协议访问。主要的块存储系统有Ceph块存储、SheepDog等。主要用来存储结构化数据,如数据库数据。
- 对象存储
对象存储系统综合了文件系统(NAS)和块存储(SAN)的优点,同时具有SAN的高速直接访问和NAS的数据共享等优势。以对象作为基本的存储单元,向外提供Restful数据读写接口,常以网络服务的形式提供数据访问。主要的对象存储系统有AWS、Swift和Ceph对象存储。主要用来存储费结构化数据
GlusterFS是Scale-out存储解决方案Gluster的核心,它是一个开源的分布式文件系统,具有强大的横向扩展能力,通过扩展能够支持数PB存储容量和处理数千客户端。GlusterFS借助TCP/IP或者InfiniBandRDMA网络将物理分布的存储资源聚集在一起,具有高扩展性、高可用性、高性能的特点,并且没有元数据服务器的设计,让整个服务没有单点故障的隐患,使用单一全局命名空间来管理数据。
二.GlusterFS架构
三.GlusterFS特点
- 优势:
- 扩展性和高性能
GlusterFS采用无中心对称式结构,没有专用的元数据服务器,也就不存在元数据服务器瓶颈。元数据存在于文件的属性和扩展属性中,当需要访问某文件时,客户端使用DHT算法,根据文件的路径和文件名计算出文件所在的brick,然后由客户端从此brick获取数据,省去了同元数据服务器通信的过程。 - 良好的可扩展性
使用弹性hash算法替代传统的有元数据节点服务,获得了接近线性的高扩展性 - 高可用
采用副本、EC等冗余设计,保证在冗余范围内的节点掉线时,任然可以从其它服务器节点获取数据,保证高可用性。采用弱一致性的设计,当向副本中文件写入数据时,客户端计算出文件所在brick,然后通过网络把数据传给所在brick,当其中有一个成功返回,就认为数据成功写入,不必等待其它brick返回,就会避免当某个节点网络异常或磁盘损坏时,因为一个brick没有成功写入而导致写操作等待。
服务器端还会随着存储池的启动,而开启一个glustershd进程,这个进程会定期检查副本和EC卷中各个brick之间数据的一致性,并恢复。 - 存储池类型丰富
包括粗粒度、条带、副本、条带副本和EC,可以根据用户的需求,满足不同程度的冗余。
粗粒度不带任何冗余,文件不进行切片,是完整的存放在某个brick上。
条带卷不带任何冗余,文件会切片存储(默认大小为128kB)在不同的brick上,这些切片可以并发读写(并发粒度是条带块),可以明显提高读写性能。该模式一般只适合用于处理超大型文件和多节点性能要求高的情况。
副本卷冗余度高,副本数量可以灵活配置,可以保证数据的安全性
条带副本卷是条带卷和副本卷的结合
EC卷使用EC校验算法,提供了低于副本卷的冗余度,冗余度小于100%,满足比较低的数据安全性,例如可以使2+1(冗余度为50%),5+3(冗余度为60%)等。这个可以满足安全性要求不高的数据
- 缺点:
- 扩容、缩容时影响的服务器较多
GlusterFS对逻辑卷中的存储单元brick划分hash分布式空间(会以brick所在磁盘大小作为权重,空间总范围为0~2**32-1),一个brick占一份空间,当访问某文件时,使用Davies-Meyer算法根据文件名计算出hash值,比较hash值落在哪个范围内,即可确认文件所在的brick,这样定位文件会很快。但是在向逻辑卷中添加或移除brick时,hash分布空间会重新计算,每个brick的hash范围都会变化,文件定位就会失败,因此需要遍历文件,把文件移动到正确的hash分布范围对应的brick上,移动的文件可能会很多,加载系统负载,影响到正常的文件访问操作。 - 遍历目录下文件耗时
GlusterFS没有元数据节点,而是根据hash算法来确定文件的分布,目录利用扩展属性记录子卷中的brick的hash分布范围,每个brick的范围均不重叠,遍历目录时,需要readdir子卷中每个brick中的目录。获取每个文件的属性和扩展属性,然后进行聚合,相对于有专门元数据节点的分布式存储,遍历效率会差很多,当目录下有大量文件时,遍历会非常缓慢。
删除目录也会遇到此问题,目前提供的解决方法是合理组织目录结构,目录层级不要太深,目录下文件数量不要太多,增大GlusterFS目录缓存,另外,还可以设计把元数据和数据分离,把元数据放到内存数据库中(如redis、memcache),并在ssd上持久保持 - 小文件性能较差
GlusterFS主要是为大文件设计,如io-cache、read-ahead、write-behind和条带等都是为优化大文件访问,对小文件的优化很少。
GlusterFS采用无元数据节点的设计,文件的元数据和数据一起保持在文件中,访问元数据和数据的速率相同。访问元数据的时间与访问数据的时间比例会较大,而有元数据中心的分布式存储系统,对元数据服务器可以采用快速的SSD盘(如ceph),可以采用更大的元数据缓存等优化措施才减少访问元数据时间与访问数据时间的比值,来提高小文件性能
四.GlusterFS的组件
-
brick
GlusterFS中的存储单元,通常是一个受信存储池中的服务器的一个导出目录。可以通过主机名和目录名来标识,如'SERVER:EXPORT'- brick是一个节点和一个导出目录的集合,e.g.node1:/brick1
- brick是底层的RAID或磁盘经XFS或ext4文件系统格式化而来,所以继承了文件系统的限制
- 每个节点上的brick数是不限的
- 理想的状况是,一个集群的所有brick大小都一样
-
Client
挂载了GlusterFS卷的设备 -
GFID
GlusterFS卷中的每个文件或目录都有一个唯一的128位的数据相关联,其用于模拟inode -
Namespace
每个Gluster卷都导出单个ns作为POSIX的挂载点 -
Node
一个拥有若干brick的设备 -
RDMA
远程直接内存访问,支持不通过双方的OS进行直接内存访问 -
RRDNS
round robin DNS是一种通过DNS轮转返回不同的设备以进行负载均衡的方法 -
Self-heal
用于后台运行监测副本卷中文件和目录的不一致性并解决这些不一致 -
Split-brain
脑裂 -
Volfile
GlusterFS进程的配置文件,通常位于/var/lib/glusterd/vols/volname -
Volume
- 是brick的逻辑组合
- 创建时命名来识别
-
Trushed Storage Pool
- 一堆存储节点的集合
- 通过一个节点邀请其它节点创建,这里叫probe
- 成员可以动态加入,动态删除
五.GlusterFS集群的部署
我们使用GlusterF作为K8S的后端存储,在使用动态存储StorageClass时,集群至少需要配置三节点,如果GlusterFS集群少于三节点,在创建volume时会提示Failed to allocate new volume: No space报错
这里我们采用的集群节点如下:
节点hostname 节点IP 数据盘
glusterfs-01 192.168.1.123 /dev/sdb,/dev/sdc
glusterfs-02 192.168.1.125 /dev/sdb,/dev/sdc
glusterfs-03 192.168.1.127 /dev/sdb
5.1 部署配置GlusterFS集群
1)所有节点安装gluster
[root@glusterfs-01 ~]#yum install centos-release-gluster -y
[root@glusterfs-01 ~]#yum install -y glusterfs glusterfs-server glusterfs-fuse glusterfs-rdma glusterfs-geo-replication glusterfs-devel
2)启动glusterFS
[root@glusterfs-01 ~]#systemctl start glusterd.service
[root@glusterfs-01 ~]#systemctl enable glusterd.service
[root@glusterfs-01 ~]#systemctl status glusterd.servic
3)在集群任意节点,将glusterfs的peer添加进去
[root@glusterfs-01 ~]#gluster peer probe glusterfs-01
[root@glusterfs-01 ~]#gluster peer probe glusterfs-02
[root@glusterfs-01 ~]#gluster peer probe glusterfs-03
4)格式化集群所有节点的所有数据盘,并将物理硬盘分区初始化为物理卷
[root@glusterfs-01 ~]#mkfs.xfs /dev/sdb -f
[root@glusterfs-01 ~]#pvcreate /dev/sdb
[root@glusterfs-01 ~]#mkfs.xfs /dev/sdc -f
[root@glusterfs-01 ~]#pvcreate /dev/sdc
5.2 部署配置GlusterFS的动态存储StorageClass
heketi提供一个RESTful管理节点界面,可以用来管理GlusterFS卷的生命周期,通过heketi,就可以像使用Opentack Manila,kubernetes和openShift一样申请可以动态配置GlusterFS卷,Heketi会动态在集群内选择bricks构建所需的volumes,这样以确保数据的副本会分散到集群不同的故障域内。
1)所有节点安装heketi和heketi的客户端
[root@glusterfs-01 ~]#yum install -y heketi heketi-client
2)在集群主节点配置SSH密钥
[root@glusterfs-01 ~]#ssh-keygen -f /etc/heketi/heketi_key -t rsa -N ''
[root@glusterfs-01 ~]#ssh-copy-id -i /etc/heketi/heketi_key.pub root@glusterfs-01
[root@glusterfs-01 ~]#ssh-copy-id -i /etc/heketi/heketi_key.pub root@glusterfs-02
[root@glusterfs-01 ~]#ssh-copy-id -i /etc/heketi/heketi_key.pub root@glusterfs-03
[root@glusterfs-01 ~]#chown heketi:heketi /etc/heketi/heketi_key*
3)编辑修改heketi的配置文件/etc/heketi/heketi.json
heketi有三种executor,分别为mock,ssh,kubernetes,建议在测试环境使用mock,生成环境使用ssh,当glusterfs以容器的方式部署在Kubernetes上时,才是用kubernetes。
[root@glusterfs-01 ~]# cat /etc/heketi/heketi.json
{
"_port_comment": "Heketi Server Port Number",
#这里端口默认为8080,如果在实际的使用过程中,可以修改此端口号
"port": "8080",
"_use_auth": "Enable JWT authorization. Please enable for deployment",
"use_auth": false,
"_jwt": "Private keys for access",
"jwt": {
"_admin": "Admin has access to all APIs",
"admin": {
"key": "My Secret"
},
"_user": "User only has access to /volumes endpoint",
"user": {
"key": "My Secret"
}
},
"_glusterfs_comment": "GlusterFS Configuration",
"glusterfs": {
"_executor_comment": [
"Execute plugin. Possible choices: mock, ssh",
"mock: This setting is used for testing and development.",
" It will not send commands to any node.",
"ssh: This setting will notify Heketi to ssh to the nodes.",
" It will need the values in sshexec to be configured.",
"kubernetes: Communicate with GlusterFS containers over",
" Kubernetes exec api."
],
#修改执行插件为ssh
"executor": "ssh",
#配置ssh所需的证书
"_sshexec_comment": "SSH username and private key file information",
"sshexec": {
"keyfile": "/etc/heketi/heketi_key",
"user": "root",
"port": "22",
"fstab": "/etc/fstab"
},
"_kubeexec_comment": "Kubernetes configuration",
"kubeexec": {
"host" :"https://kubernetes.host:8443",
"cert" : "/path/to/crt.file",
"insecure": false,
"user": "kubernetes username",
"password": "password for kubernetes user",
"namespace": "OpenShift project or Kubernetes namespace",
"fstab": "Optional: Specify fstab file on node. Default is /etc/fstab"
},
"_db_comment": "Database file name",
"db": "/var/lib/heketi/heketi.db",
"_loglevel_comment": [
"Set log level. Choices are:",
" none, critical, error, warning, info, debug",
"Default is warning"
],
"loglevel" : "debug"
}
}
4)在集群所有节点将heketi服务加入开机启动,并启动服务
[root@glusterfs-01 ~]#systemctl restart heketi
[root@glusterfs-01 ~]#systemctl enable heketi
5)测试heketi的连接
[root@glusterfs-01 ~]# curl http://glusterfs-01:8080/hello
Hello from Heketi[root@glusterfs-01 ~]#
[root@glusterfs-01 ~]# curl http://glusterfs-02:8080/hello
Hello from Heketi[root@glusterfs-01 ~]#
[root@glusterfs-01 ~]# curl http://glusterfs-03:8080/hello
Hello from Heketi[root@glusterfs-01 ~]#
6)在集群的主节点设置环境变量
注意这里的端口号要和/etc/heketi/heketi.json配置文件中的保持一致
[root@glusterfs-01 ~]#export HEKETI_CLI_SERVER=http://glusterfs-01:8080
7)在集群的主节点修改/usr/share/heketi/topology-sample.json配置文件,执行添加节点和添加device的操作
manage为GFS管理服务的Node节点主机名,storage为Node节点IP,device为Node节点上的裸设备
[root@glusterfs-01 ~]# cat /usr/share/heketi/topology-sample.json
{
"clusters": [
{
"nodes": [
{
"node": {
"hostnames": {
"manage": [
"glusterfs-01"
],
"storage": [
"192.168.1.123"
]
},
"zone": 1
},
"devices": [
{
"name": "/dev/sdb",
"destroydata": false
},
{
"name": "/dev/sdc",
"destroydata": false
}
]
},
{
"node": {
"hostnames": {
"manage": [
"glusterfs-02"
],
"storage": [
"192.168.1.125"
]
},
"zone": 1
},
"devices": [
{
"name": "/dev/sdb",
"destroydata": false
},
{
"name": "/dev/sdc",
"destroydata": false
}
]
},
{
"node": {
"hostnames": {
"manage": [
"glusterfs-03"
],
"storage": [
"192.168.1.127"
]
},
"zone": 1
},
"devices": [
{
"name": "/dev/sdb",
"destroydata": false
}
]
}
]
}
]
}
执行创建:
[root@glusterfs-01 ~]# heketi-cli topology load --json=/usr/share/heketi/topology-sample.json
Creating cluster ... ID: 1a182a5967c9b6689ac0e60f6bf037fe
Allowing file volumes on cluster.
Allowing block volumes on cluster.
Creating node glusterfs-01 ... ID: f70e977eb2a36b8ab4a5fa1f4fb5dadc
Adding device /dev/sdb ... OK
Adding device /dev/sdc ... OK
Creating node glusterfs-02 ... ID: f2b6e9a3e32e8aea3ee04aa39430a538
Adding device /dev/sdb ... OK
Adding device /dev/sdc ... OK
Creating node glusterfs-03 ... ID: f4c6e9a3e32a36b8ab39430dadc300aa3
Adding device /dev/sdb ... OK
创建一个volume测试
[root@glusterfs-01 ~]# heketi-cli volume create --size=2
Name: vol_3488b923b854a115cec5354ad9941489
Size: 2
Volume Id: 3488b923b854a115cec5354ad9941489
Cluster Id: b994b57f05d2868f12ae046caaaa33f5
Mount: 192.168.1.125:vol_3488b923b854a115cec5354ad9941489
Mount Options: backup-volfile-servers=192.168.1.123,192.168.1.127
Block: false
Free Size: 0
Reserved Size: 0
Block Hosting Restriction: (none)
Block Volumes: []
Durability Type: replicate
Distributed+Replica: 3
查看volume的信息:
[root@glusterfs-01 ~]# gluster volume info
Volume Name: vol_3488b923b854a115cec5354ad9941489
Type: Replicate
Volume ID: dd8d5556-f74b-4757-9678-3736f8a50540
Status: Started
Snapshot Count: 0
Number of Bricks: 1 x 3 = 3
Transport-type: tcp
Bricks:
Brick1: 192.168.1.125:/var/lib/heketi/mounts/vg_114274eb7da9d241080e09673f6b4920/brick_06260836e395cfb530f5fe3470681e7b/brick
Brick2: 192.168.1.123:/var/lib/heketi/mounts/vg_671bf19791b555b967c1537bf3c383c5/brick_f118bb0aca3f85eef02725041411c9f3/brick
Brick3: 192.168.1.127:/var/lib/heketi/mounts/vg_98733e5faf54c9c023d928d1858f1aaf/brick_b6e86351244a8e028f3838e90b51ab9e/brick
Options Reconfigured:
transport.address-family: inet
nfs.disable: on
performance.client-io-threads: off
8)配置StorageClass
创建glusterfs-storageclass.yaml文件内容如下:
[root@glusterfs-01 ~]# cat glusterfs-storageclass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: gluster-dynmic
provisioner: kubernetes.io/glusterfs
parameters:
#这里根据配置的环境变量指定主机的hostname或者ip都可
resturl: http://192.168.1.123:8080
restauthenabled: "false"
执行storageclass的创建
[root@glusterfs-01 ~]# kubectl create -f glusterfs-storageclass.yaml
查看storageclass
[root@glusterfs-01 ~]# kubectl get sc
NAME PROVISIONER AGE
gluster-dynmic kubernetes.io/glusterfs 2d17h
9)执行PVC的创建测试
pvc的yaml文件如下:
[root@glusterfs-01 ~]# cat pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: gluster-dyn-pvc
annotations:
volume.beta.kubernetes.io/storage-class: gluster-dynmic
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 2Gi
执行PVC的创建:
[root@glusterfs-01 ~]# kubectl create -f pvc.yaml
persistentvolumeclaim/gluster-dyn-pvc created
查看创建的PVC的状态:
[root@glusterfs-01 ~]# kubectl get pvc
gluster-dyn-pvc Bound pvc-0c1cb408-1a08-11e9-9f58-000c29c0255c 2Gi RWX gluster-dynmic 42s
10)Pod的创建验证
[root@glusterfs-01 ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
mysql-6668bbf547-rvmfb 1/1 Running 0 106m
redis-c744fd4c9-5p44g 1/1 Running 0 96m
[root@glusterfs-01 ~]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
bigdata-pv-claim Bound pvc-1c74445b-19f9-11e9-9f58-000c29c0255c 2Gi RWX gluster-dynmic 108m
gluster-dyn-pvc Bound pvc-0c1cb408-1a08-11e9-9f58-000c29c0255c 2Gi RWX gluster-dynmic 109s
log-pv-claim Bound pvc-1c77ffc0-19f9-11e9-9f58-000c29c0255c 2Gi RWX gluster-dynmic 108m
mysql-pv-claim Bound pvc-1c771e55-19f9-11e9-9f58-000c29c0255c 2Gi RWX gluster-dynmic 108m
shared-pv-claim Bound pvc-1c762bd8-19f9-11e9-9f58-000c29c0255c 2Gi RWX gluster-dynmic 108m
六.节点扩容
monmaptool --add node1 10.0.2.21:6789 --add node2 10.0.2.22:6789 --add node3 10.0.2.23:6789 map