概述
K8S适配各种存储系统,包括本地存储EmptyDir,HostPath,网络存储NFS,GlusterFS,PV/PVC等 为什么Pod使用这个PVC就可以实现容器的持久存储呢? 容器的Volume就是将一个宿主机上的目录跟一个容器里的目录绑定挂载。只要宿主机上的这个路径的目录是持久的,那么在容器中的路径Volume也就是持久的了。所谓的持久,就是容器被删除,而Volume可以保留。
### 卷
卷就相当于一个存储,卷是Pod的一个组成部分,不属于kubernetes对象,所以不能单独创建和删除, Pod中的所有容器都可以使用卷,但是必须先将它挂载到需要访问它的容器中 在每个容器中都可以在自身文件系统的任意位置挂载卷,一个卷可以被多个容器挂载,当pod删除后,卷也将不存在
- 生命周期
该pod启动时创建
该pod被移除时删除
容器崩溃数据不丢失
常用挂载类型:
(emptyDir、hostPath、pvc+pv、StorageClass)
挂载YMAL写法
volumeMounts:
- mountPath: /etc/mysql/mariadb.conf.d/ #容器内的挂载目录
name: zizhen #随便给一个名字,这个名字必须与volumes.name一致
- mountPath: /var/lib/mysql #容器内的挂载目录**
name: zizhen-mysql
pvc+pv
PVC表达的是用户对存储的请求(persistant volume claim),也是kubernetes中独立存在的API资源。
Pod 会耗用节点资源,Pod 可以请求特定数量的资源(CPU 和内存) pv:向存储申请空间并关联pvc,不受namespace限制 PVC:请求特定的大小和访问模式, PVC 申领会耗用 PV 资源。 PVC和PV之间的绑定是一种一对一的映射。
- 一个pod可以挂载多个pvc
- 多个pod可挂载一个pvc
- pv和pvc一一对应
kubernetes的API资源,就是storageClass。 storageClass可以说是PV的创建模板。 前面提到,PV可以是集群管理员事先供应的,就是所谓的静态供应(static provisioning)。 这个方法很大的一个问题在于,当kubernetes集群规模很大时,需要管理员手工去创建成千上万的PV来对应存储资源,这是很繁琐的,因此,kubernetes中PV的创建一般会使用动态供应(dynamic provisioning)。
在storageClass中会定义: (1)PV的属性,如存储类型和大小; (2)创建PV需要的存储插件,如NFS。
这样,kubernetes就可以根据PVC指定的storageClass,调用指定的存储插件,创建所需的PV。
Pod、PVC、PV、StorageClass的关系图可以解释如下:
Pod中使用持久存储
例如,用户创建一个PVC,如下:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs
spec:
accessModes:
- ReadWriteMany
storageClassName: manual
resources:
requests:
storage: 1Gi
集群中的Volume Controller发现这个PVC后,就会主动在集群中寻找合适的PV,来和PVC绑定。 只有和PV绑定了的PVC,才能被pod正常挂载使用。 Volume Controller寻找PV的条件主要是: (1)PVC和PV的spec字段中指定的规格,例如存储(storage)的大小; (2)PVC和PV的storageClassName必须一样。(这里的storage)
如果集群中存在类型下面这样能满足PVC条件的PV,则可能会被绑定:
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs
spec:
storageclassName: manual
capacity:
storage: 1Gi
accessModes:
- ReadWriteMany
nfs:
server: 10.0.0.1
path: "/"
这个准备“持久化”宿主机目录的过程,我们可以形象地称为“两阶段处理”。
(1)Attach:为宿主机挂载远程存储;(如果是NFS的话,其实没有这个过程,因为不需要“挂载存储设备到宿主机”) (2)Mount:将远程存储格式化挂载到宿主机的指定目录,对应容器中的Volume。
我们可以创建这样的一个Pod来使用PVC
apiVersion: v1
kind: Pod
metadata:
labels:
role: web-frontend
spec:
containers:
- name: web
image: nginx
ports:
- name: web
containerPort: 80
volumeMounts:
- name: nfs
mountPath: "/usr/share/nginx/html"
volumes:
- name: nfs
persistentVolumeClaim:
claimName: nfs
本地存储 EmptyDir 容器->宿主机
文件在 pod所在的node节点
docker ps |grep nginx
查找该文件所在位置: find / -name hellohello
apiVersion: v1
kind: Pod #类型是Pod
metadata:
labels:
name: redis
role: master #定义为主redis
name: redis-master
spec:
containers:
- name: master
image: redis:latest
env: #定义环境变量
- name: MASTER
value: "true"
ports: #容器内端口
- containerPort: 6379
volumeMounts: #容器内挂载点
- mountPath: /data
name: redis-data #必须有名称
volumes:
- name: redis-data #跟上面的名称对应
emptyDir: {} #宿主机挂载点
kubectl create -f emptydir.yaml
此时Emptydir已经创建成功,在宿主机上的访问路径为/var/lib/kubelet/pods//volumes/kubernetes.io~empty-dir/redis-data,如果在此目录中创建删除文件,都将对容器中的/data目录有影响,如果删除Pod,文件将全部删除,即使是在宿主机上创建的文件也是如此,在宿主机上删除容器则k8s会再自动创建一个容器,此时文件仍然存在。
HostDir hostPath
在宿主机上指定一个目录,挂载到Pod的容器中,当pod删除时,本地仍然保留文件
ymal
apiVersion: apps/v1
kind: Deployment
metadata:
name: appname
namespace: common
spec:
progressDeadlineSeconds: 600
replicas: 2
selector:
matchLabels:
app: appname
template:
metadata:
labels:
app: appname
spec:
containers:
- env:
- name: MYENV_APPLICATIONNAME
value: appname
- name: MYENV_CONFIGPROFILE
value: zs
- name: MYENV_CONFIGGROUP
value: common
- name: MYENV_OTHERCONFIGDATAID1
value: zs.properties
- name: MYENV_OTHERCONFIGGROUP1
value: common
- name: cs.yamlFIGGROUP1
value: common
- name: MYENV_NACOSURL
value: 'ip:8848'
- name: MYENV_TOMCATPORT
value: '0000'
- name: JAVA_OPTS
value: >-
-Xms6g -Xmx6g -Xmn2g -XX:MetaspaceSize=256m
-XX:MaxDirectMemorySize=1g -XX:SurvivorRatio=8
-XX:+UseConcMarkSweepGC -XX:CMSMaxAbortablePrecleanTime=5000
-XX:+CMSClassUnloadingEnabled
-XX:CMSInitiatingOccupancyFraction=80
-XX:+UseCMSInitiatingOccupancyOnly
-XX:+ExplicitGCInvokesConcurrent
-Dsun.rmi.dgc.server.gcInterval=2592000000
-Dsun.rmi.dgc.client.gcInterval=2592000000
-XX:ParallelGCThreads=2
-Dsun.net.client.defaultConnectTimeout=10000
-Dsun.net.client.defaultReadTimeout=30000
-javaagent:/usr/local/soft/agent/skywalking-agent.jar
-DSW_AGENT_COLLECTOR_BACKEND_SERVICES=ip:1100 //skywalking的collector服务的IP及端口
-DSW_AGENT_NAME=springboot-skywalking-demo
-DSW_KAFKA_BOOTSTRAP_SERVERS=ip:9092
image: >-
registry-vpc.cn-beijing.aliyuncs.com/appname:release
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 3
initialDelaySeconds: 1000
periodSeconds: 10
successThreshold: 1
tcpSocket:
port: 0001
timeoutSeconds: 10
name: appname
ports:
- containerPort: 0001
hostPort: 0001
protocol: TCP
- containerPort: 9292
hostPort: 9292
protocol: TCP
readinessProbe:
failureThreshold: 3
initialDelaySeconds: 100
periodSeconds: 5
successThreshold: 1
tcpSocket:
port: 0001
timeoutSeconds: 10
resources:
limits:
cpu: 2500m
memory: 10Gi
requests:
cpu: 100m
memory: 4Gi
volumeMounts:
- mountPath: /home/nacfg.ini
name: nacfg #必须要有名字
- mountPath: /srv/access/resPinYin.dat
name: pinyin
- mountPath: /tmp/dump
name: dump
dnsPolicy: ClusterFirstWithHostNet
hostNetwork: true
imagePullSecrets:
- name: regcred
nodeSelector:
env: common
restartPolicy: Always
volumes:
- hostPath:
path: /home/nacfg.ini #宿主机挂载点
type: ''
name: nacfg
- hostPath:
path: /app/programfiles/resPinYin.dat #宿主机挂载点
type: ''
name: pinyin
- hostPath:
path: /tmp/dump #宿主机挂载点
type: ''
name: dump
网络数据卷(NFS) Network File System
映射的是代码目录,在/share 目录中创建index.html文件后,这个文件也将在容器中生效,当Pod删除时,文件不受影响,实现了数据持久化。
apiVersion: v1
kind: Pod
metadata:
name: nfs-web
spec:
containers:
- name: web
image: nginx
imagePullPolicy: Never #如果已经有镜像,就不需要再拉取镜像
ports:
- name: web
containerPort: 80
hostPort: 80 #将容器的80端口映射到宿主机的80端口
volumeMounts:
- name : nfs #指定名称必须与下面一致
mountPath: "/usr/share/nginx/html" #容器内的挂载点
volumes:
- name: nfs #指定名称必须与上面一致
nfs: #nfs存储
server: 192.168.1.0 #nfs服务器ip或是域名
path: "/share" #nfs服务器共享的目录
配置每个机器上的 机器码、机器文件 都不一样。保证不出现分布式并发线程不安全问题
配置映射 configMap
创建容器的时候,把宿主机机器码、机器文件对应的目录挂在到容器上
创建卷并挂载到容器
piVersion: v1
kind: Pod
metadata:
name: pod-demo
namespace: default
labels:
app: myapp
tier: frontend
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
volumeMounts: #在容器内定义挂载存储名称和挂载路径
- name: html
mountPath: /usr/share/nginx/html/
- name: busybox
image: busybox:latest
imagePullPolicy: IfNotPresent
volumeMounts:
- name: html
mountPath: /data/ #在容器内定义挂载存储名称和挂载路径
readOnly: true
command: ['/bin/sh','-c','while true;do echo $(date) >> /data/index.html;sleep 2;done']
volumes: #定义存储卷
- name: html #定义存储卷名称
emptyDir: {} #定义存储卷类型
将卷文件储存在内存
volumes:
- name: html
emptyDir: {}
medium: Memory
使用Git仓库作为储存卷
gitRepo 卷基本上也是一个emptyDir卷,它通过克隆Git仓库并在pod启动时(在创建之前)将git仓文件填充到卷
apiVersion: v1
kind: Pod
metadata:
name: gitrepo-volume-pod
spec:
containers:
- image: nginx:alpine
name: web-server
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
readOnly: true
ports:
- containerPort: 80
protocol: TCP
volumes:
- name: html
gitRepo:
repository: https://github.com/luksa/kubia-website-example.git
revision: master
directory: .
hostPath卷指向节点文件系统上的特定文件或目录。
在同一个节点上运行并在其 hostPath 卷中使用相同路径的 pod 可以看到相同的文件
hostPath卷是一种持久性存储,并不会像emptyDir卷和gitRepo卷一样在pod被删除时被删除, 如果删除一个pod,而下一个pod使用了指向主机上相同路径的hostPath卷,则新的pod将会发现上一个pod留下的数据(也就是说hostPath卷数据不会随着pod的删除而清除),前提是两个pod在同一个节点
volumes:
- hostPath:
path: /home/nacfg.ini
type: ''
name: nacfg