k8s入门教程十五:使用ceph实现storageclass动态存储

简介

从上文我们可知,虽然K8S提供了PVC的方式进行存储的便利性,但是PV的创建还是要手工的,使用起来不是很方便,在k8s 1.4以后,kubernetes提供了一种更加方便的动态创建PV的方式,即StorageClass。使用StorageClass时无需预先创建固定大小的PV来等待使用者创建PVC使用,而是直接创建PVC即可使用。

本文主要使用Rook搭建一个ceph集群,然后使用StorageClass来直接创建PVC。

Rook与ceph

Rook 是基于Kubernetes之上,提供一键部署存储系统的编排系统。Rook 将存储软件转变成自我管理、自我扩展和自我修复的存储服务,通过自动化部署、启动、配置、供应、扩展、升级、迁移、灾难恢复、监控和资源管理来实现。Rook 底层使用云原生容器管理、调度和编排平台提供的能力来提供这些功能。

Rook 使用底层云本机容器管理、调度和编排平台提供的工具来实现它自身的功能。Rook 目前支持Ceph、NFS、Minio Object Store和CockroachDB。

ook使用Kubernetes原语使Ceph存储系统能够在Kubernetes上运行。下图说明了Ceph Rook如何与Kubernetes集成:

随着Rook在Kubernetes集群中运行,Kubernetes应用程序可以挂载由Rook管理的块设备和文件系统,或者可以使用S3 / Swift API提供对象存储

Rook组件

Rook的主要组件有两个,功能如下:

  1. Rook Operator:Rook oprerator是一个简单的容器,具有引导和监视存储集群所需的全部功能。oprerator将启动并监控ceph monitor pods和OSDs的守护进程,它提供基本的RADOS存储。oprerator通过初始化运行服务所需的pod和其他组件来管理池,对象存储(S3 / Swift)和文件系统的CRD。
    • Rook与Kubernetes交互的组件
    • 整个Rook集群只有一个
  2. Rook Agent
    • 与Rook Operator交互,执行命令
    • 每个Kubernetes的Node上都会启动一个
    • 不同的存储系统,启动的Agent是不同的

Rook & Ceph框架

使用Rook部署Ceph集群的架构图如下:

img

从上面可以看出,通过Rook部署完Ceph集群后,就可以提供Volume Claim给Kubernetes集群里的App使用了。

ceph简介

Ceph 是一个分布式存储系统,具备大规模、高性能、无单点失败的特点。Ceph 是一种高度可扩展的分布式存储解决方案,用于具有多年生产部署的块存储,对象存储和共享文件系统。

Ceph 包括多个组件:

  • Ceph Monitors(MON):负责生成集群票选机制。所有的集群节点都会向 Mon 进行汇报,并在每次状态变更时进行共享信息。
  • Ceph Object Store Devices(OSD):负责在本地文件系统保存对象,并通过网络提供访问。通常 OSD 守护进程会绑定在集群的一个物理盘上,Ceph 客户端直接和 OSD 打交道。
  • Ceph Manager(MGR):提供额外的监控和界面给外部的监管系统使用。
  • Reliable Autonomic Distributed Object Stores:Ceph 存储集群的核心。这一层用于为存储数据提供一致性保障,执行数据复制、故障检测以及恢复等任务。

为了在 Ceph 上进行读写,客户端首先要联系 MON,获取最新的集群地图,其中包含了集群拓扑以及数据存储位置的信息。Ceph 客户端使用集群地图来获知需要交互的 OSD,从而和特定 OSD 建立联系。

而对应的三种存储类型对应的区别如下:

  • 共享文件存储:类似我们平时看到的文件,有点类似NFS
  • 块存储:硬盘等裸设备的存储方式,ceph创建时,就是创建一个虚拟的硬盘
  • 对象存储:用 S3 兼容接口开放存储服务。

Rook部署

本次的测试环境如下:

  • 三台k8s v1.17.2节点

    | 主机名 | IP地址 |
    | :——: | :—————: |
    | master | 192.168.1.60 |
    | node1 | 192.168.1.61 |
    | node2 | 192.168.1.62 |

  • rook 1.2版本

本次的主要资源列表:

Rook Operator部署

克隆rook github仓库到本地,然后运行common.yaml 与 operator.yaml 两个资源清单文件:

1
2
3
4
git clone --single-branch --branch release-1.2 https://github.com/rook/rook.git
cd cluster/examples/kubernetes/ceph
kubectl create -f common.yaml
kubectl create -f operator.yaml

由于docker镜像比较大,建议先下载以下镜像:

1
2
3
4
5
6
7
rook/ceph:v1.2.4
ceph/ceph:v14.2.7
quay.io/cephcsi/cephcsi:v1.2.2
quay.io/k8scsi/csi-snapshotter:v1.2.2
quay.io/k8scsi/csi-provisioner:v1.4.0
quay.io/k8scsi/csi-node-driver-registrar:v1.2.0
quay.io/k8scsi/csi-attacher:v1.2.0

操作完成之后,必须保证rook-ceph-operator有在正常运行(1.2版本的namespace为rook-ceph):

1
2
3
[root@master Rook]# kubectl get pods -n rook-ceph -o wide -l app=rook-ceph-operator
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
rook-ceph-operator-69d86f8597-7txd9 1/1 Running 37 7h49m 10.44.0.8 node1 <none> <none>

部署rook-ceph-operator过程中,会触发以DaemonSet的方式在集群部署Agent和Discoverpods。operator会在集群内的每个主机创建两个pod:rook-discover,rook-ceph-agent。

ceph集群创建

现在 Rook Operator 处于 Running 状态,接下来我们就可以创建 Ceph 集群了。为了使集群在重启后不受影响,请确保设置的 dataDirHostPath 属性值为有效得主机路径,同时要保证有5G以上的空闲空间。

官方提供了3个yaml用来创建集群:

  • cluster.yaml: 该文件包含生产存储集群的通用设置。至少需要三个节点。
  • cluster-test.yaml: 未配置冗余的测试群集的设置。只需要一个节点。
  • cluster-minimal.yaml: 仅使用一个ceph-mon和一个ceph-mgr创建一个集群,因此Ceph仪表板可用于其余集群配置。

如果你有自定义了dataDirHostPath ,就需要修改。

当检查到Rook operator, agent, and discover pods已经是running状态后,就可以部署rook cluster了。

同时,Ceph 有一个 Dashboard 工具,我们可以在上面查看集群的状态,包括总体运行状态,mgr、osd 和其他 Ceph 进程的状态,查看池和 PG 状态,以及显示守护进程的日志等等。只需要设置dashboard.enable=true即可,这样 Rook Operator 就会启用 ceph-mgr dashboard 模块

就直接 kubectl apply -f cluster.yaml即可。这时就需要漫长的等待了,因为需要下载镜像,必须保证以下pod都是在正常运行:

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
[root@master Rook]# kubectl get pods -n rook-ceph -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
csi-cephfsplugin-2rmx5 3/3 Running 36 104d 192.168.1.60 master <none> <none>
csi-cephfsplugin-ctf49 3/3 Running 9 104d 192.168.1.62 node2 <none> <none>
csi-cephfsplugin-gjt57 3/3 Running 33 104d 192.168.1.61 node1 <none> <none>
csi-cephfsplugin-provisioner-5c85564f4c-frj6m 4/4 Running 50 8h 10.44.0.2 node1 <none> <none>
csi-cephfsplugin-provisioner-5c85564f4c-lx8jm 4/4 Running 55 9d 10.32.0.2 master <none> <none>
csi-rbdplugin-bhmdv 3/3 Running 9 104d 192.168.1.62 node2 <none> <none>
csi-rbdplugin-chh2t 3/3 Running 34 104d 192.168.1.61 node1 <none> <none>
csi-rbdplugin-provisioner-7bb78d6c66-8s62m 5/5 Running 79 8h 10.44.0.10 node1 <none> <none>
csi-rbdplugin-provisioner-7bb78d6c66-s9gq4 5/5 Running 70 9d 10.32.0.9 master <none> <none>
csi-rbdplugin-rcrcc 3/3 Running 36 104d 192.168.1.60 master <none> <none>
rook-ceph-crashcollector-master-75f895557f-8zhlh 1/1 Running 1 14d 10.32.0.8 master <none> <none>
rook-ceph-crashcollector-node1-59866fd66-fv5gg 1/1 Running 0 9h 10.44.0.7 node1 <none> <none>
rook-ceph-crashcollector-node2-54c9469bcf-kh4zj 1/1 Running 0 8h 10.36.0.2 node2 <none> <none>
rook-ceph-mon-a-848bc78f6d-4xmv6 1/1 Running 0 8h 10.36.0.3 node2 <none> <none>
rook-ceph-mon-b-5dbdbc4df-2jpr7 1/1 Running 1 14d 10.32.0.3 master <none> <none>
rook-ceph-mon-c-6f6558f959-x7mnd 1/1 Running 1 9h 10.44.0.5 node1 <none> <none>
rook-ceph-operator-69d86f8597-7txd9 1/1 Running 38 8h 10.44.0.8 node1 <none> <none>
rook-ceph-osd-0-6fd84c65c4-d8q8b 1/1 Running 0 8h 10.36.0.1 node2 <none> <none>
rook-ceph-osd-1-67bf5c7686-q4cgk 1/1 Running 178 14d 10.32.0.5 master <none> <none>
rook-ceph-osd-2-6b65ddbdf9-p5m8f 1/1 Running 0 9h 10.44.0.6 node1 <none> <none>
rook-ceph-osd-prepare-master-lxgjt 0/1 Completed 0 16h 10.32.0.13 master <none> <none>
rook-discover-6x98l 1/1 Running 10 104d 10.44.0.1 node1 <none> <none>
rook-discover-fg9zh 1/1 Running 11 104d 10.32.0.6 master <none> <none>
rook-discover-mxlwt 1/1 Running 3 104d 10.36.0.6 node2 <none> <none>

如果pod运行失败,则需要使用kubectl describe pod -n rook-ceph xxx查看具体失败的原因。

Ceph Dashboard

前文说到,设置dashboard.enable=true即可,这样 Rook Operator 就会启用 ceph-mgr dashboard 模块,并将创建一个 Kubernetes Service 来暴露该服务,将启用端口 7000 进行 https 访问,如果 Ceph 集群部署成功了,我们可以使用下面的命令来查看 Dashboard 的 Service:

1
2
3
4
5
6
7
8
9
[root@master ~]# kubectl get svc -n rook-ceph
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
csi-cephfsplugin-metrics ClusterIP 10.96.0.151 <none> 8080/TCP,8081/TCP 105d
csi-rbdplugin-metrics ClusterIP 10.97.104.240 <none> 8080/TCP,8081/TCP 105d
rook-ceph-mgr ClusterIP 10.105.202.253 <none> 9283/TCP 105d
rook-ceph-mgr-dashboard NodePort 10.104.133.172 <none> 7000:30024/TCP 105d
rook-ceph-mon-a ClusterIP 10.99.134.244 <none> 6789/TCP,3300/TCP 105d
rook-ceph-mon-b ClusterIP 10.99.12.131 <none> 6789/TCP,3300/TCP 105d
rook-ceph-mon-c ClusterIP 10.97.137.167 <none> 6789/TCP,3300/TCP 105d

这里的 rook-ceph-mgr 服务用于报告 Prometheus metrics 指标数据的,而后面的的 rook-ceph-mgr-dashboard 服务就是我们的 Dashboard 服务,如果在集群内部我们可以通过 DNS 名称 http://rook-ceph-mgr-dashboard.rook-ceph:7000 或者 CluterIP http://10.104.133.172:7000 来进行访问,如果想要外部访问的话,则使用kubectl edit svc -n rook-ceph rook-ceph-mgr-dashboard将TYPE修改为NodePort即可。

这时就可以使用 http://<NodeIp>:30024 就可以访问到 Dashboard 了。但是在访问的时候需要我们登录才能够访问,Rook 创建了一个默认的用户 admin,并在运行 Rook 的命名空间中生成了一个名为 rook-ceph-dashboard-admin-password 的 Secret,要获取密码,可以运行以下命令:

1
2
3
[root@master ~]# kubectl -n rook-ceph get secret rook-ceph-dashboard-password -o jsonpath="{['data']['password']}" | base64 --decode && echo
xjDadabefO
[root@master ~]#

Rook工具箱安装

要验证集群是否处于正常状态,我们可以使用 Rook 工具箱 来运行 ceph status 命令查看。

Rook 工具箱是一个用于调试和测试 Rook 的常用工具容器,该工具基于 CentOS 镜像,所以可以使用 yum 来轻松安装更多的工具包。 我们这里用 Deployment 控制器来部署 Rook 工具箱,kubectl create -f toolbox.yaml

一旦 toolbox 的 Pod 运行成功后,我们就可以使用下面的命令进入到工具箱内部进行操作:

1
kubectl -n rook-ceph exec -it $(kubectl -n rook-ceph get pod -l "app=rook-ceph-tools" -o jsonpath='{.items[0].metadata.name}') bash

工具箱中的所有可用工具命令均已准备就绪,可满足您的故障排除需求。例如:

1
2
3
4
ceph status
ceph osd status
ceph df
rados df

比如现在我们要查看集群的状态,需要满足下面的条件才认为是健康的:

  • 所有 mons 应该达到法定数量
  • mgr 应该是激活状态
  • 至少有一个 OSD 处于激活状态
  • 如果不是 HEALTH_OK 状态,则应该查看告警或者错误信息

以下为实操部分:

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
[root@master Rook]# kubectl -n rook-ceph exec -it $(kubectl -n rook-ceph get pod -l "app=rook-ceph-tools" -o jsonpath='{.items[0].metadata.name}') bash
bash: warning: setlocale: LC_CTYPE: cannot change locale (en_US.UTF-8): No such file or directory
bash: warning: setlocale: LC_COLLATE: cannot change locale (en_US.UTF-8): No such file or directory
bash: warning: setlocale: LC_MESSAGES: cannot change locale (en_US.UTF-8): No such file or directory
bash: warning: setlocale: LC_NUMERIC: cannot change locale (en_US.UTF-8): No such file or directory
bash: warning: setlocale: LC_TIME: cannot change locale (en_US.UTF-8): No such file or directory
[root@node2 /]#
[root@node2 /]#
[root@node2 /]# ceph status
cluster:
id: 892bda12-220d-4ec3-a0be-2e123c7d4a66
health: HEALTH_OK

services:
mon: 3 daemons, quorum a,b,c (age 2h)
mgr: a(active, since 2h)
osd: 3 osds: 3 up (since 2h), 3 in (since 2h)

data:
pools: 0 pools, 0 pgs
objects: 0 objects, 0 B
usage: 56 GiB used, 231 GiB / 286 GiB avail
pgs:
# 所有 OSD 的状态
[root@node2 /]# ceph osd status
+----+--------+-------+-------+--------+---------+--------+---------+-----------+
| id | host | used | avail | wr ops | wr data | rd ops | rd data | state |
+----+--------+-------+-------+--------+---------+--------+---------+-----------+
| 0 | node2 | 12.3G | 83.0G | 0 | 0 | 0 | 0 | exists,up |
| 1 | master | 25.3G | 70.1G | 0 | 0 | 0 | 0 | exists,up |
| 2 | node1 | 18.0G | 77.3G | 0 | 0 | 0 | 0 | exists,up |
+----+--------+-------+-------+--------+---------+--------+---------+-----------+
[root@node2 /]# ceph osd df
ID CLASS WEIGHT REWEIGHT SIZE RAW USE DATA OMAP META AVAIL %USE VAR PGS STATUS
1 hdd 0.09319 1.00000 95 GiB 25 GiB 70 GiB 26 KiB 0 B 70 GiB 26.51 1.36 0 up
2 hdd 0.09319 1.00000 95 GiB 18 GiB 77 GiB 26 KiB 0 B 77 GiB 18.94 0.97 0 up
0 hdd 0.09319 1.00000 95 GiB 12 GiB 83 GiB 26 KiB 0 B 83 GiB 12.98 0.67 0 up
TOTAL 286 GiB 56 GiB 231 GiB 80 KiB 0 B 231 GiB 19.47
MIN/MAX VAR: 0.67/1.36 STDDEV: 5.54

# 显示 Pool 和总体用量
[root@node2 /]# rados df
POOL_NAME USED OBJECTS CLONES COPIES MISSING_ON_PRIMARY UNFOUND DEGRADED RD_OPS RD WR_OPS WR USED COMPR UNDER COMPR

total_objects 0
total_used 56 GiB
total_avail 231 GiB
total_space 286 GiB

ceph的挂载方法

创建完ceph集群之后,那我们要如何使用呢?

块设备

如果需要挂载ceph的分区,首先需要创建一个POD:kubectl create -f direct-mount.yaml,完成之后,查看且进入POD:

1
2
3
4
5
6
7
8
9
10
11
[root@master ~]# kubectl -n rook-ceph get pod -l app=rook-direct-mount
NAME READY STATUS RESTARTS AGE
rook-direct-mount-6c9d444887-fgj5g 1/1 Running 0 161m
[root@master ~]#
[root@master ~]# kubectl -n rook-ceph exec -it rook-direct-mount-6c9d444887-fgj5g -- bash
bash: warning: setlocale: LC_CTYPE: cannot change locale (en_US.UTF-8): No such file or directory
bash: warning: setlocale: LC_COLLATE: cannot change locale (en_US.UTF-8): No such file or directory
bash: warning: setlocale: LC_MESSAGES: cannot change locale (en_US.UTF-8): No such file or directory
bash: warning: setlocale: LC_NUMERIC: cannot change locale (en_US.UTF-8): No such file or directory
bash: warning: setlocale: LC_TIME: cannot change locale (en_US.UTF-8): No such file or directory
[root@master /]#

进入之后,创建一个10MB的RBD:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[root@master /]# rbd create replicapool/test --size 10
[root@master /]# rbd info replicapool/test
rbd image 'test':
size 10 MiB in 3 objects
order 22 (4 MiB objects)
snapshot_count: 0
id: 163a80e6351c31
block_name_prefix: rbd_data.163a80e6351c31
format: 2
features: layering
op_features:
flags:
create_timestamp: Sun Jun 14 15:56:33 2020
access_timestamp: Sun Jun 14 15:56:33 2020
modify_timestamp: Sun Jun 14 15:56:33 2020
[root@master /]# rbd map replicapool/test
/dev/rbd1
[root@master /]# lsblk | grep rbd
rbd0 253:0 0 20G 0 disk
rbd1 253:16 0 10M 0 disk

使用lsblk 就可以看到创建出来的块设备了。这个就可以理解为普通的磁盘分区,我们可以直接格式化且挂载他:

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
[root@master /]# mkfs.ext4 -m0 /dev/rbd1
mke2fs 1.42.9 (28-Dec-2013)
Discarding device blocks: done
Filesystem label=
OS type: Linux
Block size=1024 (log=0)
Fragment size=1024 (log=0)
Stride=4096 blocks, Stripe width=4096 blocks
2560 inodes, 10240 blocks
0 blocks (0.00%) reserved for the super user
First data block=1
Maximum filesystem blocks=10485760
2 block groups
8192 blocks per group, 8192 fragments per group
1280 inodes per group
Superblock backups stored on blocks:
8193

Allocating group tables: done
Writing inode tables: done
Creating journal (1024 blocks): done
Writing superblocks and filesystem accounting information: done

[root@master /]# mkdir /tmp/rook-volume
[root@master /]# mount /dev/rbd1 /tmp/rook-volume
[root@master /]# df -h |grep rook
/dev/rbd1 8.7M 172K 8.4M 2% /tmp/rook-volume

这样就像使用本地磁盘一样了。

其他的使用方法见:https://rook.io/docs/rook/v1.2/direct-tools.html

实例:wordpress

我们建一个wordpress+mysql+StorageClass的例子,参考了官网的例子:https://rook.io/docs/rook/v1.2/ceph-block.html

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
apiVersion: ceph.rook.io/v1
kind: CephBlockPool
metadata:
name: replicapool
namespace: rook-ceph
spec:
failureDomain: host
replicated:
size: 3
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: rook-ceph-block
# Change "rook-ceph" provisioner prefix to match the operator namespace if needed
provisioner: rook-ceph.rbd.csi.ceph.com
parameters:
# clusterID is the namespace where the rook cluster is running
clusterID: rook-ceph
# Ceph pool into which the RBD image shall be created
pool: replicapool

# RBD image format. Defaults to "2".
imageFormat: "2"

# RBD image features. Available for imageFormat: "2". CSI RBD currently supports only `layering` feature.
imageFeatures: layering

# The secrets contain Ceph admin credentials.
csi.storage.k8s.io/provisioner-secret-name: rook-csi-rbd-provisioner
csi.storage.k8s.io/provisioner-secret-namespace: rook-ceph
csi.storage.k8s.io/node-stage-secret-name: rook-csi-rbd-node
csi.storage.k8s.io/node-stage-secret-namespace: rook-ceph

# Specify the filesystem type of the volume. If not specified, csi-provisioner
# will set default as `ext4`.
csi.storage.k8s.io/fstype: xfs

# Delete the rbd volume when a PVC is deleted
reclaimPolicy: Delete

将上述yaml保存为storageclass.yaml,然后 kubectl create -f storageclass.yaml,再查看创建好的资源:

1
2
3
4
5
6
[root@master Rook]# kubectl -n rook-ceph get cephblockpools.ceph.rook.io
NAME AGE
replicapool 2m28s
[root@master Rook]# kubectl -n rook-ceph get storageclasses.storage.k8s.io
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
rook-ceph-block rook-ceph.rbd.csi.ceph.com Delete Immediate false 2m8s

下载相关的资料:

1
2
3
4
5
wget https://raw.githubusercontent.com/rook/rook/release-1.2/cluster/examples/kubernetes/mysql.yaml
wget https://raw.githubusercontent.com/rook/rook/release-1.2/cluster/examples/kubernetes/wordpress.yaml
kubectl apply -f mysql.yaml
# 先将svc的type修改为NodePort
kubectl apply -f wordpress.yaml

创建成功之后,查看PVC已经成功绑定了2个了。

1
2
3
4
5
6
[root@master ~]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mysql-pv-claim Bound pvc-9b861f8f-f32c-4b0e-90cc-ac15931d1677 20Gi RWO rook-ceph-block 24h
wp-pv-claim Bound pvc-57724038-8e8c-40c3-b636-f7ece2bf194f 20Gi RWO rook-ceph-block 24h
[root@master ~]# lsblk |grep rbd
rbd0 253:0 0 20G 0 disk /var/lib/kubelet/pods/4399e9ab-55ec-4ec2-9979-6a968e486f0e/volumes/kubernetes.io~csi/pvc-57724038-8e8c-40c3-b636-f7ece2bf194f/mount

使用 lsblk |grep rbd 可以看到有一块rbd是挂载在master上面的,也可以看到具体的文件内容,那为什么可看到呢?这是因为wp的pod是运行在master上面。

最后清理掉测试的文件:

1
2
3
4
kubectl delete -f wordpress.yaml
kubectl delete -f mysql.yaml
kubectl delete -n rook-ceph cephblockpools.ceph.rook.io replicapool
kubectl delete storageclass rook-ceph-block

参考链接

https://rook.io/docs/rook/v1.2/ceph-quickstart.html

使用 Rook 快速搭建 Ceph 集群

K8s企业实践使用storageclass实现动态存储

Rook & Ceph 简介

kubernetes挂载ceph rbd和cephfs的方法

Kubernetes部署rook+ceph存储系统

Kubernetes上使用Rook部署Ceph系统并提供PV服务

0%