极客天成有个很厉害的scaleflash网络文件系统,充分利用rdma的无损网络特性,并进一步发扬光大。研发出来了可以顶替EMC盘阵的存储系统,可以在其上跑Oracle等数据库系统,真的是国货之光了。

帮它们改写了一个csi的存储插件,记录一下,基于yandex的s3 csi而来,这样既可以跑到底层上提供文件块设备,也可以上升到类似NFS或者S3的层次提供文件系统,能满足大多数需求。那源代码就绝对不提供了,只说一下过程:

CSI PLUGIN的使用方法:

一、导入镜像

csi存储插件的image

cr.yandex/crp9ftr22d26age3hulg/csi-s3:0.42.66

文件:csi.tar

将文件放到所有worknode上,导入本地镜像库:

ctr --address /run/k3s/containerd/containerd.sock -n k8s.io images import /root/csi.tar

crictl -r unix:///run/k3s/containerd/containerd.sock images

看到有 cr.yandex/crp9ftr22d26age3hulg/csi-s3:0.42.66 既是导入成功

image-20250807151100045

二、安装controller和nodeserver

安装driver

文件:driver.yaml

kubectl apply -f driver.yaml

安装controller

文件:controller.yaml

kubectl apply -f controller.yaml

安装nodeserver

文件:nodeserver.yaml

解释一下,controller是一个statefulset,整个集群运行一个即可;而nodeservershi则是一个daemonset,每个worknode都会运行一个副本

image-20250807151213874

上面 worknode 是3个,所以有3个副本

三、scaleflash 块设备的使用

块设备最小单位是G,所以如果需求了100M,那也是1G。

首先必须定义一个storageclass,以后使用这一个storageclass就可以了:

cat << EOF >scaleflash-storageclass.yaml
---
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: scaleflash
provisioner: ru.yandex.s3.csi
parameters:
  mounter: scaleflash
  clstID: "nvmatrix_101"
  fsType: "xfs"
EOF

kubectl apply -f scaleflash-storageclass.yaml

再定义pvc

cat << EOF > csi-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: csi-pvc
  namespace: default
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 100M
  storageClassName: scaleflash
EOF

kubectl apply -f csi-pvc.yaml

定义Pod使用这个pvc

cat << EOF > nginx-csi-pvc.yaml
---
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod-s3
spec:
  containers:
  - name: nginx-pod-s3
    image: docker.io/library/nginx:latest
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - mountPath: "/usr/share/nginx/html"
      name: dynamic-storage
  volumes:
  - name: dynamic-storage
    persistentVolumeClaim:
      claimName: csi-pvc
EOF

kubectl apply -f nginx-csi-pvc.yaml

进入pod,看到lun已经被mount上了,就ok了

image-20250807151232387

反向删除掉Pod和pvc资源:

kubectl delete -f nginx-csi-pvc.yaml
kubectl delete -f csi-pvc.yaml

验证statefulset:

cat << EOF > stateful.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: statefulset-smb
  namespace: default
  labels:
    app: nginx
spec:
  serviceName: statefulset-smb
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: statefulset-smb
          image: docker.io/library/nginx:latest
          imagePullPolicy: IfNotPresent
          command:
            - "/bin/bash"
            - "-c"
            - set -euo pipefail; while true; do echo $(date) >> /mnt/data/outfile; sleep 1; done
          volumeMounts:
            - name: persistent-storage
              mountPath: /mnt/data
              readOnly: false
  updateStrategy:
    type: RollingUpdate
  selector:
    matchLabels:
      app: nginx
  volumeClaimTemplates:
    - metadata:
        name: persistent-storage
        namespace: default
      spec:
        storageClassName: scaleflash
        accessModes:
          - ReadWriteOnce
        resources:
          requests:
            storage: 1Gi
EOF

kubectl apply -f stateful.yaml

可以放缩副本数,然后观察 /mnt/data/outfile 是否带有刚启动时候的时间戳来确定卷是否是保留的。

四、nvfile的使用(类似NFS)

注意点:rootPath 必须是存在/mnt下,因为只有/mnt被挂进了controller,挂/进去是不行的,所以其它的都无法识别出来

卷容量大小跟NFS一样没有意义,写了也不起任何作用。

定义storageclass

cat << EOF > storageclass01.yaml
---
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: nvfile
provisioner: ru.yandex.s3.csi
parameters:
  mounter: nvfile
  rootPath: "/mnt/nvfile"
  modePerm: "0777"
EOF

kubectl apply -f storageclass01.yaml

注意上面,没有定义subPath,所以新目录都会被建立在/mnt/nvfile下。

如果要定义在某个子目录(比如prod)下, 可以再定义一个storageClass,到时候引用这个新storageclass即可

---
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: nvfile-prod
provisioner: ru.yandex.s3.csi
parameters:
  mounter: nvfile
  rootPath: "/mnt/nvfile"
  subPath: "/prod"
  modePerm: "0777"

定义pvc

cat << EOF > nvfile-pvc.yaml
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nvfile-pvc
  namespace: default
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 100M
  storageClassName: nvfile
EOF

kubectl apply -f nvfile-pvc.yaml

定义一个Pod,使用上面的pvc

cat << EOF > nginx-nvfile.yaml
---
apiVersion: v1
kind: Pod
metadata:
  name: nginx-nvfile
spec:
  containers:
  - name: nginx-nvfile
    image: docker.io/library/nginx:latest
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - mountPath: "/usr/share/nginx/html"
      name: dynamic-storage
  volumes:
  - name: dynamic-storage
    persistentVolumeClaim:
      claimName: nvfile-pvc
EOF

kubectl apply -f nginx-nvfile.yaml

进入容器,看到nvfile_nodev的mount点就是成功

image-20250807151249979

statefulset的验证:

cat << EOF > stateful-nvfile.yaml
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: statefulset-nvfile-ng
  namespace: default
  labels:
    app: nvfile-nginx
spec:
  serviceName: statefulset-nvfie-ng
  replicas: 3
  template:
    metadata:
      labels:
        app: nvfile-nginx
    spec:
      containers:
        - name: statefulset-nvfile-ng
          image: docker.io/library/nginx:latest
          imagePullPolicy: IfNotPresent
          command:
            - "/bin/bash"
            - "-c"
            - set -euo pipefail; while true; do echo $(date) >> /mnt/smb/outfile; sleep 1; done
          volumeMounts:
            - name: pvc
              mountPath: /mnt/smb
              readOnly: false
  updateStrategy:
    type: RollingUpdate
  selector:
    matchLabels:
      app: nvfile-nginx
  volumeClaimTemplates:
    - metadata:
        name: pvc
      spec:
        storageClassName: nvfile
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: 1Gi
EOF

kubectl apply -f stateful-nvfile.yaml

同样进行伸缩,查看mount点上的文件/mnt/smb/outfile,是否带有刚启动时候的时间戳来判断是否为原始卷

五、S3的使用

S3的话就没有任何约束,只要能跑S3协议,虚机也可以用。

首先建立个minio的S3来模拟,因为9000端口被占,所以用8000端口

http://192.168.66.101:8000

username: abcdefg

password: abcdefg

进入后,gen一对key,赋予S3的所有权限,确保可以建立新bucket

{
 "Version": "2012-10-17",
 "Statement": [
  {
   "Effect": "Allow",
   "Action": [
    "s3:*"
   ],
   "Resource": [
    "arn:aws:s3:::*"
   ]
  }
 ]
}

key:

  accessKeyID: lj7mL2gAFgRCykTaaabbb
  secretAccessKey: 0YklYzUmcxYcPjZKtmvHRN3cMQrUaCraaaabbbb

然后建立secret

cat << EOF > secret-s3.yaml
---
apiVersion: v1
kind: Secret
metadata:
  name: csi-s3-secret
  # Namespace depends on the configuration in the storageclass.yaml
  namespace: kube-system
stringData:
  accessKeyID: lj7mL2gAFgRCyaaaabbbb
  secretAccessKey: 0YklYzUmcxYcPjZKtmvHRN3cMQrUaCraaaabbbb
  # For AWS set it to "https://s3.<region>.amazonaws.com", for example https://s3.eu-central-1.amazonaws.com
  endpoint: http://192.168.66.101:8000
  # For AWS set it to AWS region
  #region: ""
EOF

kubectl apply -f secret-s3.yaml

再建立storageclass

cat << EOF > s3-storageclass.yaml
---
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: csi-s3
provisioner: ru.yandex.s3.csi
parameters:
  mounter: geesefs
  # you can set mount options here, for example limit memory cache size (recommended)
  options: "--memory-limit 1000 --dir-mode 0777 --file-mode 0666"
  # to use an existing bucket, specify it here:
  #bucket: some-existing-bucket
  csi.storage.k8s.io/provisioner-secret-name: csi-s3-secret
  csi.storage.k8s.io/provisioner-secret-namespace: kube-system
  csi.storage.k8s.io/controller-publish-secret-name: csi-s3-secret
  csi.storage.k8s.io/controller-publish-secret-namespace: kube-system
  csi.storage.k8s.io/node-stage-secret-name: csi-s3-secret
  csi.storage.k8s.io/node-stage-secret-namespace: kube-system
  csi.storage.k8s.io/node-publish-secret-name: csi-s3-secret
  csi.storage.k8s.io/node-publish-secret-namespace: kube-system
EOF

kubectl apply -f s3-storageclass.yaml

建立pvc

cat << EOF > pvc-s3.yaml
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: csi-s3-pvc
  namespace: default
spec:
  accessModes:
  - ReadWriteMany
  resources:
    requests:
      storage: 5Gi
  storageClassName: csi-s3
EOF

kubectl apply -f csi-s3.yaml

建立pod

cat << EOF > nginx-s3.yaml
---
apiVersion: v1
kind: Pod
metadata:
  name: csi-s3-test-nginx
  namespace: default
spec:
  containers:
   - name: csi-s3-test-nginx
     image: docker.io/library/nginx:latest
     imagePullPolicy: IfNotPresent
     volumeMounts:
       - mountPath: /usr/share/nginx/html/s3
         name: webroot
  volumes:
   - name: webroot
     persistentVolumeClaim:
       claimName: csi-s3-pvc
       readOnly: false
EOF

kubectl apply -f nginx-s3.yaml

进入容器,看到一个pvc的卷即可

image-20250807151406970

去minio的界面,看到这个新卷对应的桶

image-20250807151433567

statefulset也一样。

s3更具体的可以参看:https://github.com/yandex-cloud/k8s-csi-s3/