云原生工程师-7.k8s持久化存储

六.k8s持久化存储

个人博客

6.1-k8s持久化存储相关概念

6.1.1-持久化存储是什么

​ 在 k8s 中部署的应用都是以 pod 容器的形式运行的,假如我们部署 MySQL、Redis 等数据库,需要 对这些数据库产生的数据做备份。因为 Pod 是有生命周期的,如果 pod 不挂载数据卷,那 pod 被删除或 重启后这些数据会随之消失,如果想要长久的保留这些数据就要用到 pod 数据持久化存储。

k8s支持的持久化存储:

kubectl explain pods.spec.volumes #列举常用的
1.cephfs
2.configmap
3.emptyDir
4.hostPath
5.nfs
6.persistentVolumeClaim (PVC)
7.secret

我们想要使用存储卷,需要经历如下步骤

1、定义 pod 的 volume,这个 volume 指明它要关联到哪个存储上的

2、在容器中要使用 volumemounts 挂载对应的存储

6.2-k8s持久化存储的应用

6.2.1-emptyDir-临时目录

​ emptyDir 类型的 Volume 是在 Pod 分配到 Node 上时被创建,Kubernetes 会在 Node上自动分配一个 目录,因此无需指定宿主机 Node 上对应的目录文件。 这个目录的初始内容为空,当 Pod 从 Node 上移除 时,emptyDir 中的数据会被永久删除。empty DirVolume 主要用于某些应用程序无需永久保存的临时目 录,多个容器的共享目录等。

apiVersion: v1
kind: Pod
metadata:
  name: pod-empty
spec:
  containers:
  - name: container-empty
    image: docker.io/library/nginx:latest
    volumeMounts:
    - mountPath: /cache
      name: cache-volume
  volumes:
  - name: cache-volume
    emptyDir:
      {}
[root@master2 pvpvc]# kubectl get pods -o wide |grep pod-empty
pod-empty   1/1     Running   0          55s   192.168.180.43   master2   <none>           <none>
[root@master2 pvpvc]# kubectl get pods pod-empty -o yaml |grep uid
  uid: b738d209-d77a-4295-abfc-37eba1b0e9b2
[root@master2 pvpvc]# tree /var/lib/kubelet/pods/b738d209-d77a-4295-abfc-37eba1b0e9b2/
/var/lib/kubelet/pods/b738d209-d77a-4295-abfc-37eba1b0e9b2/
├── containers
│   └── container-empty
│       └── 838ca8cf
├── etc-hosts
├── plugins
│   └── kubernetes.io~empty-dir
│       ├── cache-volume
│       │   └── ready
│       └── wrapped_kube-api-access-p52cq
│           └── ready
└── volumes
    ├── kubernetes.io~empty-dir
    │   └── cache-volume
    └── kubernetes.io~projected
        └── kube-api-access-p52cq
            ├── ca.crt -> ..data/ca.crt
            ├── namespace -> ..data/namespace
            └── token -> ..data/token

kubernetes的emptyDir的临时目录/var/lib/kubelet/pods/uid

6.2.2-hostPath-宿主机

hostPath Volume 是指 Pod 挂载宿主机上的目录或文件。 hostPath Volume 使得容器可以使用宿主机的文件系统进行存储,hostpath(宿主机路径):节点级别的存储卷,在 pod 被删除,这个存储卷还是存在的,不会被删除,所以只要同一个 pod 被调度到同一个节点上来,在 pod 被删除重新被调度到这个节点之后,对应的数据依然是存在的。

apiVersion: v1
kind: Pod
metadata:
  name: pod-hostpath
spec:
  containers:
  - name: container-hostpath-nginx
    image: docker.io/library/nginx:latest
    volumeMounts:
    - mountPath: /nginx
      name: hostpath-volume-test
  - name: container-hostpath-tomcat
    image: docker.io/library/tomcat:latest
    volumeMounts:
    - mountPath: /tomcat
      name: hostpath-volume-test
  volumes:
  - name: hostpath-volume-test
    hostPath:
      path: /k8s-hostpath-volume
      type: DirectoryOrCreate #本地有就用,没有就在pod调度的节点自动创建 
[root@master2 pvpvc]# ls /k8s-hostpath-volume/
[root@master2 pvpvc]# touch /k8s-hostpath-volume/test
[root@master2 pvpvc]# kubectl exec -it pod-hostpath -c container-hostpath-nginx -- sh
# ls /nginx/
test
[root@master2 pvpvc]# kubectl exec -it pod-hostpath -c container-hostpath-tomcat -- sh
# ls /tomcat/
test
#两个容器共用一个文件目录,三者任意修改都会随之变动

hostpath 存储卷缺点: pod 删除之后重新创建必须调度到同一个 node 节点,数据才不会丢失

6.2.3-nfs存储

用nfs需要所有节点都安装nfs-utils

1.搭建nfs服务
yum install nfs-utils -y
2.在宿主机创建NFS需要的共享目录
mkdir /data/volumes -pv
3.设置链接权限vim /etc/exports
echo '/data/volumes 192.168.0.0/16(rw,no_root_squash)'>>/etc/exports #no_root_squash 完全权限
4.使 NFS 配置生效
exportfs -arv
5.启动服务
service nfs start
systemctl enable nfs
systemctl status nfs
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sqlserver-nfs
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sql
      version: v1
  template:
    metadata:
      labels:
        app: sql
        version: v1
    spec:
      containers:
      - name: sqlserver-nfs
        image: mcr.microsoft.com/mssql/server:2017-latest
        ports:
        - containerPort: 1443
        volumeMounts:
        - mountPath: /public/sqlserver/bak
          name: nfs-volume-test
          mountPath: /public/sqlserver/data
          name: nfs-volume-test
        env:
        - name: ACCEPT_EULA
          value: Y
          name: SA_PASSWORD
          value: Sql123456
      volumes:
      - name: nfs-volume-test
        nfs:
          path: /data/volumes
          server: 192.168.3.111
---
apiVersion: v1
kind: Service
metadata:
  name: sqlserver-nfs-svc
  labels:
    app: sql
spec:
  type: NodePort
  ports:
  - port: 1443 #集群内部中暴露的端口
    protocol: TCP
    targetPort: 1443 #容器端口
    nodePort: 31443
  selector:
    app: sql
---
apiVersion: v1
kind: Pod
metadata:
  name: pod-nfs
spec:
  containers:
  - name: container-nfs-nginx
    image: docker.io/library/nginx:latest
    volumeMounts:
    - mountPath: /nginx
      name: nfs-volume-test
  - name: container-nfs-tomcat
    image: docker.io/library/tomcat:latest
    volumeMounts:
    - mountPath: /tomcat
      name: nfs-volume-test
  volumes:
  - name: nfs-volume-test
    nfs:
      path: /data/volumes
      server: 192.168.3.111
#检验结果
[root@master2 pvpvc]# kubectl get pods
NAME      READY   STATUS    RESTARTS   AGE
pod-nfs   2/2     Running   0          40s
[root@master2 pvpvc]# kubectl exec -it pod-nfs -c container-nfs-nginx -- sh
# ls /nginx
# touch /nginx/aa
# exit
[root@master2 pvpvc]# ls /data/volumes/
aa

6.2.4-Pv-Pvc-持久化存储卷

概念:

PersistentVolume(PV):

是群集中的一块存储,由管理员配置或使用存储类动态配置。 它是集群中 的资源,就像 pod 是 k8s 集群资源一样。 PV 是容量插件,如 Volumes,其生命周期独立于使用 PV 的任 何单个 pod。

PersistentVolumeClaim(PVC):

是一个持久化存储卷,我们在创建 pod 时可以定义这个类型的存储 卷。 它类似于一个 pod。 Pod 消耗节点资源,PVC 消耗 PV 资源。 Pod 可以请求特定级别的资源(CPU 和 内存)。 pvc 在申请 pv 的时候也可以请求特定的大小和访问模式(例如,可以一次读写或多次只读)。

工作原理:

pv 的供应方式 :静态或者动态

静态:

集群管理员创建了许多 PV。它们包含可供群集用户使用的实际存储的详细信息。它们存在于 Kubernetes API 中,可供使用。标签链接;

动态:

当管理员创建的静态 PV 都不匹配用户的 PersistentVolumeClaim 时,群集可能会尝试为 PVC 专门动态配置卷。此配置基于 StorageClasses,PVC 必须请求存储类,管理员必须创建并配置该类,以便进行 动态配置。默认存储自动创建;

绑定:

用户创建 pvc 并指定需要的资源和访问模式。在找到可用 pv 之前,pvc 会保持未绑定状态

回收策略:

Retain: 删除pvc的时候,保留pv,处于released状态,不能被其他pvc绑定,数据还在。

Delete: 删除pvc的时候同时移除pv,相应删除外部存储资源。

ReadWriteOnce:可读科写,但支持被单个node挂载

ReadOnlyMany:可以以读的方式被多个node挂载

ReadWriteMany:可以以读写的方式被多个node挂载

通过nfs定义pv和pvc:

apiVersion: v1 
kind: PersistentVolume 
metadata: 
  name: v2 
spec: 
  capacity:
    storage: 2Gi
  accessModes:  ["ReadWriteOnce"] #ReadWriteMany,ReadOnlyMany
  nfs: 
    path: /data/volume_test/v2 #把 nfs 的存储空间创建成 pv 
    server: 192.168.3.111 #nfs 服务器的地址 
---
apiVersion: v1 
kind: PersistentVolumeClaim 
metadata: 
  name: my-pvc 
spec: 
  accessModes: ["ReadWriteOnce"] 
  resources: 
    requests: 
      storage: 2Gi
[root@master2 pvpvc]# kubectl apply -f pvc.yaml
persistentvolume/v2 created
persistentvolumeclaim/my-pvc created
[root@master2 pvpvc]# kubectl get pv
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM            STORAGECLASS   REASON   AGE
v2     2Gi        RWO            Retain           Bound    default/my-pvc                           3s
[root@master2 pvpvc]# kubectl get pvc
NAME     STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
my-pvc   Bound    v2       2Gi        RWO                           5s
#Bound绑定,Available 可用

1、我们每次创建 pvc 的时候,需要事先有划分好的 pv,这样可能不方便,那么可以在创建 pvc 的时 候直接动态创建一个 pv 这个存储类,pv 事先是不存在的

2、pvc 和 pv 绑定,如果使用默认的回收策略 retain,那么删除 pvc 之后,pv 会处于 released 状 态,我们想要继续使用这个 pv,需要手动删除 pv,kubectl delete pv pv_name,删除 pv,不会删除 pv 里的数据,当我们重新创建 pvc 时还会和这个最匹配的 pv 绑定,数据还是原来数据,不会丢失。

6.2.5-storageclass默认存储sc

​ 上面介绍的 PV 和 PVC 模式都是需要先创建好 PV,然后定义好 PVC 和 pv 进行一对一的 Bond,但是如 果 PVC 请求成千上万,那么就需要创建成千上万的 PV,对于运维人员来说维护成本很高,Kubernetes 提 供一种自动创建 PV 的机制,叫StorageClass,它的作用就是创建 PV 的模板。k8s 集群管理员通过创建storageclass 可以动态生成一个存储卷 pv 供 k8s pvc 使用。

具体来说,StorageClass 会定义以下两部分:

1、PV 的属性 ,比如存储的大小、类型等;

2、创建这种 PV 需要使用到的存储插件,比如 Ceph、NFS 等

#安装nfs provisioner,用于配合存储类动态生成 pv 
#(1)创建运行nfs-provisioner的sa账号
#cat serviceaccount.yaml
#kubectl apply -f serviceaccount.yaml
#(2)对sa账号做rbac授权
#cat rbac.yaml
#kubectl apply -f rbac.yaml
#(3)通过deployment创建pod用来运行nfs-provisioner程序(用来划分pv的程序)
#1.创建目录mkdir /data/nfs_pro -p;
#2.权限:echo '/data/nfs_pro 192.168.0.0/16(rw,no_root_squash)' >> /etc/exports
#3.exportfs -arv
#cat nfs-deployment.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-provisioner
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: nfs-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"]
  - apiGroups: [""]
    resources: ["services", "endpoints"]
    verbs: ["get"]
  - apiGroups: ["extensions"]
    resources: ["podsecuritypolicies"]
    resourceNames: ["nfs-provisioner"]
    verbs: ["use"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: run-nfs-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-provisioner
    namespace: default
roleRef:
  kind: ClusterRole
  name: nfs-provisioner-runner
  apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-provisioner
rules:
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-provisioner
    namespace: default
roleRef:
  kind: Role
  name: leader-locking-nfs-provisioner
  apiGroup: rbac.authorization.k8s.io
---
kind: Deployment
apiVersion: apps/v1
metadata:
  name: nfs-provisioner
spec:
  selector:
    matchLabels:
       app: nfs-provisioner
  replicas: 1
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: nfs-provisioner
    spec:
      serviceAccount: nfs-provisioner
      containers:
        - name: nfs-provisioner
          image: registry.cn-beijing.aliyuncs.com/pylixm/nfs-subdir-external-provisioner:v4.0.0
          volumeMounts:
            - name: nfs-client-root
              mountPath: /persistentvolumes
          env:
            - name: PROVISIONER_NAME
              value: example.com/nfs
            - name: NFS_SERVER
              value: 192.168.3.111
            - name: NFS_PATH
              value: /data/nfs_pro
      volumes:
        - name: nfs-client-root
          nfs:
            server: 192.168.3.111
            path: /data/nfs_pro
#创建sc
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: nfs
provisioner: example.com/nfs
reclaimPolicy: Retain
allowVolumeExpansion: true #
[root@master2 storageclass]# kubectl get sc
NAME   PROVISIONER       RECLAIMPOLICY   VOLUMEBINDINGMODE
nfs    example.com/nfs   Retain          Immediate          

测试是否成功:

kind: PersistentVolumeClaim 
apiVersion: v1 
metadata: 
  name: test-claim1
spec: 
  accessModes: ["ReadWriteMany"] 
  resources: 
    requests: 
      storage: 1Gi 
  storageClassName: nfs
[root@master2 storageclass]# kubectl get pvc
NAME          STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
test-claim1   Bound    pvc-4a49c5a1-1f21-46ec-aac8-219aff6eaa8f   1Gi        RWX            nfs            6m33s
[root@master2 storageclass]# ls /data/nfs_pro/
default-test-claim1-pvc-4a49c5a1-1f21-46ec-aac8-219aff6eaa8f

6.2.6-测试-deployment-mysql

#创建deployment测试

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: sql
  name: mysql-1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sql
  template:
    metadata:
      labels:
        app: sql
    spec:
      containers:
      - image: docker.io/library/mysql:5.6
        name: mysql-1
        resources: {}
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: "Mysql123456"
        args:
          - "--bind-address=0.0.0.0"
        ports:
        - name: mysql-3306
          containerPort: 3306
          protocol: TCP
        volumeMounts:
        - name: mysql-persistent-storage
          mountPath: /var/lib/mysql
      volumes:
      - name: mysql-persistent-storage
        persistentVolumeClaim:
          claimName: mysql-1
---
apiVersion: v1
kind: Service
metadata:
  name: mysql-1-svc
  labels:
    app: sql
spec:
  type: NodePort
  selector:
    app: sql
  sessionAffinity: None
  ports:
    - nodePort: 30336
      protocol: TCP
      port: 3306
      targetPort: 3306
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: mysql-1
spec:
  accessModes: ["ReadWriteMany"]
  resources:
    requests:
      storage: 5Gi
  storageClassName: nfs

结果:

[root@master2 mysql]# mysql -u root -h master1 -P 30336 -pMysql123456
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 4
Server version: 5.6.51 MySQL Community Server (GPL)

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MySQL [(none)]>
[root@master2 mysql]# ls /data/nfs_pro/default-mysql-1-pvc-85898039-a97f-4ab8-880f-6379c8fa6578/
auto.cnf  ibdata1  ib_logfile0  ib_logfile1  mysql  performance_schema

你可能感兴趣的:(kubernetes,云原生,docker)