写点什么

Kubernetes 手记(10)- POD 存储卷

用户头像
雪雷
关注
发布于: 2021 年 06 月 13 日
Kubernetes手记(10)- POD 存储卷

十 POD 存储卷

大部分有状态的应用都有持久存储,在 Docker 上我们将容器所需要的存储卷放在宿主机上,但是 k8s 上不行,因为 POD 会被在不同的 node 节点上创建删除,所以 k8s 需要一套另外的存储卷机制,它能脱离节点为整个集群提供持久存储。


k8s 提供了多种不同的存储卷,k8s 中存储卷属于 POD 而不是容器,POD 可以挂载,POD 为什么能有存储卷呢?这是因为在所有节点上运行了一个 Pause 的镜像,它是 POD 的基础架构容器,它拥有存储卷,同一个 POD 内的所有容器是一个网络名称空间的。

10.1 卷的类型

查看 POD 支持的存储类型:kubectl explain pods.spec.volumes


  1. HostPath:在节点本地新建一个路径,与容器建立关联关系,但节点挂了的数据也不存在了,所以也不具有持久性,容器被调度到别的 node 时候不能跨节点使用 HostPath。

  2. Local:直接使用节点的设备、也支持一个目录类似于 HostPath。

  3. EmptyDir:只在节点本地使用,一旦 POD 删除,存储卷也会删除,它不具有持久性,当临时目录或者缓存。

  4. 网络存储:iSCSI、NFS、Cifs、glusterfs、cephfs、EBS(AWS)、Disk(Azone)

10.2 容器挂载选项

在 K8S 中卷是属于 POD 的,而不是容器,所以卷的定义在 POD 中,一个 POD 中可以定义多个卷。


  • 在 POD 中挂载使用,kubectl explain pods.spec.containers.volumeMounts


apiVersion: v1kind: Podmetadata:  name: myapp  namespace: default  labels:    app: myappspec:  containers:  - name: myapp    image: ikubernetes/myapp:v1    volumeMounts        <[]Object>  # 卷挂载对象      mountPath          <string>    # 挂载路径      mountPropagation  <string>    # 确定挂载如何从主机传播到容器      name              <string>    # 挂载哪个卷      readOnly          <boolean>   # 是否只读挂载      subPath          <string>    # 挂载在子路径下      subPathExpr       <string>    # 与 subPath 类似,挂载在子路径下,不同的是可以使用 $(VAR_NAME) 表示容器扩展这个变量
复制代码

10.3 节点存储

10.3.1 hostpath 存储卷

在宿主机的路径挂载到 POD 上,POD 删除后,卷数据是不会随之删除的,但如果 node 节点挂掉,那么数据有可能丢失,如果 POD 被调度到其他的节点,那么原来卷的数据就访问不到了。


https://kubernetes.io/docs/concepts/storage/volumes/#hostpath


  • 定义参数,kubectl explain pods.spec.volumes.hostPath


path  <string>  # 主机上目录的路径。 如果路径是符号链接,则会跟随真实路径的链接。type  <string>  # 见下表
复制代码



  • 示例


apiVersion: v1kind: Podmetadata:  name: myapp  namespace: default  labels:    app: myappspec:  containers:  - name: myapp    image: ikubernetes/myapp:v1    volumeMounts:                       # 容器挂载哪些卷    - name: webstore                    # 挂载哪个卷      mountPath: /usr/share/nginx/html  # 挂载到容器内哪个目录      readOnly: false                   # 是否只读  volumes:                              # 存储卷属于POD的(不属于容器)  - name: webstore                      # 存储卷对象名字    hostPath:                           # hostpath 类型的存储卷对象      path: /data/myapp                 # 处于宿主机的目录      type: DirectoryOrCreate           # 不存在则创建
复制代码

10.3.2 gitRepo 卷

将 git 仓库的内容当作存储使用,在 POD 创建时候连接到仓库,并拉取仓库,并将它挂载到容器内当作一个存储卷。


它其实是建立在 emptyDir 的基础上,但是对卷的操作不会同步到 gitrepo 上。


注意:需要在各运行 pod 的 node 节点上安装 git 工具,用于 git 的拉取


apiVersion: v1kind: Podmetadata:  labels:    run: gitrepo  name: gitrepospec:  containers:  - image: nginx:latest    name: gitrepo    volumeMounts:      - name: gitrepo        mountPath: /usr/share/nginx/html  volumes:    - name: gitrepo      gitRepo:        repository: "https://gitee.com/rocket049/mysync.git"        revision: "master"
复制代码

10.3.3 emptyDir 缓存卷

它使用宿主机一个目录作为挂载点,随着 POD 生命周期的结束,其中的数据也会丢失,但是它有一个非常大的优点就是可以使用内存当作存储空间挂载使用。


它可以用在 POD 中两个容器中有一些数据需要共享时候选用。


  • 定义 emptyDir 参数,kubectl explain pods.spec.volumes.emptyDir


medium      <string>    # 使用 "" 表示使用 Disk 来存储,使用 Memory 表示使用内存sizeLimit   <string>    # 限制存储空间的大小
复制代码


  • 使用示例


apiVersion: v1kind: Podmetadata:  name: pod-volume-demo  namespace: default  labels:    app: myapp    tier: frontendspec:  volumes:    - name: html      emptyDir: {}      # 使用磁盘,且没有容量限制  containers:    - name: myapp      image: ikubernetes/myapp:v1      imagePullPolicy: IfNotPresent      volumeMounts:        - name: html          mountPath: /usr/share/nginx/html/      ports:        - name: http          containerPort: 80        - name: https          containerPort: 443    - name: busybox      image: busybox:latest      imagePullPolicy: IfNotPresent      volumeMounts:        - name: html          mountPath: /data/      command:        - "/bin/sh"        - "-c"        - "while true; do date >> /data/index.html; sleep 10; done"
复制代码


  • 使用示例


apiVersion: v1kind: Podmetadata:  name: pod-volume-demo  namespace: default  labels:    app: myapp    tier: frontendspec:  containers:    - name: myapp      image: ikubernetes/myapp:v1      imagePullPolicy: IfNotPresent      volumeMounts:        - name: html          mountPath: /usr/share/nginx/html/      ports:        - name: http          containerPort: 80        - name: https          containerPort: 443    - name: busybox      image: busybox:latest      imagePullPolicy: IfNotPresent      volumeMounts:        - name: html          mountPath: /data/      command:        - "/bin/sh"        - "-c"        - "while true; do date >> /data/index.html; sleep 10; done"  volumes:    - name: html      emptyDir:        medium: ""        sizeLimit: 1536Mi
复制代码

10.4 网络存储

网络存储,就是脱离了节点生命周期的存储设备,即使 pod 被调度到别的 node 节点上,仍然可以挂载使用其中的数据。

10.4.1 nfs

nfs 服务器是存在于集群之外的服务器,它不受 node 节点的影响,因而在 node 节点宕机后仍然能够提供持久存储给其他 POD。


  • 在 k8s 的 node 找一个主机,安装配置 nfs 服务器并启动


$ yum install nfs-utils                                                     # 安装 nfs 服务$ mkdir -p /data/volumes                                                    # 创建 volume 卷目录echo '/data/volumes  172.16.100.0/16(rw,no_root_squash)' >> /etc/exports    # 配置 nfs 服务器$ systemctl start nfs                                                       # 启动 nfs 服务器$ ss -tnl                                                                   # 确认监听端口,nfs 监听 TCP 2049 端口
复制代码


  • 在 k8s 集群的 node 节点安装 nfs 驱动,测试挂载是否正常


$ yum install nfs-utils$ mount -t nfs 172.16.100.104:/data/volumes /mnt
复制代码


  • 定义 nfs 参数,kubectl explain pods.spec.volumes.nfs


path    <string>       # nfs 服务器的路径readOnly  <boolean>      # 是否只读server    <string>       # nfs 服务器地址
复制代码


  • 使用示例


apiVersion: v1kind: Podmetadata:  name: pod-vol-nfs-demo  namespace: defaultspec:  containers:  - name: myapp    image: ikubernetes/myapp:v1    volumeMounts:      - name: html        mountPath: /usr/share/nginx/html/  volumes:    - name: html      nfs:        path: /data/volumes        server: 172.16.100.104
复制代码


注意⚠️:各个 node 节点也需要安装yum install nfs-utils,不让在挂载的时候会出现异常。

10.5 分布式存储

分布式存储能提供脱离节点生命周期的存储,又比网络存储更加健壮,它是分布式的,有很强的高可用性,但是分布式存储配置复杂,在由 NFS 提供的网络储存中,用户需要知道分配给 POD 的 NFS 存储的地址才能使用,而在由分布式提供的存储能力的存储上,用户需要充分了解该分布式存储的配置参数,才能够使用这个分布式存储。


由此 K8S 提供了 PV、PVC 两种机制,让普通用户无需关心底层存储参数的配置,只需要说明需要使用多大的持久存储,就可以了。


一般 PV 与 PVC 是一对绑定的,PV 属于全局,PVC 属于某个名称空间,当一个 PV 被一个 PVC 绑定,别的名称空间 PVC 就不可以再绑定了。请求绑定某个 PV 就是由 PVC 来完成的,被 PVC 绑定的 PV 称作 PV 的绑定状态。


PVC 绑定了一个 PV,那么 PVC 所处名称空间定义的 POD 就可以使用 persistentVolumeClaim 类型的 volumes 了,然后容器就可以通过 volumeMounts 挂载 PVC 类型的卷了。


persistentVolumeClaim 卷是否允许多路读写,这取决于 PV 定义时候的读写特性:单路读写、多路读写、多路只读。


如果某个 POD 不在需要了,我们把它删除了、同时也删除了 PVC、那么此时 PV 还可以有自己的回收策略: delete 删除 PV、Retain 什么都不做。

10.5.1 PersistentVolume

由管理员添加的的一个存储的描述,是一个集群级别的全局资源,包含存储的类型,存储的大小和访问模式等。它的生命周期独立于 Pod,例如当使用它的 Pod 销毁时对 PV 没有影响。


见:kubectl explain PersistentVolume.spec


  • 在 nfs 上定义存储,/etc/exports,并且导出 nfs 定义


/data/volumes/v1    172.16.100.0/16(rw,no_root_squash)/data/volumes/v2    172.16.100.0/16(rw,no_root_squash)/data/volumes/v3    172.16.100.0/16(rw,no_root_squash)/data/volumes/v4    172.16.100.0/16(rw,no_root_squash)/data/volumes/v5    172.16.100.0/16(rw,no_root_squash)
复制代码


exportfs -arv
复制代码


  • 将 nfs 在 k8s 中定义为 PersistentVolume,详见:kubectl explain PersistentVolume.spec.nfs


apiVersion: v1kind: PersistentVolumemetadata:  name: pv-001  labels:    name: pv001spec:  accessModes:    - ReadWriteMany    - ReadWriteOnce  capacity:    storage: 1Gi  nfs:    path: /data/volumes/v1    server: 172.16.100.104
---apiVersion: v1kind: PersistentVolumemetadata: name: pv-002 labels: name: pv003spec: accessModes: - ReadWriteMany - ReadWriteOnce capacity: storage: 2Gi nfs: path: /data/volumes/v2 server: 172.16.100.104
---apiVersion: v1kind: PersistentVolumemetadata: name: pv-003 labels: name: pv003spec: accessModes: - ReadWriteMany - ReadWriteOnce capacity: storage: 3Gi nfs: path: /data/volumes/v3 server: 172.16.100.104
复制代码


kubectl get persistentvolumeNAME     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGEpv-001   1Gi        RWO,RWX        Retain           Available                                   3m38spv-002   2Gi        RWO,RWX        Retain           Available                                   3m38spv-003   3Gi        RWO,RWX        Retain           Available                                   3m38s
复制代码

10.5.2. PersistentVolumeClaim

是 Namespace 级别的资源,描述对 PV 的一个请求。请求信息包含存储大小,访问模式等。


  • 定义 PVC,kubectl explain PersistentVolumeClaim.spec


accessModes         <[]string>  # 设置访问模式    ReadWriteOnce               # 单个节点以读写方式挂载    ReadOnlyMany                # - 多节点以只读方式挂载    ReadWriteMany               # - 多节点以读写方式挂载
dataSource <Object> # 如果配置程序可以支持 Volume Snapshot 数据源,它将创建一个新卷,并且数据将同时还原到该卷。 resources <Object> # 资源表示 PersistentVolume 应具有的最小资源selector <Object> # 选择哪个 PersistentVolumestorageClassName <string> # 存储类名称volumeMode <string> # 定义声明所需的 PersistentVolume 类型才能被选中volumeName <string> # 后端 PersistentVolume ,就是精确选择 PersistentVolume ,而不是使用 selector 来选定
复制代码


  • 在 volumes 中使用 PVC,kubectl explain pods.spec.volumes.persistentVolumeClaim


persistentVolumeClaim    claimName    <string>  # 在当前名称空间已经创建号的 PVC 名称    readOnly     <boolean> # 是否只读
复制代码


  • 定义 PersistentVolumeClaim,详见:kubectl explain PersistentVolumeClaim.spec


apiVersion: v1kind: PersistentVolumeClaimmetadata:  name: my-pvc  namespace: defaultspec:  accessModes:    - ReadWriteMany        # 访问模式  resources:               # 资源条件    requests:              # 挑选 PV 时候必须满足的条件,不满足则一直等待      storage: 2Gi         # 存储大小
复制代码


  • 在 pod 清单中定义 persistentVolumeClaim 类型的 volumes ,并在容器中挂载 volumeMounts。


apiVersion: v1kind: Podmetadata:  name: pod-vol-nfs-demo  namespace: defaultspec:  containers:  - name: myapp    image: ikubernetes/myapp:v1    volumeMounts:      - name: html        mountPath: /usr/share/nginx/html/  volumes:    - name: html      persistentVolumeClaim:        claimName: my-pvc            # 使用的 PVC 的名称
复制代码

10.5.3 StorageClass

PVC 申请 PV 的时候,未必有符合条件的 PV,k8s 为我们准备了 StorageClass 可以在 PVC 申请 PV 的时候通过 StorageClass 动态生成 PV。


StorageClass 可以动态的到 CephFS 、NFS 等存储(或者云端存储)产生一个 PV,要求存储设备必须支持 RESTfull 风格的接口。

10.6 StorageClass Ceph RBD

10.6.1 配置 Ceph 储存池

  • 创建 ceph 存储池


yum install -y ceph-common                                                                   # 在所有节点安装 ceph-common
复制代码


ceph osd pool create kube 4096                                                               # 创建 poolceph osd pool ls                                                                             # 查看 pool
ceph auth get-or-create client.kube mon 'allow r' osd 'allow rwx pool=kube' -o /etc/ceph/ceph.client.kube.keyringceph auth list # 授权 client.kube 用户访问 kube 这个 pool
scp /etc/ceph/ceph.client.kube.keyring node1:/etc/ceph/ # 将用户 keyring 文件拷贝到各个 ceph 节点scp /etc/ceph/ceph.client.kube.keyring node1:/etc/ceph/
复制代码

10.6.2 安装 rbd-provisioner

  • 1.12 版本后 kube-controller-manager 不再内置 rbd 命令,所以 StorageClass 的 provisioner 而是通过外部的插件来实现


https://github.com/kubernetes-incubator/external-storage/tree/master/ceph/rbd/deploy/rbac    # rbd-provisioner
复制代码


$ git clone https://github.com/kubernetes-incubator/external-storage.git                     # 下载 rbd-provisioner$ cat >>external-storage/ceph/rbd/deploy/rbac/clusterrole.yaml<<EOF                          # 允许 rbd-provisioner 访问 ceph 的密钥  - apiGroups: [""]    resources: ["secrets"]    verbs: ["create", "get", "list", "watch"]EOF$ kubectl apply -f external-storage/ceph/rbd/deploy/rbac/                                    # 安装 rbd-provisioner
复制代码

10.6.3 使用 StorageClass

  • 创建 CephX 验证 secret


https://github.com/kubernetes-incubator/external-storage/tree/master/ceph/rbd/examples       # rbd-provisioner 使用 ceph rbd 的示例
复制代码


---apiVersion: v1kind: Secretmetadata:  name: ceph-admin-secret  namespace: kube-systemtype: "kubernetes.io/rbd"data:  # ceph auth get-key client.admin | base64                                                  # 从这个命令中取得 keyring 认证的 base64 密钥串复制到下面  key: QVFER3U5TmM1NXQ4SlJBQXhHMGltdXZlNFZkUXRvN2tTZ1BENGc9PQ==

---apiVersion: v1kind: Secretmetadata: name: ceph-secret namespace: kube-systemtype: "kubernetes.io/rbd"data: # ceph auth get-key client.kube | base64 # 从这个命令中取得 keyring 认证的 base64 密钥串复制到下面 key: QVFCcUM5VmNWVDdQRlJBQWR1NUxFNzVKeThiazdUWVhOa3N2UWc9PQ==
复制代码


  • 创建 StorageClass 指向 rbd-provisioner,


---kind: StorageClassapiVersion: storage.k8s.io/v1metadata:  name: ceph-rbdprovisioner: ceph.com/rbdreclaimPolicy: Retainparameters:  monitors: 172.16.100.9:6789  pool: kube  adminId: admin  adminSecretName: ceph-admin-secret  adminSecretNamespace: kube-system  userId: kube  userSecretName: ceph-secret  userSecretNamespace: kube-system  fsType: ext4  imageFormat: "2"  imageFeatures: "layering"
复制代码


  • 创建 PersistentVolumeClaim


---kind: PersistentVolumeClaimapiVersion: v1metadata:  name: ceph-rbd-pvc  data-kong-postgresql-0 spec:  storageClassName: ceph-rbd  accessModes:  - ReadWriteOnce  resources:    requests:      storage: 1Gi
复制代码


  • 在 POD 中使用 PVC,最后在容器中挂载 PVC。


---apiVersion: v1kind: Podmetadata:  name: ceph-sc-pvc-demo  namespace: defaultspec:  containers:  - name: myapp    image: ikubernetes/myapp:v1    volumeMounts:      - name: pvc-volume        mountPath: /usr/share/nginx/html/  volumes:    - name: pvc-volume      persistentVolumeClaim:        claimName: ceph-rbd-pvc
复制代码

其他

自己将手记发在:https://github.com/redhatxl/awesome-kubernetes-notes欢迎一键三连

发布于: 2021 年 06 月 13 日阅读数: 15
用户头像

雪雷

关注

stay hungry stay foolish 2019.08.16 加入

Devops,python,shell,云原生,云架构,kubernetes https://github.com/redhatxl

评论

发布
暂无评论
Kubernetes手记(10)- POD 存储卷