在 Raspberry Pi 家庭实验室上配置 Kubernetes NFS 客户端

使用 NFS 客户端 Provisioner 在 Raspberry Pi Kubernetes 集群上创建动态持久卷。
121 位读者喜欢这篇文章。
Ships at sea on the web

临时容器非常有用,但有时数据需要在容器之间持久化,或者在多个容器之间共享。解决方案是在容器内部挂载外部卷,这在 Kubernetes 中是通过持久卷实现的。在大型公有云部署中,Kubernetes 与云供应商的块存储后端集成,允许开发人员创建卷声明以用于其部署,Kubernetes 与云供应商协作创建卷并将其挂载到开发人员的 Pod 中。

您可以使用来自 Kubernetes Incubator 外部存储项目的网络文件系统 (NFS) 客户端 Provisioner,在“家庭私有云” Kubernetes 集群中复制相同的行为。

在之前的一篇文章中,我解释了如何使用 Raspberry Pi 设置 NFS 服务器。您可以使用此 NFS 服务器来支持 NFS 客户端 Provisioner 提供的存储。当创建持久卷声明,请求 Pod 的卷时,Provisioner 运行一个容器,该容器从您的 NFS 服务器挂载 NFS 导出,并将其划分为“卷”。Kubernetes 原生支持 NFS 卷类型,并在 Pod 启动时处理容器内部卷的挂载。

NFS 客户端 Provisioner 提供了动态配置卷的优势,并使家庭实验室集群的行为类似于云中的 Kubernetes 集群。此外,由于这些是 NFS 卷,它们可以支持多读/多写操作,因此多个 Pod 可以同时挂载这些卷。这对于负载均衡服务(如 Web 服务器)非常有用,在 Web 服务器中,多个 Pod 可以分布在多个节点上以处理流量并提供更高的可用性。也可以创建仅支持单读/单写操作的 NFS 卷。这更适合数据库或其他实例,因为写入卷的软件无法很好地处理多个写入操作。

持久卷、持久卷声明和存储类

持久卷是由非临时存储支持的卷,可以作为卷挂载到容器内部。写入该卷的数据在容器重启后仍然存在,并且可以在新 Pod 替换旧 Pod 时挂载到新 Pod 中。对于 NFS 卷,这些卷可以同时挂载到多个容器中,从而允许数据共享。

开发人员可以使用持久卷声明 (PVC) 从 Kubernetes 请求持久卷。这正是它的字面意思:请求一个满足某些条件(如访问模式或大小)的卷,并且可以声明并用于项目。持久卷使用存储类请求特定的卷类型。

存储类描述了可以声明的卷类型,允许开发人员选择最适合他们需求的卷类型。管理员可以创建多种类型的存储类来满足特定需求,例如访问模式或大小(如上所述),甚至速度/IOPS 类或不同的后端技术。存储类还可以指定特定的 Provisioner 或负责创建和管理卷的软件。

集群管理员可以维护大量预先配置的手动创建的卷池,以满足存储类的需求,但这需要手动操作,并且可扩展性不佳。使用动态卷 Provisioner,软件可以在创建新声明时按需处理卷的创建。默认情况下,NFS 客户端 Provisioner 具有单个存储类,并且请求从此存储类获取卷的 PVC 由 Provisioner 满足。

NFS 客户端 Provisioner

NFS 客户端 Provisioner 是 Kubernetes Incubator 项目的一部分。在 Kubernetes 集群中,此 Provisioner 在容器中运行,该容器从现有 NFS 服务器挂载 NFS 导出——它本身不运行 NFS 服务器。使用该容器,它监听与其存储类匹配的 PVC,在 NFS 导出中创建目录,并将每个目录报告给 Kubernetes 作为持久卷。然后,Kubernetes 可以将卷挂载到使用来自该 PVC 的卷的容器中。

NFS 客户端 Provisioner 的安装可以使用 Helm Chart 完成,如README 项目中所述,但出于教育原因,并且因为本教程是关于在 Raspberry Pi 上运行 Kubernetes 集群,并且需要进行一些调整,您将在家庭私有云集群上手动安装它。

先决条件

在使用 NFS 客户端 Provisioner 之前,有两个先决条件

  1. 查找 NFS 服务器的详细信息(IP 地址和导出的路径)
  2. 在可能运行需要 NFS 卷的 Pod 的每个 Kubernetes 节点上安装 NFS 库

如果您安装了自己的 NFS 服务器,例如 将您的 Raspberry Pi 家庭实验室变成网络文件系统 中的服务器,您应该已经知道服务器的 IP 地址及其导出文件系统的路径。确保导出列表包含所有可能运行带有 NFS 卷的 Pod 的 Kubernetes 节点。

您还需要在每个节点上安装 NFS 库,以便它们能够挂载 NFS 卷。在 Ubuntu、Raspbian 或其他基于 Debian 的操作系统上,您可以使用 apt 安装 nfs-common 包。对于基于 Red Hat 的发行版,请安装 nfs-utils 包。

在解决先决条件之后,您可以继续将 NFS 客户端 Provisioner 安装到 Kubernetes 集群上。

安装

NFS 客户端 Provisioner 使用标准 Kubernetes 对象进行部署。您可以在 Kubernetes Incubator 外部存储项目的 nfs-client 目录中找到这些对象。要开始使用,请克隆 https://github.com/kubernetes-incubator/external-storage 存储库

# Clone the external-storage repo
# (output omitted)
$ git clone https://github.com/kubernetes-incubator/external-storage.git

安装 NFS 客户端 Provisioner 所需的特定部分位于 nfs-client/deploy

$ cd external-storage/nfs-client/deploy
$ ls
class.yaml           deployment.yaml  rbac.yaml        test-pod.yaml
deployment-arm.yaml  objects          test-claim.yaml

请注意,objects 目录包含其父目录中的所有内容,只是将其分解为每个对象一个文件。

在执行任何其他操作之前,您必须创建适当的基于角色的访问控制 (RBAC) 权限,以允许 NFS 客户端 Provisioner 服务帐户挂载卷、创建 PVC 等。可以使用 kubectl create 命令在集群上创建它们。

如果您计划在默认命名空间以外的命名空间中部署 Provisioner,请确保首先更改 RBAC 和部署文件中的命名空间

# Create the RBAC permissions needed by the provisioner
kubectl create -f rbac.yaml
serviceaccount/nfs-client-provisioner created
clusterrole.rbac.authorization.k8s.io/nfs-client-provisioner-runner created
clusterrolebinding.rbac.authorization.k8s.io/run-nfs-client-provisioner created
role.rbac.authorization.k8s.io/leader-locking-nfs-client-provisioner created
rolebinding.rbac.authorization.k8s.io/leader-locking-nfs-client-provisioner created

接下来,为 NFS 客户端 Provisioner Pod 创建部署。如果您按照 使用 Raspberry Pi 构建 Kubernetes 集群 说明创建了 Kubernetes 集群,或者您在基于 ARM 的系统上创建了自己的集群,则需要修改并使用 deployment-arm.yaml 文件进行部署。如果您使用的是基于 x86_64 的系统,请使用 deployment.yaml 文件。

使用您选择的编辑器编辑文件。您需要更改四件事。首先,从 .spec.template.containers.env 列表中设置三个环境变量

  • PROVISIONER_NAME 的值更改为 nfs-storage(可选;这只是使其更人性化一些)。
  • NFS_SERVER 值更改为您的 NFS 服务器的 IP 地址。
  • NFS_PATH 值更改为您的 NFS 导出的路径。

最后,在 .spec.template.spec.volumes.nfs 下,将 serverpath 值更改为您为 NFS_SERVERNFS_PATH 设置的相同值。

例如,在 NFS 服务器和导出路径为 192.168.2.123:/srv 的情况下,deployment-arm.yaml 文件将如下所示

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfs-client-provisioner
  labels:
    app: nfs-client-provisioner
  namespace: default
spec:
  replicas: 1
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: nfs-client-provisioner
  template:
    metadata:
      labels:
        app: nfs-client-provisioner
    spec:
      serviceAccountName: nfs-client-provisioner
      containers:
        - name: nfs-client-provisioner
          image: quay.io/external_storage/nfs-client-provisioner-arm:latest
          volumeMounts:
            - name: nfs-client-root
              mountPath: /persistentvolumes
          env:
            - name: PROVISIONER_NAME
              value: nfs-storage
            - name: NFS_SERVER
              value: 192.168.2.123
            - name: NFS_PATH
              value: /srv
      volumes:
        - name: nfs-client-root
          nfs:
            server: 192.168.2.123
            path: /srv

修改 deployment-arm.yaml 文件后,使用 kubectl create 命令创建部署

# Create the deployment
$ kubectl create -f deployment-arm.yaml
deployment.apps/nfs-client-provisioner created

# Check that the deployment created the provisioner pod correctly
$ kubectl get po
NAME                                      READY   STATUS    RESTARTS   AGE
nfs-client-provisioner-6ddfb9bb6d-x4zwt   1/1     Running   0          54s

如果一切正常,则 NFS 客户端 Provisioner 现在正在您的集群中运行。如果 Pod 的状态为 ContainerCreating,并且最终没有更改为 Running,请使用 kubectl get events 命令检查任何相关事件。确保用户“nobody”对 NFS 服务器上的导出目录具有写入权限。如果没有问题,请继续创建存储类。

需要修改 class.yaml 文件以将 provisioner 值设置为 nfs-storage,或者您在 deployment-arm.yaml 中为 PROVISIONER_NAME 值设置的任何值。这告诉 Kubernetes 需要使用哪个 Provisioner 来满足此存储类的 PVC。假设您选择 nfs-storage,则 class.yaml 文件应如下所示

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: managed-nfs-storage
provisioner: nfs-storage # or choose another name, must match deployment's env PROVISIONER_NAME'
parameters:
  archiveOnDelete: "false"

使用 kubectl create 命令创建存储类

# Create the storage class
$ kubectl create -f class.yaml
storageclass.storage.k8s.io/managed-nfs-storage created

# Verify the storage class was created
$ kubectl get storageClass
NAME                  PROVISIONER   RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
managed-nfs-storage   nfs-storage   Delete          Immediate           false

成功!NFS 客户端 Provisioner 现在已安装在 Kubernetes 集群中,并准备好响应针对 managed-nfs-storage 存储类的持久卷声明,为您的 Pod 动态分配持久存储卷。

测试您的新卷 Provisioner

安装和配置 Provisioner 后,您可以通过创建一个 PVC 来请求持久卷和一个 Pod 来挂载该卷来对其进行测试。幸运的是,测试对象与用于部署的文件一起提供:test-claim.yamltest-pod.yaml

在创建 PVC 和 Pod 之前,先查看一下已有的内容。除非您已经创建了一些,否则不应有任何持久卷或 PVC

# Look for existing persistent volumes
$ kubectl get persistentvolumes
No resources found in default namespace.

# Look for existing persistent volume claims
$ kubectl get persistentvolumeclaims
No resources found in default namespace.

现在,从 test-claim.yaml 文件创建一个新的 PVC

# Create a test PVC
$ kubectl create -f test-claim.yaml
persistentvolumeclaim/test-claim created

$ kubectl get persistentvolumeclaims
NAME         STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS          AGE
test-claim   Bound    pvc-bdf41489-abe4-4508-8adc-74e80f70c626   1Mi        RWX            managed-nfs-storage   7s

从上面的输出中,您可以看到名为 test-claim 的 PVC 已创建并绑定。此声明绑定到一个名为 pvc-bdf41489-abe4-4508-8adc-74e80f70c626 的卷,该卷由 NFS 客户端 Provisioner 自动创建。另请注意,STORAGECLASS 称为 managed-nfs-storage——您创建的存储类的名称。

不要担心 CAPACITY 值。NFS 客户端 Provisioner 没有办法强制执行存储配额;NFS 没有该功能。1Mi 只是一个占位符。

现在 NFS 客户端 Provisioner 已经创建了 PVC 及其关联的持久卷,请登录您的 NFS 服务器并查看后端发生了什么

# From the NFS host, with the directory used for the NFS export
$ ls -la
total 4
drwxr-xr-x  3 nobody  root      73 May 25 18:46 .
drwxr-xr-x 21 root   root    4096 May 25 17:37 ..
drwxrwxrwx  2 nobody nogroup    6 May 25 18:45 default-test-claim-pvc-bdf41489-abe4-4508-8adc-74e80f70c626

已创建一个新目录,该目录遵循命名约定 namespace-pvc_name-pv_name。此目录最初为空。您可以创建一个测试 Pod 以通过 NFS 挂载此目录并写入。

首先,如果您的集群使用 Raspberry Pi 或其他基于 ARM 的主机,则需要修改 test-pod.yaml 文件以使用为 ARM 主机创建的 busybox 镜像。默认设置将从 gcr.io 注册表拉取,但没有 sh 二进制文件的正确架构,从而导致“exec format”错误。如果您的 Kubernetes 集群在 x86_64 主机上运行,则可以跳过此步骤。

更改 test-pod.yaml 文件以使用 docker.io/aarch64/busybox:latest 容器镜像。您的文件应如下所示

kind: Pod
apiVersion: v1
metadata:
  name: test-pod
spec:
  containers:
  - name: test-pod
    image: docker.io/aarch64/busybox:latest
    # image: gcr.io/google_containers/busybox:1.24
    command:
      - "/bin/sh"
    args:
      - "-c"
      - "touch /mnt/SUCCESS && exit 0 || exit 1"
    volumeMounts:
      - name: nfs-pvc
        mountPath: "/mnt"
  restartPolicy: "Never"
  volumes:
    - name: nfs-pvc
      persistentVolumeClaim:
        claimName: test-claim

文件中描述的 Pod 将创建一个 busybox 容器,将来自 test-claim 持久卷的 NFS 卷挂载到容器内的 /mnt,并创建一个名为 SUCCESS 的文件。

# Creae the test pod container
$ kubectl create -f test-pod.yaml
pod/test-pod created

# Validate the container ran without problem
$ kubectl get po
NAME                                      READY   STATUS      RESTARTS   AGE
nfs-client-provisioner-6ddfb9bb6d-x4zwt   1/1     Running     0          20m
test-pod                                  0/1     Completed   0          65s

如果容器运行正确且状态为 Completed,则检查 NFS 服务器上卷的内容

# From the NFS server, within the directory for the PVC
$ ls default-test-claim-pvc-bdf41489-abe4-4508-8adc-74e80f70c626/
SUCCESS

确实成功了!Pod 能够挂载由 NFS 客户端 Provisioner 创建的 NFS 卷并写入!

使用 kubectl delete 命令清理测试 Pod 和 PVC

# Cleanup the test-pod pod
$ kubectl delete po test-pod
pod "test-pod" deleted

# Cleanup the test-claim pvc
$ kubectl delete pvc test-claim
persistentvolumeclaim "test-claim" deleted

加大音量(卷)

感谢 NFS 客户端 Provisioner 和您的 NFS 服务器,您现在可以通过创建 PVC 来请求持久卷以用于您的 Pod,并且声明将自动填充动态配置的 NFS 卷。这在大多数方面都镜像了 Kubernetes 在大型公有云中与动态 Provisioner 的工作方式,并允许您使用类似于您在公有云提供商处使用的工作流程。实际上,存储类的一个好处是在 PVC 和卷提供商之间创建了抽象。这允许您在本地私有云和任何公有云提供商之间使用具有相同名称和不同提供商的存储类,从而更容易在它们之间“迁移”工作负载。

通过使用 NFS,您还可以支持卷上的多读和多写操作,从而允许多个 Pod 同时使用卷。如前所述,这非常适合负载均衡 Web 服务器或类似服务,并允许您同时跨多个节点扩展服务。

最重要的是,NFS 客户端 Provisioner 是自动的,可以在 NFS 导出中动态创建卷,因此不必提前手动配置卷!

试用一下您的新 NFS 客户端 Provisioner,并创建一些需要持久存储的项目。也许可以尝试(无耻地推销一下)在 Kubernetes 上部署 InfluxDB 和 Grafana 以收集 Twitter 统计信息

接下来阅读什么
Chris Collins
Chris Collins 是 Red Hat 的 SRE 和 OpenSource.com 的记者,热衷于自动化、容器编排及其周围的生态系统,并喜欢在家中为了乐趣而重建企业级技术。

评论已关闭。

Creative Commons License本作品根据 Creative Commons Attribution-Share Alike 4.0 International License 获得许可。
© 2025 open-source.net.cn. All rights reserved.