由于pod是有生命周期的,pod一重启,里面的数据就没了。因此咱们须要数据持久化存储。 css
在k8s中,存储卷不属于容器,而是属于pod。也就是说同一个pod中的容器能够共享一个存储卷。 html
存储卷能够是宿主机上的目录,也能够是挂载在宿主机上的外部设备。 node
存储卷类型
emptyDIR存储卷 :pod一重启,存储卷也删除,这叫emptyDir存储卷。通常用于当作临时空间或缓存关系 python
hostPath存储卷 :宿主机上目录做为存储卷,这种也不是真正意义实现了数据持久性。 mysql
SAN(iscsi)或NAS(nfs、cifs): 网络存储设备 nginx
分布式存储(ceph,glusterfs,cephfs,rbd) : web
云存储(亚马逊的EBS,Azure Disk,阿里云): 这种通常k8s也在云上部署的。 sql
关键数据必定要有异地备份,不然数据一删,多少个副本都没用。docker
[root@master ingress]# kubectl explain pods.spec.volumes
hostPath
功能:使用宿主机上目录做为存储卷,这种也不是真正意义实现了数据持久性。api
[root@master ~]# kubectl explain pods.spec.volumes.hostPath.type KIND: Pod VERSION: v1 FIELD: type <string> DESCRIPTION: Type for HostPath Volume Defaults to "" More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath
查看帮助: https://kubernetes.io/docs/concepts/storage/volumes#hostpath
hostPath.type的类型说明:
DirectoryOrCreate:意思是咱们要挂载的路径在宿主机上是个已经存在的目录,不存在就建立一个新的目录。
Directory:宿主机上必须实现存在目录,若是不存在就报错
FileOrCreate:表示挂载的是文件,若是不存在就挂载一个文件。文件也能够当作存储挂载的。
File:表示要挂载的文件必须事先存在,不然就报错。
Socket:表示必须是一个Socket类型的文件。
CharDevice:表示是一个字符类型的设备文件。
BlockDevice:表示的是一个块类型的设备文件。
例子:
[root@master volumes]# cat pod-hostpath-vol.yaml
apiVersion: v1 kind: Pod metadata: name: pod-vol-hostpath namespace: default spec: containers: - name: myapp image: ikubernetes/myapp:v1 volumeMounts: - name: html #存储卷的名字叫html mountPath: /usr/share/nginx/html/ #挂载路径 volumes: - name: html hostPath: path: /data/pod/volume1 type: DirectoryOrCreate
[root@master volumes]# kubectl apply -f pod-hostpath-vol.yaml pod/pod-vol-hostpath created
而后到node1节点上能够看到/data/pod/volume1目录已经建立出来了。
[root@master volumes]# kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE client 0/1 Error 0 15d 10.244.2.4 node2 pod-vol-hostpath 1/1 Running 0 4m 10.244.1.105 node1
当node1节点宕机后,pod就飘到node2节点上,并使用node2节点上的/data/pod/volume1目录。这就有问题了,由于node2节点上的目录并无同步node1节点上目录的数据,因此出现数据不一致。
解决这个问题的方法就是使用相似nfs方法,让两个node节点共享一个存储。
使用nfs作共享存储
我这里为了方便,把master节点当作nfs存储。
[root@master ~]# yum -y install nfs-utils
[root@master ~]# mkdir /data/volumes
[root@master ~]# cat /etc/exports #no_root_squash:登入 NFS 主机使用分享目录的使用者,若是是 root 的话,那么对于这个分享的目录来讲,他就具备 root 的权限!这个项目『极不安全』,不建议使用! #root_squash:在登入 NFS 主机使用分享之目录的使用者若是是 root 时,那么这个使用者的权限将被压缩成为匿名使用者,一般他的 UID 与 GID 都会变成 nobody 那个系统帐号的身份; /data/volumes 172.16.0.0/16(rw,no_root_squash)
[root@master ~]# systemctl start nfs
在node1和node2上也安装nfs-utils包
[root@node1 ~]# yum -y install nfs-utils
在node1和node2上挂载:
[root@node1 ~]# mount -t nfs 172.16.1.100:/data/volumes /mnt
在master上
[root@master ~]# kubectl explain pods.spec.volumes.nfs
[root@master volumes]# cat pod-vol-nfs.yaml apiVersion: v1 kind: Pod metadata: name: pod-vol-nfs namespace: default spec: containers: - name: myapp image: ikubernetes/myapp:v1 volumeMounts: - name: html #存储卷的名字叫html mountPath: /usr/share/nginx/html/ #挂载路径,myapp容器里面的路径 volumes: - name: html nfs: path: /data/volumes server: 172.16.1.100 #nfs server ip
[root@master volumes]# kubectl apply -f pod-vol-nfs.yaml
[root@master volumes]# kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE pod-vol-nfs 1/1 Running 0 1m 10.244.1.106 node1
[root@master volumes]# cat /data/volumes/index.html hello world
[root@master volumes]# curl 10.244.1.106 #容器的ip hello world
可见容器使用的是nfs提供的共享存储。
不过,nfs自身没有冗余能力,因此若是nfs宕机了,数据也丢了。所以,咱们通常用glusterfs或者cephfs分布式存储。
pvc和pv
用户只须要挂载pvc到容器中而不须要关注存储卷采用何种技术实现。pvc和pv的关系与pod和node关系相似,前者消耗后者的资源。pvc能够向pv申请指定大小的存储资源并设置访问模式。
在定义pod时,咱们只须要说明咱们要一个多大的存储卷就好了。pvc存储卷必须与当前namespace的pvc创建直接绑定关系。pvc必须与pv创建绑定关系。而pv是真正的某个存储设备上的空间。
[root@master volumes]# kubectl explain pods.spec.volumes.persistentVolumeClaim
[root@master volumes]# kubectl explain pvc
一个pvc和pv是一一对应关系,一旦一个pv被一个pvc绑定了,那么这个pv就不能被其余pvc绑定了。
一个pvc是能够被多个pod所访问的。
在存储机器上创建以下几个目录(这里我以master节点作存储,生产中能够单独拿出 一个机器作存储):
[root@master volumes]# mkdir v{1,2,3,4,5}
[root@master volumes]# cat /etc/exports #no_root_squash:登入 NFS 主机使用分享目录的使用者,若是是 root 的话,那么对于这个分享的目录来讲,他就具备 root 的权限!这个项目『极不安全』,不建议使用! #root_squash:在登入 NFS 主机使用分享之目录的使用者若是是 root 时,那么这个使用者的权限将被压缩成为匿名使用者,一般他的 UID 与 GID 都会变成 nobody 那个系统帐号的身份; /data/volumes/v1 172.16.0.0/16(rw,no_root_squash) /data/volumes/v2 172.16.0.0/16(rw,no_root_squash) /data/volumes/v3 172.16.0.0/16(rw,no_root_squash) /data/volumes/v4 172.16.0.0/16(rw,no_root_squash) /data/volumes/v5 172.16.0.0/16(rw,no_root_squash)
[root@master volumes]# exportfs -arv #不用重启nfs服务,配置文件就会生效 exporting 172.16.0.0/16:/data/volumes/v5 exporting 172.16.0.0/16:/data/volumes/v4 exporting 172.16.0.0/16:/data/volumes/v3 exporting 172.16.0.0/16:/data/volumes/v2 exporting 172.16.0.0/16:/data/volumes/v1
[root@master volumes]# showmount -e Export list for master: /data/volumes/v5 172.16.0.0/16 /data/volumes/v4 172.16.0.0/16 /data/volumes/v3 172.16.0.0/16 /data/volumes/v2 172.16.0.0/16 /data/volumes/v1 172.16.0.0/16
[root@master volumes]# kubectl explain pv.spec.nfs
[root@master ~]# kubectl explain pv.spec FIELDS: accessModes<[]string> AccessModes contains all ways the volume can be mounted. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes
访问 https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes看帮助。
accessModes模式有:
ReadWriteOnce:单路读写,能够简写为RWO
ReadOnlyMany:多路只读,能够简写为ROX
ReadWriteMany :多路读写,能够简写为RWX
不一样类型的存储卷支持的accessModes也不一样。
[root@master volumes]# cat pv-demo.yaml apiVersion: v1 kind: PersistentVolume metadata: name: pv001 #注意,定义pv时必定不要加名称空间,由于pv是属于整个集群,而不是属于某个名称空间。但pvc是属于某个名称空间的 labels: name: pv001 spec: nfs: path: /data/volumes/v1 server: 172.16.1.100 accessModes: ["ReadWriteMany","ReadWriteOnce"] capacity: #分配磁盘空间大小 storage: 1Gi --- apiVersion: v1 kind: PersistentVolume metadata: name: pv002 #注意,定义pv时必定不要加名称空间,由于pv是属于整个集群,而不是属于某个名称空间。但pvc是属于某个名称空间的 labels: name: pv002 spec: nfs: path: /data/volumes/v2 server: 172.16.1.100 accessModes: ["ReadWriteOnce"] capacity: #分配磁盘空间大小 storage: 2Gi --- apiVersion: v1 kind: PersistentVolume metadata: name: pv003 #注意,定义pv时必定不要加名称空间,由于pv是属于整个集群,而不是属于某个名称空间。但pvc是属于某个名称空间的 labels: name: pv003 spec: nfs: path: /data/volumes/v3 server: 172.16.1.100 accessModes: ["ReadWriteMany","ReadWriteOnce"] capacity: #分配磁盘空间大小 storage: 1Gi --- apiVersion: v1 kind: PersistentVolume metadata: name: pv004 #注意,定义pv时必定不要加名称空间,由于pv是属于整个集群,而不是属于某个名称空间。但pvc是属于某个名称空间的 labels: name: pv004 spec: nfs: path: /data/volumes/v4 server: 172.16.1.100 accessModes: ["ReadWriteMany","ReadWriteOnce"] capacity: #分配磁盘空间大小 storage: 1Gi --- apiVersion: v1 kind: PersistentVolume metadata: name: pv005 #注意,定义pv时必定不要加名称空间,由于pv是属于整个集群,而不是属于某个名称空间。但pvc是属于某个名称空间的 labels: name: pv005 spec: nfs: path: /data/volumes/v5 server: 172.16.1.100 accessModes: ["ReadWriteMany","ReadWriteOnce"] capacity: #分配磁盘空间大小 storage: 1Gi
[root@master volumes]# kubectl apply -f pv-demo.yaml persistentvolume/pv001 created persistentvolume/pv002 created persistentvolume/pv003 created persistentvolume/pv004 created persistentvolume/pv005 created
[root@master volumes]# kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pv001 1Gi RWO,RWX Retain Available 2m pv002 2Gi RWO Retain Available 2m pv003 1Gi RWO,RWX Retain Available 2m pv004 1Gi RWO,RWX Retain Available 2m pv005 1Gi RWO,RWX Retain Available 2m
回收策略 :若是某个pvc在pv里面存数据了,后来pvc删了,那么 pv里面的数据怎么处理呢。有以下几种策略:
reclaim_policy:即pvc删了,可是pv里面的数据不擅长,还保留着。
recycle:即pvc删了,那么就把pv里面的数据也删了。
delete:即pvc删了,那么就把pv也删了。
下面咱们再建立pvc的清单文件。
[root@master ~]# kubectl explain pvc.spec [root@master ~]# kubectl explain pods.spec.volumes.persistentVolumeClaim [root@master volumes]# cat pod-vol-pvc.yaml apiVersion: v1 kind: PersistentVolumeClaim #简称pvc metadata: name: mypvc namespace: default #pvc和pod是在同一个名称空间 spec: accessModes: ["ReadWriteMany"] #必定是pv策略的子集 resources: requests: storage: 1Gi #表示我要pvc 为1G的空间 --- apiVersion: v1 kind: Pod metadata: name: pod-vol-pvc namespace: default spec: containers: - name: myapp image: ikubernetes/myapp:v1 volumeMounts: - name: html #存储卷的名字叫html mountPath: /usr/share/nginx/html/ #挂载路径 volumes: - name: html persistentVolumeClaim: claimName: mypvc #表示我要使用哪一个pvc
[root@master volumes]# kubectl apply -f pod-vol-pvc.yaml persistentvolumeclaim/mypvc created pod/pod-vol-pvc created
[root@master volumes]# kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pv001 1Gi RWO,RWX Retain Available 7h pv002 2Gi RWO Retain Available 7h pv003 1Gi RWO,RWX Retain Available 7h pv004 1Gi RWO,RWX Retain Bound default/mypvc 7h pv005 1Gi RWO,RWX Retain Available
上面看到pv004被default名称空间的mypvc绑定了。
[root@master volumes]# kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE mypvc Bound pv004 1Gi RWO,RWX 33m [root@master volumes]# kubectl get pods NAME READY STATUS RESTARTS AGE client 0/1 Error 0 16d pod-vol-pvc 1/1 Running 0 35m
生产上,pv并不属于node节点,而是独立于node节点的。因此,node节点坏了,pv里面的数据还在。另外,pod才是属于node节点的。
在k8s 1.10以后,不能手工从底层删除pv,这样作很安全。
StorageClass(存储类)
Kubernetes集群管理员经过提供不一样的存储类,能够知足用户不一样的服务质量级别、备份策略和任意策略要求的存储需求。动态存储卷供应使用StorageClass进行实现,其容许存储卷按需被建立。若是没有动态存储供应,Kubernetes集群的管理员将不得不经过手工的方式类建立新的存储卷。经过动态存储卷,Kubernetes将可以按照用户的须要,自动建立其须要的存储。
storageclass底层能够是glusterfs,cephfs等不一样的集群。
configmap
configmap和secret是两种特殊的存储卷,它们不是给pod提供存储空间用的,而是给咱们的管理员或者用户提供了从外部向pod内部注入信息的方式。
configmap :把配置文件放在配置中心上,而后多个pod读取配置中心的配置文件。不过,configmap中的配置信息都是明文的,因此不安全。
secret: 功能和configmap同样,只不过配置中心存储的配置文件不是明文的。
configmap和secret也是专属于某个名称空间的。
[root@master ~]# kubectl explain configmap [root@master ~]# kubectl explain cm #简写 [root@master ~]# kubectl create configmap --help
简单的咱们能够用命令行来建立configmap。
[root@master ~]# kubectl create configmap nginx-config --from-literal=nginx_port=80 --from-literal=server_name=myapp.zhixin.com configmap/nginx-config created
[root@master ~]# kubectl get cm NAME DATA AGE nginx-config 2 3m
[root@master ~]# kubectl describe cm nginx-config Name: nginx-config Namespace: default Labels: <none> Annotations: <none> Data ==== nginx_port: ---- 80 server_name: ---- myapp.zhixin.com
下面咱们用配置清单的方式来建立configmap:
[root@master configmap]# cat www.conf server { server_name myapp.zhixin.com; listen 80; root /data/web/html; }
[root@master configmap]# kubectl create configmap nginx-www --from-file=www.conf configmap/nginx-www created
[root@master configmap]# kubectl get cm NAME DATA AGE nginx-config 2 3m nginx-www 1 7s
[root@master configmap]# kubectl describe cm nginx-www Name: nginx-www Namespace: default Labels: <none> Annotations: <none> Data ==== www.conf: ---- server { server_name myapp.zhixin.com; listen 80; root /data/web/html; }
咱们建立的configmap,可用ENV等方式注入到Pod中。
咱们用ENV方式来把configmap注入到pod中去。
[root@master configmap]# cat pod-configmap.yaml apiVersion: v1 kind: Pod metadata: name: pod-cm-1 namespace: default labels: app: myapp #kv格式的,也能够用花括号表示 tier: frontend #定义所属的层次 annotations: chenzx.com/created-by: "cluster-admin" #这是注解的键值对 spec: containers: - name: myapp #前面的-号表示这是一个列表格式的,也能够用中括号表示 image: tomcat ports: - name: http containerPort: 80 env: #这是一个容器的属性 - name: NGINX_SERVER_PORT valueFrom: #kubectl explain pods.spec.containers.env.valueFrom configMapKeyRef: #表示咱们要引用一个configmap来获取数据 name: nginx-config #这是configmap的名字,也就是经过kubectl get cm获取的名字 key: nginx_port #经过kubectl describe cm nginx-config的键 #下面开始引用第二个环境变量 - name: NGINX_SERVER_NAME valueFrom: configMapKeyRef: name: nginx-config key: server_name
[root@master configmap]# kubectl apply -f pod-configmap.yaml pod/pod-cm-1 created
这样,咱们就创建了一个pod-cm-1的pod,而且这个pod的配置文件来自于configmap。
[root@master configmap]# kubectl get pods NAME READY STATUS RESTARTS AGE pod-cm-1 0/1 Running 0 15m
[root@master configmap]# kubectl exec -it pod-cm-1 -- /bin/sh # printenv NGINX_SERVER_PORT=80 NGINX_SERVER_NAME=myapp.zhixin.com
[root@master configmap]# kubectl edit cm nginx-config configmap/nginx-config edited
经过edit方式编辑的配置文件,在Pod里面不会当即理解生效,须要重启pod才能生效。
[root@master configmap]# kubectl delete -f pod-configmap.yaml pod "pod-cm-1" deleted
下面咱们用配置mount存储卷的方法把configmap注入到pod中。
[root@master configmap]# cat pod-configmap2.ymal apiVersion: v1 kind: Pod metadata: name: pod-cm-2 namespace: default labels: app: myapp #kv格式的,也能够用花括号表示 tier: frontend #定义所属的层次 annotations: chenzx.com/created-by: "cluster-admin" #这是注解的键值对 spec: containers: - name: myapp #前面的-号表示这是一个列表格式的,也能够用中括号表示 image: ikubernetes/myapp:v1 ports: - name: http containerPort: 80 volumeMounts: - name: nginxconf mountPath: /etc/nginx/conf.d/ readOnly: true volumes: - name: nginxconf configMap: name: nginx-config
[root@master configmap]# kubectl apply -f pod-configmap2.ymal pod/pod-cm-2 created
[root@master configmap]# kubectl get pods NAME READY STATUS RESTARTS AGE pod-cm-2 1/1 Running 0 1m
[root@master configmap]# kubectl exec -it pod-cm-2 -- /bin/sh / # cd /etc/nginx/conf.d/ /etc/nginx/conf.d # ls nginx_port server_name /etc/nginx/conf.d # ls -l total 0 lrwxrwxrwx 1 root root 17 Sep 27 05:07 nginx_port -> ..data/nginx_port lrwxrwxrwx 1 root root 18 Sep 27 05:07 server_name -> ..data/server_name /etc/nginx/conf.d # cat nginx_port 8080
下面咱们再把前面咱们建立的www.conf文件注入到pod中:
[root@master configmap]# cat pod-configmap3.yaml apiVersion: v1 kind: Pod metadata: name: pod-cm-3 namespace: default labels: app: myapp #kv格式的,也能够用花括号表示 tier: frontend #定义所属的层次 annotations: chenzx.com/created-by: "cluster-admin" #这是注解的键值对 spec: containers: - name: myapp #前面的-号表示这是一个列表格式的,也能够用中括号表示 image: ikubernetes/myapp:v1 ports: - name: http containerPort: 80 volumeMounts: - name: nginxconf mountPath: /etc/nginx/conf.d/ readOnly: true volumes: - name: nginxconf configMap: name: nginx-www
[root@master configmap]# kubectl apply -f pod-configmap3.yaml pod/pod-cm-3 created
[root@master configmap]# [root@master configmap]# kubectl get pods NAME READY STATUS RESTARTS AGE client 0/1 Error 0 16d pod-cm-3
[root@master configmap]# kubectl exec -it pod-cm-3 -- /bin/sh / # cd /etc/nginx/conf.d/ /etc/nginx/conf.d # ls www.conf /etc/nginx/conf.d # cat www.conf server { server_name myapp.zhixin.com; listen 80; root /data/web/html; }
经过上面的例子,你们看到咱们已经把 www.conf中的内容注入到了pod myapp中。
[root@master configmap]# kubectl edit cm nginx-www
改个端口,而后再到pod里面,多等一会就会看到刚才修改的在pod里面生效了。
[root@master configmap]# kubectl exec -it pod-cm-3 -- /bin/sh / # cd /etc/nginx/conf.d/ /etc/nginx/conf.d # cat www.conf server { server_name myapp.zhixin.com; listen 8081; root /data/web/html; [root@master configmap]# /etc/init.d/nginx reload #重载nginx使8081端口生效
若是咱们指望只注入部分,而非全部,该怎么作呢?
[root@master configmap]# kubectl explain pods.spec.volumes.configMap.items [root@master configmap]# kubectl create secret generic --help
经过items来注入部分,这里面就不演示了,请读者自行解决。
secret
功能和configmap同样,只不过secret配置中心存储的配置文件不是明文的。
[root@master configmap]# kubectl create secret --help generic:保存密码用的类型 tls:保存证书用的类型 docker-registry:保存docker认证信息用的类型,好比从私有docker仓库拉镜像时,就用这个类型。 备注:k8s拖镜像的进程是kublete
[root@master configmap]# kubectl explain pods.spec.imagePullSecrets 若是是从私有仓库拉镜像,就用imagePullSecrets存登陆验证的信息
例子:
[root@master configmap]# kubectl create secret generic mysql-root-password --from-literal=password=123456 secret/mysql-root-password created
[root@master configmap]# kubectl get secret NAME TYPE DATA AGE default-token-5r85r kubernetes.io/service-account-token 3 19d mysql-root-password Opaque 1 40s tomcat-ingress-secret kubernetes.io/tls 2 2d
[root@master configmap]# kubectl describe secret mysql-root-password Name: mysql-root-password Namespace: default Labels: <none> Annotations: <none> Type: Opaque Data ==== password: 6 bytes
看到password的内容就是base64加密的形式了。
[root@master configmap]# kubectl get secret mysql-root-password -o yaml apiVersion: v1 data: password: MTIzNDU2 kind: Secret metadata: creationTimestamp: 2018-09-27T06:01:24Z name: mysql-root-password namespace: default resourceVersion: "2482795" selfLink: /api/v1/namespaces/default/secrets/mysql-root-password uid: c3d3e8ec-c21a-11e8-bb35-005056a24ecb type: Opaque
能够用命令base64命令进行明文解码:
[root@master configmap]# echo MTIzNDU2 |base64 -d 123456
可见secret是防君子不防小人,是个伪加密,哈哈。
下面咱们把secret经过env的方式注入到pod里面。
[root@master configmap]# cat pod-secret-1.yaml apiVersion: v1 kind: Pod metadata: name: pod-secret-1 namespace: default labels: app: myapp #kv格式的,也能够用花括号表示 tier: frontend #定义所属的层次 annotations: chenzx.com/created-by: "cluster-admin" #这是注解的键值对 spec: containers: - name: myapp #前面的-号表示这是一个列表格式的,也能够用中括号表示 image: tomcat ports: - name: http containerPort: 80 env: #这是一个容器的属性 - name: MYSQL_ROOT_PASSWORD valueFrom: #kubectl explain pods.spec.containers.env.valueFrom secretKeyRef: #表示咱们要引用一个configmap来获取数据 name: mysql-root-password #这是configmap的名字,也就是经过kubectl get secret获取的名字 key: password #经过kubectl describe secret mysql-root-password的键 #下面开始引用第二个环境变量 - name: NGINX_SERVER_NAME valueFrom: configMapKeyRef: name: nginx-config key: server_name
[root@master configmap]# kubectl apply -f pod-secret-1.yaml pod/pod-secret-1 created
[root@master configmap]# kubectl get pods NAME READY STATUS RESTARTS AGE pod-secret-1 1/1 Running 0 1m
[root@master configmap]# kubectl exec -it pod-secret-1 -- /bin/sh # printenv MYSQL_ROOT_PASSWORD=123456
看到secret经过env的方式,是以明文注入到pod里面的。
另外,secret还能够用mount的方式注入pod中,这部分略,如须要请参考本小节的configmap的相关例子。
由于pod是有生命周期的,pod一重启,里面的数据就没了。因此咱们须要数据持久化存储。
在k8s中,存储卷不属于容器,而是属于pod。也就是说同一个pod中的容器能够共享一个存储卷。
存储卷能够是宿主机上的目录,也能够是挂载在宿主机上的外部设备。
存储卷类型
emptyDIR存储卷 :pod一重启,存储卷也删除,这叫emptyDir存储卷。通常用于当作临时空间或缓存关系
hostPath存储卷 :宿主机上目录做为存储卷,这种也不是真正意义实现了数据持久性。
SAN(iscsi)或NAS(nfs、cifs): 网络存储设备
分布式存储(ceph,glusterfs,cephfs,rbd) :
云存储(亚马逊的EBS,Azure Disk,阿里云): 这种通常k8s也在云上部署的。
关键数据必定要有异地备份,不然数据一删,多少个副本都没用。
1
|
[root@master ingress]# kubectl explain pods.spec.volumes
|
hostPath
功能:使用宿主机上目录做为存储卷,这种也不是真正意义实现了数据持久性。
1
2
3
4
5
6
7
|
[root@master ~]# kubectl explain pods.spec.volumes.hostPath.type
KIND: Pod
VERSION: v
1
FIELD: type <string>
DESCRIPTION:
Type for HostPath Volume Defaults to
""
More info:
https://kubernetes.io/docs/concepts/storage/volumes#hostpath
|
查看帮助: https://kubernetes.io/docs/concepts/storage/volumes#hostpath
hostPath.type的类型说明:
DirectoryOrCreate:意思是咱们要挂载的路径在宿主机上是个已经存在的目录,不存在就建立一个新的目录。
Directory:宿主机上必须实现存在目录,若是不存在就报错
FileOrCreate:表示挂载的是文件,若是不存在就挂载一个文件。文件也能够当作存储挂载的。
File:表示要挂载的文件必须事先存在,不然就报错。
Socket:表示必须是一个Socket类型的文件。
CharDevice:表示是一个字符类型的设备文件。
BlockDevice:表示的是一个块类型的设备文件。
例子:
[root@master volumes]# cat pod-hostpath-vol.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
apiVersion: v
1
kind: Pod
metadata:
name: pod-vol-hostpath
namespace:
default
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v
1
volumeMounts:
- name: html #存储卷的名字叫html
mountPath: /usr/share/nginx/html/ #挂载路径
volumes:
- name: html
hostPath:
path: /data/pod/volume
1
type: DirectoryOrCreate
|
1
2
|
[root@master volumes]# kubectl apply -f pod-hostpath-vol.yaml
pod/pod-vol-hostpath created
|
而后到node1节点上能够看到/data/pod/volume1目录已经建立出来了。
1
2
3
4
|
[root@master volumes]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
client
0
/
1
Error
0
15
d
10.244
.
2.4
node
2
pod-vol-hostpath
1
/
1
Running
0
4
m
10.244
.
1.105
node
1
|
当node1节点宕机后,pod就飘到node2节点上,并使用node2节点上的/data/pod/volume1目录。这就有问题了,由于node2节点上的目录并无同步node1节点上目录的数据,因此出现数据不一致。
解决这个问题的方法就是使用相似nfs方法,让两个node节点共享一个存储。
使用nfs作共享存储
我这里为了方便,把master节点当作nfs存储。
1
|
[root@master ~]# yum -y install nfs-utils
|
1
|
[root@master ~]# mkdir /data/volumes
|
1
2
3
4
|
[root@master ~]# cat /etc/exports
#no_root_squash:登入 NFS 主机使用分享目录的使用者,若是是 root 的话,那么对于这个分享的目录来讲,他就具备 root 的权限!这个项目『极不安全』,不建议使用!
#root_squash:在登入 NFS 主机使用分享之目录的使用者若是是 root 时,那么这个使用者的权限将被压缩成为匿名使用者,一般他的 UID 与 GID 都会变成 nobody 那个系统帐号的身份;
/data/volumes
172.16
.
0.0
/
16
(rw,no_root_squash)
|
1
|
[root@master ~]# systemctl start nfs
|
在node1和node2上也安装nfs-utils包
1
|
[root@node
1
~]# yum -y install nfs-utils
|
在node1和node2上挂载:
1
|
[root@node
1
~]# mount -t nfs
172.16
.
1.100:
/data/volumes /mnt
|
在master上:
1
|
[root@master ~]# kubectl explain pods.spec.volumes.nfs
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
[root@master volumes]# cat pod-vol-nfs.yaml
apiVersion: v
1
kind: Pod
metadata:
name: pod-vol-nfs
namespace:
default
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v
1
volumeMounts:
- name: html #存储卷的名字叫html
mountPath: /usr/share/nginx/html/ #挂载路径,myapp容器里面的路径
volumes:
- name: html
nfs:
path: /data/volumes
server:
172.16
.
1.100
#nfs server ip
|
1
|
[root@master volumes]# kubectl apply -f pod-vol-nfs.yaml
|
1
2
3
|
[root@master volumes]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
pod-vol-nfs
1
/
1
Running
0
1
m
10.244
.
1.106
node
1
|
1
2
|
[root@master volumes]# cat /data/volumes/index.html
hello world
|
1
2
|
[root@master volumes]# curl
10.244
.
1.106
#容器的ip
hello world
|
可见容器使用的是nfs提供的共享存储。
不过,nfs自身没有冗余能力,因此若是nfs宕机了,数据也丢了。所以,咱们通常用glusterfs或者cephfs分布式存储。
pvc和pv
用户只须要挂载pvc到容器中而不须要关注存储卷采用何种技术实现。pvc和pv的关系与pod和node关系相似,前者消耗后者的资源。pvc能够向pv申请指定大小的存储资源并设置访问模式。
在定义pod时,咱们只须要说明咱们要一个多大的存储卷就好了。pvc存储卷必须与当前namespace的pvc创建直接绑定关系。pvc必须与pv创建绑定关系。而pv是真正的某个存储设备上的空间。
1
|
[root@master volumes]# kubectl explain pods.spec.volumes.persistentVolumeClaim
|
1
|
[root@master volumes]# kubectl explain pvc
|
一个pvc和pv是一一对应关系,一旦一个pv被一个pvc绑定了,那么这个pv就不能被其余pvc绑定了。
一个pvc是能够被多个pod所访问的。
在存储机器上创建以下几个目录(这里我以master节点作存储,生产中能够单独拿出 一个机器作存储):
1
|
[root@master volumes]# mkdir v{
1
,
2
,
3
,
4
,
5
}
|
1
2
3
4
5
6
7
8
|
[root@master volumes]# cat /etc/exports
#no_root_squash:登入 NFS 主机使用分享目录的使用者,若是是 root 的话,那么对于这个分享的目录来讲,他就具备 root 的权限!这个项目『极不安全』,不建议使用!
#root_squash:在登入 NFS 主机使用分享之目录的使用者若是是 root 时,那么这个使用者的权限将被压缩成为匿名使用者,一般他的 UID 与 GID 都会变成 nobody 那个系统帐号的身份;
/data/volumes/v
1
172.16
.
0.0
/
16
(rw,no_root_squash)
/data/volumes/v
2
172.16
.
0.0
/
16
(rw,no_root_squash)
/data/volumes/v
3
172.16
.
0.0
/
16
(rw,no_root_squash)
/data/volumes/v
4
172.16
.
0.0
/
16
(rw,no_root_squash)
/data/volumes/v
5
172.16
.
0.0
/
16
(rw,no_root_squash)
|
1
2
3
4
5
6
|
[root@master volumes]# exportfs -arv #不用重启nfs服务,配置文件就会生效
exporting
172.16
.
0.0
/
16:
/data/volumes/v
5
exporting
172.16
.
0.0
/
16:
/data/volumes/v
4
exporting
172.16
.
0.0
/
16:
/data/volumes/v
3
exporting
172.16
.
0.0
/
16:
/data/volumes/v
2
exporting
172.16
.
0.0
/
16:
/data/volumes/v
1
|
1
2
3
4
5
6
7
|
[root@master volumes]# showmount -e
Export list for master:
/data/volumes/v
5
172.16
.
0.0
/
16
/data/volumes/v
4
172.16
.
0.0
/
16
/data/volumes/v
3
172.16
.
0.0
/
16
/data/volumes/v
2
172.16
.
0.0
/
16
/data/volumes/v
1
172.16
.
0.0
/
16
|
1
|
[root@master volumes]# kubectl explain pv.spec.nfs
|
1
2
3
4
5
|
[root@master ~]# kubectl explain pv.spec
FIELDS:
accessModes<[]string>
AccessModes contains
all
ways the volume can be mounted. More info:
https://kubernetes.io/docs/concepts/storage/persistent-volumes
#acce
ss-modes
|
访问 https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes看帮助。
accessModes模式有:
ReadWriteOnce:单路读写,能够简写为RWO
ReadOnlyMany:多路只读,能够简写为ROX
ReadWriteMany :多路读写,能够简写为RWX
不一样类型的存储卷支持的accessModes也不一样。
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
|
[root@master volumes]# cat pv-demo.yaml
apiVersion: v
1
kind: PersistentVolume
metadata:
name: pv
001
#注意,定义pv时必定不要加名称空间,由于pv是属于整个集群,而不是属于某个名称空间。但pvc是属于某个名称空间的
labels:
name: pv
001
spec:
nfs:
path: /data/volumes/v
1
server:
172.16
.
1.100
accessModes: [
"ReadWriteMany"
,
"ReadWriteOnce"
]
capacity: #分配磁盘空间大小
storage:
1
Gi
---
apiVersion: v
1
kind: PersistentVolume
metadata:
name: pv
002
#注意,定义pv时必定不要加名称空间,由于pv是属于整个集群,而不是属于某个名称空间。但pvc是属于某个名称空间的
labels:
name: pv
002
spec:
nfs:
path: /data/volumes/v
2
server:
172.16
.
1.100
accessModes: [
"ReadWriteOnce"
]
capacity: #分配磁盘空间大小
storage:
2
Gi
---
apiVersion: v
1
kind: PersistentVolume
metadata:
name: pv
003
#注意,定义pv时必定不要加名称空间,由于pv是属于整个集群,而不是属于某个名称空间。但pvc是属于某个名称空间的
labels:
name: pv
003
spec:
nfs:
path: /data/volumes/v
3
server:
172.16
.
1.100
accessModes: [
"ReadWriteMany"
,
"ReadWriteOnce"
]
capacity: #分配磁盘空间大小
storage:
1
Gi
---
apiVersion: v
1
kind: PersistentVolume
metadata:
name: pv
004
#注意,定义pv时必定不要加名称空间,由于pv是属于整个集群,而不是属于某个名称空间。但pvc是属于某个名称空间的
labels:
name: pv
004
spec:
nfs:
path: /data/volumes/v
4
server:
172.16
.
1.100
accessModes: [
"ReadWriteMany"
,
"ReadWriteOnce"
]
capacity: #分配磁盘空间大小
storage:
1
Gi
---
apiVersion: v
1
kind: PersistentVolume
metadata:
name: pv
005
#注意,定义pv时必定不要加名称空间,由于pv是属于整个集群,而不是属于某个名称空间。但pvc是属于某个名称空间的
labels:
name: pv
005
spec:
nfs:
path: /data/volumes/v
5
server:
172.16
.
1.100
accessModes: [
"ReadWriteMany"
,
"ReadWriteOnce"
]
capacity: #分配磁盘空间大小
storage:
1
Gi
|
1
2
3
4
5
6
|
[root@master volumes]# kubectl apply -f pv-demo.yaml
persistentvolume/pv
001
created
persistentvolume/pv
002
created
persistentvolume/pv
003
created
persistentvolume/pv
004
created
persistentvolume/pv
005
created
|
1
2
3
4
5
6
7
|
[root@master volumes]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv
001
1
Gi RWO,RWX Retain Available
2
m
pv
002
2
Gi RWO Retain Available
2
m
pv
003
1
Gi RWO,RWX Retain Available
2
m
pv
004
1
Gi RWO,RWX Retain Available
2
m
pv
005
1
Gi RWO,RWX Retain Available
2
m
|
回收策略 :若是某个pvc在pv里面存数据了,后来pvc删了,那么 pv里面的数据怎么处理呢。有以下几种策略:
reclaim_policy:即pvc删了,可是pv里面的数据不擅长,还保留着。
recycle:即pvc删了,那么就把pv里面的数据也删了。
delete:即pvc删了,那么就把pv也删了。
下面咱们再建立pvc的清单文件。
1
|
[root@master ~]# kubectl explain pvc.spec
|
1
|
[root@master ~]# kubectl explain pods.spec.volumes.persistentVolumeClaim
|
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
|
[root@master volumes]# cat pod-vol-pvc.yaml
apiVersion: v
1
kind: PersistentVolumeClaim #简称pvc
metadata:
name: mypvc
namespace:
default
#pvc和pod是在同一个名称空间
spec:
accessModes: [
"ReadWriteMany"
] #必定是pv策略的子集
resources:
requests:
storage:
1
Gi #表示我要pvc 为
1
G的空间
---
apiVersion: v
1
kind: Pod
metadata:
name: pod-vol-pvc
namespace:
default
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v
1
volumeMounts:
- name: html #存储卷的名字叫html
mountPath: /usr/share/nginx/html/ #挂载路径
volumes:
- name: html
persistentVolumeClaim:
claimName: mypvc #表示我要使用哪一个pvc
|
1
2
3
|
[root@master volumes]# kubectl apply -f pod-vol-pvc.yaml
persistentvolumeclaim/mypvc created
pod/pod-vol-pvc created
|
1
2
3
4
5
6
7
|
[root@master volumes]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv
001
1
Gi RWO,RWX Retain Available
7
h
pv
002
2
Gi RWO Retain Available
7
h
pv
003
1
Gi RWO,RWX Retain Available
7
h
pv
004
1
Gi RWO,RWX Retain Bound
default
/mypvc
7
h
pv
005
1
Gi RWO,RWX Retain Available
7
h
|
上面看到pv004被default名称空间的mypvc绑定了。
1
2
3
|
[root@master volumes]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mypvc Bound pv
004
1
Gi RWO,RWX
33
m
|
1
2
3
4
|
[root@master volumes]# kubectl get pods
NAME READY STATUS RESTARTS AGE
client
0
/
1
Error
0
16
d
pod-vol-pvc
1
/
1
Running
0
35
m
|
生产上,pv并不属于node节点,而是独立于node节点的。因此,node节点坏了,pv里面的数据还在。另外,pod才是属于node节点的。
在k8s 1.10以后,不能手工从底层删除pv,这样作很安全。
StorageClass(存储类)
Kubernetes集群管理员经过提供不一样的存储类,能够知足用户不一样的服务质量级别、备份策略和任意策略要求的存储需求。动态存储卷供应使用StorageClass进行实现,其容许存储卷按需被建立。若是没有动态存储供应,Kubernetes集群的管理员将不得不经过手工的方式类建立新的存储卷。经过动态存储卷,Kubernetes将可以按照用户的须要,自动建立其须要的存储。
storageclass底层能够是glusterfs,cephfs等不一样的集群。
configmap
configmap和secret是两种特殊的存储卷,它们不是给pod提供存储空间用的,而是给咱们的管理员或者用户提供了从外部向pod内部注入信息的方式。
configmap :把配置文件放在配置中心上,而后多个pod读取配置中心的配置文件。不过,configmap中的配置信息都是明文的,因此不安全。
secret: 功能和configmap同样,只不过配置中心存储的配置文件不是明文的。
configmap和secret也是专属于某个名称空间的。
1
2
3
|
[root@master ~]# kubectl explain configmap
[root@master ~]# kubectl explain cm #简写
[root@master ~]# kubectl create configmap --
help
|
简单的咱们能够用命令行来建立configmap。
1
2
|
[root@master ~]# kubectl create configmap nginx-config --from-literal=nginx_port=
80
--from-literal=server_name=myapp.zhixin.com
configmap/nginx-config created
|
1
2
3
|
[root@master ~]# kubectl get cm
NAME DATA AGE
nginx-config
2
3
m
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
[root@master ~]# kubectl describe cm nginx-config
Name: nginx-config
Namespace:
default
Labels: <
none
>
Annotations: <
none
>
Data
====
nginx_port:
----
80
server_name:
----
myapp.zhixin.com
|
下面咱们用配置清单的方式来建立configmap:
1
2
3
4
5
6
|
[root@master configmap]# cat www.conf
server {
server_name myapp.zhixin.com;
listen
|