Kubernetes权威指南——共享存储原理(二)

Kubernetes权威指南第5版:从Docker到Kubernetes实践全接触 快速深入学习kubernetes 阅读详情

5、StorageClass详解

StorageClass作为对存储资源的抽象定义,对用户设置的PVC申请屏蔽后端存储的细节,一方面减少了用户对于存储资源细节的关注,另一方面减轻了管理员手工管理PV的工作,由系统自动完成PV的创建和绑定,实现了动态的资源供应。基于StorageClass的动态资源供应模式将逐步成为云平台的标准存储配置模式。

StorageClass的定义主要包括名称、后端存储的提供者(provisioner)和后端存储的相关参数配置。StorageClass一旦被创建出来,则将无法修改。如需更改,则只能删除原StorageClass的定义重建。下例定义了一个名为standard的StorageClass,提供者为aws-ebs,其参数设置了一个type,值为gp2:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: standard        # StorageClass 的名称(集群中的默认存储类通常取名为 standard)
provisioner: kubernetes.io/aws-ebs  # 动态卷供应商,指定使用 AWS EBS 插件
parameters:
  type: gp2             # EBS 卷类型参数,gp2 表示通用型 SSD (General Purpose SSD)

5.1、StorageClass的关键配置参数

5.1.1、提供者(Provisioner)

描述存储资源的提供者,也可以看作后端存储驱动。目前Kubernetes支持的Provisioner都以“ kubernetes.io/”为开头,用户也可以使用自定义的后端存储提供者。为了符合StorageClass的用法,自定义Provisioner需要符合存储卷的开发规范,详见https://github.com/kubernetes/community/blob/master/contributors/design-proposals/volume-provisioning.md的说明。

5.1.2、参数(Parameters

后端存储资源提供者的参数设置,不同的Provisioner包括不同的参数设置。某些参数可以不显示设定,Provisioner将使用其默认值。

接下来通过几种常见的Provisioner对StorageClass的定义进行详细说明。

  • 1)AWS EBS存储卷

    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: slow                # StorageClass 的名称(此处虽然叫 slow,但实际配置了高性能 io1 类型)
    provisioner: kubernetes.io/aws-ebs  # 动态卷供应商,指定使用 AWS EBS 插件
    parameters:
      type: io1                 # EBS 卷类型参数,io1 表示 provisioned IOPS SSD(适用于高吞吐量、低延迟的关键业务数据库)
      zone: us-east-1d          # 指定动态创建云硬盘所在的 AWS 可用区 (Availability Zone)
      iopsPerGB: "10"           # 每 GB 盘容量所分配的 IOPS 比例(io1 类型专属参数,用于计算最大 IOPS)
    

    参数说明如下(详细说明请参考AWS EBS文档)​。

    • type:可选项为io1,gp2,sc1,st1,默认值为gp2。
    • zone:AWS zone的名称。
    • iopsPerGB:仅用于io1类型的Volume,意为每秒每GiB的I/O操作数量。
    • encrypted:是否加密。
    • kmsKeyId:加密时的Amazon Resource Name。
  • 2)GCE PD存储卷

    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: slow                # StorageClass 的名称(此处命名为 slow,代表标准性能存储)
    provisioner: kubernetes.io/gce-pd  # 动态卷供应商,指定使用 Google Compute Engine (GCE) 磁盘插件
    parameters:
      type: pd-standard         # 磁盘类型参数,pd-standard 表示标准机械硬盘(HDD),适用于吞吐量要求不高、成本敏感的场景
      zone: us-central1-a       # 指定动态创建云硬盘所在的具体 GCP 可用区 (Availability Zone)
    

    参数说明如下(详细说明请参考GCE文档)​。

    • type:可选项为pd-standard、pd-ssd,默认值为pd-standard。
    • zone:GCE zone名称。
  • 3)GlusterFS存储卷

    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: slow                # StorageClass 的名称
    provisioner: kubernetes.io/glusterfs  # 动态卷供应商,指定使用 GlusterFS 插件(通常结合 Heketi 管理器)
    parameters:
      resturl: "http://127.0.0.1:8081"      # Heketi REST API 的服务地址,用于自动化动态卷的创建与删除
      clusterid: "630372ccdc720a92c681fb928f27b53f"  # Heketi 管理的 GlusterFS 集群唯一标识符 (Cluster ID)
      restauthenabled: "true"               # 是否启用 Heketi REST API 的身份认证
      restuser: "admin"                     # 用于访问 Heketi REST API 的用户名
      secretNamespace: "default"            # 存储访问 Heketi 所需凭据的 Kubernetes Secret 所在的命名空间
      secretName: "heketi-secret"           # 存储 Heketi 认证密码的 Kubernetes Secret 名称
      gidMin: "40000"                       # 动态分配卷时的最小 GID(组 ID),用于控制访问权限
      gidMax: "50000"                       # 动态分配卷时的最大 GID(组 ID)
      volumetype: "replicate:3"             # 卷类型配置,此处设置为 3 副本模式 (replicate:3),提供高可用和数据冗余
    

    参数说明如下(详细说明请参考GlusterFS和Heketi的文档)​。

    • resturl:Gluster REST服务(Heketi)的URL地址,用于自动完成GlusterFSvolume的设置。
    • restauthenabled:是否对Gluster REST服务启用安全机制。
    • restuser:访问Gluster REST服务的用户名。
    • secretNamespace和secretName:保存访问Gluster REST服务密码的Secret资源对象名。
    • clusterid:GlusterFS的Cluster ID。
    • gidMin和gidMax:StorageClass的GID范围,用于动态资源供应时为PV设置的GID。
    • volumetype:设置GlusterFS的内部Volume类型,例如replicate:3(Replicate类型,3份副本)​;disperse:4:2(Disperse类型,数据4份,冗余两份;​“none”​(Distribute类型)​。
  • 4)OpenStack Cinder存储卷

    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: gold                # StorageClass 的名称(此处命名为 gold,通常用于代表高性能或优选存储层)
    provisioner: kubernetes.io/cinder  # 动态卷供应商,指定使用 OpenStack Cinder 插件
    parameters:
      type: fast                # 存储后端类型参数(例如对应 OpenStack 中配置的 SSD 或高性能卷类型)
      availability: nova        # 指定 OpenStack 卷所在的可用区(通常与 Compute 服务的可用域相对应,如 nova)
    

    参数说明如下。

    • type:Cinder的VolumeType,默认值为空。
    • availability:Availability Zone,默认值为空。

    其他Provisioner的StorageClass相关参数设置请参考它们各自的配置手册。

5.2、设置默认的StorageClass

要在系统中设置一个默认的StorageClass,则首先需要启用名为DefaultStorageClass的admission controller,即在kube-apiserver的命令行参数–admission-control中增加:

--admission-control=...,DefaultStorageClass

然后,在StorageClass的定义中设置一个annotation:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: gold                # StorageClass 的名称(此处命名为 gold,代表黄金/高性能存储层)
  annotations:
    storageclass.beta.kubernetes.io/is-default-class: "true"  # 关键注解:将此存储类设为集群默认存储类。当用户创建 PVC 时若未显式指定 storageClassName,将自动使用该存储类
provisioner: kubernetes.io/gce-pd  # 动态卷供应商,指定使用 Google Compute Engine (GCE) 磁盘插件
parameters:
  type: pd-ssd              # 磁盘类型参数,pd-ssd 表示高性能固态硬盘 (SSD),提供极高的 IOPS 和低延迟

通过kubectl create命令创建成功后,查看StorageClass列表,可以看到名为gold的StorageClass被标记为default:

# kubectl get sc
NAME        TYPE
gold (default)   kubernetes.io/gce-pd

6、动态存储管理实战:GlusterFS

本节以GlusterFS为例,从定义StorageClass、创建GlusterFS和Heketi服务、用户申请PVC到创建Pod使用存储资源,对StorageClass和动态资源分配进行详细说明,进一步剖析Kubernetes的存储机制。

6.1、准备工作

为了能够使用GlusterFS,首先在计划用于GlusterFS的各Node上安装GlusterFS客户端:

# yum install glusterfs glusterfs-fuse

GlusterFS管理服务容器需要以特权模式运行,在kube-apiserver的启动参数中增加:

--allow-privileged=true

给要部署GlusterFS管理服务的节点打上“storagenode=glusterfs”的标签,是为了将GlusterFS容器定向部署到安装了GlusterFS的Node上:

# kubectl label node k8s-node-1 storagenode=glusterfs
# kubectl label node k8s-node-2 storagenode=glusterfs
# kubectl label node k8s-node-3 storagenode=glusterfs

6.2、创建GlusterFS管理服务容器集群

GlusterFS管理服务容器以DaemonSet的方式进行部署,确保在每个Node上都运行一个GlusterFS管理服务。glusterfs-daemonset.yaml的内容如下:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: glusterfs             # DaemonSet 名称
  labels:
    glusterfs: daemonset      # 标签
  annotations:
    description: GlusterFS DaemonSet
    tags: glusterfs
spec:
  selector:
    matchLabels:
      glusterfs-node: pod     # 现代化 apps/v1 必须的 selector 配置,用于匹配管理的 Pod
  template:
    metadata:
      name: glusterfs
      labels:
        glusterfs-node: pod   # Pod 标签,与上面的 matchLabels 相对应
    spec:
      hostNetwork: true       # 允许 Pod 使用宿主机的网络命名空间
      nodeSelector:
        storagenode: glusterfs  # 节点选择器:仅在带有 storagenode=glusterfs 标签的节点上部署守护进程
      containers:
        - image: gluster/gluster-centos:latest
          name: glusterfs
          volumeMounts:
            - name: glusterfs-heketi
              mountPath: "/var/lib/heketi"
            - name: glusterfs-run
              mountPath: "/run"
            - name: glusterfs-lvm
              mountPath: "/run/lvm"
            - name: glusterfs-etc
              mountPath: "/etc/glusterfs"
            - name: glusterfs-logs
              mountPath: "/var/log/glusterfs"
            - name: glusterfs-config
              mountPath: "/var/lib/glusterd"
            - name: glusterfs-dev
              mountPath: "/dev"
            - name: glusterfs-misc
              mountPath: "/var/lib/misc/glusterfsd"
            - name: glusterfs-cgroup
              mountPath: "/sys/fs/cgroup"
              readOnly: true
            - name: glusterfs-ssl
              mountPath: "/etc/ssl"
              readOnly: true
          securityContext:
            capabilities: {}
            privileged: true  # 赋予特权模式,以便容器内部能够操作底层存储与挂载
          readinessProbe:
            timeoutSeconds: 3
            initialDelaySeconds: 60
            exec:
              command:
                - "/bin/bash"
                - "-c"
                - systemctl status glusterd.service  # 探针:检查 glusterd 服务状态
          livenessProbe:
            timeoutSeconds: 3
            initialDelaySeconds: 60
            exec:
              command:
                - "/bin/bash"
                - "-c"
                - systemctl status glusterd.service  # 存活探针
      volumes:
        - name: glusterfs-heketi
          hostPath:
            path: "/var/lib/heketi"
        - name: glusterfs-run
          hostPath:
            path: "/run"
        - name: glusterfs-lvm
          hostPath:
            path: "/run/lvm"
        - name: glusterfs-etc
          hostPath:
            path: "/etc/glusterfs"
        - name: glusterfs-logs
          hostPath:
            path: "/var/log/glusterfs"
        - name: glusterfs-config
          hostPath:
            path: "/var/lib/glusterd"
        - name: glusterfs-dev
          hostPath:
            path: "/dev"
        - name: glusterfs-misc
          hostPath:
            path: "/var/lib/misc/glusterfsd"
        - name: glusterfs-cgroup
          hostPath:
            path: "/sys/fs/cgroup"
        - name: glusterfs-ssl
          hostPath:
            path: "/etc/ssl"
# kubectl create -f glusterfs-daemonset.yaml
daemonset "glusterfs" created

# kubectl get po
NAME				READY		STATUS		RESTARTS		AGE
glusterfs-k2src		1/1			Running 	0				1m
glusterfs-q32z2		1/1			Running 	0				1m

6.3、创建Heketi服务

Heketi是一个提供RESTful API管理GlusterFS卷的框架,并能够在OpenStack、Kubernetes、OpenShift等云平台上实现动态存储资源供应,支持GlusterFS多集群管理,便于管理员对GlusterFS进行操作。下图简单描述了Heketi的作用。

在这里插入图片描述
在部署Heketi服务之前,需要为它创建一个ServiceAccount对象:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: heketi-service-account  # 服务账号名称,通常用于赋予 Heketi 访问 Kubernetes API 的权限
kubectl create -f heketi-service-account.yaml

部署Heketi服务:

# heketi-deployment-svc.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deploy-heketi
  labels:
    glusterfs: heketi-deployment
    deploy-heketi: heketi-deployment
  annotations:
    description: Defines how to deploy Heketi
spec:
  replicas: 1
  selector:
    matchLabels:
      name: deploy-heketi        # 现代化 apps/v1 必须的 selector 配置
  template:
    metadata:
      name: deploy-heketi
      labels:
        name: deploy-heketi
        glusterfs: heketi-pod
    spec:
      serviceAccountName: heketi-service-account  # 关联前面创建的 ServiceAccount
      containers:
        - image: heketi/heketi
          name: deploy-heketi
          env:
            - name: HEKETI_EXECUTOR
              value: kubernetes
            - name: HEKETI_FSTAB
              value: "/var/lib/heketi/fstab"
            - name: HEKETI_SNAPSHOT_LIMIT
              value: '14'
            - name: HEKETI_KUBE_GLUSTER_DAEMONSET
              value: "y"
          ports:
            - containerPort: 8080
          volumeMounts:
            - name: db
              mountPath: "/var/lib/heketi"
          readinessProbe:
            timeoutSeconds: 3
            initialDelaySeconds: 3
            httpGet:
              path: "/hello"
              port: 8080
          livenessProbe:
            timeoutSeconds: 3
            initialDelaySeconds: 30
            httpGet:
              path: "/hello"
              port: 8080
      volumes:
        - name: db
          hostPath:
            path: "/heketi-data"

---

apiVersion: v1
kind: Service
metadata:
  name: deploy-heketi
  labels:
    glusterfs: heketi-service
    deploy-heketi: support
  annotations:
    description: Exposes Heketi Service
spec:
  selector:
    name: deploy-heketi          # 匹配 Heketi Deployment 所管理的 Pod 标签
  ports:
    - name: deploy-heketi
      port: 8080
      targetPort: 8080

需要注意的是,Heketi的DB数据需要持久化保存,建议使用hostPath或其他共享存储进行保存:

# kubectl create -f heketi-deployment-svc.yaml
deployment "deploy-heketi" created
service "deploy-heketi" created

6.4、为Heketi设置GlusterFS集群

在Heketi能够管理GlusterFS集群之前,首先要为其设置GlusterFS集群的信息。可以用一个topology.json配置文件来完成各个GlusterFS节点和设备的定义。Heketi要求在一个GlusterFS集群中至少有3个节点。在topology.json配置文件hostnames字段的manage上填写主机名,在storage上填写IP地址,devices要求为未创建文件系统的裸设备(可以有多块盘)​,以供Heketi自动完成PV(Physical Volume)​、VG(Volume Group)和LV(Logical Volume)的创建。topology.json文件的内容如下:

{
  "clusters": [
    {
      "nodes": [
      {
        "node": {
          "hostnames": {
            "manage": [
              "k8s-node-1"
            ],
            "storage": [
              "192.168.18.3"
            ]
          },
          "zone": 1
        },
        "devices": [
          "/dev/sdb"
        ]
      },
      {
        "node": {
          "hostnames": {
            "manage": [
              "k8s-node-2"
            ],
            "storage": [
              "192.168.18.4"
            ]
          },
          "zone": 1
        },
        "devices": [
          "/dev/sdb"
        ]
      },
      {
        "node": {
            "hostnames": {
              "manage": [
                "k8s-node-3"
              ],
              "storage": [
                "192.168.18.5"
              ]
            },
            "zone": 1
          },
          "devices": [
            "/dev/sdb"
          ]
        }
      ]
      }
    ]
  }

进入Heketi容器,使用命令行工具heketi-cli完成GlusterFS集群的创建:

# export HEKETI_CLI_SERVER=http://localhost:8080
# heketi-cli topology load --json=topology.json
Creating cluster ... ID: f643da1cd64691c5705932a46a95d1d5
      Creating node k8s-node-1 ... ID: 883506b091a22bd13f10bc3d0fb51223
              Adding device /dev/sdb ... OK
      Creating node k8s-node-2 ... ID: e64b879689106f82a9c4ac910a865cc8
              Adding device /dev/sdb ... OK
      Creating node k8s-node-3 ... ID: b7783484180f6a592a30baebfb97d9be
              Adding device /dev/sdb ... OK

经过这个操作,Heketi完成了GlusterFS集群的创建,同时在GlusterFS集群的各个节点的/dev/sdb盘上成功创建了PV和VG。

查看Heketi的topology信息,可以看到Node和Device的详细信息,包括磁盘空间的大小和剩余空间。此时,Volume和Brick还未创建:

# heketi-cli topology info
Cluster Id: f643da1cd64691c5705932a46a95d1d5

    Volumes:
    Nodes:

        Node Id: 883506b091a22bd13f10bc3d0fb51223
        State: online
        Cluster Id: f643da1cd64691c5705932a46a95d1d5
        Zone: 1
        Management Hostname: k8s-node-1
        Storage Hostname: 192.168.18.3
        Devices:
              Id:b474f14b0903ed03ec80d4a989f943f2   Name:/dev/sdb
State:online    Size (GiB):9       Used (GiB):0       Free (GiB):9
                      Bricks:

        Node Id: b7783484180f6a592a30baebfb97d9be
        State: online
        Cluster Id: f643da1cd64691c5705932a46a95d1d5
        Zone: 1
        Management Hostname: k8s-node-3
        Storage Hostname: 192.168.18.5
        Devices:
              Id:fac3fa5ac1de3d5bde3aa68f6aa61285   Name:/dev/sdb
State:online    Size (GiB):9       Used (GiB):0       Free (GiB):9
                      Bricks:

        Node Id: e64b879689106f82a9c4ac910a865cc8
        State: online
        Cluster Id: f643da1cd64691c5705932a46a95d1d5
        Zone: 1
        Management Hostname: k8s-node-2
        Storage Hostname: 192.168.18.4
        Devices:
              Id:05532e7db723953e8643b64b36aee1d1   Name:/dev/sdb
State:online    Size (GiB):9       Used (GiB):0       Free (GiB):9
                      Bricks:

6.5、定义StorageClass

准备工作已经就绪,集群管理员现在可以在Kubernetes集群中定义一个StorageClass了。storageclass-gluster-heketi.yaml配置文件的内容如下:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: gluster-heketi    # StorageClass 的名称
provisioner: kubernetes.io/glusterfs  # 动态卷供应商,指定使用 GlusterFS 插件
parameters:
  resturl: "http://172.17.2.2:8080"   # Heketi REST API 的服务地址(无需认证的内网或测试环境地址)
  restauthenabled: "false"            # 关闭 Heketi REST API 的身份认证(适用于便捷测试,生产环境强烈建议设为 true 并配置 Secret)

Provisioner参数必须被设置为“kubernetes.io/glusterfs”​。

resturl的地址需要被设置为API Server所在主机可以访问到的Heketi服务的某个地址,可以使用服务ClusterIP+端口号、容器IP地址+端口号,或将服务映射到物理机,使用物理机IP+NodePort。

创建这个StorageClass资源对象:

# kubectl create -f storageclass-gluster-heketi.yaml
storageclass "gluster-heketi" created

6.6、定义PVC

现在,用户可以申请一个PVC了。例如,一个用户申请一个1GiB空间的共享存储资源,StorageClass使用“gluster-heketi”​,未定义任何Selector,说明使用动态资源供应模式:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-gluster-heketi  # 持久卷声明 (PVC) 的名称
spec:
  storageClassName: gluster-heketi  # 指定关联前面创建的 Heketi StorageClass,实现动态开辟存储
  accessModes:
    - ReadWriteOnce       # 访问模式:请求单节点以读写方式挂载
  resources:
    requests:
      storage: 1Gi        # 申请的存储空间大小为 1 吉字节 (Gi)
kubectl create -f pvc-gluster-heketi.yaml

PVC的定义一旦生成,系统便将触发Heketi进行相应的操作,主要为在GlusterFS集群上创建brick,再创建并启动一个Volume。整个过程可以在Heketi的日志中查到:

......
[kubeexec] DEBUG 2017/04/26 00:51:30
/src/github.com/heketi/heketi/executors/kubeexec/kubeexec.go:250: Host:
k8s-node-1 Pod: glusterfs-ld7nh Command: gluster --mode=script volume create
vol_87b9314cb76bafacfb7e9cdc04fcaf05 replica 3
192.168.18.3:/var/lib/heketi/mounts/vg_b474f14b0903ed03ec80d4a989f943f2/brick_d0
8520c9ff7b9a0a9165f9815671f2cd/brick
192.168.18.5:/var/lib/heketi/mounts/vg_fac3fa5ac1de3d5bde3aa68f6aa61285/brick_68
18dce118b8a54e9590199d44a3817b/brick
192.168.18.4:/var/lib/heketi/mounts/vg_05532e7db723953e8643b64b36aee1d1/brick_9e
cb8f7fde1ae937011f04401e7c6c56/brick
Result: volume create: vol_87b9314cb76bafacfb7e9cdc04fcaf05: success: please
start the volume to access data
......
[kubeexec] DEBUG 2017/04/26 00:51:33
/src/github.com/heketi/heketi/executors/kubeexec/kubeexec.go:250: Host:
k8s-node-1 Pod: glusterfs-ld7nh Command: gluster --mode=script volume start
vol_87b9314cb76bafacfb7e9cdc04fcaf05
Result: volume start: vol_87b9314cb76bafacfb7e9cdc04fcaf05: success
......

查看PVC的状态,可见其已经为Bound(已绑定)​:

# kubectl get pvc
NAME                   STATUS    VOLUME                                      CAPACITY
ACCESSMODES   STORAGECLASS     AGE
pvc-gluster-heketi   Bound    pvc-783cf949-2a1a-11e7-8717-000c29eaed40   1Gi
RWX           gluster-heketi   6m

查看PV,可见系统自动创建的PV:

查看该PV的详细信息,可以看到其容量、引用的StorageClass等信息都已正确设置,状态也为Bound,回收策略则为默认的Delete。同时Gluster的Endpoint和Path也由Heketi自动完成了设置:

# kubectl describe pv pvc-783cf949-2a1a-11e7-8717-000c29eaed40
Name:           pvc-783cf949-2a1a-11e7-8717-000c29eaed40
Labels:         <none>
Annotations:    pv.beta.kubernetes.io/gid=2000
              pv.kubernetes.io/bound-by-controller=yes
              pv.kubernetes.io/provisioned-by=kubernetes.io/glusterfs
StorageClass:   gluster-heketi
Status:         Bound
Claim:          default/pvc-gluster-heketi
Reclaim Policy: Delete
Access Modes:   RWX
Capacity:       1Gi
Message:
Source:
    Type:               Glusterfs (a Glusterfs mount on the host that shares a pod's
lifetime)
    EndpointsName:      glusterfs-dynamic-pvc-gluster-heketi
    Path:               vol_87b9314cb76bafacfb7e9cdc04fcaf05
    ReadOnly:           false
Events:                 <none>

至此,一个可供Pod使用的PVC就创建成功了。接下来Pod就能通过Volume的设置将这个PVC挂载到容器内部进行使用了。

6.7、Pod使用PVC的存储资源

在Pod中使用PVC定义的存储资源非常容易,只需设置一个Volume,其类型为persistentVolumeClaim,即可轻松引用一个PVC。下例中使用一个busybox容器验证对PVC的使用,注意Pod需要与PVC属于同一个Namespace:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-gluster-heketi  # 持久卷声明 (PVC) 的名称
spec:
  storageClassName: gluster-heketi  # 指定关联前面创建的 Heketi StorageClass,实现动态开辟存储
  accessModes:
    - ReadWriteOnce       # 访问模式:请求单节点以读写方式挂载
  resources:
    requests:
      storage: 1Gi        # 申请的存储空间大小为 1 吉字节 (Gi)
kubectl create -f pvc-gluster-heketi.yaml

进入容器pod-use-pvc,在/pv-data目录下创建一些文件:

# kubectl exec -ti pod-use-pvc -- /bin/sh
/ # cd /pv-data
/ # touch a
/ # echo "hello" > b

可以验证文件a和b在GlusterFS集群中是否正确生成。

至此,使用Kubernetes最新的动态存储供应模式,配合StorageClass和Heketi共同搭建基于GlusterFS的共享存储就完成了。有兴趣的读者可以继续尝试StorageClass的其他设置,例如调整GlusterFS的Volume类型、修改PV的回收策略等。

在使用动态存储供应模式的情况下,相对于静态模式的优势至少包括如下两点。

  • (1)管理员无须预先创建大量的PV作为存储资源。
  • (2)用户在申请PVC时无法保证容量与预置PV的容量完全匹配。从Kubernetes 1.6版本开始,建议用户优先考虑使用StorageClass的动态存储供应模式进行存储管理。

7、CSI存储机制详解

Kubernetes从1.9版本开始引入容器存储接口ContainerStorage Interface(CSI)机制,用于在Kubernetes和外部存储系统之间建立一套标准的存储管理接口,通过该接口为容器提供存储服务。CSI到Kubernetes 1.10版本升级为Beta版,到Kubernetes 1.13版本升级为GA版,已逐渐成熟。

7.1、CSI的设计背景

Kubernetes通过PV、PVC、Storageclass已经提供了一种强大的基于插件的存储管理机制,但是各种存储插件提供的存储服务都是基于一种被称为“in-true”​(树内)的方式提供的,这要求存储插件的代码必须被放进 Kubernetes的主干代码库中才能被Kubernetes调用,属于紧耦合的开发模式。这种“in-tree”方式会带来一些问题:

  • 存储插件的代码需要与Kubernetes的代码放在同一代码库中,并与Kubernetes的二进制文件共同发布;
  • 存储插件代码的开发者必须遵循Kubernetes的代码开发规范;
  • 存储插件代码的开发者必须遵循Kubernetes的发布流程,包括添加对Kubernetes存储系统的支持和错误修复;
  • Kubernetes社区需要对存储插件的代码进行维护,包括审核、测试等工作;
  • 存储插件代码中的问题可能会影响Kubernetes组件的运行,并且很难排查问题;
  • 存储插件代码与Kubernetes的核心组件(kubelet和kube-controller-manager)享有相同的系统特权权限,可能存在可靠性和安全性问题。

Kubernetes已有的Flex Volume插件机制试图通过为外部存储暴露一个基于可执行程序(exec)的API来解决这些问题。尽管它允许第三方存储提供商在Kubernetes核心代码之外开发存储驱动,但仍然有两个问题没有得到很好的解决:

  • 部署第三方驱动的可执行文件仍然需要宿主机的root权限,存在安全隐患;
  • 存储插件在执行mount、attach这些操作时,通常需要在宿主机上安装一些第三方工具包和依赖库,使得部署过程更加复杂,例如部署Ceph时需要安装rbd库,部署GlusterFS时需要安装mount.glusterfs库,等等。

基于以上这些问题和考虑,Kubernetes逐步推出与容器对接的存储接口标准,存储提供方只需要基于标准接口进行存储插件的实现,就能使用Kubernetes的原生存储机制为容器提供存储服务。这套标准被称为CSI(容器存储接口)​。在CSI成为Kubernetes的存储供应标准之后,存储提供方的代码就能和Kubernetes代码彻底解耦,部署也与Kubernetes核心组件分离,显然,存储插件的开发由提供方自行维护,就能为Kubernetes用户提供更多的存储功能,也更加安全可靠。基于CSI的存储插件机制也被称为“out-of-tree”​(树外)的服务提供方式,是未来Kubernetes第三方存储插件的标准方案。

7.2、CSI存储插件的关键组件和部署架构

下图描述了Kubernetes CSI存储插件的关键组件和推荐的容器化部署架构。

在这里插入图片描述

其中主要包括两种组件:CSI Controller和CSI Node。

  • 1.CSI Controller
    CSI Controller的主要功能是提供存储服务视角对存储资源和存储卷进行管理和操作。在Kubernetes中建议将其部署为单实例Pod,可以使用StatefulSet或Deployment控制器进行部署,设置副本数量为1,保证为一种存储插件只运行一个控制器实例。

    在这个Pod内部署两个容器,如下所述。

    • (1)与Master(kube-controller-manager)通信的辅助sidecar容器。

      在sidecar容器内又可以包含external-attacher和external-provisioner两个容器,它们的功能分别如下。

      • external-attacher:监控VolumeAttachment资源对象的变更,触发针对CSI端点的ControllerPublish和ControllerUnpublish操作。
      • external-provisioner:监控PersistentVolumeClaim资源对象的变更,触发针对CSI端点的CreateVolume和DeleteVolume操作。

      这两个容器通过本地Socket(Unix Domain Socket,UDS)​,并使用gPRC协议进行通信。sidecar容器通过Socket调用CSI Driver容器的CSI接口,CSI Driver容器负责具体的存储卷操作。

  • 2.CSI Node
    CSI Node的主要功能是对主机(Node)上的Volume进行管理和操作。在Kubernetes中建议将其部署为DaemonSet,在每个Node上都运行一个Pod。

    在这个Pod中部署以下两个容器:

    • (1)与kubelet通信的辅助sidecar容器node-driver-registrar,主要功能是将存储驱动注册到kubelet中;
    • (2)CSI Driver存储驱动容器,由第三方存储提供商提供,主要功能是接收kubelet的调用,需要实现一系列与Node相关的CSI接口,例如NodePublishVolume接口(用于将Volume挂载到容器内的目标路径)​、NodeUnpublishVolume接口(用于从容器中卸载Volume)​,等等。

    node-driver-registrar容器与kubelet通过Node主机的一个hostPath目录下的unix socket进行通信。CSI Driver容器与kubelet通过Node主机的另一个hostPath目录下的unix socket进行通信,同时需要将kubelet的工作目录(默认为/var/lib/kubelet)挂载给CSI Driver容器,用于为Pod进行Volume的管理操作(包括mount、umount等)​。

7.3、CSI存储插件的使用示例

下面以csi-hostpath插件为例,对如何部署CSI插件、用户如何使用CSI插件提供的存储资源进行详细说明。

  • (1)设置Kubernetes服务启动参数。为kube-apiserver、kube-controller-manager和kubelet服务的启动参数添加:

    --feature-gates=VolumeSnapshotDataSource=true,CSINodeInfo=true,CSIDriverRegi
    stry=true
    

    这3个特性开关是Kubernetes从1.12版本引入的Alpha版功能,CSINodeInfo和CSIDriverRegistry需要手工创建其相应的CRD资源对象。

    Kubernetes 1.10版本所需的CSIPersistentVolume和MountPropagation特性开关已经默认启用,KubeletPluginsWatcher特性开关也在Kubernetes 1.12版本中默认启用,无须在命令行参数中指定。

  • (2)创建CSINodeInfo和CSIDriverRegistry CRD资源对象。
    csidriver.yaml的内容如下:

    apiVersion: apiextensions.k8s.io/v1
    kind: CustomResourceDefinition
    metadata:
      name: csidrivers.csi.storage.k8s.io  # CRD 的完整名称,通常为 plural.group
      labels:
        addonmanager.kubernetes.io/mode: Reconcile  # 标记由集群插件管理器进行调谐管理
    spec:
      group: csi.storage.k8s.io  # API 组名称
      names:
        kind: CSIDriver          # 自定义资源对应的 Kind 名称
        plural: csidrivers       # API 路径中使用的复数名称
      scope: Cluster             # 作用域:集群级别 (Cluster-scoped)
      versions:
        - name: v1               # 将 API 版本升级为标准稳定的 v1
          served: true           # 是否由 API Server 提供此版本的服务
          storage: true          # 是否为底层存储的版本(必须且只能有一个版本设置为 true)
          schema:
            openAPIV3Schema:
              type: object
              properties:
                spec:
                  description: "Specification of the CSI Driver."
                  type: object
                  properties:
                    attachRequired:
                      description: "Indicates this CSI volume driver requires an attach operation, and that Kubernetes should call attach and wait for any attach operation to complete before proceeding to mount."
                      type: boolean
                    podInfoOnMountVersion:
                      description: "Indicates this CSI volume driver requires additional pod information (like podName, podUID, etc.) during mount operations."
                      type: string
                    volumeLifecycleModes:
                      description: "volumeLifecycleModes defines what modes of volume lifecycle the CSI driver supports."
                      type: array
                      items:
                        type: string
    

    csinodeinfo.yaml的内容如下:

    apiVersion: apiextensions.k8s.io/v1
    kind: CustomResourceDefinition
    metadata:
      name: csinodeinfos.csi.storage.k8s.io  # CRD 的完整名称,通常为 plural.group
      labels:
        addonmanager.kubernetes.io/mode: Reconcile  # 标记由集群插件管理器进行调谐管理
    spec:
      group: csi.storage.k8s.io  # API 组名称
      names:
        kind: CSINodeInfo        # 自定义资源对应的 Kind 名称
        plural: csinodeinfos     # API 路径中使用的复数名称
      scope: Cluster             # 作用域:集群级别 (Cluster-scoped)
      versions:
        - name: v1               # 将 API 版本升级为标准稳定的 v1(或根据需求保留 v1alpha1/v1beta1,此处升级为标准 v1 结构)
          served: true           # 是否由 API Server 提供此版本的服务
          storage: true          # 是否为底层存储的版本(必须且只能有一个版本设置为 true)
          schema:
            openAPIV3Schema:
              type: object
              properties:
                spec:
                  description: "Specification of CSINodeInfo"
                  type: object
                  properties:
                    drivers:
                      description: "List of CSI drivers running on the node and their specs."
                      type: array
                      items:
                        type: object
                        properties:
                          name:
                            description: "The CSI driver that this object refers to."
                            type: string
                          nodeID:
                            description: "The node from the driver point of view."
                            type: string
                          topologyKeys:
                            description: "List of keys supported by the driver."
                            type: array
                            items:
                              type: string
                status:
                  description: "Status of CSINodeInfo"
                  type: object
                  properties:
                    drivers:
                      description: "List of CSI drivers running on the node and their statuses."
                      type: array
                      items:
                        type: object
                        properties:
                          name:
                            description: "The CSI driver that this object refers to."
                            type: string
                          available:
                            description: "Whether the CSI driver is installed."
                            type: boolean
                          volumePluginMechanism:
                            description: "Indicates to external components the required mechanism to use for any in-tree plugins replaced by this driver."
                            pattern: "^(in-tree|csi)$"
                            type: string
    

    使用kubectl create命令完成创建:

    # kubectl create -f csidriver.yaml
    customresourcedefinition.apiextensions.k8s.io/csidrivers.csi.storage.k8s.io
    created
    # kubectl create -f csinodeinfo.yaml
    customresourcedefinition.apiextensions.k8s.io/csinodeinfos.csi.storage.k8s.i
    o created
    
  • (3)创建csi-hostpath存储插件相关组件,包括csi-hostpath-attacher、csi-hostpath-provisioner和csi-hostpathplugin(其中包含csi-node-driver-registrar和hostpathplugin)​。其中为每个组件都配置了相应的RBAC权限控制规则,对于安全访问Kubernetes资源对象非常重要。

    csi-hostpath-attacher.yaml的内容如下:

    # ==========================================
    # 1. ServiceAccount (服务账号)
    # ==========================================
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: csi-attacher
      namespace: default   # 关联的命名空间(如需更改,请同步替换其他资源中的 namespace)
    
    ---
    
    # ==========================================
    # 2. ClusterRole (集群角色:赋予 Attacher 操作 PV、节点及卷附加的权限)
    # ==========================================
    kind: ClusterRole
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
      name: external-attacher-runner
    rules:
      - apiGroups: [""]
        resources: ["persistentvolumes"]
        verbs: ["get", "list", "watch", "update"]
      - apiGroups: [""]
        resources: ["nodes"]
        verbs: ["get", "list", "watch"]
      - apiGroups: ["csi.storage.k8s.io"]
        resources: ["csinodeinfos"]
        verbs: ["get", "list", "watch"]
      - apiGroups: ["storage.k8s.io"]
        resources: ["volumeattachments"]
        verbs: ["get", "list", "watch", "update"]
    
    ---
    
    # ==========================================
    # 3. ClusterRoleBinding (集群角色绑定)
    # ==========================================
    kind: ClusterRoleBinding
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
      name: csi-attacher-role
    subjects:
      - kind: ServiceAccount
        name: csi-attacher
        namespace: default
    roleRef:
      kind: ClusterRole
      name: external-attacher-runner
      apiGroup: rbac.authorization.k8s.io
    
    ---
    
    # ==========================================
    # 4. Role (普通角色:用于当前命名空间下的领导者选举 ConfigMap 操作)
    # ==========================================
    kind: Role
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
      name: external-attacher-cfg
      namespace: default
    rules:
      - apiGroups: [""]
        resources: ["configmaps"]
        verbs: ["get", "watch", "list", "delete", "update", "create"]
    
    ---
    
    # ==========================================
    # 5. RoleBinding (角色绑定)
    # ==========================================
    kind: RoleBinding
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
      name: csi-attacher-role-cfg
      namespace: default
    subjects:
      - kind: ServiceAccount
        name: csi-attacher
        namespace: default
    roleRef:
      kind: Role
      name: external-attacher-cfg
      apiGroup: rbac.authorization.k8s.io
    
    ---
    
    # ==========================================
    # 6. Service (服务定义)
    # ==========================================
    kind: Service
    apiVersion: v1
    metadata:
      name: csi-hostpath-attacher
      labels:
        app: csi-hostpath-attacher
    spec:
      selector:
        app: csi-hostpath-attacher
      ports:
        - name: dummy
          port: 12345
    
    ---
    
    # ==========================================
    # 7. StatefulSet (有状态工作负载:运行 csi-attacher 进程)
    # ==========================================
    kind: StatefulSet
    apiVersion: apps/v1
    metadata:
      name: csi-hostpath-attacher
    spec:
      serviceName: "csi-hostpath-attacher"
      replicas: 1
      selector:
        matchLabels:
          app: csi-hostpath-attacher
      template:
        metadata:
          labels:
            app: csi-hostpath-attacher
        spec:
          serviceAccountName: csi-attacher  # 使用上面创建的 ServiceAccount
          containers:
            - name: csi-attacher
              image: quay.io/k8scsi/csi-attacher:v1.0.1
              imagePullPolicy: IfNotPresent
              args:
                - --v=5                     # 日志详细级别
                - --csi-address=$(ADDRESS)  # 指定 CSI Socket 路径
              env:
                - name: ADDRESS
                  value: /csi/csi.sock
              volumeMounts:
                - name: socket-dir
                  mountPath: /csi
          volumes:
            - name: socket-dir
              hostPath:
                path: /var/lib/kubelet/plugins/csi-hostpath
                type: DirectoryOrCreate
    

    csi-hostpath-provisioner.yaml的内容如下:

    # ==========================================
    # 1. ServiceAccount (服务账号)
    # ==========================================
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: csi-provisioner
      namespace: default   # 关联的命名空间(如需更改,请同步替换其他资源中的 namespace)
    
    ---
    
    # ==========================================
    # 2. ClusterRole (集群角色:赋予 Provisioner 操作 Secret、PV、PVC 及存储类的权限)
    # ==========================================
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
      name: external-provisioner-runner
    rules:
      - apiGroups: [""]
        resources: ["secrets"]
        verbs: ["get", "list"]
      - apiGroups: [""]
        resources: ["persistentvolumes"]
        verbs: ["get", "list", "watch", "create", "delete"]
      - apiGroups: [""]
        resources: ["persistentvolumeclaims"]
        verbs: ["get", "list", "watch", "update"]
      - apiGroups: ["storage.k8s.io"]
        resources: ["storageclasses"]
        verbs: ["get", "list", "watch"]
      - apiGroups: [""]
        resources: ["events"]
        verbs: ["list", "watch", "create", "update", "patch"]
      - apiGroups: ["snapshot.storage.k8s.io"]
        resources: ["volumesnapshots"]
        verbs: ["get", "list"]
      - apiGroups: ["snapshot.storage.k8s.io"]
        resources: ["volumesnapshotcontents"]
        verbs: ["get", "list"]
      - apiGroups: ["csi.storage.k8s.io"]
        resources: ["csinodeinfos"]
        verbs: ["get", "list", "watch"]
      - apiGroups: [""]
        resources: ["nodes"]
        verbs: ["get", "list", "watch"]
    
    ---
    
    # ==========================================
    # 3. ClusterRoleBinding (集群角色绑定)
    # ==========================================
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
      name: csi-provisioner-role
    subjects:
      - kind: ServiceAccount
        name: csi-provisioner
        namespace: default
    roleRef:
      kind: ClusterRole
      name: external-provisioner-runner
      apiGroup: rbac.authorization.k8s.io
    
    ---
    
    # ==========================================
    # 4. Role (普通角色:用于当前命名空间下的领导者选举 Endpoints 操作)
    # ==========================================
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
      name: external-provisioner-cfg
      namespace: default
    rules:
      - apiGroups: [""]
        resources: ["endpoints"]
        verbs: ["get", "watch", "list", "delete", "update", "create"]
    
    ---
    
    # ==========================================
    # 5. RoleBinding (角色绑定)
    # ==========================================
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
      name: csi-provisioner-role-cfg
      namespace: default
    subjects:
      - kind: ServiceAccount
        name: csi-provisioner
        namespace: default
    roleRef:
      kind: Role
      name: external-provisioner-cfg
      apiGroup: rbac.authorization.k8s.io
    
    ---
    
    # ==========================================
    # 6. Service (服务定义)
    # ==========================================
    apiVersion: v1
    kind: Service
    metadata:
      name: csi-hostpath-provisioner
      labels:
        app: csi-hostpath-provisioner
    spec:
      selector:
        app: csi-hostpath-provisioner
      ports:
        - name: dummy
          port: 12345
    
    ---
    
    # ==========================================
    # 7. StatefulSet (有状态工作负载:运行 csi-provisioner 进程)
    # ==========================================
    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
      name: csi-hostpath-provisioner
    spec:
      serviceName: "csi-hostpath-provisioner"
      replicas: 1
      selector:
        matchLabels:
          app: csi-hostpath-provisioner
      template:
        metadata:
          labels:
            app: csi-hostpath-provisioner
        spec:
          serviceAccountName: csi-provisioner  # 使用上面创建的 ServiceAccount
          containers:
            - name: csi-provisioner
              image: quay.io/k8scsi/csi-provisioner:v1.0.1
              imagePullPolicy: IfNotPresent
              args:
                - "--provisioner=csi-hostpath"  # 指定供应器的名称
                - "--csi-address=$(ADDRESS)"    # 指定 CSI Socket 路径
                - "--connection-timeout=15s"    # 连接超时时间
              env:
                - name: ADDRESS
                  value: /csi/csi.sock
              volumeMounts:
                - name: socket-dir
                  mountPath: /csi
          volumes:
            - name: socket-dir
              hostPath:
                path: /var/lib/kubelet/plugins/csi-hostpath
                type: DirectoryOrCreate
    

    csi-hostpathplugin.yaml的内容如下:

    # ==========================================
    # 1. ServiceAccount (服务账号)
    # ==========================================
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: csi-node-sa
      namespace: default   # 关联的命名空间(如需更改,请同步替换其他资源中的 namespace)
    
    ---
    
    # ==========================================
    # 2. ClusterRole (集群角色:赋予节点端插件必要事件上报权限)
    # ==========================================
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
      name: driver-registrar-runner
    rules:
      - apiGroups: [""]
        resources: ["events"]
        verbs: ["get", "list", "watch", "create", "update", "patch"]
      # 注:旧版本若不使用 --kubelet-registration-path 参数,可能需要对 nodes 进行 patch,
      # 现代 CSI 架构推荐使用 registration 机制,故此处注释按需保留。
    
    ---
    
    # ==========================================
    # 3. ClusterRoleBinding (集群角色绑定)
    # ==========================================
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
      name: csi-driver-registrar-role
    subjects:
      - kind: ServiceAccount
        name: csi-node-sa
        namespace: default
    roleRef:
      kind: ClusterRole
      name: driver-registrar-runner
      apiGroup: rbac.authorization.k8s.io
    
    ---
    
    # ==========================================
    # 4. DaemonSet (守护进程集:在每个节点上部署 CSI 插件及 Registrar)
    # ==========================================
    apiVersion: apps/v1
    kind: DaemonSet
    metadata:
      name: csi-hostpathplugin
    spec:
      selector:
        matchLabels:
          app: csi-hostpathplugin
      template:
        metadata:
          labels:
            app: csi-hostpathplugin
        spec:
          serviceAccountName: csi-node-sa
          hostNetwork: true
          containers:
            # 1. Driver Registrar 边车容器:负责向 kubelet 注册 CSI 插件
            - name: driver-registrar
              image: quay.io/k8scsi/csi-node-driver-registrar:v1.0.1
              imagePullPolicy: IfNotPresent
              args:
                - --v=5
                - --csi-address=/csi/csi.sock
                - --kubelet-registration-path=/var/lib/kubelet/plugins/csi-hostpath/csi.sock
              env:
                - name: KUBE_NODE_NAME
                  valueFrom:
                    fieldRef:
                      apiVersion: v1
                      fieldPath: spec.nodeName
              volumeMounts:
                - mountPath: /csi
                  name: socket-dir
                - mountPath: /registration
                  name: registration-dir
    
            # 2. Hostpath 插件主容器
            - name: hostpath
              image: quay.io/k8scsi/hostpathplugin:v1.0.1
              imagePullPolicy: IfNotPresent
              args:
                - "--v=5"
                - "--endpoint=$(CSI_ENDPOINT)"
                - "--nodeid=$(KUBE_NODE_NAME)"
              env:
                - name: CSI_ENDPOINT
                  value: unix:///csi/csi.sock
                - name: KUBE_NODE_NAME
                  valueFrom:
                    fieldRef:
                      apiVersion: v1
                      fieldPath: spec.nodeName
              securityContext:
                privileged: true
              volumeMounts:
                - mountPath: /csi
                  name: socket-dir
                - mountPath: /var/lib/kubelet/pods
                  mountPropagation: Bidirectional
                  name: mountpoint-dir
    
          volumes:
            - name: socket-dir
              hostPath:
                path: /var/lib/kubelet/plugins/csi-hostpath
                type: DirectoryOrCreate
            - name: mountpoint-dir
              hostPath:
                path: /var/lib/kubelet/pods
                type: DirectoryOrCreate
            - name: registration-dir
              hostPath:
                path: /var/lib/kubelet/plugins_registry
                type: Directory
    

    使用kubectl create命令完成创建:

    # kubectl create -f csi-hostpath-attacher.yaml
    serviceaccount/csi-attacher created
    clusterrole.rbac.authorization.k8s.io/external-attacher-runner created
    clusterrolebinding.rbac.authorization.k8s.io/csi-attacher-role created
    role.rbac.authorization.k8s.io/external-attacher-cfg created
    rolebinding.rbac.authorization.k8s.io/csi-attacher-role-cfg created
    service/csi-hostpath-attacher created
    statefulset.apps/csi-hostpath-attacher created
    
    # kubectl create -f csi-hostpath-provisioner.yaml
    serviceaccount/csi-provisioner created
    clusterrole.rbac.authorization.k8s.io/external-provisioner-runner created
    clusterrolebinding.rbac.authorization.k8s.io/csi-provisioner-role created
    role.rbac.authorization.k8s.io/external-provisioner-cfg created
    rolebinding.rbac.authorization.k8s.io/csi-provisioner-role-cfg created
    service/csi-hostpath-provisioner created
    statefulset.apps/csi-hostpath-provisioner created
    
    # kubectl create -f csi-hostpathplugin.yaml
    serviceaccount/csi-node-sa created
    clusterrole.rbac.authorization.k8s.io/driver-registrar-runner created
    clusterrolebinding.rbac.authorization.k8s.io/csi-driver-registrar-role
    created
    daemonset.apps/csi-hostpathplugin created
    

    确保3个Pod都正常运行:

    # kubectl get pods
    NAME                             READY   STATUS     RESTARTS   AGE
    csi-hostpath-attacher-0       1/1     Running    1           4m41s
    csi-hostpath-provisioner-0    1/1     Running    0           84s
    csi-hostpathplugin-t6qzs      2/2     Running    0           39s
    

    至此就完成了CSI存储插件的部署。

  • (4)应用容器使用CSI存储。应用程序如果希望使用CSI存储插件提供的存储服务,则仍然使用Kubernetes动态存储管理机制。首先通过创建StorageClass和PVC为应用容器准备存储资源,然后容器就可以挂载PVC到容器内的目录进行使用了。

    创建一个StorageClass,provisioner为CSI存储插件的类型,在本例中为csi-hostpath:

    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: csi-hostpath-sc       # StorageClass 的名称
    provisioner: csi-hostpath     # 动态卷供应商,指定使用 CSI HostPath 插件
    reclaimPolicy: Delete         # 回收策略:当关联的 PVC 被删除时,底层的存储卷也将被自动删除 (Delete)
    volumeBindingMode: Immediate  # 卷绑定模式:立即绑定(PVC 创建后会立即触发 PV 的动态供应和绑定,而不等待 Pod 调度)
    
    kubectl create -f csi-storageclass.yaml
    

    创建一个PVC,引用刚刚创建的StorageClass,申请存储空间为1GiB:

    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: csi-pvc             # 持久卷声明 (PVC) 的名称
    spec:
      accessModes:
        - ReadWriteOnce         # 访问模式:单节点读写挂载
      resources:
        requests:
          storage: 1Gi          # 请求分配 1 吉字节 (Gi) 的存储容量
      storageClassName: csi-hostpath-sc  # 显式指定使用前面创建的 csi-hostpath-sc 存储类进行动态供应
    
    kubectl create -f csi-pvc.yaml
    

    查看PVC和系统自动创建的PV,状态为Bound,说明创建成功:

    # kubectl get pvc
    NAME      STATUS   VOLUME                                      CAPACITY   ACCESS MODES
    STORAGECLASS      AGE
    csi-pvc   Bound   pvc-f8923093-3e25-11e9-a5fa-000c29069202   1Gi        RWO
    csi-hostpath-sc   40s
    
    # kubectl get pv
    NAME                                        CAPACITY   ACCESS MODES   RECLAIM POLICY
    STATUS   CLAIM             STORAGECLASS      REASON   AGE
    pvc-f8923093-3e25-11e9-a5fa-000c29069202   1Gi        RWO            Delete
    Bound   default/csi-pvc   csi-hostpath-sc            42s
    

    最后,在应用容器的配置中使用该PVC:

    apiVersion: v1
    kind: Pod
    metadata:
      name: my-csi-app              # 测试应用的 Pod 名称
    spec:
      containers:
        - name: my-csi-app          # 容器名称
          image: busybox            # 使用轻量级的 busybox 镜像
          imagePullPolicy: IfNotPresent
          command: [ "sleep", "1000000" ]  # 保持容器持续运行,以便挂载和测试存储卷
          volumeMounts:
            - mountPath: "/data"    # 将存储卷挂载到容器内的 /data 目录
              name: my-csi-volume
      volumes:
        - name: my-csi-volume
          persistentVolumeClaim:
            claimName: csi-pvc      # 引用前面创建的 csi-pvc 持久卷声明
    
    kubectl create -f csi-app.yaml
    
    kubectl get pods
    

    在Pod创建成功之后,应用容器中的/data目录使用的就是CSI存储插件提供的存储。

    我们通过kubelet的日志可以查看到Volume挂载的详细过程:

    I0304 10:39:27.408018   29488 operation_generator.go:1196] Controller attach
    succeeded for volume"pvc-f8923093-3e25-11e9-a5fa-000c29069202" (UniqueName:
    "kubernetes.io/csi/csi-hostpath^f89c8e8e-3e25-11e9-8d66-000c29069202") pod
    "my-csi-app" (UID: "b624c688-3e26-11e9-a5fa-000c29069202") device path:
    "csi-43a8c0897d21520e942e9ceea0b1ddac36c8c462d726780bed5f50841f0b0871"
    I0304 10:39:27.501816   29488 operation_generator.go:501]
    MountVolume.WaitForAttach entering for volume
    "pvc-f8923093-3e25-11e9-a5fa-000c29069202" (UniqueName:
    "kubernetes.io/csi/csi-hostpath^f89c8e8e-3e25-11e9-8d66-000c29069202") pod
    "my-csi-app" (UID: "b624c688-3e26-11e9-a5fa-000c29069202") DevicePath
    "csi-43a8c0897d21520e942e9ceea0b1ddac36c8c462d726780bed5f50841f0b0871"
    I0304 10:39:27.504542   29488 operation_generator.go:510]
    MountVolume.WaitForAttach succeeded for volume
    "pvc-f8923093-3e25-11e9-a5fa-000c29069202" (UniqueName:
    "kubernetes.io/csi/csi-hostpath^f89c8e8e-3e25-11e9-8d66-000c29069202") pod
    "my-csi-app" (UID: "b624c688-3e26-11e9-a5fa-000c29069202") DevicePath
    "csi-43a8c0897d21520e942e9ceea0b1ddac36c8c462d726780bed5f50841f0b0871"
    I0304 10:39:27.506867   29488 csi_attacher.go:360] kubernetes.io/csi:
    attacher.MountDevice STAGE_UNSTAGE_VOLUME capability not set. Skipping
    MountDevice...
    I0304 10:39:27.506894   29488 operation_generator.go:531]
    MountVolume.MountDevice succeeded for volume
    "pvc-f8923093-3e25-11e9-a5fa-000c29069202" (UniqueName:
    "kubernetes.io/csi/csi-hostpath^f89c8e8e-3e25-11e9-8d66-000c29069202") pod
    "my-csi-app" (UID: "b624c688-3e26-11e9-a5fa-000c29069202") device mount path
    "/var/lib/kubelet/plugins/kubernetes.io/csi/pv/pvc-f8923093-3e25-11e9-a5fa-000c2
    9069202/globalmount"
    

7.4、CSI的发展

目前可用于生产环境的CSI插件如表所示。
在这里插入图片描述
在这里插入图片描述
实验性的CSI插件如表所示。
在这里插入图片描述
每种CSI存储插件都提供了容器镜像,与external-attacher、external-provisioner、node-driver-registrar等sidecar辅助容器共同完成存储插件系统的部署,每个插件的部署配置详见官网https://kubernetes-csi.github.io/docs/drivers.html中的链接。

Kubernetes从1.12版本开始引入存储卷快照(VolumeSnapshots)功能,通过新的CRD自定义资源对象 VolumeSnapshotContent、VolumeSnapshot和VolumeSnapshotClass进行管理。VolumeSnapshotContent定义从当前PV创建的快照,类似于一个新的PV;VolumeSnapshot定义需要绑定某个快照的请求,类似于PVC的定义;VolumeSnapshotClass用于屏蔽VolumeSnapshotContent的细节,类似于StorageClass的功能。

下面是一个VolumeSnapshotContent的例子:

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotContent
metadata:
  name: new-snapshot-content-test  # VolumeSnapshotContent 的名称
spec:
  volumeSnapshotClassName: csi-hostpath-snapclass  # 关联的快照类名称(注意:v1 版本字段名变更为 volumeSnapshotClassName)
  driver: csi-hostpath             # 提供底层卷快照功能的 CSI 驱动名称
  deletionPolicy: Delete           # 删除策略:当 VolumeSnapshotContent 被删除时,底层实际的快照数据也将被删除 (Delete)
  volumeSnapshotRef:
    name: new-snapshot-test        # 关联的命名空间级 VolumeSnapshot 对象名称
    namespace: default             # VolumeSnapshot 所在的命名空间
  source:
    volumeHandle: 7bdd0de3-aaeb-11e8-9aae-0242ac110002  # 底层存储卷的唯一标识符 (Volume Handle),用于指向被备份的源卷

下面是一个VolumeSnapshot的例子:

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
  name: new-snapshot-test     # VolumeSnapshot 的名称,供用户层引用
spec:
  volumeSnapshotClassName: csi-hostpath-snapclass  # 关联的卷快照类名称(注意:v1 版本字段名变更为 volumeSnapshotClassName)
  source:
    apiGroup: ""              # API 组为空字符串,代表核心资源
    kind: PersistentVolumeClaim  # 源资源类型:持久卷声明 (PVC)
    name: pvc-test            # 需要被备份/快照的目标 PVC 名称(例如需要备份的数据库 PVC)

后续要进一步完善的工作如下。

  • (1)将以下Alpha版功能更新到Beta版:
    • Raw Block类型的Volumes;
    • 拓扑感知,Kubernetes理解和影响CSI卷的配置位置(如zone、region等)的能力;
    • 完善基于CRD的扩展功能(例如Skip attach、Podinfo on mount等)​。
  • (2)完善对本地短暂卷(Local Ephemeral Volume)的支持。
  • ** (3)将Kubernetes“in-tree”存储卷插件迁移到CSI。**
医学图像配准实战:用Python实现MIND特征提取(附完整代码) 本文详细介绍了MIND(模态无关邻域描述符)在医学图像配准中的应用。通过Python和PyTorch实战,从原理到代码逐步解析了如何提取MIND特征,并利用其对结构而非灰度的感知能力,有效解决CT、MRI等多模态图像的对齐难题,为病灶追踪、手术导航等高级应用提供鲁棒性基础。 阅读详情

相关推荐

别再用UE5默认材质:用Substance Painter一招提升写实感

今天,我就把这套从模型准备到引擎最终呈现的全流程技巧毫无保留地分享出来,这套方法不仅能让你的作品细节炸裂,更能让你理解次世代PBR(Physically Based Rendering)材质的底层逻辑。

海外旅居设计师,15年设计经验。 1325

【k8s权威指南】第1章 Kubernetes入门

本系列文章是<Kubernetes权威指南(第四版)>的学习笔记,适合初学者,理清k8s学习框架

weixin_45909424的博客 2645

图自编码器(GAE)与变分图自编码器(VGAE)

在学习图自编码器之前先了解一下自编码器与变分自编码器。

小鱼的小窝 1万+

Kubernetes权威指南

网站 更多书籍点击进入>> CiCi岛 下载 电子版仅供预览及学习交流使用,下载后请24小时内删除,支持正版,喜欢的请购买正版书籍 电子书下载(皮皮云盘-点击“普通下载”) 购买正版 封页 编辑推荐 本书是容器圈Kubernetes重磅开山作《Kubernetes权威指南》的纪念版,内容更新到Kubernetes v1.6 版本。   本书作者全部来自惠普公司云计算实战一线,敏锐...

CiCi岛的专栏 2084

【Zotero】《纯操作讲解,GPT配置,Zotero标注可视化》- 知识点目录

Temperature分布在。之间,越低输出越稳定;

墨门 3396

实测iOS和安卓大不同!用NFC触发小程序跳转的完整避坑指南(含NFC Tools配置)

本文深入解析iOS与安卓在NFC触发小程序跳转时的核心差异,提供微信与支付宝小程序的NFC实现方案及优化技巧。涵盖跨平台兼容性测试方法论、性能优化策略及异常处理方案,帮助开发者避开常见陷阱,提升跳转成功率。特别针对NFC Tools配置和微信Universal Link使用进行详细指导。

asdfghjkl978564的博客 434

赠书福利丨Kubernetes权威指南第4版

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_33904756的博客 3839

手把手教你用Apple Developer App完成苹果开发者账号注册(含证件验证失败解决方案)

本文详细指导如何通过Apple Developer App完成苹果开发者账号注册,特别针对证件验证失败问题提供解决方案。从准备工作到注册流程,再到验证失败的处理方法,帮助开发者顺利完成注册,避免常见错误。

moss5的博客 1160

kubernetes权威指南_如何快速上手成为大厂标配的kubernetes?

程序员书库(ID:CodingBook) 猿妹 综合整理过去几年,以docker、kubernetes为代表的容器技术已发展为一项通用技术,BAT、滴滴、京东、头条等大厂,都争相把容器和k8s项目作为技术重心。据说阿里巴巴目前所有业务都跑在云上,许多业务已迁移到自己定制 kubernetes 集群上。然而,要想用好Kubernetes,还是有些东西要注意的,否则可能会给自己带来一些小麻烦...

weixin_42375011的博客 202

anaconda navigator 卡在loading application界面一直没有反应解决办法

在打开anaconda navigator 程序的时候,遇到了2个问题。第一个问题:打开的时候弹出提示:There is an instance of Anaconda Navigator already running.第二个问题:程序一直卡在loading application界面一直没有反应。以下是我的解决办法,有效果。

猿小白的博客 4384

《拉依达的嵌入式\驱动面试宝典》—计算机硬件篇(一)

《拉依达的嵌入式\驱动面试宝典》 整合了所有我认为可能遇到的技术面试问题。所有问题及其答案均是我个人学习后查阅资料总结,每个回答都做了仔细的分析,在嵌入式及其驱动面试相关问题解析做到全网独一无二。学习或者转行到嵌入式、驱动开发领域学习的不二之选

拉依达不拉跨的博客 768

Kubernetes权威指南——共享存储原理(一)

Kubernetes对于有状态的容器应用或者对数据需要持久化的应用,不仅需要将容器内的目录挂载到宿主机的目录或者emptyDir临时存储卷,而且需要更加可靠的存储来保存应用产生的重要数据,以便容器应用在重建之后仍然可以使用之前的数据。不过,存储资源和计算资源(CPU/内存)的管理方式完全不同。

吴声子夜歌的博客 296

Meta新开源多语种语音基座Omnilingual ASR

Meta FAIR 团队最近祭出了,这不仅仅是对 MMS (Massively Multilingual Speech) 的简单升级,而是一次从“多任务学习”向“上下文学习(In-Context Learning)”的范式跃迁。通过将 Wav2Vec 2.0 编码器扩展至 70 亿参数,并引入 LLM 风格的 Decoder,Omnilingual ASR 不仅原生支持 1600+ 种语言,更具备了类似 GPT 的“零样本”扩展能力——只需给出几个音频-文本对,即可解锁从未见过的语言。

kakaZhui的博客 146

读《Kubernetes 权威指南》纪念版

做个读书计划,加快此书阅读。 共 6 章,677页。 1,45页 2,181页 3,162页 4,46页 5,134页 6,109页 初步定 70+页每天。 第一天读到35页,第二天读230页,第三天读到300页,第四天读到567页,第五天读到630页,第六天读完。 1,入门 2,实践指南 3,核心原理 4,开发指南 5,运维指南 6,源码导读 ...

程序员写的技术 FAQ 和杂文 1131

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

《Kubernetes网络权威指南》读书笔记 | 容器基础设施的代言人:Kubernetes

Kubernetes,简称k8s(k和s中间有8个英文字符),是谷歌照着鼎鼎大名的Borg系统开源的容器编排、调度和管理平台。Kubernetes通过将容器分类组成Pod。Pod是Kubernetes最小的部署单元。Kubernetes基于Pod提供工作负载的概念及网络、存储等能力。从底层实现技术的角度看,Pod内不同容器之间共享存储和一些namespace。3.1.2。

团爸讲算法 987

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

卧槽,陈坤的电视剧里出现了《K8s 权威指南》!

--文末赠书--在云原生领域,流传着一本神书,它不仅在领域内几乎人手一本,甚至还客串了陈坤主演的电视剧《输赢》!这本神书就是人人都想拥有的Kubernetes重磅级案头手册——《Kubernetes权威指南》!《Kubernetes权威指南》系列图书自2016年出版以来,便受到大家的追捧,多年来与时俱进,迭代更新,已有10万+销量!如今,在Kubernetes十周年之际,这本神书也全新升级到第6版...

云原生实验室 891

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

Kubernetes深度探索:两本让我一读再读的顶级神作|赠书

今年6月份,在Kubernetes十周年之际,云原生领域的神书、人人都想拥有的Kubernetes重磅级案头手册——《Kubernetes权威指南》全新升级到第6版,其上册率先与大家见面!当时,有不少小伙伴问下册什么时候可以出版?想要迫不及待地看到下册的内容!今天,博文菌开心地告诉大家,《Kubernetes权威指南(第6版)》下册终于和大家见面啦!这样不仅在云原生领域内几乎人手一本,甚至还客串了...

u012516914的专栏 1784

Kubernetes权威指南:从Docker到Kubernete

容器圈Kubernetes重磅开山作《Kubernetes木又威指南》的纪念版 《Kubernetes权威指南:从Docker到Kubernetes实践全接触(纪念版)》作者全部来自惠普公司云计算实战一线,敏锐地捕获和探索着各种IT前瞻技术,有着全面而扎实的技术架构体系、对创新技术天生的热情、国际技术领先者的视野,还有着对企业级IT架构的深入把握。 纪念并不是为了结束,而是为了新的写作思路的展...

互动出版网 561

kubernetes权威指南第五版_《Kubernetes权威指南:企业级容器云实战》读书笔记

基于Kubernetes的企业级容器云落地,为企业传递IT转型和业务上云提供助力。使用容器技术支撑微服务化的应用。云计算 -- 是IT信息技术发展和服务模式创建的集中体现,是信息化发展的必然趋势。应用部署;应用配置;应用的灰度发布更新策略;弹性扩缩容;以Kubernetes + Docker为基础,搭建企业级容器云PaaS平台,支撑基于微服务架构开发的应用程序,实现对大规模容器集群的有效管理。容器...

weixin_39896617的博客 8785

k8s源码分析 pdf_Kubernetes权威指南:从Docker到Kubernetes实践全接触(纪念版) 完整pdf扫描版...

Kubernetes 是由谷歌开源的Docker 容器集群管理系统,为容器化的应用提供了资源调度、部署运行、服务发现、扩容及缩容等一整套功能。《Kubernetes 木又威指南:从Docker 到Kubernetes 实践全接触(纪念版)》从架构师、开发人员和运维人员的角度,阐述了Kubernetes 的基本概念、实践指南、核心原理、开发指导、运维指南及源码分析等内容,图文并茂、内容丰富、由浅入深...

weixin_39691968的博客 1188

跟着GPT学习 Kubernetes ,简称 K8s(一)

学习 Kubernetes (简称 K8s) 是一个有些挑战性的任务,但只要按部就班,你一定可以掌握它的基本概念和操作。Kubernetes 是一个开源的容器编排平台,用于自动化部署、扩展和管理容器化应用程序。下面我会从零基础开始,为你介绍 Kubernetes 的基础知识和学习路线。

科技追踪者的专栏 1902

Kubernetes权威指南第2版 和 Docker技术入门与实战第2版 两本容器的书下载地址

两本书的下载链接 下载链接 链接:https://pan.baidu.com/s/13gv0ZQRiHfvLwgwjsvUiEA 密码:hi8oKubernetes权威指南第2版目录:第1章 Kubernetes入门 11.1 Kubernetes是什么 11.2 为什么要用Kubernetes 41.3 从一个简单的例子开始 51.3.1 环境准备 61.3.2 启动MySQ...

测试 642

NNI 训练服务(Training Service)全景指南:从本地、远程到 Kubernetes 集群的九大平台配置与复用模式解析

NNI(Neural Network Intelligence)是一款开源 AutoML 工具包,其训练服务(Training Service)模块负责把 Tuner 生成的 Trial 调度到各种计算平台上运行。本文基于仓库文档 [docs/source/experiment/training_service/overview.rst](https://link.gitcode.com/i/eb

gitblog_00885的博客 439
上一篇: Kubernetes权威指南——共享存储原理(一)
下一篇: Kubernetes权威指南——Kubernetes开发指南(一)
吴声子夜歌
博客等级 码龄9年 2688粉丝 2270原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值