使用 Rook 部署 Ceph+NFS 服务器集群
使用 rook.io 可以在 kubernetes (也称为 k8s) 之上部署 Ceph 集群。 Ceph 集群可以使用每个 k8s 集群节点上的存储,就像在常规主机上部署时一样。 Rook 和 Ceph 的新版本还支持使用 nfs-ganesha 用户空间服务器部署 CephFS 到 NFS 网关。 本文将介绍如何部署 Ceph 集群、CephFS 以及最终使用 rook 的 NFS 网关。
概述 ¶
我们将首先使用 minikube 创建一个单 VM kubernetes 集群。 之后,我们将创建一个最小集群(只有一个 mon 和一个 mgr),然后在其之上创建一个文件系统,最后创建一个运行在其之上的 NFS 网关集群。 最后,我们将介绍如何将 NFS 服务暴露到外部世界。
一旦我们启动了一个最小集群。 请注意,我们将使用 ceph-mgr 编排器模块和仪表板来帮助我们扩展集群。 那里的命令行和 API 仍然有些“原始”。 最终的想法是在这些命令之上添加一个漂亮的 UI 外观,但这仍然在规划阶段,因此我们必须忍受一些笨拙的命令行工作。
Kubernetes 集群 ¶
首先,您需要一个 kubernetes 集群。 如果您已经有了,那就太好了——它只需要每个节点上的一些本地存储(文件系统或块设备上的目录)来容纳 ceph 特定的信息,以及充当对象存储设备的后端存储。
没有? 没问题! 您可以使用 minikube 在 vm 中部署一个单节点 kubernetes 集群。 对于本教程,我正在 minikube 下运行 KVM 部署集群。
工作站准备 ¶
如果您需要使用 minikube,则需要准备好您的工作站。 我正在使用 Fedora 29 和 KVM 作为本示例。 Minikube 会在您的主目录中创建其本地镜像,因此您需要那里的一些磁盘空间(25g 或更多)。
首先,安装用于远程与 kubernetes 集群交互的必要工具
# dnf install kubernetes-client docker-common git
部署 Minikube ¶
如果您已经有 kubernetes 集群,则可以跳过此部分。 首先,下载最新的 minikube 二进制文件和 kvm2 驱动程序,使其可执行,并将其放在您的 $PATH 中
[jlayton@workstation ~]$ cd /tmp [jlayton@workstation ~]$ curl -Lo minikube https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 [jlayton@workstation ~]$ curl -LO https://storage.googleapis.com/minikube/releases/latest/docker-machine-driver-kvm2 [jlayton@workstation ~]$ chmod 0755 minikube docker-machine-driver-kvm2 [jlayton@workstation ~]$ cp minikube docker-machine-driver-kvm2 ~/bin
启动 minikube。 请注意,这将在 $HOME/.minikube 下创建文件,因此请确保您的主目录有足够的空间。 Minikube 默认值为 20GB 镜像,这足以进行一些基本测试。 我有一台相当强大的开发机器,因此我通常会给 VM 比默认值更多的 CPU 和 RAM。 运行“minikube start --help”以获取完整的选项列表。
[jlayton@workstation ~]$ minikube start --vm-driver=kvm2 --memory 8192 --cpus 16
这将启动一个 KVM,吸取 minikube ISO 镜像,并开始部署 kubernetes 集群。 此外,本地机器上的 kubectl 将配置为与新集群通信。 命令返回后,您应该能够运行类似这样的命令并查看集群上运行的 pod 列表
[jlayton@workstation ~]$ kubectl get pods --all-namespaces
Rook 和 Ceph 容器镜像 ¶
Rook 的早期版本使用与 rook 操作员使用的相同镜像部署 Ceph 集群。 在现代版本中,您运行的 ceph 版本与您使用的 rook 版本大多分离。 在部署 rook 集群时,我们关注两个容器镜像
- rook 镜像:rook 操作员使用此镜像
- ceph 镜像:ceph 守护程序容器使用此镜像
您需要使用最新的 rook 镜像。 在撰写本文时,rook/ceph:master 镜像应该足够新,可以用于 rook 镜像。
ceph 镜像应基于 Nautilus (v14) 版本或更高版本。 我们将使用 mgr/dashboard 基础设施将集群扩展到更大的规模,在部署最小集群之后。 很多基础设施在 Nautilus 之前的镜像中不存在。
为了准备本文,我使用了自定义 ceph 镜像,但到您阅读本文时,您应该能够使用官方 Ceph Nautilus 容器镜像。
部署 Rook 操作员 ¶
首先,下载 rook 源代码。 我们不是要在这里修改 rook 代码(尽管欢迎提供帮助),但该树包含一组我们可以提供给 kubernetes 以引导 ceph 集群的 yaml 文件。
[jlayton@workstation ~]$ mkdir -p ~/git [jlayton@workstation ~]$ cd ~/git [jlayton@workstation ~]$ git clone https://github.com/rook/rook.git
更改目录到 ceph 示例目录,然后继续部署 rook 操作员。 这将添加一个名为“rook-ceph-system”的 kubernetes 命名空间,并启动一些用于 rook 操作员的 pod。
[jlayton@workstation ~]$ cd ~/git/rook/cluster/examples/kubernetes/ceph [jlayton@workstation ~]$ git checkout v1.0.0 [jlayton@workstation ~]$ kubectl create -f ./common.yaml [jlayton@workstation ~]$ kubectl create -f ./operator.yaml
部署最小 Ceph 集群 ¶
打开 cluster-minimal.yaml 在您最喜欢的编辑器中。 确保 spec.cephVersion.image 字段设置为您想要用作基础的镜像。 如果它不基于官方版本,您可能还需要将 spec.cephVersion.allowUnsupported 设置为 true。
如果使用 minikube,请将 spec.dataDirHostPath 设置为 /data/rook。 Minikube 会跨重启持久化 /data 目录,而 /var 从只读 ISO 镜像挂载。 我们无法在那里存储任何内容。
创建一个最小的 ceph 集群和一个工具箱容器。 然后使用“get pods”命令观察它们启动。
[jlayton@workstation ~]$ kubectl create -f ./cluster-minimal.yaml [jlayton@workstation ~]$ kubectl create -f ./toolbox.yaml [jlayton@workstation ~]$ kubectl get pods --all-namespaces -w
如果一切顺利,您最终将获得三个处于运行状态的新 pod。 一个 ceph 监视器 (rook-ceph-mon-a-...),一个 ceph 管理器 (rook-ceph-mgr-a...),和一个工具箱容器 (rook-ceph-tools-...)。
使用 Rook 工具箱 ¶
现在我们有一个小的 ceph 集群,但它相对无法访问。 每个守护程序通常都会获得自己的 kubernetes 服务对象,并且它们都默认为 ClusterIP 服务,这意味着它们只能从其他 kubernetes pod 访问
[jlayton@workstation ~]$ kubectl get services --all-namespaces NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE default kubernetes ClusterIP 10.96.0.1 443/TCP 54m kube-system kube-dns ClusterIP 10.96.0.10 53/UDP,53/TCP 54m rook-ceph rook-ceph-mgr ClusterIP 10.100.141.156 9283/TCP 8m rook-ceph rook-ceph-mgr-dashboard ClusterIP 10.97.104.227 8443/TCP 8m rook-ceph rook-ceph-mon-a ClusterIP 10.104.122.195 6789/TCP,3300/TCP 9m
要与 ceph 守护程序交互,我们需要一个在 kubernetes 集群内的 pod 中运行的 shell。 工具箱是一个无所事事的容器,用于此目的。 当 kubernetes 启动新的 pod 时,它会为其提供一个唯一的名称。 确定该名称,然后在该 pod 内执行 shell
[jlayton@workstation ~]$ kubectl get pods -n rook-ceph | grep tools [jlayton@workstation ~]$ kubectl exec -ti -n rook-ceph rook-ceph-tools-... bash
这应该会为您提供一个 shell 提示符。 通常,它会在提示符中显示主机名,对于 minikube 设置,主机名为“minikube”。 从那里,我们可以运行低级 ceph 客户端工具,如“ceph”和“rados”。
minikube# ceph status cluster: id: 065b0695-9293-4153-831e-a11919a4fe96 health: HEALTH_ERR Module 'dashboard' has failed: IOError("Port 8443 not bound on '::'",) services: mon: 1 daemons, quorum a (age 40m) mgr: a(active, since 8s) osd: 0 osds: 0 up, 0 in data: pools: 0 pools, 0 pgs objects: 0 objects, 0 B usage: 0 B used, 0 B / 0 B avail pgs
那里的 HEALTH_ERR 似乎是由于 仪表板中的一个错误,但我们可以看到这里单个 mon 和 mgr 存在。
添加更多监视器 ¶
从这里,我们可以使用 ceph 命令行工具(特别是 orchestrator mgr 模块)来扩展我们的集群。 单个 mon 是单点故障,所以让我们将 mon 计数扩展到 3
minikube# ceph orchestrator mon update 3
Kubernetes 和 rook 有些“懒惰”地启动守护程序——它们可能需要几分钟才能启动。 我们可以通过定期运行以下命令来查看哪些守护程序已启动
minikube# ceph orchestrator service ls
添加后端存储 ¶
到目前为止,我们实际上无法使用 ceph 集群来存储任何对象数据。 对于大多数环境,我们希望分配每个物理节点的一个或多个块设备,并使用这些来支持一组 bluestore OSD。 不幸的是,将块设备添加到 minikube 并不容易,因此我们必须满足于由节点上文件系统中的目录支持的 OSD。
在工具箱 shell 中,创建一个 /osd.json 文件,内容如下,并使用 orchestrator cli 命令将其提供给 rook。
minikube# cat /osd.json { "host_pattern": "minikube", "data_directories": [ "/data/rook" ] } minikube# ceph orchestrator osd create -i /osd.json
该命令应立即返回,并且操作员最终将在 /data/rook 中创建 OSD 的后端存储,然后在新的 pod 中启动 OSD 守护程序。
如果您正在运行的集群确实分配了用于使用的块设备,则 rook 操作员应在启动时索引它们。 它们应该出现在“device ls”中。
minikube# Host node1.example.com: Device Path Type Size Rotates Available Model
sda hdd 18.6G False False
sdb hdd 20.0G False False
在这里,我们知道 /dev/sdb 是我们想要支持此 OSD 的设备,因此我们创建如下 json 文件,并将其提供给 orchestrator
minikube# cat /osd.json { "host_pattern": "node1.example.com", "data_devices": { "paths": [ "sdb" ] }, }
ceph orchestrator osd create -i /osd.json
在任何一种情况下,该命令应立即返回,并且在几分钟内,我们应该看到“ceph status”报告一个“up”OSD 。
创建一个 CephFS 文件系统 ¶
我们现在有了后端存储。 让我们在顶部分层一个 CephFS 分布式文件系统。 ceph-mgr 具有一个“volumes”模块,可以简化此操作。 我们将其命名为 myfs
minikube# ceph fs volume create myfs
默认情况下,会创建 2 个 MDS 守护程序——一个活动和一个备用重放。 几秒钟后,我们应该在工具箱容器上的“ceph status”中看到 mds 服务。
minikube# ceph status ... services: mon: 3 daemons, quorum a,b,c (age 91m) mgr: a(active, since 89m) mds: myfs:1 {0=myfs-b=up:active} 1 up:standby-replay osd: 1 osds: 1 up (since 88m), 1 in (since 88m) ...
添加 NFS 服务器集群 ¶
现在让我们创建一个 NFS 服务器集群,我们可以使用它来导出新的 CephFS 文件系统。 最近,一个新的集群恢复后端已添加到 nfs-ganesha,rook 编排器知道如何配置 nfs-ganesha 守护程序以使用它,以便它们协调恢复以防止在重新启动时发生恢复冲突。
nfs-ganesha 守护程序需要少量存储空间用于自己的目的,并且可以将该信息直接存储在 RADOS 中。 不同的 nfs-ganesha 守护程序还使用这些对象来相互通信。 让我们为 ganesha 创建一个名为“nfs-ganesha”的池,并使用 64 个放置组
minikube# ceph osd pool create nfs-ganesha 64 pool 'nfs-ganesha' created
返回后,我们可以启动一个 nfs-ganesha 容器
minikube# ceph orchestrator nfs add mynfs nfs-ganesha mynfs
这将创建一个名为“mynfs”的新 NFS 服务器集群,该集群使用 nfs-ganesha 池中的 mynfs 命名空间来存储其用于恢复的信息。 它当前只有一个服务器节点,所以让我们使用第二个节点扩展。
minikube# ceph orchestrator nfs update mynfs 2
操作员然后应该启动第二个 nfs-ganesha 容器,与第一个位于同一个 NFS 服务器集群中。
使 Ceph 仪表板可访问 ¶
NFS 服务器目前还不太好用,因为它们没有配置任何导出。Ceph 控制台可以为我们创建导出配置块,但目前只能通过其后端 API 访问。我们需要向它提供更多的 JSON 数据,但首先我们需要从工作站访问它。
当 Rook 启动 ceph-mgr 容器时,它会为控制台创建一个服务,但初始类型是 ClusterIP 服务,这意味着它只能从同一个 Kubernetes 集群中的其他 Pod 访问。我们需要将端口暴露到外部世界。
有多种方法可以做到这一点,但 kubectl patch 命令允许我们用一行命令完成。在工作站 shell 中
[jlayton@workstation ~]$ kubectl patch service -n rook-ceph -p '{"spec":{"type": "NodePort"}}' rook-ceph-mgr-dashboard [jlayton@workstation ~]$ kubectl get service -n rook-ceph rook-ceph-mgr-dashboard NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE rook-ceph-mgr-dashboard NodePort 10.111.49.190 8443:30064/TCP 3h
这会将服务从 ClusterIP 服务转换为 NodePort 服务。执行此命令后,控制台将可以从可以访问其运行的节点上的任何主机访问。
创建导出 ¶
我们需要向控制台提供一个 JSON 文件来创建导出。有很多不同的方法来设置 nfs-ganesha 的导出表,但我们在这里保持简单,只将我们的 cephfs 树的根目录作为 NFSv4 伪根目录中的 /cephfs 导出。
为了明确起见,没有导出控制(“clients”列表为空),并且禁用了 root squashing。ganesha 守护进程也将以“client.admin”身份访问文件系统,这可能不是我们在“真实”部署中想要做的。
[jlayton@workstation ~]$ cat ~/export.json { "cluster_id": "mynfs", "path": "/", "fsal": {"name": "CEPH", "user_id":"admin", "fs_name": "myfs", "sec_label_xattr": null}, "pseudo": "/cephfs", "tag": null, "access_type": "RW", "squash": "no_root_squash", "protocols": [4], "transports": ["TCP"], "security_label": true, "daemons": ["mynfs.a", "mynfs.b"], "clients": [] }
将此提供给控制台。Ceph 树有一个 方便的脚本,用于在控制台在 Rook 下运行时执行此操作。下载它,并运行它,将您创建的 export.json 文件的内容传递给它。
[jlayton@workstation ~]$ ./run-backend-rook-api-request.sh POST /api/nfs-ganesha/export "$(cat ~/export.json)"
暴露 NFS 服务器 ¶
现在我们只需要让 Kubernetes 集群外部的主机可以访问 NFS 服务器。每个 ganesha 守护进程都有自己的服务,因此我们需要暴露两个不同的端口。
我们可以使用与我们用于控制台相同的 patch 命令来执行此操作。
[jlayton@workstation ~]$ kubectl patch service -n rook-ceph -p '{"spec":{"type": "NodePort"}}' rook-ceph-nfs-mynfs-a [jlayton@workstation ~]$ kubectl patch service -n rook-ceph -p '{"spec":{"type": "NodePort"}}' rook-ceph-nfs-mynfs-b [jlayton@workstation ~]$ kubectl get services -n rook-ceph rook-ceph-nfs-mynfs-a rook-ceph-nfs-mynfs-b NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE rook-ceph-nfs-mynfs-a NodePort 10.109.205.86 2049:30806/TCP 1h rook-ceph-nfs-mynfs-b NodePort 10.108.117.78 2049:32376/TCP 5m
现在,任何具有到运行这些守护进程的 Kubernetes 节点的网络路由的主机都可以通过端口 30806 和 32376 访问 NFS 服务器。
请注意,当您运行此命令时,您可能会得到不同的端口对。可以从 NodePort 服务请求特定的端口,但 Kubernetes 将使用的范围非常有限,因此与让它选择一个端口相比,没有太多的好处。
测试 NFS 访问 ¶
如果您正在运行 minikube,则唯一可以访问这些服务器的客户端是运行 VM 的主机。如果您正在运行在真实的集群上,那么只要它们可以访问 Kubernetes 集群节点 IP 地址,您就可以使用不同的主机作为客户端。
在任何情况下,尝试从主机挂载其中一个服务器。生成一个 root shell,然后
minikube# mkdir -p /mnt/rook minikube# mount -t nfs -o port=30806 $(minikube ip):/cephfs /mnt/rook
如果一切顺利,挂载将成功,然后您可以在 /mnt/rook 下开始执行正常的文件操作。
结论和说明 ¶
遵循此博客文章应该可以为您提供一个功能配置,但对于真实的生产集群,您当然需要一个具有多个物理 Kubernetes 节点以实现冗余的设置。
Kubernetes 网络是一个极其复杂的主题。虽然我们提到了将 ClusterIP 服务转换为 NodePort,但还有许多其他方法可以使这些服务可供集群外部的主机访问。
我们也大多忽略了 Ceph 的安全性。在实际部署中,我们希望分配细粒度的 cephx 服务帐户,以将对 RADOS 对象的访问限制为需要访问它们的参与者。