05 kubernetes 持久化存储数据业务

Kubernetes 的卷 volume

容器中的文件在磁盘上是临时存放的,这给容器中运行的特殊应用程序带来一些问题。首先,容器崩溃时,kubelet 将重新启动容器,容器中的文件将会丢失。其次,当在一个 Pod 中同时运行多个容器的时候,常常需要在这些容器之间共享文件。kubernetes 抽象出 volume 对象来解决这两个问题。

Docker 也有 volume 的概念,在 Docker 中,volume 是磁盘上或者另外一个容器内的一个目录。直到最近,Docker 才支持对基于本地磁盘的 volumn 的生存期进行管理。虽然 Docker 现在也提供 volumn 驱动程序,但是目前功能还非常有限(比如截止 Docker1.7 每个容器只允许有一个 volume 驱动程序,并且无法将参数传递给卷)。

另一方面,kubernetes 卷具有明确的声明周期--与包裹它的 Pod 不同。因此,卷比 Pod 中运行的任何容器的存活期都长,在容器重新启动时数据也会得到保留。当然,当一个 Pod 不再存在时,卷也将不再存在。也许更重要的是,kubernetes 可以支持许多类型的卷,Pod 也能同时使用任意数量的卷。

卷的核心是包含一些数据的目录,Pod 中的容器可以访问该目录。特定的卷类型可以决定这个目录是如何形成的,并能决定它支持何种介质,以及目录中存放什么内容。

使用卷时,Pod 声明中需要提供卷的类型(通过 .spec.volumes 字段)和卷挂载的位置(通过 .spec.containers.volumeMounts 字段)

Kubernetes 提供了众多的 volume 类型,包括:emptyDir, hostPth, nfs, glusterfs, cephfs, ceph

emptyDir

当 Pod 指定到某个节点上时,首先创建的是一个 emptyDir 卷,并且只要 Pod 在该节点上运行,卷就一直存在。当 Pod 被从节点删除时,emptyDir 卷中的数据也会永久删除。容器崩溃并不会导致 Pod 被从节点上删除,因此容器崩溃时,emptyDir 卷中的数据是安全的。

emptyDir 的一些用途:

  • 缓存空间,例如基于磁盘的归并排序
  • 为耗时较长的计算任务提供检查点,以便任务能方便的从崩溃前状态恢复执行
  • 在 web 服务器容器服务数据时,保存内容管理器类型容器获取的文件

hostPath

hostPath 卷能将主机节点文件系统上的文件或目录挂载到 Pod 中。虽然这不是大多数 Pod 需要的,但是它为一些应用程序提供了强大的持久化能力。

vim hostpath.yaml
apiVersion: v1
kind: Pod
metadata:
  name: hostpath-pod
spec:
  containers:
  - name: test-container
    image: nginx
    volumeMounts:
    # 容器内的挂载目录
    - mountPath: /test-nginx
      name: myhostpath
  volumes:
  - name: myhostpath
    hostPath:
        # 虚拟机上的挂载目录
      path: /tmp/nginx
      type: DirectoryOrCreate
mkdir /tmp/nginx -p
kubectl create -f hostpath.yaml

可以看到

pod/hostpath-pod created
kubectl get po hostpath-pod

可以看到

NAME           READY   STATUS    RESTARTS   AGE
hostpath-pod   1/1     Running   0          59s
[root@node2 ~]# kubectl exec -it hostpath-pod sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
# ls
bin  boot  dev  docker-entrypoint.d  docker-entrypoint.sh  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  test-nginx  tmp  usr  var
# cd test-nginx
# touch a.txt
# echo 1 > a.txt
# exit
[root@node2 ~]# cd /tmp/nginx/
[root@node2 nginx]# ls
a.txt
[root@node2 nginx]# cat a.txt
1
[root@node2 nginx]# echo 2 > a.txt
[root@node2 nginx]# kubectl exec -it hostpath-pod sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
# ls
bin  boot  dev  docker-entrypoint.d  docker-entrypoint.sh  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  test-nginx  tmp  usr  var
# cd test-nginx
# pwd
/test-nginx
# ls
a.txt
# cat a.txt
2
# exit
[root@node2 nginx]#

挂载 NFS 卷

应用场景:很多应用需要在集群内部有一个统一的地方存储文件,比如图片,日志等。而使用 hostpath 的方式不够灵活,因为需要指定 host 地址。

  1. 在 master 节点安装 nfs 服务

    yum install -y nfs-utils rpcbind
    
  2. 在 master 节点配置共享目录

    mkdir /nfsdata
    
    vim /etc/exports
    
    /nfsdata   *(rw,sync,no_root_squash)
    
  3. 在 master 节点配置自动启动

    systemctl enable --now rpcbind
    systemctl enable --now nfs
    
  4. 在 master 节点激活配置

    exportfs -r
    
  5. 查看挂载效果

    showmount -e master
    

    可以看到

    Export list for master:
    /nfsdata *
    
  6. 在 node2 节点安装 nfs 服务

    yum install -y nfs-utils rpcbind
    
  7. 在 node2 节点创建 Pod ,引用 NFS 存储

    vim nfs.yaml
    
    apiVersion: v1
    kind: Pod
    metadata:
      name: nfs-pd
    spec:
      containers:
      - name: test-container
        image: nginx
        volumeMounts:
        # node 节点挂载的目录
        - mountPath: /usr/share/nginx/html
          name: test-volume
      volumes:
      - name: test-volume
        nfs:
          server: master
          # master 节点挂载的目录
          path: /nfsdata
    
    kubectl apply -f nfs.yaml
    

    可以看到

    pod/nfs-pd created
    
  8. 进入 pod

    [root@node2 ~]# kubectl exec -it nfs-pd sh
    kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
    # mount |grep nfs
    master:/nfsdata on /usr/share/nginx/html type nfs4 (rw,relatime,vers=4.1,rsize=524288,wsize=524288,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=192.168.190.133,local_lock=none,addr=192.168.190.131)
    # cd /usr/share/nginx/html
    # pwd
    /usr/share/nginx/html
    # ls
    # touch a.txt
    # echo hello kubernetes volume > a.txt
    # cat a.txt
    hello kubernetes volume
    # exit
    [root@node2 ~]#
    
  9. 进入 master 节点

    [root@master ~]# cd /nfsdata/
    [root@master nfsdata]# ls
    a.txt
    [root@master nfsdata]# cat a.txt
    hello kubernetes volume
    [root@master nfsdata]#
    

持久化存储

存储的管理是一个与计算实例的管理完全不同的问题。 PersistentVolume 子系统为用户和管理员提供了一组 API ,将存储如何供应的细节从其如何被使用中抽象出来。为了实现这点,我们引入了两个新的 API 资源 PersistenVolume 和 PersistenVolumeClaim。

持久卷(PersistenVolume, PV)是集群中的一块存储,可以有管理员事先供应,或者使用存储类(Storage Class)来动态供应。持久卷是集群资源,就像节点也是集群资源一样。PV 和普通的 volume 一样,也是使用卷插件来实现的,只是它们拥有独立于任何使用 PV 的 Pod 的生命周期。此 API 对象中记述了存储的实现细节,无论其背后是 NFS, iSCSI 还是特定于云平台的存储系统。

创建 PV

vim pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-demo
spec:
    # 容量
  capacity:
    storage: 5Gi
  accessModes:
    # 每次允许一个 worknode 读写
    - ReadWriteOnce
   # 回收策略:可回收
  persistentVolumeReclaimPolicy: Recycle
  nfs:
    # 挂载路径
    path: /nfsdata
    server: master
kubectl apply -f pv.yaml

可以看到

persistentvolume/pv-demo created
kubectl get pv pv-demo

可以看到

NAME      CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
pv-demo   1Gi        RWO            Recycle          Available                                   53s

持久卷申领

持久卷申领(PersistenVolumeClaim)

每个 PVC 对象都有 spec 和 status 部分,分别对应 Claim 的规定和状态

vim pvc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: pvc-demo
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
        # 申领的容量
      storage: 2Gi
kubectl apply -f pvc.yaml

可以看到

persistentvolumeclaim/pvc-demo created
kubectl get pvc pvc-demo

可以看到

NAME       STATUS   VOLUME    CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pvc-demo   Bound    pv-demo   5Gi        RWO                           10s
kubectl get pv pv-demo

可以看到 ,PV 的状态由 Available 变成了 Bound

NAME      CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM              STORAGECLASS   REASON   AGE
pv-demo   5Gi        RWO            Recycle          Bound    default/pvc-demo                           61s

存储类

什么是 StorageClass

Kubernetes 提供了一套可以自动创建 PV 的机制:Dynamic Provisioning 。这个机制的核心是 StorageClass 这个API 对象。

StorageClass 对象会定义下面两部分内容:

  1. PV 的属性,比如存储类型,volume 的大小等
  2. 创建这种 PV 需要用到的存储插件

为什么需要 StorageClass

在一个大规模的 kubernetes 集群里,可能有成千上万个 PVC,这就意味着必须创建出多个 PV ,而且随着项目的需要,会有新的 PVC 不断被提交,那么就需要不断添加新的 PV ,否则新的 Pod 就会因为 PVC 绑定不到 PV 而创建失败。不同的应用程序对于存储性能的要求不尽相同,比如读写速度、并发性能等。通过 StorageClass ,可以将存储资源定义为某种类型的资源,比如快速存储、慢速存储等,这样就可以根据应用的特性去申请合适的存储资源了。

NFS Provisioner

NFS Provisioner 是一个自动配置卷程序,它使用现有的 NFS 服务器来支持通过持久卷声明动态配置 kubernetes 持久卷。

持久卷配置为:{namespace}-{pvcName}-{pvName}

在下面的实例中,通过该容器定义 ENV 变量:PROVISONER_NAME 为 qgg-nfs-storage,该容器会动态生产一个名为 qgg-nfs-storage 的持久卷,从而实现持久卷的动态创建。

实践 PV, PVC 挂载 NFS

在 node2 节点:

创建 Persistent Volume Provisioner

vim nfs-provisioner.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfs-client-provisioner
  labels:
    app: nfs-client-provisioner
  namespace: default 
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nfs-client-provisioner
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: nfs-client-provisioner
  template:
    metadata:
      labels:
        app: nfs-client-provisioner
    spec:
      serviceAccountName: nfs-client-provisioner
      containers:
        - name: nfs-client-provisioner
          image: registry.cn-beijing.aliyuncs.com/qingfeng666/nfs-client-provisioner:v3.1.0
          volumeMounts:
            - name: nfs-client-root
              mountPath: /persistentvolumes
          env:
            - name: PROVISIONER_NAME
              value: qgg-nfs-storage 
            - name: NFS_SERVER
              value: master   
            - name: NFS_PATH
              value: /nfsdata   
      volumes:
        - name: nfs-client-root
          nfs:
            server: master  
            path: /nfsdata    

kubectl apply -f nfs-provisioner.yaml

可以看到

deployment.apps/nfs-client-provisioner created

创建用户和角色

vim rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-client-provisioner
  namespace: default        
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: nfs-client-provisioner-runner
rules:
  - 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: ["create", "update", "patch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: run-nfs-client-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    namespace: default
roleRef:
  kind: ClusterRole
  name: nfs-client-provisioner-runner
  apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner
  namespace: default
rules:
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    namespace: default
roleRef:
  kind: Role
  name: leader-locking-nfs-client-provisioner
  apiGroup: rbac.authorization.k8s.io

kubectl apply -f rbac.yaml

可以看到

serviceaccount/nfs-client-provisioner created
clusterrole.rbac.authorization.k8s.io/nfs-client-provisioner-runner created
clusterrolebinding.rbac.authorization.k8s.io/run-nfs-client-provisioner created
role.rbac.authorization.k8s.io/leader-locking-nfs-client-provisioner created
rolebinding.rbac.authorization.k8s.io/leader-locking-nfs-client-provisioner created

创建 StorageClass

vim storageclass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: managed-nfs-storage
provisioner: qgg-nfs-storage 
parameters:
  archiveOnDelete: "false"
kubectl apply -f storageclass.yaml

可以看到

storageclass.storage.k8s.io/managed-nfs-storage created
kubectl get storageclass.storage.k8s.io/managed-nfs-storage

可以看到

NAME                  PROVISIONER       RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
managed-nfs-storage   qgg-nfs-storage   Delete          Immediate           false                  101s

创建 PVC

vim 8-6-pvc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: test-claim
  annotations:
    volume.beta.kubernetes.io/storage-class: "managed-nfs-storage"  
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1Mi
      
kubectl apply -f 8-6-pvc.yaml

可以看到

persistentvolumeclaim/test-claim created
kubectl get pvc test-claim

可以看到

NAME         STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS          AGE
test-claim   Bound    pvc-bcdc3799-27bc-4c38-a335-4050ac611fe5   1Mi        RWX            managed-nfs-storage   72s
kubectl get po

可以看到

NAME                                      READY   STATUS    RESTARTS   AGE
nfs-client-provisioner-6976b6b79c-hdqm9   1/1     Running   0          13m
kubectl get storageclass

可以看到

NAME                  PROVISIONER                    RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
managed-nfs-storage   qgg-nfs-storage                Delete          Immediate              false                  9m56s
my-storage-class      kubernetes.io/no-provisioner   Delete          WaitForFirstConsumer   false                  29h

创建测试 Pod

vim 8-6-pod.yaml
kind: Pod
apiVersion: v1
metadata:
  name: test-pod
spec:
  containers:
  - name: test-pod
    image: nginx
    command:
      - "/bin/sh"
    args:
      - "-c"
      - "touch /mnt/SUCCESS && exit 0 || exit 1"   
    volumeMounts:
      - name: nfs-pvc
        mountPath: "/mnt"
  restartPolicy: "Never"
  volumes:
    - name: nfs-pvc
      persistentVolumeClaim:
        claimName: test-claim  
kubectl apply -f 8-6-pod.yaml

可以看到

pod/test-pod created
kubectl get po

可以看到 test-pod 的 状态是 Completed,说明任务已经执行完成。

NAME                                      READY   STATUS      RESTARTS   AGE
nfs-client-provisioner-6976b6b79c-hdqm9   1/1     Running     0          20m
test-pod                                  0/1     Completed   0          80s

在 master 节点:

cd /nfsdata
ls

可以看到

[root@master nfsdata]# ls
default-test-claim-pvc-bcdc3799-27bc-4c38-a335-4050ac611fe5
[root@master nfsdata]# cd default-test-claim-pvc-bcdc3799-27bc-4c38-a335-4050ac611fe5/
[root@master default-test-claim-pvc-bcdc3799-27bc-4c38-a335-4050ac611fe5]# ls
SUCCESS
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容

  • 今天感恩节哎,感谢一直在我身边的亲朋好友。感恩相遇!感恩不离不弃。 中午开了第一次的党会,身份的转变要...
    迷月闪星情阅读 10,498评论 0 11
  • 彩排完,天已黑
    刘凯书法阅读 4,147评论 1 3
  • 表情是什么,我认为表情就是表现出来的情绪。表情可以传达很多信息。高兴了当然就笑了,难过就哭了。两者是相互影响密不可...
    Persistenc_6aea阅读 120,539评论 2 7