个人博客
在 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 挂载对应的存储
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
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 节点,数据才不会丢失
用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
概念:
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 绑定,数据还是原来数据,不会丢失。
上面介绍的 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
#创建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