需要脱离容器和主机的生命周期。
存储卷不属于容器,而属于pod,k8s基本调度单元是pod .pod 本身就是抽象出来的。
一个pod当中可能运行一到多个容器。有一个主容器叫做maincontainer,其他容器叫做sidercar
一个pod 中多个容器是可以使用同一组存储卷的。因为存储卷属于pod 的
pod 中有一个底层的基础架构容器叫做pause.运行的每个容器都有一个pause 容器存在。容器借助于内核中的6个名称空间提供服务,其中pause提供一个基础的容器,其他加入进pod中的其他容器都共享网络名称空间,ipc ,uts
存储卷也属于pause,每个加入pod中的容器可以复制pause 中存储卷。谁复制就能使用添加进来的存储卷,k8s pod 中容器要使用存储卷,自己明确挂载存储卷挂载到本地某个目录下。
共享式存储设备:
多路并行读写
多路只读
单路读写
Pod使用Volume步骤:
1、在Pod上定义存储卷,并关联至目标存储服务上;
2、在需要用到存储卷的容器上,挂载其所属的Pod的存储卷;
存储卷类型:
Host级别:hostPath, Local
网络级别:NFS、GlusterFS、rbd(块设备)、CephFS(文件系统)、...
临时存储:emptyDir
kubelet 通过volumn plugin要支持这种存储卷类型。
csi:容器存储接口
k8s 特性:作为用户来讲。运行一个程序,以pod 规范定义出pod和容器。至于如何运行,如何分配在底层,资源等。用户不用管。要想使用存储卷在pod上,节点级别要能驱动存储服务外。所有的关于服务的配置都是在pod上通过卷插件来指定的, 在pod上使用volume 来定义的。volumn 卷插件时kubelet 级别的,需要通过pod向kubelet 传递配置参数
查看支持的类型配置详解:kubectl explain pods.spec.volumes
kubectl explain pods.spec.volumes.rbd
如何使用存储卷?
案例 :创建hostpath类型存储卷。
[root@master ~]# mkdir volumes
[root@master ~]# vi vol-demo.yaml
[root@master ~]# kubectl create ns vol
namespace/vol created
[root@master ~]# kubectl apply -f vol-demo.yaml
pod/myapp created
[root@master ~]# cat vol-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: myapp
namespace: vol
labels:
app: myapp
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
volumeMounts:
- name: webstor
mountPath: /usr/share/nginx/html
readOnly: true
volumes:
- name: webstor
hostPath:
path: /volumes/myapp
type: DirectoryOrCreate
#查看详细信息
[root@master ~]#
[root@master ~]# kubectl get pods -n vol
NAME READY STATUS RESTARTS AGE
myapp 1/1 Running 0 96s
[root@master ~]# kubectl describe pods myapp -n vol
Name: myapp
Namespace: vol
#去宿主机上查看挂载
ster ~]#
[root@master ~]#
[root@master ~]# kubectl get pods -n vol -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myapp 1/1 Running 0 5m58s 10.244.2.41 node2.k8s.com <none> <none>
登录node2 的相应节点,多了一个volumes 的目录ls /
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var volumes
#创建一个index.html
[root@node2 ~]# cd /volumes/myapp/
[root@node2 myapp]# vi index.html
[root@node2 myapp]# cat index.html
<h1>Page @pod_volumes_hostpath</h1>#curl 检测
oot@master ~]# curl 10.244.2.41
<h1>Page @pod_volumes_hostpath</h1>
[root@master ~]#
#以上问题是,如果被删除了,并且pod 不在这个node 了,那么就不能共享挂载这个目录了。
还有就是硬绑定到这个节点上
nodename这个字段可以硬绑定到这个节点上。
案例:local 类型。可以直接使用设备文件。可以
案例:emptydir
场景一 : 可以为容器提供缓存存储空间。
场景二:两个容器共享数据。
案例:nfs 存储卷
yum install -y nfs-utils
mkdir /vols/v{1,2,3,4,5} -p[root@localhost ~]# systemctl start nfs
[root@localhost ~]# cat /etc/exports
/vols/v1 10.0.0.0/24(rw,no_root_squash)[root@localhost ~]# exportfs -rav
exporting 10.0.0.0/24:/vols/v1
其他node 节点也需要安装util包。其他节点进行挂载测试
[root@master mnt]# mount -t nfs 10.0.0.120:/vols/v1 /mnt
[root@master /]# kubectl explain pods.spec.volumes.nfs
[root@master chapter7]# cat vol-nfs.yaml
apiVersion: v1
kind: Pod
metadata:
name: vol-nfs-pod
namespace: vol
labels:
app: redis
spec:
containers:
- name: redis
image: redis:alpine
ports:
- containerPort: 6379
name: redis
volumeMounts:
- mountPath: /data
name: data
volumes:
- name: data
nfs:
server: 10.0.0.120
path: /vols/v1
[root@master chapter7]# kubectl apply -f vol-nfs.yaml
pod/vol-nfs-pod created
# kubectl describe pods vol-nfs-pod -n vol #查看报错
之后将pod 删除,重建后看这个文件是否存在
=====================================================================
pv 和存储系统间没有一一对应关系,对应存储系统逻辑上可被访问的逻辑单元,nfs 是一个可被导出的路径,rbd 是个img.
pv 属于集群级别的资源。要想在名称空间使用pv.还需要把pv 注册到名称空间中去。pv是一对一的。A名称空间占用,B 就不能在用了。现在有一个名称空间叫做myns---->pvc(请求占住一个pv)
pv和pvc 之间没有直接关系,但pvc 是请求占住pv的。被占用的叫做binding, 没占用的叫做available.一旦占住,这个名称空间中就有pod 能使用pvc 了。 pod--->pvc--->pv
kubectl explain pods.spec.volumes.persistentVolumeClaim
在pod 上定义了存储卷,容器是不能使用他的。在容器上还要挂载它。container 应该使用volumemounts 进行挂载。
如何使用pvc:
pod上使用pvc 插件,pod 同一个名称空间当中,使用一种资源,pvc 资源来创建申请。
存储类:pv 创建模板。按用户的需求生成pv. nfs 要不定义在pv 上了。而定义在模板上
spec:
volumes:
- name <string> # 存储卷名称标识,仅可使用DNS标签格式的字符,在当前Pod中必须唯一
VOL_TYPE <Object> # 存储卷插件及具体的目标存储供给方的相关配置
containers:
- name: …
image: …
volumeMounts:
- name <string> # 要挂载的存储卷的名称,必须匹配存储卷列表中某项的定义
mountPath <string> # 容器文件系统上的挂载点路径
readOnly <boolean> # 是否挂载为只读模式,默认为“否”
subPath <string> # 挂载存储卷上的一个子目录至指定的挂载点
subPathExpr <string> # 挂载由指定的模式匹配到的存储卷的文件或目录至挂载点
============================================
回顾:在pod 上要想使用存储卷,1,在pod级别定义存储卷,定义的过程(卷名称,卷插件类型,卷插件对接下的存储设备)。2.在特定的容器使用 volumeMounts挂载之前定义的卷,
示例1:emptydir 存储卷
tmptydir :临时存储卷,借助于外部的高速存储,或内存空间来临时存储某些数据,为应用提供缓存,为同一个pod内不同的容器提供共享的数据
medium: 存储介质,默认磁盘
[root@node01 chapter5]# cat volumes-emptydir-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: volumes-emptydir-demo
namespace: default
spec:
initContainers:
- name: config-file-downloader
image: ikubernetes/admin-box
imagePullPolicy: IfNotPresent
command: ['/bin/sh','-c','wget -O /data/envoy.yaml http://ilinux.io/envoy.yaml']
volumeMounts:
- name: config-file-store
mountPath: /data
containers:
- name: envoy
image: envoyproxy/envoy-alpine:v1.13.1
command: ['/bin/sh','-c']
args: ['envoy -c /etc/envoy/envoy.yaml']
volumeMounts:
- name: config-file-store
mountPath: /etc/envoy
readOnly: true
volumes:
- name: config-file-store
emptyDir:
medium: Memory
sizeLimit: 16Mi
kubectl describe pods volumes-emptydir-demo 如果容器不能启动,或者有其他问题,可以用此命令查看详细的状态
示例2:hostPath(docker 默认)
kubectl explain pods.spec.volumes.hostPath
path:宿主机的路径
hostPath的文件类型
File:事先必须存在的文件路径;
Directory:事先必须存在的目录路径;
DirectoryOrCreate:指定的路径不存时自动将其创建为0755权限的空目录,属主属组均为kubelet;
FileOrCreate:指定的路径不存时自动将其创建为0644权限的空文件,属主和属组同为kubelet;
Socket:事先必须存在的Socket文件路径;
CharDevice:事先必须存在的字符设备文件路径;
BlockDevice:事先必须存在的块设备文件路径;
"":空字符串,默认配置,在关联hostPath存储卷之前不进行任何检查。
[root@node01 chapter5]# cat volumes-hostpath-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: volumes-hostpath-demo
spec:
containers:
- name: filebeat
image: ikubernetes/filebeat:5.6.7-alpine
env:
- name: REDIS_HOST
value: redis.ilinux.io:6379
- name: LOG_LEVEL
value: info
volumeMounts:
- name: varlog
mountPath: /var/log
- name: socket
mountPath: /var/run/docker.sock
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
- name: socket
hostPath:
path: /var/run/docker.sock
kubectl exec -it volumes-hostpath-demo -- /bin/sh #进入容器内部查看相关挂载,有的目录和宿主机的目录内内容一致。
示例3:nfs存储卷
节点配置好nfs的驱动,但是不需要挂载,也就时安装nfs-utils
===========================
pvc
消费和供给职能分离开来,管理员去管理存储设备的细节,用户表明需求就能创建出来。
pv有两种供给方式,1.管理员手动事先创建好,按照开发需求 2.借助stroge class所创建的模版来实现,动态供给。(需要存储本身支持,nfs不支持。ceph 支持)
#pv 也属于集群级别,不属于任何名称空间。
Dev 需要claims pv,需要通过claim去跟某一个由管理员手动创建的pv建立绑定关系(有那么多pv ,那到底分配那个呢。是有申请需求来决定的)
需求:希望的存储空间多大,是否支持单路读写,多路读写。期望到那个存储类上去申请(存储类既是动态创建存储卷pv的模版也是pv和pvc的名称空间 ,一旦创建一个存储类叫storage class,任何pvc申请的目标只能在同一存储类中去找pv,pvc去绑定pv 时只能在同一存储类中实现。而pv与pvc 的关联关系,我们称为bound。一个pv 和一个pvc一一对应。pvc 的作用是可以被pod当中 使用类似hostpath 一样的存储卷来使用)
kubectl explain pods.spec.volumes.persistentVolumeClaim
使用过程
管理员设置一个pv的供给方,在存储集群上创建一到多个pv,每一个pv 一定要关联到真正的存储空间上。
当用户使用pv的存储空间的时候 1.先创建一个pvc资源,pvc资源与pv建立关联关系2.创建pod,使用pvc类型的存储卷与pvc建立关联关系,
=====================
马哥笔记
PVC和PV
PVC: Persistent Volume Claim,持久卷申请,简称PVC;k8s上标准的资源类型之一; 由用户使用;名称空间级别;
PV: Persistent Volume,持久卷,可被PVC绑定;而PV一定要与某个真正的存储空间(一般是网络存储服务上的存储空间)对应起来,才能真正存储数据。由集群管理员负责管理。集群级别。
PVC创建完成后,需要找到最为匹配的PV,并与之绑定。
在哪儿找:
二者要么都不属于任何StorageClass资源,要么属于同一个StorageClass资源;
怎么找:
===============================================================
除了存储卷插件之外,PersistentVolume资源规范Spec字段主要支持嵌套以下几个通用字段,它们用于定义PV的容量、访问模式和回收策略等属性。
capacity <map[string]string>:指定PV的容量;目前,Capacity仅支持存储容量设定,将来还应该可以指定IOPS和吞吐量(throughput)。
accessModes <[]string>:指定当前PV支持访问模式;存储系统支持存取能力大体可分为ReadWriteOnce(单路读写)、ReadOnlyMany(多路只读)和ReadWriteMany(多路读写)三种类型,某个特定的存储系统可能会支持其中的部分或全部的能力。
persistentVolumeReclaimPolicy <string>:PV空间被释放时的处理机制;可用类型仅为Retain(默认)、Recycle或Delete。目前,仅nfs和hostPath支持Recycle策略,也仅有部分存储系统支持Delete策略。
#pv 回收策略,删掉pvc后,pv 保留--Retain,其余略
存储类动态创建默认是delete
Recycle :只把pv 上的数据删掉。--新版被废弃
delete :不但删除数据,连pv 都删掉
所以pv 的状态就有binding available,reclaim(回收状态)
总结pv 的生命周期
provisioning
binding
Using
Reclaiming 回收又有上述三种回收策略
问题:为什么pvc 创建就能触发动态供给pv ?
依赖于存储类,将外部系统在k8s 之上定义另外一个类型资源叫做存储类,sc. sc 定义了如何链接外置存储系统的管理接口,api 的。它配置了各种各样的参数,并且知道怎么链接到管理接口上,如果需要认证还能认证过去,必要时调管理命令,创建逻辑单元。并在sc 内部创建pv.所以pv 是属于sc 的。pvc去请求pv 时如果通过sc 来实现。那么pvc 也属于sc.pvc 只能向同一个sc 请求绑定。pvc 只能向同一个sc 请求绑定,pvc 属于存储类1,那么只能向存储类1提供申请。
以上是动态供给内容,
没有动态存储类那么就要自己pv.
1.k8s 管理员负责把外部系统之上的存储空间映射到集群上定义pv
2.用户使用时定义pvc绑定pv,定义pod 使用pvc
3;.pod 容器挂载存储卷
集群访问模式的几种类型。
volumeMode <string>:该PV的卷模型,用于指定此存储卷被格式化为文件系统使用还是直接使用裸格式的块设备;默认值为Filesystem,仅块设备接口的存储系统支持该功能。
storageClassName <string>:当前PV所属的StorageClass资源的名称,指定的存储类需要事先存在;默认为空值,即不属于任何存储类。
mountOptions <string>:挂载选项组成的列表,例如ro、soft和hard等。
nodeAffinity <Object>:节点亲和性,用于限制能够访问该PV的节点,进而会影响到使用与该PV关联的PVC的Pod的调度结果。
创建pvc
NFS服务器操作:
mkdir /data/redis00{1..5} -p
yum install nfs-utils
mkdir /data/redis
[root@yuanlichao ~]# cat /etc/exports
/data/redis 172.16.1.0/24(rw)
/data/redis001 172.16.1.0/24(rw)exportfs -ar #不用重启直接导出
showmount -e
useradd redis -u 1005
chown redis /data/*
客户端查看:showmount -e 172.16.1.203
pv 演示:pv 不属于名称空间,不能定义namespace,只不过将volume资源让管理员来做了。加了一个中间层。
[root@node01 chapter5]# cat pv-nfs-002.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs-002
spec:
capacity:
storage: 10Gi
volumeMode: Filesystem
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
mountOptions:
- hard
- nfsvers=4.1
nfs:
path: "/data/redis002"
server: 172.16.1.203
创建pvc : pvc 是属于名称空间的。
[root@node01 chapter5]# cat pvc-demo-0001.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-demo-0001
namespace: default
spec:
accessModes: ["ReadWriteMany"]
volumeMode: Filesystem
resources:
requests:
storage: 3Gi
limits:
storage: 10Gi
创建pod 使用pvc。pod 使用,容器挂载
[root@node01 chapter5]# cat volumes-pvc-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: volumes-pvc-demo
namespace: default
spec:
containers:
- name: redis
image: redis:alpine
imagePullPolicy: IfNotPresent
securityContext:
runAsUser: 1005
ports:
- containerPort: 6379
name: redisport
volumeMounts:
- mountPath: /data
name: redis-rbd-vol
volumes:
- name: redis-rbd-vol
persistentVolumeClaim:
claimName: pvc-demo-0001
即使pod删掉重建数据也没有问题
pv 删除保护机制。
用户需要删除一个pv的时候,pv 正在被pvc所使用着呢。是不会被删掉的,1.11版本引入的
1.删除一个pvc 测试
2.一直处于等待状态,并没有被真正删除
3.删除pod,pvc 返回删除成功
4.pv 状态
released 表示曾经有用过这个数据,只不过被释放了。不允许再被绑定
5.手动删除pv
6.手动删除数据
在处处上手动删除数据,需要很谨慎。
动态类
目前只能了解
特殊类型的存储卷,configmap ,secret
环境变量配置缺陷:启动时被装入,被替换的。一旦替换,无法更改。
存储卷方式去做:也是启动时被装入。即使更改,进程也需要reload.
如果一个控制器下要求有5个pod ,那么配置信息应该是一样的。共享一个存储卷,才是正确的。
================================================
OCI:open container initiative 属于LSF
RUNTIME-SPEC:运行时规范.容器运行时
image-spec: 镜像规范
docker将容器镜像格式,runtime(runc),捐给了 OCI。之后将更高级运行时,containerd又捐给了CNCF
Pod使用这类存储的步骤:
Admin:创建好PV;
User: 按需创建PVC,而后创建Pod,在Pod调用persistentVolumeClaim类型的存储卷插件调用同一个名称空间中的PVC资源;
存储卷:隶属于Pod,而非容器; pause容器支撑
kubelet为了支持存储卷,内建了很多存储服务的客户端;
临时存储:emptyDir
主机级别的存储:hostPath
网络级别的存储:具有持久能力的存储;
云存储:awsEBS、...
NAS(network attached storage):NFS、...
SAN(Storage Area Network):FC,iSCSI, ...
SDS(Software Defined Storage): Ceph(rbd, cephfs)、...
pvc:
Pod(pvc plugin) -> PVC(同一名称空间) -> PV
In-Tree 存储插件
PV、PVC:将存储消费,存储创建的职能分离开来,
PV:由管理员定义的,将真正的存储设备上的一段存储空间抽象成的k8s的对象; 集群级别;
NFS exported 目录;
PVC:由用户定义出存储消费需求,而后根据需求条件与现有各PV进行匹配检测,找出一个最佳的使用。名称空间级别,Pod只能调用与自己在同一名称空间中的PVC;
StorageClass:模板, 简称SC;PV和PVC都可属于某个特定的SC;
模拟为名称空间:一个PVC只能够在自己所处的SC内找PV;或者,一个不属于任何SC的PVC只能够在不属于任何SC的PV中进行筛选;
创建PV的模板:可以将某个存储服务与SC关联起来,并且将该存储服务的管理接口提供给SC,从而让SC能够在存储服务上CRUD(Create、Read、Update和Delete)存储单元;
因而,在同一个SC上声明PVC时,若无现存可匹配的PV,则SC能够调用管理接口直接创建出一个符合PVC声明的需求的PV来。这种PV的提供机制,就称为Dynamic Provision。
ceph中的rbd支持动态预配,但kubeadm部署的k8s集群,却不支持该功能,原因在于kube-controller-manager镜像内部未内置ceph客户端工具。
Controller Manager,静态Pod,PV Controller和PVC Controller都在controller manager 的pod中,容器的文件系统与其他容器的文件系统是隔离的。与宿主机的文件系统也是隔离的。任何一个对应的组件想访问存储服务。要有存储服务的客户端,这个镜像没有客户端,所以不能对ceph进行增删改查。
Out-of-Tree:存储卷,由管理员通过flexVolume或CSI接入的第三方存储卷类型;
=======================================================
kubeadm token create --print-join-command
单独生成token加入节点命令