如果不用volume
不用volume,pod的文件默认用docker的overlay2存储,pod删除后里面存储的内容就没了
举例说明 查看pod在docker里的存储目录
先查看pod的docker id
kubectl describe pods -n kube-system kube-controller-manager-iz8vb91u3c0d2ijj2osl5wz
根据docker id查找pod所在的目录
docker ps -q | xargs docker inspect --format '{{.State.Pid}}, {{.Id}}, {{.Name}}, {{.GraphDriver.Data.WorkDir}}' | grep d20802c90a2a6818c6d13bcd1bfd
可用的卷
有多种卷类型可供选择。其中一些是通用的,而另一些则相对于当前常用的存储技术有较大差别。如果从来没有听说过这些技术,也别太担心一一其中至少一半我也没有昕说过。你有可能只会用到那些自己熟悉和曾经用过的卷技术。以下是
几种可用卷类型的列表:
• emptyDir一一用于存储临时数据的简单空目录。
• hostPath -一用于将目录从工作节点的文件系统挂载到pod 中。
• gitRepo一一通过检出Git 仓库的内容来初始化的卷。
• nfs一一挂载到pod 中的NFS 共享卷。
• gcePersistentDisk (Google 高效能型存储磁盘卷)、awsElastic、BlockStore (AmazonWeb 服务弹性块存储卷)、azureDisk (Microso负Azure 磁盘卷)一一用于挂载云服务商提供的特定存储类型。
• cindr 、cephfs 、iscsi 、flocker 、glusterfs 、quobyte 、rbd 、flexVolume 、vsphere -Volume 、photoPersistentDisk 、scaleIO用于挂载其他类型的网络存储。
• configMap 、secret 、downwardAPI 一一用于将Kubemetes 部分资源和集
群信息公开给pod 的特殊类型的卷。
• persistentVolume一一一种使用预置或者动态配置的持久存储类型
emptyDir
最简单的卷类型是emp tyDir 卷,所以作为第一个例子让我们看看如何在pod中定义卷。顾名思义,卷从一个空目录开始,运行在pod 内的应用程序可以写入它需要的任何文件。因为卷的生存周期与pod 的生存周期相关联,所以当删除pod 时,卷的内容就会丢失。
使用 emptyDir
cat > myapp-empty.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
name: myapp-empty
namespace: default
labels:
app: myapp
type: pod
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
ports:
- name: http
containerPort: 80
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html/
- name: busybox
image: busybox:latest
imagePullPolicy: IfNotPresent
command:
- "/bin/sh"
- "-c"
- "echo 'this is test' > /data/test.html;sleep 3600"
volumeMounts:
- name: html
mountPath: /data/
volumes:
- name: html
emptyDir: {}
EOF
指定用于emptyDir 的介质
作为卷来使用的emptyDir ,是在承载pod 的工作节点的实际磁盘上创建的,因此其性能取决于节点的磁盘类型。但我们可以通知Kubemetes 在tmfs 文件系统( 存在内存而非硬盘) 上创建emptyDir 。因此,将emptyDir 的medium 设置为Memory
指定emptyDir 的大小
gitRepo
gitRepo volume可以理解为是emptyDir volume的一种实际应用,使用该volume的Pod可以在挂载目录访问指定的代码仓库。
使用gitRepo
cat > gitRepo.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
name: vol-gitrepo-pod
spec:
containers:
- name: nginx
image: nginx:1.12-alpine
volumeMounts:
- name: html
mountPath: /usr/share/nginx/
volumes:
- name: html
gitRepo:
repository: "https://github.com/tangwei0928/html.git"
revision: "master"
EOF
hostPath
大多数 pod 应该忽略它们的主机节点,因此它们不应该出问节点文件系统上的任何文件。 但是某些系统级别的 pod(切记,这些通常由 DaemonSet 管理)确实需要读取节点的文件或使用节点文件系统来访问节点设备。 Kubemetes 通过 hostPath 卷实现了这一点。
hostPath 卷指向节点文件系统上的特定文件或目录。在同一 个节点上运行并在其 hostPath 卷中使用相同路径的 pod 可以看到相同的文件。
hostPath 卷是我们介绍的第一种类型的持久性存储,因为 gitRepo 和 emptyDir 卷的内容都会在 pod 被删除时被删除,而 hostPath 卷的内容则不会被删除。如果删除了一个 pod,并且下一个 pod 使用了指向主机上相同路径的 hostPath 卷,则新 pod 将会发现上一个 pod 留下的数据,但前提是必须将其调度到与第一个 pod 相同的节点上。 如果你正在考虑使用 hostPath 卷作为存储数据库数据的目录,请重新考虑。 因为卷的内容存储在特定节点的文件系统中,所以当数据库 pod 被重新安排在另一 个节点时,会找不到数据。 这解释了为什么对常规 pod 使用 hostPath 卷不是一个好主意,因为这会使 pod 对预定规划的节点很敏感。
使用hostPath
cat > myapp-hostpath.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
name: myapp-hostpath
namespace: default
labels:
app: myapp
type: pod
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
ports:
- name: http
containerPort: 80
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html/
volumes:
- name: html
hostPath:
path: /data/pod/
type: DirectoryOrCreate
EOF
在pod节点hostpath目录下添加内容
NFS
如果集群是运行在自有的一组服务器上,那么就有大量其他可移植的选项用于在卷内挂载外部存储。 例如,要挂载一个简单的 NFS 共享,只需指定 NFS 服务器和共享路径。
使用NFS
cat > myapp-nfs.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
name: myapp-nfs
namespace: default
labels:
app: myapp
type: pod
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
ports:
- name: http
containerPort: 80
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html/
volumes:
- name: html
nfs:
path: /data/volumes
server: 192.168.33.101
EOF
在nfs服务器上添加内容
PV
在 pod 中使用 PersistentVolume (持久卷,简称 PV) 要比使用常规的 pod 卷复杂一些。 研发人员无须向他们的 pod 中添加特定技术的卷,而是由集群管理员设置底层存储,然后通过 Kubernetes API 服务器创建持久卷并注册。 在创建持久卷时,管理员可以指定其大小和所支持的访问模式。 当集群用户需要在其 pod 中使用持久化存储时,他们首先创建持久卷声明 ( PersistentVolumeClaim, 简称 PVC)清单,指定所需要的最低容量要求和访问模式, 然后用户将持久卷声明清单提交给 Kubernetes API 服务器, Kubernetes 将找到可匹 配的持久卷并将其绑定到持久卷声明 。 持久卷声明可以当作 pod 中的一个卷来使用,其他用户不能使用相同的持久卷, 除非先通过删除障久卷声明绑定来释放。
pv参数
capacity
一般来说,一个 PV 对象都要指定一个存储能力,通过 PV 的 capacity属性来设置的,目前只支持存储空间的设置,就是我们这里的 storage=1Gi,不过未来可能会加入 IOPS、吞吐量等指标的配置。
accessModes
AccessModes 是用来对 PV 进行访问模式的设置,用于描述用户应用对存储资源的访问权限,访问权限包括下面几种方式:
- ReadWriteOnce(RWO):读写权限,但是只能被单个节点挂载
- ReadOnlyMany(ROX):只读权限,可以被多个节点挂载
- ReadWriteMany(RWX):读写权限,可以被多个节点挂载
persistentVolumeReclaimPolicy
我这里指定的 PV 的回收策略为 Recycle,目前 PV 支持的策略有三种:
- Retain(保留)- 保留数据,需要管理员手工清理数据
- Recycle(回收)- 清除 PV 中的数据,效果相当于执行 rm -rf
- Delete(删除)- 与 PV 相连的后端存储完成 volume 的删除操作,当然这常见于云服务商的存储服务,比如 ASW EBS。
不过需要注意的是,目前只有 NFS 和 HostPath 两种类型支持回收策略。当然一般来说还是设置为 Retain 这种策略保险一点。
使用nfs做pv
cat > myapp-pv.yaml << EOF
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv01
labels:
name: pv01
type: pv
spec:
nfs:
path: /data/volumes/v1
server: 192.168.33.101
accessModes: ["ReadWriteMany"]
capacity:
storage: 5Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myapp-pvc
namespace: default
labels:
app: myapp
type: pvc
spec:
accessModes: ["ReadWriteMany"]
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: Pod
metadata:
name: myapp-nfs-pvc
namespace: default
labels:
app: myapp
type: pod
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
ports:
- name: http
containerPort: 80
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html/
volumes:
- name: html
persistentVolumeClaim:
claimName: myapp-pvc
EOF
使用local pv
local pv前提条件
- 节点上需要打上调度需要的label
- 节点上手动创建目录
cat > local-pv.yaml << EOF
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: local-storage
namespace: default
provisioner: kubernetes.io/no-provisioner
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: local-pv
namespace: default
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage
local:
path: /data/localpv
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: local-pv
operator: In
values:
- node01
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: local-pvc
namespace: default
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: local-storage
---
apiVersion: v1
kind: Pod
metadata:
name: myapp-local-pvc
namespace: default
labels:
app: myapp
type: pod
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
ports:
- name: http
containerPort: 80
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html/
volumes:
- name: html
persistentVolumeClaim:
claimName: local-pvc
EOF
在local pv的目录里添加内容
Storageclass
学习了 PV 和 PVC 的使用方法,但是前面的 PV 都是静态的,什么意思?就是我要使用的一个 PVC 的话就必须手动去创建一个 PV,我们也说过这种方式在很大程度上并不能满足我们的需求,比如我们有一个应用需要对存储的并发度要求比较高,而另外一个应用对读写速度又要求比较高,特别是对于 StatefulSet 类型的应用简单的来使用静态的 PV 就很不合适了,这种情况下我们就需要用到动态 PV,也就是我们今天要讲解的 StorageClass。
要使用 StorageClass,我们就得安装对应的自动配置程序,比如我们这里存储后端使用的是 nfs,那么我们就需要使用到一个 nfs-client 的自动配置程序,我们也叫它 Provisioner,这个程序使用我们已经配置好的 nfs 服务器,来自动创建持久卷,也就是自动帮我们创建 PV。
使用nfs Storageclass
cat > myapp-sc.yaml << EOF
kind: Deployment
apiVersion: apps/v1
metadata:
name: nfs-client-provisioner
spec:
replicas: 1
selector:
matchLabels:
app: nfs-client-provisioner
strategy:
type: Recreate
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccountName: nfs-client-provisioner
containers:
- name: nfs-client-provisioner
image: quay.io/external_storage/nfs-client-provisioner:latest
imagePullPolicy: IfNotPresent
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: nfs-provisioner
- name: NFS_SERVER
value: 192.168.33.101
- name: NFS_PATH
value: /data/volumes
volumes:
- name: nfs-client-root
nfs:
server: 192.168.33.101
path: /data/volumes
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-client-provisioner
---
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", "create"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch", "create"]
- apiGroups: [""]
resources: ["events"]
verbs: ["list", "watch", "create", "update", "patch"]
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["create", "delete", "get", "list", "watch", "patch", "update"]
---
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
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: k8s-nfs-storage
provisioner: nfs-provisioner
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: nfs-web
spec:
serviceName: "nginx"
replicas: 1
selector:
matchLabels:
app: nfs-web
template:
metadata:
labels:
app: nfs-web
spec:
terminationGracePeriodSeconds: 10
containers:
- name: nginx
image: ikubernetes/myapp:v1
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
annotations:
volume.beta.kubernetes.io/storage-class: k8s-nfs-storage
spec:
accessModes: [ "ReadWriteMany" ]
resources:
requests:
storage: 1Gi
EOF
实际工作中,使用 StorageClass 更多的是 StatefulSet 类型的服务,StatefulSet 类型的服务我们也可以通过一个 volumeClaimTemplates 属性来直接使用 StorageClass
在nfs目录里添加内容
local-volume-provisioner
在Kubernetes系统中使用本地盘可以通过HostPath、LocalVolume等类型的PV使用:
HostPath: 卷本身不带有调度信息,如果想对每个pod固定在某个节点上,就需要对pod配置nodeSelector等调度信息; LocalVolume: 卷本身包含了调度信息,使用这个卷的pod会被固定在特定的节点上,这样可以很好的保证数据的连续性。
通过local-volume-provisioner自动创建LocalVolume,PV附带所属的节点信息;
然后创建pvc, 使用这个pvc的pod都会调度到pv所指定的节点;
监听宿主机上的某一个路径下面是不是有已经mount的好的文件夹,一旦看到有新的文件夹创建,那么它就会创建一个新的PV.
官方网站:https://github.com/kubernetes-sigs/sig-storage-local-static-provisioner
查看pod
kubectl get pods -n acs-system
查看pv
kubectl get pv
查看监控的目录
kubectl get cm -n acs-system local-provisioner-config -o yaml
kubectl get daemonsets.apps -n acs-system local-volume-provisioner -o yaml
挂载目录
在node01节点做测试
lsblk
mkdir /data/vol1
mkdir /data/vol2
mount /dev/sdc1 /data/vol1
mount /dev/sdc2 /data/vol2
挂载目录后
自动生成pv
kubectl get pv
测试pod
创建pvc pod,查看是否成功
vim local-volume.yaml
##创建pvc
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myclaim
namespace: acs-system
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: fast-disks
---
##创建pod
kind: Pod
apiVersion: v1
metadata:
name: mypod
namespace: acs-system
spec:
containers:
- name: myfrontend
image: nginx:1.16
volumeMounts:
- mountPath: "/data"
name: mypd
volumes:
- name: mypd
persistentVolumeClaim:
claimName: myclaim