K8S中部署MinIO集群提供块存储服务

动机:当前的我们的微服务之间是通过NFS共享附件等文件的,要逐步向S3演进。一段时间内同时存在S3读写和本地读写。因此搭建MinIO存储文件,提供S3访问,同时挂载到POD内部提供本地访问。

此为过渡方案,风险点:

  • 性能损耗:csi-s3模拟块存储会有额外开销,性能远不如原生块存储,随机读写性能差
  • 稳定性风险:MinIO需手动保障集群高可用(如多副本、纠删码),csi-s3插件的生产级案例较少,故障排查难度高于原生块存储CSI(如Ceph CSI)。
  • 运维复杂:在kubernetes中同时维护MinIO集群和csi-s3插件

集群中部署MinIO

在Kubernetes集群中拿4个节点专门部署MinIO,每个节点提供一块本地磁盘,每块磁盘大小一致500G。

由于节点数量 4 小于默认的 EC:4+2 所需的最小节点数 6,MinIO 会自动降级为 单副本 + 校验块 策略,具体为 EC:2+2(2 数据块 + 2 校验块),以适配 4 节点集群的容错能力(最多允许 2 节点故障而不丢失数据)。

因此总容量为1000G。

使用LocalVolume做存储

创建4个LocalVolume,为节点上跑的MinIO实例提供持久存储。

四个LocalVolume只有LocalVolume名称所在的节点不同。 LocalVolume名称只要区别于其他PV的名字就可以了,MinIO的StatefulSet通过PVC绑定PV,绑定时只匹配存储类名和PV容量。

yaml 复制代码
# pv-1.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: node1-local-volume   # LocalVolume名称  
spec:
  capacity:
    storage: 500Gi # PV容量
  accessModes:
    - ReadWriteOnce  
  persistentVolumeReclaimPolicy: Retain  # 默认值为 Retain
  storageClassName: minio-local-storage  # 存储类名,这是自定义的
  local:
    path: /data/minio  # 本地磁盘挂载路径
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - 192.168.64.9 # 所在的节点

PersistentVolume的persistentVolumeReclaimPolicy有2种取值:

Retain

  • 当 PVC 被删除后,PV 会从 Bound(绑定)状态变为 Released(已释放),但不会自动删除,也不会清除 PV 中的数据
  • 适合需要手动管理数据的场景(如数据需要备份、迁移或二次使用)
  • Released 状态的 PV 无法直接被新的 PVC 绑定,需手动操作(如删除 PV 重新创建,或清理数据后修改 PV 状态为 Available

Delete

  • 当 PVC 被删除后,PV 会自动被 Kubernetes 删除,同时关联的底层存储资源(如云厂商的云盘、分布式存储的卷)也会被删除,数据会被清除
  • 适合临时存储、测试环境,或希望自动清理资源的场景(避免存储资源泄漏)
  • 依赖存储插件支持(如 AWS EBS、GCE PD、Ceph RBD 等通常支持)

存储类 StorageClass

yaml 复制代码
# minio-storage-class.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: minio-local-storage                # 存储类名和上面的对应
provisioner: kubernetes.io/no-provisioner  # Local PV 无动态供应器
volumeBindingMode: WaitForFirstConsumer    # 等待 Pod 调度后再绑定 PV

StorageClass 的 reclaimPolicy 用于为动态创建的PV设定默认回收策略,默认值为 Delete。LocalVolume 不支持动态 provisioning,必须由管理员手动创建 PV 并指定 local 类型, persistentVolumeReclaimPolicy 由用户指定(若未指定则默认 Retain),与 StorageClass 无关

MinIO集群,四节点

yaml 复制代码
apiVersion: v1
kind: Service
metadata:
  labels:
    app: minio
  name: minio-svc
spec:
  ports:
  - name: http
    port: 9000
    protocol: TCP
    targetPort: 9000
  - name: console
    port: 8000
    protocol: TCP
    targetPort: 8000
  selector:
    app: minio
  type: ClusterIP
---
apiVersion: v1
kind: Secret
metadata:
  name: minio-secret
stringData:
  password: pwd4minio@qx   # 管理员密码
  username: admin          # 管理员账户
type: kubernetes.io/basic-auth
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: minio
spec:
  podManagementPolicy: Parallel # 适用于 Pod 之间无严格依赖关系,可以快速完成创建或删除
  replicas: 4
  selector:
    matchLabels:
      app: minio
  serviceName: minio-svc
  template:
    metadata:
      labels:
        app: minio
    spec:
      affinity:
        nodeAffinity: # 节点亲和调度,打了hcm-minio=minio标签的节点上才有磁盘(local pv)
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: hcm-minio
                operator: In
                values:
                - "minio"
        podAntiAffinity: # pod反亲和调度,避免两个pod调度到一个节点上争夺同一磁盘(local pv)
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - minio
            topologyKey: kubernetes.io/hostname
      containers:
      - args:
        - server
        - http://minio-{0...3}.minio-svc.default.svc.cluster.local/data
        - --console-address
        - :8000   # Web 控制台
        - --address
        - :9000   # S3 API 端口
        env:
        - name: MINIO_ROOT_USER # 通过环境变量指定账户名密码
          valueFrom:
            secretKeyRef:
              key: username
              name: minio-secret
        - name: MINIO_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              key: password
              name: minio-secret
        image: m.daocloud.io/minio/minio:RELEASE.2024-09-22T00-33-43Z
        imagePullPolicy: IfNotPresent
        name: minio
        ports:
        - containerPort: 9000
          name: http
          protocol: TCP
        - containerPort: 8000
          name: console
          protocol: TCP
        resources:
          limits:
            cpu: 12
            memory: 32Gi
          requests:
            cpu: 2
            memory: 4Gi
        volumeMounts:
        - mountPath: /data
          name: minio
  volumeClaimTemplates:
  - metadata:
      name: minio
    spec:
      storageClassName: minio-local-storage # 存储类
      accessModes:
      - ReadWriteOnce
      resources:
        requests:
          storage: 500Gi                    # 用于和PV中的容量匹配。

PV和PVC中的容量仅用于声明、绑定。MinIO将bucket挂载挂载到POD中模拟块存储时,真正限制容量的是bucket。

部署CSI-S3

公开的 CSI-S3 插件并不多,github上受欢迎的两个的项目也不足1000 star,这里用了 github.com/yandex-clou... 。为什么?因为这个项目的中的镜像(它的examples中)有现成的。

先部署csi-s3

部署 /deploy/kubernetes 下的三个文件 csi-s3.yaml、driver.yaml和provisioner.yaml

例子

例子在 /deploy/kubernetes/examples目录下,下面稍作了改动,是自己的环境中用到的

secret & storageclass

yaml 复制代码
apiVersion: v1
kind: Secret
metadata:
  namespace: kube-system
  name: csi-s3-secret
stringData:
  accessKeyID: admin
  secretAccessKey: pwd4minio@qx
  endpoint: http://minio-svc.default.svc.cluster.local:9000
  region: ""
---
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

动态提供PV(bucket)

yaml 复制代码
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: csi-s3-pvc
  namespace: default
spec:
  accessModes:
  - ReadWriteMany
  resources:
    requests:
      storage: 5Gi
  storageClassName: csi-s3
---
apiVersion: v1
kind: Pod
metadata:
  name: csi-s3-test-nginx
  namespace: default
spec:
  containers:
   - name: csi-s3-test-nginx
     image: nginx
     volumeMounts:
       - mountPath: /usr/share/nginx/html/s3
         name: webroot
  volumes:
   - name: webroot
     persistentVolumeClaim:
       claimName: csi-s3-pvc
       readOnly: false
相关推荐
wei_shuo2 小时前
从云原生部署到智能时序分析:基于 Kubernetes 的 Apache IoTDB 集群实战与 TimechoDB 国产化增强特性深度解析
云原生·kubernetes·iotdb
做运维的阿瑞3 小时前
Kubernetes 原生滚动更新(Rolling Update)完整实践指南
云原生·容器·kubernetes
神秘人X7073 小时前
K8s Pod生命周期完全指南
容器·kubernetes
!chen11 小时前
k8s-Pod中的网络通信
网络·docker·kubernetes
熙客15 小时前
Kubernetes是如何保证有状态应用数据安全和快速恢复的
mysql·云原生·容器·kubernetes
似水流年 光阴已逝17 小时前
Kubernetes Pod 基本原理:全面详解
云原生·容器·kubernetes·pod
高旭博20 小时前
10. kubernetes资源——statefulset有状态负载
云原生·容器·kubernetes
_Walli_21 小时前
k8s集群搭建(七)-------- 微服务间的调用
微服务·容器·kubernetes
马达加斯加D21 小时前
k8s --- resource: Pod, ReplicaSet and Deployment
云原生·容器·kubernetes