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

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

背景

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

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

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

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

使用 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/<LOCATION>/<INSTANCE_NAME>/<FILE_SHARE_NAME>"
    volumeAttributes:
      ip: <IP_ADDRESS> # Modify this to Pre-provisioned Filestore instance IP
      volume: <FILE_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 和一个 writer Pod。本教程使用 Kubernetes Deployment 来创建 Pod。Deployment 是一个 Kubernetes API 对象,可让您运行在集群节点中分布的多个 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