kubernetes存储卷:
咱们知道默认状况下容器的数据都是非持久化的,在容器销毁之后数据也跟着丢失,因此docker提供了volume机制以便将数据持久化存储。相似的,k8s提供了更强大的volume机制和丰富的插件,解决了容器数据持久化和容器间共享数据的问题。html
volume:
咱们常常会说:容器和 Pod 是短暂的。
其含义是它们的生命周期可能很短,会被频繁地销毁和建立。容器销毁时,保存在容器内部文件系统中的数据都会被清除。为了持久化保存容器的数据,可使用k8s volume。
Volume 的生命周期独立于容器,Pod 中的容器可能被销毁和重建,但 Volume 会被保留。node
k8s支持的volume类型有emptydir,hostpath,persistentVolumeClaim,gcePersistentDisk,awsElasticBlockStore,nfs,iscsi,gitRepo,secret等等,完整列表及详细文档可参考 http://docs.kubernetes.org.cn/429.html。mysql
在本文中主要实践如下几种volume类型:git
1,EmptyDir(临时存储):
emptyDir 是最基础的 Volume 类型。正如其名字所示,一个 emptyDir Volume 是 Host 上的一个空目录。也就是宿主机上没有指定的目录或文件,直接由pod内部映射到宿主机上。(相似于docker中的docker manager volume 挂载方式)sql
咱们经过下面的例子来实践emptydir:docker
[root@master yaml]# vim emptydir.yaml apiVersion: v1 kind: Pod metadata: name: read-write spec: containers: - name: write image: busybox volumeMounts: #定义数据持久化 - mountPath: /write #定义挂载目录,该目录是pod内部的目录 name: share-volume args: - /bin/sh - -c - echo "hello volumes" > /write/hello; sleep 3000; - name: read #在该pod内定义第二个容器 image: busybox volumeMounts: - mountPath: /read name: share-volume args: - /bin/sh - -c - cat /read/hello; sleep 30000; volumes: - name: share-volume emptyDir: {} #定义一个数据持久化的类型empytdir
咱们模拟一个pod里运行了两个容器,两个容器共享一个volume,一个负责写入数据,一个负责读取数据。数据库
//运行该pod, 并进行查看: [root@master yaml]# kubectl apply -f emptydir.yaml pod/read-write created
[root@master yaml]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES read-write 2/2 Running 0 14s 10.244.2.2 node02 <none> <none>
//咱们分别查看两个容器中的挂载内容: [root@master yaml]# kubectl exec -it read-write -c read cat /read/hello hello volumes [root@master yaml]# kubectl exec -it read-write -c write cat /write/hello hello volumes
参数解释:
-c :为指定某个容器,是--container= 的缩写,能够经过--help进行查看。vim
由于 emptyDir 是 Docker Host 文件系统里的目录,其效果至关于执行了 docker run -v /write 和 docker run -v /read。咱们在node02
上经过 docker inspect 分别查看容器的详细配置信息,咱们发现两个容器都 mount 了同一个目录:api
"Mounts": [ { "Type": "bind", "Source": "/var/lib/kubelet/pods/756b4f4a-917a-414d-a7ee-523eecf05465/volumes/kubernetes.io~empty-dir/share-volume", "Destination": "/read", "Mode": "", "RW": true, "Propagation": "rprivate" }, { "Type": "bind", "Source": "/var/lib/kubelet/pods/756b4f4a-917a-414d-a7ee-523eecf05465/volumes/kubernetes.io~empty-dir/share-volume", "Destination": "/write", "Mode": "", "RW": true, "Propagation": "rprivate" },
这里的"/var/lib/kubelet/pods/756b4f4a-917a-414d-a7ee-523eecf05465/volumes/kubernetes.io~empty-dir/share-volume"就是emptydir 挂载到dockerhost上的真正路径。
因此咱们能够进入到该路径下进行查看:服务器
[root@node02 ~]# cd /var/lib/kubelet/pods/756b4f4a-917a-414d-a7ee-523eecf05465/volumes/kubernetes.io~empty-dir/share-volume/ [root@node02 share-volume]# cat hello hello volumes
总结emptydir:
同个pod里边的不一样容器,共享同一个持久化目录。当pod节点删除时,volume的内容也会被删除,但若是仅是容器被销毁,pod还在,则volume不受影响。也就是说emptydir的数据持久化的生命周期和使用的pod一致。通常做为临时存储使用,以及长时间任务的中间过程checkpoint的临时保存目录,及多容器共享目录。
好比 kube-apiserver 和 kube-controller-manager 就是这样的应用。
咱们经过"kubectl edit -n kube-system pod kube-apiserver-master"命令来查看 kube-apiserver Pod 的配置,下面是 Volume 的相关部分:
volumeMounts: - mountPath: /etc/ssl/certs name: ca-certs readOnly: true - mountPath: /etc/pki name: etc-pki readOnly: true - mountPath: /etc/kubernetes/pki name: k8s-certs readOnly: true
volumes: - hostPath: path: /etc/ssl/certs type: DirectoryOrCreate name: ca-certs - hostPath: path: /etc/pki type: DirectoryOrCreate name: etc-pki - hostPath: path: /etc/kubernetes/pki type: DirectoryOrCreate name: k8s-certs
这里定义了三个 hostPath volume 分别是k8s-certs、ca-certs 和etc- pki,分别对应 Host 目录 /etc/kubernetes/pki、/etc/ssl/certs 和 /etc/pki。
若是 Pod 被销毁了,hostPath 对应的目录也还会被保留,从这点看,hostPath 的持久性比 emptyDir 强。不过一旦 Host 崩溃,hostPath 也就无法访问了。
NFS PersistentVolume
经过 NFS 实践PV和PVC。
1)咱们在master节点上部署nfs服务:
[root@master ~]# yum -y install nfs-utils [root@master ~]# mkdir /nfsdata [root@master ~]# vim /etc/exports #编写nfs配置文件 /nfsdata 172.16.1.0/24(rw,sync,no_root_squash) [root@master ~]# systemctl enable rpcbind [root@master ~]# systemctl start rpcbind [root@master ~]# systemctl enable nfs-server [root@master ~]# systemctl start nfs-server
[root@master ~]# showmount -e #查看是否挂载成功 Export list for master: /nfsdata 172.16.1.0/24
2)建立pv:
[root@master yaml]# vim nfs-pv.yaml apiVersion: v1 kind: PersistentVolume metadata: name: nfs-pv spec: capacity: storage: 1Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Recycle storageClassName: nfs nfs: path: /nfsdata #指定nfs共享目录 server: 172.16.1.30 #指定的是nfs服务器的ip地址
//经过如下命令来运行pv: [root@master yaml]# kubectl apply -f nfs-pv.yaml persistentvolume/nfs-pv created
字段解释: capacity:指定pv的容量大小,目前,capacity仅支持空间设定,未来应该还能够指定IOPS和throughput。 accessModes:访问模式,有如下几种模式: ReadWriteOnce: 以读写的方式挂载到单个节点,命令行中简写为RWO。 ReadOnlyMany:以只读的方式挂载到多个节点,命令行中简写为ROX。 ReadWriteMany: 以读写的方式挂载到多个节点,命令行中简写为RWX。 persistentVolumeReclaimPolicy:pv空间释放时的回收策略,有如下几种策略: Recycle:清除pv中的数据,而后自动回收。(自动回收策略是由pvc的保护机制保护的,当pv删除后,只要pvc还在数据就还在) Retain: 保持不动,由管理员手动回收。 Delete: 删除云存储资源,仅部分云储存系统支持,若是AWS,EBS,GCE PD,Azure Disk和Cinder。 注意:这里的回收策略是指在pv被删除以后,所存储的源文件是否删除。 storageClassName:pv和pvc关联的依据。
//验证pv是否可用: [root@master yaml]# kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE nfs-pv 1Gi (容量为1GB) RWO (读写) Recycle (自动回收) Available(可用的,确保是该状态才可被使用) nfs(基于nfs来作的) 18m(时间)
3)建立一个pvc:
[root@master yaml]# vim nfs-pvc.yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: nfs-pvc spec: accessModes: - ReadWriteOnce #pv和pvc的访问模式必须一致 resources: #在该字段下的requests子字段中定义要申请的资源 requests: storage: 1Gi storageClassName: nfs
运行该pvc: [root@master yaml]# kubectl apply -f nfs-pvc.yaml persistentvolumeclaim/nfs-pvc created
//验证pvc是否可用: [root@master yaml]# kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE nfs-pvc Bound nfs-pv 1Gi RWO nfs 3m53s [root@master yaml]# kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE nfs-pv 1Gi RWO Recycle Bound default/nfs-pvc nfs 29m
确保此时pv和pvc的状态都为Bound,则表示绑定成功。
接下来咱们实践mysql的pv使用:
1)建立一个mysql的pod:
[root@master yaml]# vim mysql-pod.yaml apiVersion: extensions/v1beta1 kind: Deployment metadata: name: mysql spec: template: metadata: labels: app: mysql spec: containers: - name: mysql image: mysql:5.7 env: #定义一个变量,将容器中mysqlroot密码映射到本地 - name: MYSQL_ROOT_PASSWORD value: 123.com #密码为123.com ports: - containerPort: 3306 volumeMounts: #定义数据持久化 - name: mysql-pv-storage mountPath: /var/lib/mysql #该目录为默认的mysql数据持久化目录 volumes: #该volumes字段为上面的一个解释 - name: mysql-pv-storage #注意名称要与上面的名称相同 persistentVolumeClaim: #指定pvc,注意下面声明的pvc要于以前建立的pvc名称一致 claimName: nfs-pvc --- apiVersion: v1 #建立一个service资源对象 kind: Service metadata: name: mysql spec: type: NodePort ports: - port: 3306 targetPort: 3306 nodePort: 30000 selector: app: mysql
经过如下命令来运行pod: [root@master yaml]# kubectl apply -f mysql-pod.yaml deployment.extensions/mysql created service/mysql created
//查看pod是否正常运行: [root@master yaml]# kubectl get pod -o wide mysql-68d65b9dd9-hf2bf NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES mysql-68d65b9dd9-hf2bf 1/1 Running 0 9m34s 10.244.1.3 node01 <none> <none>
2)登陆mysql数据库,进行写入数据:
[root@master yaml]# kubectl exec -it mysql-68d65b9dd9-hf2bf -- mysql -u root -p123.com Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql>
mysql> create database volumes_db; #建立库 Query OK, 1 row affected (0.01 sec) mysql> use volumes_db; #进入库中 Database changed mysql> create table my_id( #建立表 -> id int primary key, -> name varchar(25) -> ); Query OK, 0 rows affected (0.04 sec) mysql> insert into my_id values(1,'zhangsan'); #往表中写入数据 Query OK, 1 row affected (0.01 sec) mysql> select * from my_id; #查看数据 +----+----------+ | id | name | +----+----------+ | 1 | zhangsan | +----+----------+ 1 row in set (0.00 sec)
3)进行验证:
(1)手动删除pod,验证数据库内数据是否还会存在
[root@master ~]# kubectl delete pod mysql-68d65b9dd9-hf2bf pod "mysql-68d65b9dd9-hf2bf" deleted
[root@master ~]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES mysql-68d65b9dd9-bf9v8 1/1 Running 0 26s 10.244.1.4 node01 <none> <none>
删除pod后,kubernetes会生成新的pod,咱们登陆mysql查看
数据是否还会存在。
[root@master ~]# kubectl exec -it mysql-68d65b9dd9-bf9v8 -- mysql -u root -p123.com Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> select * from volumes_db.my_id; +----+----------+ | id | name | +----+----------+ | 1 | zhangsan | +----+----------+ 1 row in set (0.01 sec)
能够看到数据依旧会存在。
2)模拟pod运行所在节点宕机,在新生成的pod内,数据是否恢复正常。
从上面查看pod的信息中,咱们知道pod是运行在node01上,因此咱们将集群中的node01主机关机。
##[root@node01 ~]# systemctl poweroff
过一段时间后,kubernetes会将pod迁移至集群中node02主机上:
[root@master ~]# kubectl get nodes #得知node01节点已经宕机 NAME STATUS ROLES AGE VERSION master Ready master 39d v1.15.0 node01 NotReady <none> 39d v1.15.0 node02 Ready <none> 39d v1.15.0
[root@master ~]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES mysql-68d65b9dd9-bf9v8 1/1 Terminating 0 15m 10.244.1.4 node01 <none> <none> mysql-68d65b9dd9-mvxdg 1/1 Running 0 83s 10.244.2.3 node02 <none> <none>
能够看到pod已经迁移到了node02上。
最后咱们登陆mysql,验证数据是否恢复:
[root@master ~]# kubectl exec -it mysql-68d65b9dd9-mvxdg -- mysql -u root -p123.com Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> select * from volumes_db.my_id; +----+----------+ | id | name | +----+----------+ | 1 | zhangsan | +----+----------+ 1 row in set (0.09 sec)
能够得知在pod迁移以后,mysql服务正常运行,且数据也并无丢失。。。
pv和pvc实现了mysql数据的持久化,分离了管理员和普通用户的职责,更适合生产环境。