kubernetes容器持久化存储
之前使用了volume
支持了本地和网络存储,譬如之前的NFS
挂载要指定NFS
服务器IP
要挂载的数据卷和容器要挂载的目录,下面会提到两个东西,分别是PersistenVolume(PV)&PersistentVolumeClaim(PVC)
,他们作用和设计目的主要是将存储资源可以作为集群的一部分来管理。
PV
你可以理解为一个接口,例如挂载NFS
,你直接去使用PVC
资源去消费就可以了,所以我们要定义PV
的大小,以什么样的模式去访问,你知道这些信息就好了,这就是PV&PVC
的设计之初,PV
的主要作用就是将你后端的存储做一个抽象,然后在集群中可以管理这些存储,它分为静态和动态,静态就是需要手动去除创建,动态就是自动为你去创建一个PV
,一会静态和动态都会涉及到。
PVC
可以让用户不需要关心具体存储的实现,他只关心你用多大容量,PV
也就是持久卷,Pod
去使用pv
的持久卷,你在任何节点都能访问到自己的数据,即使是删除再启动,PVC
可以让用户更简单的使用,成为编排的一部分,所以PV&PVC
的关系很简,PV
是提供者,提供存储容量,PVC
是消费者,消费的过程称为绑定
PV 静态供给
要使用PV
会有三个阶段,需要定义容器应用,定义卷的需求,定义数据卷,先看容器定义
定义PVC
现在定义数据卷的来源只需要指定一个PVC
,你可以理解这个PVC
是个媒人
[root@k8s01 yml]# cat nginx-pvc.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: web
spec:
selector:
matchLabels:
app: web
replicas: 3
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
volumeMounts:
- name: wwwroot
mountPath: /usr/share/nginx/html
volumes:
- name: wwwroot
persistentVolumeClaim:
claimName: nginx-pvc
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nginx-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
目前的访问模式定义的为ReadWriteMany
,也就是卷可以由许多节点以读写方式挂载,缩写为RWX
,除了这个还有两种,分别如下。
- ReadWriteOnce
- 卷可以由单个节点以读写方式挂载,缩写
RWO
- 卷可以由单个节点以读写方式挂载,缩写
- ReadOnlyMany
- 卷可以由许多节点以只读方式挂载,缩写
ROX
- 卷可以由许多节点以只读方式挂载,缩写
这算是一个需求撒,申请5G
的使用空间,访问模式为ReadWriteMany
,pvc
需要把这个需求告诉pv
定义PV
最后定义数据卷。
[root@k8s01 yml]# cat pv.yml
apiVersion: v1
kind: PersistentVolume
metadata:
name: nginx-pv
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteMany
nfs:
path: /data/
server: 192.168.10.41
这里的访问模式和pvc
的一样,现在可以开始创建了。
开始创建
需要先创建你的pv
,其实在你创建pv
的时候你并不知道真的有人需要这个pv
,所以一般情况下会创建很多的pv
在这里等着,你找存储的需求发布到pvc
后pvc
会去匹配pv
,如果有合适直接牵线,如果没有匹配到从此你就一蹶不振,无法启动,大概就是这样,所以现在把pv
创建了吧。
[root@k8s01 yml]# kubectl create -f pv.yml
persistentvolume/nginx-pv created
[root@k8s01 yml]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
nginx-pv 5Gi RWX Retain Available 5s
使用get pv
可以看到当前集群中哪pv
可以用,可以看到就一个刚刚创建的,而且开始可用状态,模式为RWX
,你领到后想干嘛干嘛,现在可以创建应用了,找pvc的需求也写好了,所以你没写错的话创建之后就能领走了。
[root@k8s01 yml]# kubectl create -f nginx-pvc.yaml
deployment.apps/nginx-deployment created
persistentvolumeclaim/nginx-pvc created
[root@k8s01 yml]# kubectl get pods,pv,pvc
NAME READY STATUS RESTARTS AGE
pod/nginx-deployment-7b77d54576-6bnpc 1/1 Running 0 44s
pod/nginx-deployment-7b77d54576-6fwz4 1/1 Running 0 44s
pod/nginx-deployment-7b77d54576-8cx4w 1/1 Running 0 44s
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistentvolume/nginx-pv 5Gi RWX Retain Bound default/nginx-pvc 9m51s
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/nginx-pvc Bound nginx-pv 5Gi RWX 44s
[root@k8s01 yml]#
没啥子问题,Pod
正常启动并且牵线成功,试着访问一下
[root@k8s01 yml]# cat services.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-service
labels:
app: web
spec:
type: NodePort
ports:
- port: 80
targetPort: 80
selector:
app: web
[root@k8s01 yml]# kubectl create -f services.yaml
service/nginx-service created
[root@k8s01 yml]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.0.0.1 <none> 443/TCP 13h
nginx-service NodePort 10.0.0.188 <none> 80:49302/TCP 61s
[root@k8s01 yml]# curl 192.168.10.92:49302
hello world
[root@k8s01 yml]#
没问题,这就是使用一个持久卷静态模式下的部署,在pvc
匹配pv
的时候,主要看的是存储容量和访问模式,这两个都符合需求才能匹配成功,都符合两个需求的pv
可能有很多,多的话随便给你一个用就算完了,如果匹配不到合适的就绝不会牵线,可能你会感觉很麻烦,每次都要去手动创建pv
,能不能我需求发布后自动给我创建一个?可以,这就会涉及到PV
的动态供给了,手动创建在小的规模下部署的应用没几个手动搞搞也无所谓,大规模的情况下就很麻烦了,下面看看PV
动态供给。
官方文档编写yaml文件:
https://github.com/kubernetes-incubator/external-storage/tree/master/nfs/docs/demo
PV动态供给(StorageClass)
K8S
提供了一个机制叫Dynamic Provisioning
,这个机制工作的核心在于StorageClass
,这是一个存储类,他的作用是自动操作你后端的存储,为你创建相对应的PV
,一个API
对象,下面定义一个StorageClass
,后端还使用NFS
作为存储。
定义StorageClass
[root@k8s01 yml]# cat storageclass-nfs.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: managed-nfs-storage
provisioner: fuseim.pri/ifs
parameters:
archiveOnDelete: "false"
定义nfs-web插件
[root@k8s01 yml]# cat nfs-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-client-provisioner
labels:
app: nfs-client-provisioner
namespace: default
spec:
replicas: 1
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: lizhenliang/nfs-client-provisioner:latest
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: fuseim.pri/ifs
- name: NFS_SERVER
value: 192.168.10.41
- name: NFS_PATH
value: /data
volumes:
- name: nfs-client-root
nfs:
server: 192.168.10.41
path: /data
定义rbac授权
[root@k8s01 yml]# cat 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
# replace with namespace where provisioner is deployed
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
# replace with namespace where provisioner is deployed
namespace: default
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
roleRef:
kind: Role
name: leader-locking-nfs-client-provisioner
apiGroup: rbac.authorization.k8s.io
默认情况下NFS
是不支持动态供给的,需要部署一个插件,你需要改一下NFS
地址和共享目录,这个插件需要连接apiserver
的,所以要进行授权,上面的文件都已经定义了,下面直接开始创建就行了,确保Pod
启动后没有抛错。
[root@k8s01 yml]# kubectl create -f storageclass-nfs.yaml
storageclass.storage.k8s.io/managed-nfs-storage created
[root@k8s01 yml]# kubectl get storageclasses.storage.k8s.io
NAME PROVISIONER AGE
managed-nfs-storage fuseim.pri/ifs 37s
[root@k8s01 yml]#
[root@k8s01 yml]# kubectl create -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
[root@k8s01 yml]#
[root@k8s01 yml]# kubectl create -f nfs-deployment.yaml
deployment.apps/nfs-client-provisioner created
[root@k8s01 yml]#
[root@k8s01 yml]# kubectl get pods nfs-client-provisioner-c46c684fd-dz4vp
NAME READY STATUS RESTARTS AGE
nfs-client-provisioner-c46c684fd-dz4vp 1/1 Running 0 48s
[root@k8s01 yml]#
使用动态供给
部署一个资源对象会请求managed-nfs-storage,他会创建pv,nfs-client-provisioner具体会请求NFS
部署mysql
部署一个mysql,他需要一个持久化的存储和一个唯一的网络标识,现在需要写一个statefulset
和一个无头Service
[root@k8s01 yml]# cat mysql-demo.yaml
apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
ports:
- port: 3306
name: mysql
clusterIP: None
selector:
app: mysql-public
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql-db
spec:
selector:
matchLabels:
app: mysql-public
serviceName: "mysql"
template:
metadata:
labels:
app: mysql-public
spec:
containers:
- name: mysql
image: mysql:5.7
env:
- name: MYSQL_ROOT_PASSWORD
value: "123456"
ports:
- containerPort: 3306
volumeMounts:
- mountPath: "/var/lib/mysql"
name: mysql-data
volumes:
- name: mysql-data
persistentVolumeClaim:
claimName: mysql-pvc
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pvc
spec:
accessModes:
- ReadWriteMany
storageClassName: managed-nfs-storage
resources:
requests:
storage: 5Gi
数据来源指定的pv
动态供给,创建pvc
动态供给的内容如下。
volumes:
- name: mysql-data
persistentVolumeClaim:
claimName: mysql-pvc
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pvc
spec:
accessModes:
- ReadWriteMany
storageClassName: managed-nfs-storage
resources:
requests:
storage: 5Gi
使用的存储类名称为managed-nfs-storage
,就是上面我定义的那个,他和之前的数据卷引用方式差不多,只不过这个需要指定存储类帮你创建pv
,也就是这个。
[root@k8s01 yml]# kubectl get pod nfs-client-provisioner-c46c684fd-dz4vp
NAME READY STATUS RESTARTS AGE
nfs-client-provisioner-c46c684fd-dz4vp 1/1 Running 0 36m
[root@k8s01 yml]#
现在可以创建mysql服务了
[root@k8s01 yml]# kubectl create -f mysql-demo.yaml
service/mysql created
statefulset.apps/mysql-db created
persistentvolumeclaim/mysql-pvc created
[root@k8s01 yml]# kubectl get pods mysql-db-0
NAME READY STATUS RESTARTS AGE
mysql-db-0 1/1 Running 0 33s
[root@k8s01 yml]# kubectl get pv,pvc
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistentvolume/nginx-pv 5Gi RWX Retain Bound default/nginx-pvc 114m
persistentvolume/pvc-43bff7ce-bbdf-483a-8359-f03419d196ff 5Gi RWX Delete Bound default/mysql-pvc managed-nfs-storage 54s
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/mysql-pvc Bound pvc-43bff7ce-bbdf-483a-8359-f03419d196ff 5Gi RWX managed-nfs-storage 54s
persistentvolumeclaim/nginx-pvc Bound nginx-pv 5Gi RWX 105m
[root@k8s01 yml]#
就是这种效果,试试这个mysql是否能使用,需要通过DNS
名称去访问了,启动一个容器试试。
[root@k8s01 ~]# kubectl create clusterrolebinding kube-apiserver:kubelet-apis --clusterrole=system:kubelet-api-admin --user kubernetes
clusterrolebinding.rbac.authorization.k8s.io/kube-apiserver:kubelet-apis created
[root@k8s01 ~]# kubectl run -it --image=mysql:5.7 mysql-client --restart=Never --rm /bin/bash
If you don't see a command prompt, try pressing enter.
root@mysql-client:/# mysql -uroot -p123456 -hmysql-db-0.mysql
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or g.
Your MySQL connection id is 3
Server version: 5.7.30 MySQL Community Server (GPL)
Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or 'h' for help. Type 'c' to clear the current input statement.
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
+--------------------+
4 rows in set (0.00 sec)
mysql>
看一眼NFS
上的东西
[root@nfs01 data]# ll default-mysql-pvc-pvc-43bff7ce-bbdf-483a-8359-f03419d196ff/
total 188484
-rw-r----- 1 polkitd ssh_keys 56 Jun 7 13:47 auto.cnf
-rw------- 1 polkitd ssh_keys 1680 Jun 7 13:47 ca-key.pem
-rw-r--r-- 1 polkitd ssh_keys 1112 Jun 7 13:47 ca.pem
-rw-r--r-- 1 polkitd ssh_keys 1112 Jun 7 13:47 client-cert.pem
-rw------- 1 polkitd ssh_keys 1676 Jun 7 13:47 client-key.pem
-rw-r----- 1 polkitd ssh_keys 1343 Jun 7 13:47 ib_buffer_pool
-rw-r----- 1 polkitd ssh_keys 79691776 Jun 7 13:47 ibdata1
-rw-r----- 1 polkitd ssh_keys 50331648 Jun 7 13:47 ib_logfile0
-rw-r----- 1 polkitd ssh_keys 50331648 Jun 7 13:47 ib_logfile1
-rw-r----- 1 polkitd ssh_keys 12582912 Jun 7 13:48 ibtmp1
drwxr-x--- 2 polkitd ssh_keys 4096 Jun 7 13:47 mysql
drwxr-x--- 2 polkitd ssh_keys 8192 Jun 7 13:47 performance_schema
-rw------- 1 polkitd ssh_keys 1676 Jun 7 13:47 private_key.pem
-rw-r--r-- 1 polkitd ssh_keys 452 Jun 7 13:47 public_key.pem
-rw-r--r-- 1 polkitd ssh_keys 1112 Jun 7 13:47 server-cert.pem
-rw------- 1 polkitd ssh_keys 1680 Jun 7 13:47 server-key.pem
drwxr-x--- 2 polkitd ssh_keys 8192 Jun 7 13:47 sys
[root@nfs01 data]#