使用 Filestore 部署有状态工作负载


本教程介绍了如何使用永久性卷 (PV)永久性卷声明 (PVC) 在 Google Kubernetes Engine (GKE) 上部署简单的读取者/写入者有状态工作负载。按照本教程操作,了解如何使用 Google Cloud 的代管式网络文件系统 Filestore 来进行设计以实现可扩缩性。

背景

Pod 本质上是临时的。这意味着 GKE 在删除、逐出或重新安排 Pod 时会销毁 Pod 中存储的状态和值。

作为应用运营人员,您可能需要维护有状态工作负载。此类工作负载的示例包括处理 WordPress 文章的应用、即时通讯应用和处理机器学习操作的应用。

通过在 GKE 上使用 Filestore,您可以执行以下操作:

  • 部署可扩缩的有状态工作负载。
  • 使多个 Pod 的 accessModeReadWriteMany,以便多个 Pod 可以同时对同一存储进行读写。
  • 设置 GKE 以同时将卷装载到多个 Pod 中。
  • 移除 Pod 后持久保留存储空间。
  • 使 Pod 能够共享数据并轻松扩缩。

目标

本教程适用于想要使用 PVC 和 NFS 在 GKE 上设置可扩缩的有状态工作负载的应用运营人员和其他用户。

有状态工作负载 GKE 图示

本教程介绍以下步骤:

  1. 创建 GKE 集群。
  2. 使用 CSI 通过 Filestore 配置代管式文件存储。
  3. 创建 reader 和 writer Pod。
  4. 将 reader Pod 公开给 Service 负载均衡器并访问 reader Pod。
  5. 扩容 writer。
  6. 访问 writer Pod 中的数据。

费用

本教程使用 Google Cloud 的以下收费组件:

您可使用价格计算器根据您的预计使用情况来估算费用。

完成本教程后,您可以删除所创建的资源以避免继续计费。如需了解详情,请参阅清理


如需在 Google Cloud 控制台中直接遵循有关此任务的分步指导,请点击操作演示

操作演示


准备工作

设置项目

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. In the Google Cloud console, on the project selector page, click Create project to begin creating a new Google Cloud project.

    Go to project selector

  3. Make sure that billing is enabled for your Google Cloud project.

  4. Enable the Compute Engine, GKE, and Filestore APIs.

    Enable the APIs

  5. In the Google Cloud console, on the project selector page, click Create project to begin creating a new Google Cloud project.

    Go to project selector

  6. Make sure that billing is enabled for your Google Cloud project.

  7. Enable the Compute Engine, GKE, and Filestore APIs.

    Enable the APIs

设置 Google Cloud CLI 的默认值

  1. 在 Google Cloud 控制台中,启动 Cloud Shell 实例:
    打开 Cloud Shell

  2. 下载此示例应用的源代码:

    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
    cd kubernetes-engine-samples/databases/stateful-workload-filestore
    
  3. 设置默认环境变量:

    gcloud config set project PROJECT_ID
    gcloud config set compute/region COMPUTE_REGION
    gcloud config set compute/zone COMPUTE_ZONE
    gcloud config set filestore/zone COMPUTE_ZONE
    gcloud config set filestore/region COMPUTE_REGION
    

    替换以下值:

创建 GKE 集群

  1. 创建名为 stateful-cluster 的 GKE 集群:

    gcloud container clusters create-auto stateful-cluster --region COMPUTE_REGION
    

    创建集群后,结果类似于以下内容:

      gcloud container clusters describe stateful-cluster
      NAME: stateful-cluster
      LOCATION: northamerica-northeast2
      MASTER_VERSION: 1.21.11-gke.1100
      MASTER_IP: 34.130.255.70
      MACHINE_TYPE: e2-medium
      NODE_VERSION: 1.21.11-gke.1100
      NUM_NODES: 3
      STATUS: RUNNING
    

    其中,stateful-clusterSTATUSRUNNING

使用 CSI 通过 Filestore 配置代管式文件存储

GKE 提供了一种在集群中自动部署和管理 Kubernetes Filestore CSI 驱动程序的方法。使用 Filestore CSI,您可以动态创建或删除 Filestore 实例,并在具有 StorageClassDeployment 的 Kubernetes 工作负载中使用这些实例。

您可以创建新的 Filestore 实例(方法是创建动态预配 Filestore 实例和 PV 的 PVC),或访问 Kubernetes 工作负载中预先预配的 Filestore 实例。

新建实例

创建存储类别

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: filestore-sc
provisioner: filestore.csi.storage.gke.io
volumeBindingMode: Immediate
allowVolumeExpansion: true
parameters:
  tier: standard
  network: default
  • volumeBindingMode 设置为 Immediate,允许立即开始预配卷。
  • tier 设置为 standard,可以缩短 Filestore 实例创建时间。如果您需要可用性更高的 NFS 存储、数据备份快照、多个可用区中的数据复制和其他企业级功能,请将 tier 设置为 enterprise。注意:如果未设置 StorageClass 中的 reclaimPolicy,则动态创建的 PV 的收回政策默认为 Delete
  1. 创建 StorageClass 资源:

    kubectl create -f filestore-storageclass.yaml
    
  2. 验证存储类别已创建:

    kubectl get sc
    

    输出类似于以下内容:

    NAME                     PROVISIONER                    RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
    filestore-sc             filestore.csi.storage.gke.io   Delete          Immediate              true                   94m
    

预先配置的实例

创建存储类别

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: filestore-sc
provisioner: filestore.csi.storage.gke.io
volumeBindingMode: Immediate
allowVolumeExpansion: true

volumeBindingMode 设置为 Immediate 时,允许立即开始预配卷。

  1. 创建 StorageClass 资源:

      kubectl create -f preprov-storageclass.yaml
    
  2. 验证存储类别已创建:

      kubectl get sc
    

    输出类似于以下内容:

      NAME                     PROVISIONER                    RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
      filestore-sc             filestore.csi.storage.gke.io   Delete          Immediate              true                   94m
    

为 Filestore 实例创建永久性卷

apiVersion: v1
kind: PersistentVolume
metadata:
  name: fileserver
  annotations:
    pv.kubernetes.io/provisioned-by: filestore.csi.storage.gke.io
spec:
  storageClassName: filestore-sc
  capacity:
    storage: 1Ti
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Delete
  volumeMode: Filesystem
  csi:
    driver: filestore.csi.storage.gke.io
    # Modify this to use the zone, filestore instance and share name.
    volumeHandle: "modeInstance/<FILESTORE_ZONE>/<INSTANCE_NAME>/<FILESTORE_SHARE_NAME>"
    volumeAttributes:
      ip: <IP_ADDRESS> # Modify this to Pre-provisioned Filestore instance IP
      volume: <FILESTORE_SHARE_NAME> # Modify this to Pre-provisioned Filestore instance share name
  1. 验证现有的 Filestore 实例已准备就绪:

      gcloud filestore instances list
    

    输出类似于以下内容,其中 STATE 值为 READY

      INSTANCE_NAME: stateful-filestore
      LOCATION: us-central1-a
      TIER: ENTERPRISE
      CAPACITY_GB: 1024
      FILE_SHARE_NAME: statefulpath
      IP_ADDRESS: 10.109.38.98
      STATE: READY
      CREATE_TIME: 2022-04-05T18:58:28
    

    记下 Filestore 实例的 INSTANCE_NAMELOCATIONFILE_SHARE_NAMEIP_ADDRESS

  2. 填充 Filestore 实例控制台变量:

      INSTANCE_NAME=INSTANCE_NAME
      LOCATION=LOCATION
      FILE_SHARE_NAME=FILE_SHARE_NAME
      IP_ADDRESS=IP_ADDRESS
    
  3. 将文件 preprov-pv.yaml 中的占位符变量替换为上面获取的控制台变量:

      sed "s/<INSTANCE_NAME>/$INSTANCE_NAME/" preprov-pv.yaml > changed.yaml && mv changed.yaml preprov-pv.yaml
      sed "s/<LOCATION>/$LOCATION/" preprov-pv.yaml > changed.yaml && mv changed.yaml preprov-pv.yaml
      sed "s/<FILE_SHARE_NAME>/$FILE_SHARE_NAME/" preprov-pv.yaml > changed.yaml && mv changed.yaml preprov-pv.yaml
      sed "s/<IP_ADDRESS>/$IP_ADDRESS/" preprov-pv.yaml > changed.yaml && mv changed.yaml preprov-pv.yaml
    
  4. 创建 PV

      kubectl apply -f preprov-pv.yaml
    
  5. 验证 PV 的 STATUS 已设置为 Bound

      kubectl get pv
    

    输出类似于以下内容:

      NAME        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                STORAGECLASS    REASON   AGE
      fileserver  1Ti        RWX            Delete           Bound    default/fileserver   filestore-sc             46m
    

使用 PersistentVolumeClaim 访问卷

以下 pvc.yaml 清单引用了名为 filestore-sc 的 Filestore CSI 驱动程序的 StorageClass

为了让多个 Pod 读写卷,请将 accessMode 设置为 ReadWriteMany

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: fileserver
spec:
  accessModes:
  - ReadWriteMany
  storageClassName: filestore-sc
  resources:
    requests:
      storage: 1Ti
  1. 部署 PVC:

    kubectl create -f pvc.yaml
    
  2. 验证 PVC 已创建:

    kubectl get pvc
    

    输出类似于以下内容:

    NAME         STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS        AGE
    fileserver   Bound    pvc-aadc7546-78dd-4f12-a909-7f02aaedf0c3   1Ti        RWX            filestore-sc        92m
    
  3. 验证新创建的 Filestore 实例已准备就绪:

    gcloud filestore instances list
    

    输出类似于以下内容:

    INSTANCE_NAME: pvc-5bc55493-9e58-4ca5-8cd2-0739e0a7b68c
    LOCATION: northamerica-northeast2-a
    TIER: STANDARD
    CAPACITY_GB: 1024
    FILE_SHARE_NAME: vol1
    IP_ADDRESS: 10.29.174.90
    STATE: READY
    CREATE_TIME: 2022-06-24T18:29:19
    

创建 reader 和 writer Pod

创建 reader Pod

reader Pod 将读取 writer Pod 写入的文件。reader Pod 将看到写入文件的时间以及 Pod writer 副本。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: reader
spec:
  replicas: 1
  selector:
    matchLabels:
      app: reader
  template:
    metadata:
      labels:
        app: reader
    spec:
      containers:
      - name: nginx
        image: nginx:stable-alpine
        ports:
        - containerPort: 80
        volumeMounts:
        - name: fileserver
          mountPath: /usr/share/nginx/html # the shared directory 
          readOnly: true
      volumes:
      - name: fileserver
        persistentVolumeClaim:
          claimName: fileserver

reader Pod 将从在所有 Pod 之间共享的路径 /usr/share/nginx/html 读取数据。

  1. 部署 reader Pod:

    kubectl apply -f reader-fs.yaml
    
  2. 查询 Pod 列表以验证 reader 副本正在运行:

    kubectl get pods
    

    输出类似于以下内容:

    NAME                      READY   STATUS    RESTARTS   AGE
    reader-66b8fff8fd-jb9p4   1/1     Running   0          3m30s
    

创建 writer Pod

writer Pod 将定期写入其他 writer 和 reader Pod 可以访问的共享文件。writer Pod 会将其主机名写入共享文件以记录其存在。

writer Pod 使用的映像是用于实用程序和生产应用的 Alpine Linux 自定义映像。它包含一个脚本 indexInfo.html,该脚本将获取最新 writer 的元数据,并记录所有唯一 writer 的数量和写入总数。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: writer
spec:
  replicas: 2 # start with 2 replicas
  selector:
    matchLabels:
      app: writer
  template:
    metadata:
      labels:
        app: writer
    spec:
      containers:
      - name: content
        image: us-docker.pkg.dev/google-samples/containers/gke/stateful-workload:latest
        volumeMounts:
        - name: fileserver
          mountPath: /html # the shared directory
        command: ["/bin/sh", "-c"]
        args:
        - cp /htmlTemp/indexInfo.html /html/index.html;
          while true; do
          echo "<b> Date :</b> <text>$(date)</text> <b> Writer :</b> <text2> ${HOSTNAME} </text2> <br>  " >> /html/indexData.html;
          sleep 30;  
          done
      volumes:
      - name: fileserver
        persistentVolumeClaim:
          claimName: fileserver

在本教程中,writer Pod 每 30 秒向路径 /html/index.html 写入一次。修改 sleep 数字值可使用不同的写入频率。

  1. 部署 writer Pod:

    kubectl apply -f writer-fs.yaml
    
  2. 查询 Pod 列表,以验证 writer Pod 正在运行:

    kubectl get pods
    

    输出类似于以下内容:

    NAME                      READY   STATUS    RESTARTS   AGE
    reader-66b8fff8fd-jb9p4   1/1     Running   0          3m30s
    writer-855565fbc6-8gh2k   1/1     Running   0          2m31s
    writer-855565fbc6-lls4r   1/1     Running   0          2m31s
    

将 reader 工作负载公开给 Service 负载均衡器并访问 reader 工作负载

如需在集群外部公开工作负载,请创建 LoadBalancer 类型的 Service。此类型的 Service 会创建具有可通过互联网访问的 IP 地址的外部负载均衡器。

  1. 创建一个名为 reader-lbLoadBalancer 类型的 Service:

    kubectl create -f loadbalancer.yaml
    
  2. 监视该部署,直至看到 GKE 为 reader-lb Service 分配了 EXTERNAL-IP

    kubectl get svc --watch
    

    Service 准备就绪后,EXTERNAL-IP 列会显示负载均衡器的公共 IP 地址:

      NAME         TYPE           CLUSTER-IP    EXTERNAL-IP     PORT(S)        AGE
      kubernetes   ClusterIP      10.8.128.1    <none>          443/TCP        2d21h
      reader-lb    LoadBalancer   10.8.131.79   34.71.232.122   80:32672/TCP   2d20h
    
  3. Ctrl+C 终止监视过程。

  4. 使用网络浏览器访问分配给负载均衡器的 EXTERNAL-IP。该页面每 30 秒刷新一次。writer Pod 越多,频率越高,它显示的条目就越多。

如需详细了解负载均衡器服务,请参阅 loadbalancer.yaml

扩容 writer

由于 PV accessMode 设置为 ReadWriteMany,因此 GKE 可以增加 Pod 的数量,使更多 writer Pod 能够写入此共享卷(或者更多 reader 可以读取它们)。

  1. writer 扩容到五个副本:

    kubectl scale deployment writer --replicas=5
    

    输出类似于以下内容:

    deployment.extensions/writer scaled
    
  2. 验证正在运行的副本数:

    kubectl get pods
    

    输出类似于以下内容:

    NAME                      READY   STATUS    RESTARTS   AGE
    reader-66b8fff8fd-jb9p4   1/1     Running   0          11m
    writer-855565fbc6-8dfkj   1/1     Running   0          4m
    writer-855565fbc6-8gh2k   1/1     Running   0          10m
    writer-855565fbc6-gv5rs   1/1     Running   0          4m
    writer-855565fbc6-lls4r   1/1     Running   0          10m
    writer-855565fbc6-tqwxc   1/1     Running   0          4m
    
  3. 使用网络浏览器再次访问分配给负载均衡器的 EXTERNAL-IP

此时,您已对集群进行了配置和扩容,可以支持五个有状态 writer Pod。多个 writer Pod 同时写入同一文件。reader Pod 也可以轻松扩容。

可选:访问 writer Pod 中的数据

本部分演示如何使用命令行界面访问 reader 或 writer Pod。您可以看到 writer 写入和 reader 读取的共享组件。

  1. 获取 writer Pod 名称:

    kubectl get pods
    

    输出类似于以下内容:

    NAME                      READY   STATUS    RESTARTS   AGE
    writer-5465d65b46-7hxv4   1/1     Running   0          20d
    

    记下 writer Pod 的主机名(示例:writer-5465d65b46-7hxv4)。

  2. 运行以下命令以访问 writer Pod:

    kubectl exec -it WRITER_HOSTNAME -- /bin/sh
    
  3. 查看文件 indexData.html 中的共享组件:

    cd /html
    cat indexData.html
    
  4. 清除 indexData.html 文件:

    echo '' > indexData.html
    

    刷新托管 EXTERNAL-IP 地址的网络浏览器以查看变化。

  5. 退出环境:

    exit
    

清理

为避免因本教程中使用的资源导致您的 Google Cloud 账号产生费用,请删除包含这些资源的项目,或者保留项目但删除各个资源。

删除项目

  1. In the Google Cloud console, go to the Manage resources page.

    Go to Manage resources

  2. In the project list, select the project that you want to delete, and then click Delete.
  3. In the dialog, type the project ID, and then click Shut down to delete the project.

逐个删除资源

  1. 删除负载均衡器 Service:

    kubectl delete service reader-lb
    

    等待直到为 reader 服务预配的负载均衡器被删除

  2. 验证列表返回 Listed 0 items

    gcloud compute forwarding-rules list
    
  3. 删除 Deployment

    kubectl delete deployment writer
    kubectl delete deployment reader
    
  4. 验证 Pod 已删除并返回 No resources found in default namespace.

    kubectl get pods
    
  5. 删除 PVC。由于保留政策设置为 delete,此命令还将删除 PV 和 Filestore 实例

    kubectl delete pvc fileserver
    
  6. 删除 GKE 集群:

    gcloud container clusters delete stateful-cluster --zone=COMPUTE_ZONE
    

    此命令将删除构成 GKE 集群的资源,包括 reader 和 writer Pod。

后续步骤