kubernetes集群系列资料13--存储机制介绍
一、K8S存储机制介绍
k8s的stateful控制组件是为有状态服务而设计的,有状态服务需要对数据进行存储;k8s有4种存储机制,主要为:
1)configMap:为K8S存储配置文件而设计的,configMap可以用来保存单个属性,也可以用来保存整个配置文件或者JSON二进制大对象。
2)secret:为了解决密码、token、密钥等敏感数据的配置加密而设计,而不需要将这些敏感数据暴露到镜像或pod.spec中,可以volume或环境变量的方式使用。
3)volume:为容器提供共享存储卷,避免发生容器崩溃重启后容器中文件丢失的问题。当pod不存在时,volume也不复存在;k8s支持多种类型的卷,pod可使用任意数量的卷。
4)persistentVolume/persistentVolumeClai:
二、configMap介绍
许多应用程序从配置文件、命令行参数或环境变量中读取配置信息,而configMap API给我们提供了向容器注入配置信息的机制。
传统生成环境中,配置文件注册中心负责向n个集群n个节点应用程序的提供配置信息,该中心需要自行构建,没有较好的开源方案。应用程序向配置文件注册中心提供本节点的信息(如:IP、hostname),配置文件注册中心根据规则为该节点应用程序更新配置,应用程序按照新配置进行重载后运行。
k8s集群中,configMap充当了配置文件注册中心的作用。pod向configMap申请配置,configMap为各pod下发不同的配置。
configMap创建配置有3中方式:使用目录创建、使用文件创建、使用字面值创建。
1、使用目录创建configMap
mkdir configMapFile;cd configMapFile cat > game.properties <<eof enemies=aliens lives=3 enemies.cheat=true enemies.cheat.level=noGoodRotten enemies.code.passphrase=UUDDLRLRBABAS enemies.code.allowed=true enemies.code.lives=30 eof cat >ui.properties<<eof color.good=purple color.bad=yellow allow.textmode=true how.nice.to.look=fairlyNice eof kubectl create configmap game-config --from-file=../configMapFile #创建configMap; kubectl get configmap #查看configMap; kubectl get cm game-config -o yaml #以yaml格式输出game-config配置; kubectl get cm game-config -o json #以yaml格式输出game-config配置;
2、使用文件创建configMap
kubectl create configmap game-config2 --from-file=../configMapFile/game.properties #使用文件创建configMap; kubectl get cm game-config2 -o yaml
多次使用文件创建,与在该目录下一次性批量创建效果相同。
3、使用字面量创建configMap
kubectl create configmap game-config3 --from-literal=special.how=very --from-literal=special.type=charm #使用字面量创建configMap; kubectl get cm game-config3 -o yaml
4、案例:使用configMap向pod注入环境变量
cat >special-config.yaml<<eof apiVersion: v1 kind: ConfigMap metadata: name: special-config namespace: default data: special.how: very special.type: charm eof kubectl apply -f special-config.yaml #使用yaml文件创建configMap; cat >env-config.yaml<<eof apiVersion: v1 kind: ConfigMap metadata: name: env-config namespace: default data: log_level: INFO eof kubectl apply -f env-config.yaml
cat >pod_configMapTest.yaml<<eof apiVersion: v1 kind: Pod metadata: name: dapi-test-pod namespace: default spec: containers: - name: test-container image: hub.atguigu.com/library/nginx:latest command: ["/bin/sh","-c","env"] env: #设置pod环境变量; - name: SPECIAL_LEVEL_KEY valueFrom: #向pod环境变量注入special-config配置中某个键的值; configMapKeyRef: name: special-config key: special.how - name: SPECIAL_TYPE_KEY valueFrom: configMapKeyRef: name: special-config key: special.type envFrom: #向pod注入env.config配置中所有键值对; - configMapRef: name: env-config restartPolicy: Never eof kubectl apply -f pod_configMapTest.yaml
验证结果显示:环境变量包含导入的变量SPECIAL_LEVEL_KEY、SPECIAL_TYPE_KEY、log_level。
5、案例:通过volume来使用configMap
cat >pod_configMapTest_1.yaml<<eof apiVersion: v1 kind: Pod metadata: name: dapi-test-pod-1 namespace: default spec: containers: - name: test-container image: hub.atguigu.com/library/nginx:latest command: ["/bin/sh","-c","ls /etc/config/"] volumeMounts: #指定挂载卷; - name: config-volume #指定挂载卷名称; mountPath: /etc/config #指定挂载卷挂载点; volumes: - name: config-volume configMap: name: special-config #将configMap导入pod的volume中,有不同的选型。基本方式为:将文件填入volume,键以文件名显示,值以内容显示; restartPolicy: Never eof kubectl apply -f pod_configMapTest_1.yaml
5、案例:configMap热更新
cat >config_update_test.yaml<<eof apiVersion: v1 kind: ConfigMap metadata: name: log-config namespace: default data: log_level: INFO --- apiVersion: extensions/v1beta1 kind: Deployment metadata: name: my-nginx namespace: default spec: replicas: 1 template: metadata: labels: run: my-nginx spec: containers: - name: my-nginx image: hub.atguigu.com/library/nginx:latest ports: - containerPort: 80 volumeMounts: #指定挂载卷; - name: config-volume #指定挂载卷名称; mountPath: /etc/config #指定挂载卷挂载点; volumes: - name: config-volume configMap: name: log-config #将configMap导入pod的volume中,有不同的选型。基本方式为:将文件填入volume,键以文件名显示,值以内容显示; restartPolicy: Always eof kubectl apply -f config_update_test.yaml kubectl exec $(kubectl get pod -l run=my-nginx -o=name |cut -d "/" -f2) -it -- cat /etc/config/log_level #查看导入pod的configMap配置,该值以volume文件存在;
验证结果:配置写入volume的log_level文件。
6、案例:configMap热更新
cat >config_update_test.yaml<<eof apiVersion: v1 kind: ConfigMap metadata: name: log-config namespace: default data: log_level: INFO --- apiVersion: extensions/v1beta1 kind: Deployment metadata: name: my-nginx namespace: default spec: replicas: 1 template: metadata: labels: run: my-nginx spec: containers: - name: my-nginx image: hub.atguigu.com/library/nginx:latest ports: - containerPort: 80 volumeMounts: #指定挂载卷; - name: config-volume #指定挂载卷名称; mountPath: /etc/config #指定挂载卷挂载点; volumes: - name: config-volume configMap: name: log-config #将configMap导入pod的volume中,有不同的选型。基本方式为:将文件填入volume,键以文件名显示,值以内容显示; restartPolicy: Always eof kubectl apply -f config_update_test.yaml kubectl exec $(kubectl get pod -l run=my-nginx -o=name |cut -d "/" -f2) -it -- cat /etc/config/log_level #查看导入pod的configMap配置,该值以volume文件存在; kubectl edit configmap log-config #修改configMap配置log_level值改为DEBUG; kubectl patch deployment my-nginx --patch '{"spec":{"template":{"metadata":{"annotations":{"version/config":"20210618 14:53:00"}}}}}' #手动指定20210618 14:53:00重新加载配置文件,以便配置文件生效; kubectl exec $(kubectl get pod -l run=my-nginx -o=name |cut -d "/" -f2) -it -- cat /etc/config/log_level #查看log_level值是否修改;
验证结果:configMap配置已热更新。
三、secret介绍
service Account:用来访问K8S API,由K8S自动创建,并且会自动挂载到pod的/run/secrets/kubernetes.io/serviceaccount目录中;对于有些pod(如coreDNS、flannel)来说,需要与K8S API进行交互,K8S API不是谁都可以访问的,必须通过挂载SA后pod才能访问K8S API;
opaque:base64编码格式的secret,用来存储密码、密钥等;opaque类型的数据是一个map类型,要求value是base64编码格式;
kubernetes.io/dockerconfigjson:用来存储私有docker registry的认证信息;
1、SA案例
kubectl run nginx --image nginx #创建名为nginx的depoloyment及pod; kubectl get deployment;kubectl get pods kubectl exec nginx-7bb7cd8db5-czg6s ls /run/secrets/kubernetes.io/serviceaccount #查看sa的默认挂载的目录; kubectl get secret -n kube-system #查看集群自动创建的secret;
2、opaque案例
echo -n 'admin' |base64 #输出以base64加密结果为YWRtaW4=; echo -n '1qaz@WSX' |base64 #输出以base64加密结果为MXFhekBXU1g=; echo -n 'YWRtaW4=' |base64 -d #输出YWRtaW4=的解密结果为admin; cat >secret.yml<<eof apiVersion: v1 kind: Secret metadata: name: mysecret data: username: YWRtaW4= password: MXFhekBXU1g= eof kubectl apply -f secret.yml kubectl get secret #将secret挂载到volume中 cat >secret-pod.yml<<eof apiVersion: v1 kind: Pod metadata: labels: name: secret-test name: secret-test spec: volumes: - name: secrets secret: secretName: mysecret containers: - image: hub.atguigu.com/library/nginx:latest name: db volumeMounts: - name: secrets mountPath: '/etc/secrets' readOnly: true eof kubectl apply -f secret-pod.yml kubectl get pod kubectl exec secret-test -it -- cat /etc/secrets/username #验证secret是否导入volume中;
#将secret导出到pod的环境变量中 cat >secret-pod-out.yml<<eof apiVersion: extensions/v1beta1 kind: Deployment metadata: name: secret-pod-out-deployment spec: replicas: 2 template: metadata: labels: app: secret-pod-out-deployment spec: containers: - image: hub.atguigu.com/library/nginx:latest name: pod-1 ports: - containerPort: 80 env: - name: TEST_USER valueFrom: secretKeyRef: name: mysecret key: username - name: TEST_PASSWORD valueFrom: secretKeyRef: name: mysecret key: password eof kubectl apply -f secret-pod-out.yml kubectl get pod kubectl exec secret-pod-out-deployment-7b8f585846-2cvj6 -it -- echo $TEST_PASSWORD #验证secret是否导入pod中;
3、kubernetes.io/dockerconfigjson案例
kubectl create secret docker-registry myregistrykey --docker-server=hub.atguigu.com --docker-username=admin --docker-password=Harbor12345 --docker-email=admin@example.com #创建docker registry认证的secret;私有仓库不进行认证就无法下载镜像,因此需要创建该secret来存储私有docker registry的认证信息
cat >myregistry-secret.yml<<eof apiVersion: v1 kind: Pod metadata: name: foo spec: containers: - image: hub.atguigu.com/library/nginx:latest name: foo imagePullSecrets: - name: myregistrykey eof kubectl apply -f myregistry-secret.yml kubectl get pod
四、volume介绍
docker中,容器崩溃后重启时数据不会丢失;但K8S中,容器崩溃时,该容器的文件会丢失,kubelet重启该容器,容器以镜像最初状态重新启动;POD中同时运行的多个容器需要共享文件。
k8s支持以下类型的卷:emptyDir;hostPath;iscsi;local;nfs;awsElasticBlockStore;azureDisk;azureFile;cephfs;csi;downwardAPI;fc;flocker;gcePersistentDisk;gitRepo;glusterfs;persistentVolumeClaim;projected;portworxVolume;quobyte;rbd;scaleIO;secret;storageos;vsphereVolume;
1、emptyDir介绍
当pod被分配给节点时,首先创建emptyDir卷,并且只要该pod在该节点上运行,该卷就会存在。该卷最初时空的,pod中容器可以读取和写入emptyDir卷中的相同文件,尽管该卷可以挂载到每个容器中的相同或不同的路径上。当处于任何原因从节点中删除pod时,emptyDir中数据将被永久删除。
empty的用法有:
暂存空间,如:用于基于磁盘的合并排序;
用作长时间计算崩溃恢复时的检查点;
web服务器容器提供数据时,保存内容管理器容器提取的文件;
cat >volume-emptyDir.yml<<eof apiVersion: v1 kind: Pod metadata: name: volume-test-pod spec: containers: - image: hub.atguigu.com/library/nginx:latest name: volume-test-container volumeMounts: - mountPath: /cache name: cache-volume - image: hub.atguigu.com/library/busybox:latest name: liveness-exec-container imagePullPolicy: IfNotPresent command: ["/bin/sh","-c","touch /tmp/live;sleep 6000s"] volumeMounts: - mountPath: /test name: cache-volume volumes: - name: cache-volume emptyDir: {} eof kubectl apply -f volume-emptyDir.yml kubectl get pod kubectl exec volume-test-pod -c volume-test-container -it -- touch /cache/test.txt kubectl exec volume-test-pod -c volume-test-container -it -- ls /cache kubectl exec volume-test-pod -c liveness-exec-container -it -- ls /test
2、hostPath介绍
hostPath卷:将主机节点的文件系统中文件或目录挂载到集群中;用途如下:
运行需要访问docker内部的容器;使用/var/lib/docker的hostPath;
在容器中运行cAdvisor(K8S中一个用于监控docker的组件);使用/dev/cgroups的hostPath;
允许pod指定给定的hostPath是否应该在pod运行之前存在,是否应该创建,以及它应该以什么形式存在;
hostPath卷属性有path,type;
type: #type默认值为空,用于向后兼容,意味着在挂载hostPath卷之前不会执行任何检查。
type:DirectoryOrCreate #如果在给定的路径上没有任何东西存在,则根据需要创建一个空目录,权限为0755,与kubelet具有相同的组和所有权;
type:Directory #给定的路径上必须存在目录;
type:FileOrCreate #如果在给定的路径上没有任何东西存在,则根据需要创建一个空文件,权限为0644,与kubelet具有相同的组和所有权;
type:File #给定的路径上必须存在文件;
type:Socket #给定的路径上必须存在UNIX套接字;
type:CharDevice #给定的路径上必须存在字符设备;
type:BlockDevice #给定的路径上必须存在块设备;
使用hostPath卷注意事项:
由于每个节点上的文件都不同,具有相同配置的pod在不同节点上的行为可能会有所不同;
当K8S按照计划添加资源感知调度时,将无法考虑hostPath使用的资源;
在底层主机上创建的文件或目录只能由root写入。您需要在特权容器中以root身份运行进程,或修改主机上文件权限以便写入hostPath卷。
cat >volume-hostPath.yml<<eof apiVersion: v1 kind: Pod metadata: name: hostpath-volume-pod spec: containers: - image: hub.atguigu.com/library/nginx:latest name: hostPath-volume--container volumeMounts: - mountPath: /cache name: cache-volume volumes: - name: cache-volume hostPath: path: /data type: Directory eof kubectl apply -f volume-hostPath.yml #确保调度节点存在/data/目录; kubectl get pod kubectl exec volume-test-pod -c volume-test-container -it -- touch /cache/test.txt
五、PV及PVC介绍
PV(persistentVolume)
是由管理设置的存储,它是集群的一部分。就像节点是集群中的资源一样,PV也是集群中的资源。PV是volume之类的卷插件,但居于哦独立于使用PV的pod的生命周期。此API对象包含存储实现的细节,接NFS,iSCSI或特定于云供应商的存储系统。
PVC(persistentVolumeClaim)
是用户存储的请求。它与pod相似。pod消耗节点资源,PVC消耗PV资源。pod可以请求特定级别的资源(CPU和内存)。声明可以请求特定的大小和访问模式(如:可以读/写一次或只读多次模式挂载);
静态PV
集群管理员创建一些PV。他们带有可供集群用户使用的实际存储的细节他们存在于K8S API中,可用于消费;
动态PV(实现方式复杂,价格昂贵,不太成熟,但是是未来的趋势)
当管理员创建静态PV都不匹配用户的persistentVolumeClaim时,集群可能会尝试动态地为PVC创建卷。此配置基于StorageClasses:PVC必须请求[存储类],并且管理员必须创建并配置该类才能进行动态创建。声明该类为“”可以有效地金庸其动态配置。
要启用基于存储级别的动态存储配置,集群管理员需要启用API server上的DefaultStorageClass【准入控制器】。例如,通过确保DefaultStorageClass位于API server组件的--admission-control标志,使用逗号分隔的有序值列表中,可以完成此操作。
绑定
master中的控制环路监视新的PVC,寻找匹配的PV(如果可能),并将它们绑定在一起。如果为新的PVC动态调配PV,则该环路将始终将PV绑定到PVC。否则,用户总会得到他们所请求的存储,但是容量可能超出要求的数量。一旦PV和PVC绑定后,PersistentVolumeClaimbanging是排他性的,不管它们是如何绑定的。PVC跟PV绑定是一对一的映射。一般情况下,PVC规定容量一般小于PV容量,很少情况会出现近似相等。
持久化卷声明的保护
PVC保护的目的是确保由POD正在使用的PVC不会从系统中移除,因为如果被移除的话可能会导致数据丢失。当启用PVC保护alpha功能时,如果用户删除了一个pod正在使用的PVC,则该PVC不会被立即删除。PVC的删除将被推迟,指导PVC不再被任何pod使用。注意:当pod状态为pending并且pod已经分配给节点或pod为running状态时,PVC处于活动状态。
PV类型以插件形式实现。K8S目前支持以下插件类型:
awsElasticBlockStore;azureDisk;azureFile;FC(fiber channel);
FlexVolume;Flocker;NFS;iSCSI;RBD(ceph block device);cephFS;
cinder(openstack block storage);glusterfs;vshpereVolume;quobyte volumes;
HostPath;VMware Photon;portworx Volumes;scaleIO volumes;storageOS;
PV访问模式:
persistentVolume可以资源提供者支持的任何方式挂载到主机上。供应商具有不同的功能,每个PV的访问模式都将被设置为怪卷支持的特定模式;如:NFS可支持多个读/写客户端,但特定的NFS PV可能以只读方式导出到服务器。每 个PV都有一套自己的用来描述特定功能的访问模式。一个卷只能使用一种访问模式挂载,即使它支持很多访问模式。
ReadWriteOnce(RWO)---该卷可以被单个节点以读/写模式挂载;
ReadOnlyMany(ROX)---该卷可以被多个节点以只读模式挂载;
ReadWriteMany(RWX)---该卷可以被多个节点以读/写模式挂载;
回收策略:
retain(保留)---手动回收;PV不再被pod使用,但也不允许其他pod使用,等待管理员去手动释放数据。
recycle(回收)---基本擦除(rm -rf /thevolume/*);最新版K8S不支持。
delete(删除)---关联的存储资产(如AWS EBS|GCE PD|AZURE DISK|OPENSTACK CINDER)将被删除;
当前只有NFS和hostPath支持回收策略。AWS EBS|GCE PD|AZURE DISK|OPENSTACK CINDER支持删除策略。
卷可以处于以下某种状态:
available(可用)---一块空闲资源还没有被任何声明绑定;
bound(已绑定)---卷已经被声明绑定;
released(已释放)---声明被删除,但是资源还未被集群重新声明。
failed(失败)---该卷的自动回收失败。
命令行会显示绑定到PV的PVC的名称。
创建statefuSet前必须要创建要给SVC;
##安装NFS服务器(选定K8S集群之外的主机部署,IP为192.168.66.100) yum install -y nfs-common nfs-utils rpcbind mkdir /nfsdata chmod 777 /nfsdata #慎用权限; chown nfsnobody /nfsdata cat >>/etc/exports<<eof /nfsdata *(rw,no_root_squash,no_all_squash,sync) #创建一个nfs文件系统; eof systemctl enable rpcbind;systemctl start rpcbind;systemctl status rpcbind systemctl enable nfs;systemctl start nfs;systemctl status nfs mkdir /nfs{1..3} #在nfs服务器创建3个空目录; chmod 777 /nfs{1..3} chown nfsnobody /nfs{1..3} cat >>/etc/exports<<eof #在创建3个nfs文件系统; /nfs1 *(rw,no_root_squash,no_all_squash,sync) /nfs2 *(rw,no_root_squash,no_all_squash,sync) /nfs3 *(rw,no_root_squash,no_all_squash,sync) eof systemctl restart rpcbind nfs ##所有K8S集群节点安装工具 yum install -y nfs-utils rpcbind mkdir /nfs_test/ showmount -e 192.168.66.100 mount -t nfs 192.168.66.100:/nfsdata /nfs_test/ #将nfs服务器192.168.66.100的/nfsdata挂载至本机/nfs_test目录下; cd /nfs_test/ echo "nfs_test" >test.txt #在nfs服务器192.168.66.100的/nfsdata与本机/nfs_test目录下都可看见创建的test.txt; umount /nfs_test/ #卸载挂载的nfs服务器192.168.66.100的/nfsdata; ##部署PV cat >PV.yml<<eof apiVersion: v1 kind: PersistentVolume metadata: name: nfspv1 spec: capacity: storage: 10Gi #声明卷的容量; accessModes: - ReadWriteOnce #声明访问方式; persistentVolumeReclaimPolicy: Retain #声明回收策略; storageClassName: nfs #指定要绑定PV的类;非常重要的一个指标; nfs: path: /nfsdata server: 192.168.66.100 --- apiVersion: v1 kind: PersistentVolume metadata: name: nfspv01 spec: capacity: storage: 5Gi #声明卷的容量; accessModes: - ReadWriteMany #声明访问方式; persistentVolumeReclaimPolicy: Retain #声明回收策略; storageClassName: slow #指定要绑定PV的类;非常重要的一个指标; nfs: path: /nfs1 server: 192.168.66.100 --- apiVersion: v1 kind: PersistentVolume metadata: name: nfspv02 spec: capacity: storage: 5Gi #声明卷的容量; accessModes: - ReadWriteOnce #声明访问方式; persistentVolumeReclaimPolicy: Retain #声明回收策略; storageClassName: nfs #指定要绑定PV的类;非常重要的一个指标; nfs: path: /nfs2 server: 192.168.66.100 --- apiVersion: v1 kind: PersistentVolume metadata: name: nfspv03 spec: capacity: storage: 1Gi #声明卷的容量; accessModes: - ReadWriteOnce #声明访问方式; persistentVolumeReclaimPolicy: Retain #声明回收策略; storageClassName: nfs #指定要绑定PV的类;非常重要的一个指标; nfs: path: /nfs3 server: 192.168.66.100 eof kubectl apply -f PV.yml kubectl get pv #此时pv可以直接挂载至pod使用了;但正常情况下,使用PVC来调用PV; ##创建服务并使用PVC cat >PVC.yml<<eof apiVersion: v1 kind: Service metadata: name: nginx labels: app: nginx spec: ports: - port: 80 name: web clusterIP: None selector: app: nginx --- apiVersion: apps/v1 kind: StatefulSet #创建statefuSet前必须要创建要给SVC; metadata: name: web spec: selector: matchLabels: app: nginx serviceName: "nginx" #必须为无头服务(即该服务必须为clusterIP: None)才行; replicas: 3 template: metadata: labels: app: nginx spec: containers: - name: nginx image: hub.atguigu.com/library/nginx:latest ports: - containerPort: 80 name: web volumeMounts: - name: www mountPath: /usr/share/nginx/html volumeClaimTemplates: #声明卷的请求; - metadata: name: www spec: accessModes: ["ReadWriteOnce"] storageClassName: "nfs" resources: requests: storage: 1Gi eof kubectl apply -f PVC.yml kubectl get pv kubectl get pvc kubectl get pod kubectl get statefulset
###测试NFS卷的使用 ##在NFS服务器操作 cd /nfs3 #根据实际pv确定目录; echo "hello world" >index.html chmod 777 index.html chown nfsnobody index.html ##在K8S master操作 curl 10.244.2.157 #该IP为名为web-0的pod调度节点的IP; kubectl delete pod web-0 kubectl get pod -o wide curl 10.244.2.159 #访问新pod,其pv内容不会变化。
1、PV手动回收
删除statefulset、pod、pvc后,需要手动释放pv时,需要至NFS服务器删除对应目录下的文件及pv下的claimRef字段内容。