#Kubernetes
k8s的持久化存储 在k8s中部署的应用都是以pod容器的形式运行的,假如我们部署MySQL、Redis等数据库,需要对这些数据库产生的数据做备份。因为Pod是有生命周期的,如果pod不挂载数据卷,那pod被删除或重启后这些数据会随之消失,如果想要长久的保留这些数据就要用到pod数据持久化存储。
在k8s上使用存储卷,需要在对应的节点上提供对应存储系统的驱动,对应运行在该节点上的所有pod就可以使用对应的存储系统; 那么问题来了,pod怎么使用对应的存储系统呢?该怎么向其驱动程序传递参数呢?在k8s上一切皆对象,要在k8s上使用存储卷,还需要把对应的驱动抽象成k8s上的资源;在使用时,我们直接初始化对应的资源为对象即可;
为了在k8s上简化使用存储卷的复杂度,k8s内置了一些存储接口,对于不同类型的存储,其使用的接口、传递的参数也有所不同;除此之外在k8s上也支持用户使用自定义存储,通过csi接口来定义,可以去k8s官网 支持的存储类型,也可以使用 explain
命令查看;
k8s上支持的存储接口还是很多,每一个存储接口都是一种类型;对于这些存储类型我们大致可以分为云存储,分布式存储,网络存储、临时存储,节点本地存储,特殊类型存储、用户自定义存储等等;
云存储
awsElasticBlockStore
azureDisk
azureFile
gcePersistentDisk
vshperVolume
cinder
分布式存储
临时存储
本地存储
自定义存储
特殊存储
configMap
secret
downwardAPId
持久化存储示例 hostPath hostPath Volume
是指Pod挂载宿主机上的目录或文件。 hostPath Volume使得容器可以使用宿主机的文件系统进行存储,hostpath(宿主机路径):节点级别的存储卷,在pod被删除,这个存储卷还是存在的,不会被删除,所以只要同一个pod被调度到同一个节点上来 ,在pod被删除重新被调度到这个节点之后,对应的数据依然是存在的。 声明Yaml文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 $ cat hostpath.yaml apiVersion: v1 kind: Pod metadata: name: hostpath-demo spec: containers: - image: nginx:1.16-alpine name: nginx-hostpath imagePullPolicy: IfNotPresent volumeMounts: - mountPath: /test-tomcat name: test-volume - image: tomcat:8.5-jre8-alpine name: tomcat-hostpath imagePullPolicy: IfNotPresent volumeMounts: - mountPath: /test-tomcat name: test-volume volumes: - name: test-volume hostPath: path: /data1 type: DirectoryOrCreate
从上面的清单可以看出,使用 spec.volumes
字段来声明一个卷,spec.volumes.xxx
用来声明该卷的类型,比如例子中的类型则是hostPath,如果要用emptyDir类型那就写emptyDir;
类型.path
字段代表着这个pod被调度到的节点的目录被使用为卷的工作目录,那如果所声明的目录不存在呢?那就可以用 spec.volumes.卷类型.type
字段来标识; 这个字段有下面的几个值
取值
行为
空字符串(默认)用于向后兼容,这意味着在安装 hostPath 卷之前不会执行任何检查。
DirectoryOrCreate
如果在给定路径上什么都不存在,那么将根据需要创建空目录,权限设置为 0755,具有与 kubelet 相同的组和属主信息。
Directory
在给定路径上必须存在的目录。
FileOrCreate
如果在给定路径上什么都不存在,那么将在那里根据需要创建空文件,权限设置为 0644,具有与 kubelet 相同的组和所有权。
File
在给定路径上必须存在的文件。
Socket
在给定路径上必须存在的 UNIX 套接字。
CharDevice
在给定路径上必须存在的字符设备。
BlockDevice
在给定路径上必须存在的
例子中使用 DirectoryOrCreate
值,表示如果所声明的目录不存在那就创建它;
声明了存储卷,那么就在容器里面使用这个卷,使用 spec.containers.volumeMounts
字段表示挂载存储卷,spec.containers.volumeMounts.mountPath
字段代表挂载带容器内部的目录,spec.containers.volumeMounts.name
代表着我要挂载哪一个卷,这个卷要在yaml文件里面声明。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 $ kubectl apply -f hostpath.yaml $ kubectl get pods NAME READY STATUS RESTARTS AGE deployment-6cb5dc9785-4p6rm 1/1 Running 0 23h deployment-6cb5dc9785-8v25m 1/1 Running 0 23h deployment-6cb5dc9785-tdz77 1/1 Running 0 23h hostpath-demo 2/2 Running 0 16h $ kubectl exec -it hostpath-demo nginx-hostpath -- sh Defaulted container "nginx-hostpath" out of: nginx-hostpath, tomcat-hostpath / bin etc lib mnt proc run srv test-tomcat usr dev home media opt root sbin sys tmp var $ kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES deployment-6cb5dc9785-4p6rm 1/1 Running 0 23h 10.244.1.33 slave-node-1 <none> <none> deployment-6cb5dc9785-8v25m 1/1 Running 0 23h 10.244.1.31 slave-node-1 <none> <none> deployment-6cb5dc9785-tdz77 1/1 Running 0 23h 10.244.1.32 slave-node-1 <none> <none> hostpath-demo 2/2 Running 0 16h 10.244.2.32 slave-node-2 <none> <none> slave-node-2@slave-node-2:/$ ls bin boot cdrom data1 dev etc home lib lib32 lib64 libx32 lost+found media mnt opt proc root run sbin snap srv swap.img sys tmp usr var slave-node-2@slave-node-2:/$ ls data1/ test1 slave-node-2@slave-node-2:/$ $ kubectl exec -it hostpath-demo nginx-hostpath -- cat test-tomcat/test1 Defaulted container "nginx-hostpath" out of: nginx-hostpath, tomcat-hostpath 测试目录文件是否同步修改
从上面的例子可以看出,使用 hostpath
类型的存储卷是同一个节点共享使用的; 那我把pod删除之后存储卷的数据还在吗?
1 2 3 4 5 6 7 8 9 10 11 12 13 $ kubectl delete pod hostpath-demo slave-node-2@slave-node-2:/data1$ ls test1 $ kubectl apply -f hostpath.yaml $ kubectl exec -it hostpath-demo nginx-hostpath -- sh $ ls /test-tomcat test1
经过上面的测试,可以得出结论,多个容器共享一个工作节点的挂载目录,即使这个pod删除了,该工作节点的目录也不会删除;重新创建pod之后要是想继续访问该目录,得保证这个pod被调度到同一个节点之上,这里可以使用 spec.nodeName
字段来配置该pod被调度到指定节点;
kubernetes官网 hostpath 相关资料
emptyDir 当 Pod 分派到某个 Node 上时,emptyDir
卷会被创建,并且在 Pod 在该节点上运行期间,卷一直存在。 就像其名称表示的那样,卷最初是空的。 尽管 Pod 中的容器挂载 emptyDir
卷的路径可能相同也可能不同,这些容器都可以读写 emptyDir
卷中相同的文件。 当 Pod 因为某些原因被从节点上删除时,emptyDir
卷中的数据也会被永久删除。
emptyDir
的一些用途:
缓存空间,例如基于磁盘的归并排序。
为耗时较长的计算任务提供检查点,以便任务能方便地从崩溃前状态恢复执行。
在 Web 服务器容器服务数据时,保存内容管理器容器获取的文件。
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 $ cat emptyDir.yaml apiVersion: v1 kind: Pod metadata: name: emptydir-demo spec: containers: - image: nginx:1.16-alpine name: nginx-empty imagePullPolicy: IfNotPresent volumeMounts: - mountPath: /emptydir-mount name: emptydir-volume - image: tomcat:8.5-jre8-alpine name: tomcat-empty imagePullPolicy: IfNotPresent volumeMounts: - mountPath: /emptydir-mount name: emptydir-volume volumes: - name: emptydir-volume emptyDir: {}
发布该文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 $ kubectl apply -f emptyDir.yaml $ kubectl get pods NAME READY STATUS RESTARTS AGE deployment-6cb5dc9785-4p6rm 1/1 Running 0 24h deployment-6cb5dc9785-8v25m 1/1 Running 0 24h deployment-6cb5dc9785-tdz77 1/1 Running 0 24h emptydir-demo 2/2 Running 0 13m hostpath-demo2 2/2 Running 0 60m $ kubectl exec -it emptydir-demo nginx-empty -- sh Defaulted container "nginx-empty" out of: nginx-empty, tomcat-empty / drwxrwxrwx 2 root root 4096 Dec 8 02:46 emptydir-mount / /emptydir-mount /emptydir-mount /emptydir-mount nginx-empty $ kubectl exec -it emptydir-demo tomcat-empty -- ls /emptydir-mount Defaulted container "nginx-empty" out of: nginx-empty, tomcat-empty text $ kubectl exec -it emptydir-demo tomcat-empty -- cat /emptydir-mount/text Defaulted container "nginx-empty" out of: nginx-empty, tomcat-empty nginx-empty
从上面的测试可以看出,emptyDir也是同一个pod内共享目录,但是删除了pod这个目录也会被删除,即使同一个pod调度到同一个节点上,数据也会丢失。
kubernetes官网 emptyDir 相关资料
nfs存储 nfs
卷能将 NFS (网络文件系统) 挂载到你的 Pod 中。 不像 emptyDir
那样会在删除 Pod 的同时也会被删除,nfs
卷的内容在删除 Pod 时会被保存,卷只是被卸载。 这意味着 nfs
卷可以被预先填充数据,并且这些数据可以在 Pod 之间共享。
在pod挂载存储前,我们得在自己机器上搭建nfs服务,下面演示ubuntu服务器搭建nfs服务器的步骤:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 $ sudo apt-get install nfs-kernel-server $ sudo mkdir -p /nfs/data $ sudo chmod -R 777 /nfs $ sudo vim /etc/exports *代表所有网段都可以访问,rw表示读写权限,no_root_squash表示用户具有根目录的完全管理访问权限 /nfs/data *(rw,no_root_squash) $ sudo /etc/init.d/nfs-kernel-server restart $ sudo apt-get install nfs-common $ sudo mkdir /nfs-client $ sudo chmod 777 /nfs-client $ sudo mount -t nfs 10.1.3.205:/nfs/data /nfs/client -o nolock $ echo text >> tt
编写配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 $ cat nfs.yaml apiVersion: v1 kind: Pod metadata: name: nfs-demo spec: containers: - name: nfs-demo image: nginx imagePullPolicy: IfNotPresent ports: - containerPort: 80 protocol: TCP volumeMounts: - name: nfs-volumes mountPath: /usr/share/nginx/html volumes: - name: nfs-volumes nfs: path: /nfs/data server: 10.1 .3 .205
发布该配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 $ kubectl apply -f nfs.yaml $ kubectl get pods NAME READY STATUS RESTARTS AGE deployment-6cb5dc9785-4p6rm 1/1 Running 0 28h deployment-6cb5dc9785-8v25m 1/1 Running 0 28h deployment-6cb5dc9785-tdz77 1/1 Running 0 28h nfs-demo 1/1 Running 0 3s $ echo hello,this is nfs storage nginx index >> /nfs/data/index.html $ kubectl exec -it nfs-demo -- cat /usr/share/nginx/html/index.html hello,this is nfs storage nginx index $ kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nfs-demo 1/1 Running 0 97s 10.244.2.37 slave-node-2 <none> <none> $ curl 10.244.2.37 hello,this is nfs storage nginx index
nfs支持多个客户端挂载,可以创建多个pod,挂载同一个nfs服务器共享出来的目录;nfs此时是单点,一旦nfs服务器宕机挂掉,对应pod运行时产生的数据将全部丢失;所以对应外部存储系统,我们应该选择一个对数据有冗余,且k8s集群支持的类型的存储系统,比如cephfs,glusterfs等等;