K8S 存储
ConfigMap
Secret
volume
Persistent Volume(PV)
ConfigMap
ConfigMap 功能在 Kubernetes1.2 版本中引入,许多应用程序会从配置文件、命令行参数或环境变量中读取配置信息。ConfigMap API 给我们提供了向容器中注入配置信息的机制,ConfigMap 可以被用来保存单个属性,也可以用来保存整个配置文件或者 JSON 二进制大对象。
ConfigMap 的创建
使用目录创建
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 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 $ ll /etc/kubernetes/configmap-dir/ game.properties ui.properties $ cat /etc/kubernetes/configmap-dir/game.properties enemies=aliens lives=3 enemies.cheat=true enemies.cheat.level=noGoodRotten secret.code.passphrase=UUDDLRLRBABAS secret.code.allowed=true secret.code.lives=30 $ cat /etc/kubernetes/configmap-dir/ui.properties color.good=purple color.bad=yellow allow.textmode=true how.nice.to.look=fairlyNice $ kubectl create configmap game-config-1 --from-file=/etc/kubernetes/configmap-dir $ kubectl get configmap -n default -owide NAME DATA AGE game-config-1 2 <invalid> $ kubectl get configmap -n default -o yaml apiVersion: v1 items: - apiVersion: v1 data: game.properties: | enemies=aliens lives=3 enemies.cheat=true enemies.cheat.level=noGoodRotten secret.code.passphrase=UUDDLRLRBABAS secret.code.allowed=true secret.code.lives=30 ui.properties: | color.good=purple color.bad=yellow allow.textmode=true how.nice.to.look=fairlyNice kind: ConfigMap metadata: creationTimestamp: "2021-03-08T11:54:57Z" name: game-config-1 namespace: default resourceVersion: "15839" selfLink: /api/v1/namespaces/default/configmaps/game-config-1 uid: cb84c4f5-e509-4ecb-8513-68184b8b57af kind: List metadata: resourceVersion: "" selfLink: "" $ kubectl describe configmap game-config-1 -n default Name: game-config-1 Namespace: default Labels: <none> Annotations: <none> Data ==== ui.properties: ---- color.good=purple color.bad=yellow allow.textmode=true how.nice.to.look=fairlyNice game.properties: ---- enemies=aliens lives=3 enemies.cheat=true enemies.cheat.level=noGoodRotten secret.code.passphrase=UUDDLRLRBABAS secret.code.allowed=true secret.code.lives=30 Events: <none>
使用文件创建
只要指定为一个文件就可以从单个文件中创建 ConfigMap
1 2 3 $ kubectl create configmap game-config-2 --from-file=/etc/kubernetes/configmap-dir/game.properties $ kubectl get configmaps game-config-2 -o yaml
使用字面值创建
使用文字值创建,利用–from-literal参数传递配置信息,该参数可以使用多次
1 2 3 4 5 6 7 8 9 10 11 12 13 14 $ kubectl create configmap game-config-3 --from-literal=k1.how=v1 --from-literal=k2.how=v2 $ kubectl get configmaps game-config-3 -o yaml apiVersion: v1 data: k1.how: v1 k2.how: v2 kind: ConfigMap metadata: creationTimestamp: "2021-03-08T12:01:19Z" name: game-config-3 namespace: default resourceVersion: "16388" selfLink: /api/v1/namespaces/default/configmaps/game-config-3 uid: b9e048f3-a17d-4517-bfe5-311c51377aaa
Pod 中使用 ConfigMap 使用 ConfigMap 来替代环境变量
env: 指定导入k/v
envFrom: 全部导入
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 apiVersion: v1 kind: ConfigMap metadata: name: special-config namespace: default data: special.how: very special.sam: charm special.lep: value --- apiVersion: v1 kind: ConfigMap metadata: name: env-config namespace: default data: log_level: INFO log_level: DEBUG --- apiVersion: v1 kind: Pod metadata: name: cm-test-pod-01 spec: containers: - name: test-container image: arminto/my_nginx:v1 command: [ "/bin/sh" , "-c" , "env" ] env: - name: SPECIAL_HOW_KEY valueFrom: configMapKeyRef: name: special-config key: special.how - name: SPECIAL_SAM_KEY valueFrom: configMapKeyRef: name: special-config key: special.sam envFrom: - configMapRef: name: env-config restartPolicy: Never
1 2 3 4 $ kubectl apply -f cm-test-01.yaml $ kubectl logs cm-test-pod-01 | grep SPECIAL SPECIAL_HOW_KEY=very SPECIAL_SAM_KEY=charm
用 ConfigMap 设置命令行参数
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 apiVersion: v1 kind: ConfigMap metadata: name: config-01 namespace: default data: a: b c: d --- apiVersion: v1 kind: Pod metadata: name: cm-test-pod-02 spec: containers: - name: test-container image: arminto/my_nginx:v1 command: ["/bin/sh" ,"-c" ,"echo $(KEY-01) $(KEY-02)" ] env: - name: KEY-01 valueFrom: configMapKeyRef: name: config-01 key: a - name: KEY-02 valueFrom: configMapKeyRef: name: config-01 key: c restartPolicy: Never
1 2 $ kubectl logs cm-test-pod-02 b d
通过数据卷插件使用ConfigMap
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 apiVersion: v1 kind: ConfigMap metadata: name: config-02 namespace: default data: how: HOW sam: SAM --- apiVersion: v1 kind: Pod metadata: name: test-pod-02 spec: containers: - name: test-container image: arminto/my_nginx:v1 command: ["/bin/sh" ,"-c" ,"sleep 36000" ] volumeMounts: - name: config-volume mountPath: /etc/config volumes: - name: config-volume configMap: name: config-02 restartPolicy: Never
1 2 3 4 5 6 7 8 9 10 $ kubectl exec -it test-pod-02 -- ls -lh /etc/config total 0 lrwxrwxrwx 1 root root 10 Mar 9 08:44 how -> ..data/how lrwxrwxrwx 1 root root 10 Mar 9 08:44 sam -> ..data/sam $ kubectl exec -it test-pod-02 -- cat /etc/config/how HOW $ kubectl exec -it test-pod-02 -- cat /etc/config/sam SAM
ConfigMap 的热更新 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 apiVersion: v1 kind: ConfigMap metadata: name: log-config namespace: default data: log_level: INFO --- apiVersion: extensions/v1beta1 kind: Deployment metadata: name: my-nginx spec: replicas: 1 template: metadata: labels: run: my-nginx spec: containers: - name: my-nginx image: arminto/my_nginx:v1 ports: - containerPort: 80 volumeMounts: - name: config-volume mountPath: /etc/config volumes: - name: config-volume configMap: name: log-config
1 2 $ kubectl exec -it my-nginx-7b6584d96d-hpbml -- cat /etc/config/log_level INFO
修改 ConfigMap,将其INFO改为DEBUG
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 $ kubectl edit configmap log-config apiVersion: v1 data: log_level: DEBUG kind: ConfigMap metadata: annotations: kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion" :"v1" ,"data" :{"log_level" :"DEBUG" },"kind" :"ConfigMap" ,"metadata" :{"annotations" :{},"name" :"log-config" ,"namespace" :"default" }} creationTimestamp: "2021-03-09T09:04:08Z" name: log-config namespace: default resourceVersion: "36210" selfLink: /api/v1/namespaces/default/configmaps/log-config uid: 8e2f15fc-cc05-414b-ad2f-9597cacf7c9a
修改log_level的值为DEBUG等待大概 10 秒钟时间,再次查看环境变量的值
1 2 $ kubectl exec -it my-nginx-7b6584d96d-hpbml -- cat /etc/config/log_level DEBUG
Secret
Secret 解决了密码、token、密钥等敏感数据的配置问题,而不需要把这些敏感数据暴露到镜像或者 Pod Spec中。
并且,Secret 可以以 Volume 或者 环境变量的方式使用。
Secret 有三种类型
Service Account
Opaque
kubernetes.io/dockerconfigjson
Service Account
大致了解一下即可,Service Account 用来访问 Kubernetes API,由 Kubernetes 自动创建,并且会自动挂载到 Pod的/run/secrets/kubernetes.io/serviceaccount目录中。
PS : 只有与 apiserver 组件进行交互的 pod 才会有如下的 证书、命名空间、tekon密钥。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 $ kubectl exec -it kube-proxy-2x7r8 -n kube-system -- ls -lh /run/secrets/kubernetes.io/serviceaccount total 0 lrwxrwxrwx 1 root root 13 Mar 10 02:50 ca.crt -> ..data/ca.crt lrwxrwxrwx 1 root root 16 Mar 10 02:50 namespace -> ..data/namespace lrwxrwxrwx 1 root root 12 Mar 10 02:50 token -> ..data/token $ kubectl get secret NAME TYPE DATA AGE default-token-c6vqw kubernetes.io/service-account-token 3 46d $ kubectl describe secret default-token-c6vqw ………………………………………………………………………… Data ==== ca.crt: 1025 bytes namespace: 7 bytes token: eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImRlZmF1bHQtdG9rZW4tYzZ2cXciLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGVmYXVsdCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjJjNjlmMjkzLTcwNTQtNDY4My05ODNmLTljYmQyNTBlZTQ1ZiIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OmRlZmF1bHQifQ.WdQ32P2ndcKUNbyrsKouTgBEVQf8Smq1mSd9zRpBou1KN2DUJ6UPWHvMdDlqwvN2CACOWZcri0eN8NABvNEW6cQMv7O7-GoVsuv5HLDxmO1IC1tTVUr8d-uBxULiSiQZ_Mj2LK1QKYNAHzVYRlEozzI3wZAOWBDqA7ZFVpXRQMES0lzlRc9ETZqQmxIHgF_hyTzpsRaLkR480vppOTORejZGrm_wiSvYVKKvNe3H35TRtHARCZDlGv7DAickld2rnYuKSyLdZz29CmDiE6L0ZyzTCMgPalNkmjcuij28XG19I4GLio7IiS5ZMDrQsmr_4t3G449PPSiH_0C78LICuA
Opaque Secret
base64编码格式的Secret,用来存储密码、密钥等。
Opaque 类型的数据是一个 map 类型,要求 value 是 base64 编码格式。
1、创建 Opaque-Secret
1 2 3 4 5 6 $ echo -n "admin" | base64 YWRtaW4= $ echo -n "1f2d1e2e67df" | base64 MWYyZDFlMmU2N2Rm
1 2 3 4 5 6 7 8 9 apiVersion: v1 kind: Secret metadata: name: op-secret type: Opaque data: username: YWRtaW4= password: MWYyZDFlMmU2N2Rm
1 2 3 4 5 6 7 8 9 10 11 $ kubectl get secret NAME TYPE DATA AGE default-token-c6vqw kubernetes.io/service-account-token 3 46d op-secret Opaque 2 11s $ kubectl describe secret op-secret ………………………………………………………………………… Data ==== password: 12 bytes username: 5 bytes
2、使用方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 apiVersion: v1 kind: Pod metadata: labels: name: seret-test name: seret-test spec: volumes: - name: secrets secret: secretName: op-secret containers: - image: arminto/my_nginx:v1 name: db volumeMounts: - name: secrets mountPath: '/etc/secrets' readOnly: true
1 2 3 4 5 6 7 8 9 10 11 $ kubectl exec -it seret-test -- ls -l /etc/secrets total 0 lrwxrwxrwx 1 root root 15 Mar 10 03:25 password -> ..data/password lrwxrwxrwx 1 root root 15 Mar 10 03:25 username -> ..data/username $ kubectl exec -it seret-test -- cat /etc/secrets/password 1f2d1e2e67df $ kubectl exec -it seret-test -- cat /etc/secrets/username admin
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 apiVersion: extensions/v1beta1 kind: Deployment metadata: name: pod-deployment spec: replicas: 2 template: metadata: labels: app: pod-deployment spec: containers: - name: pod-1 image: arminto/my_nginx:v1 ports: - containerPort: 80 env: - name: TEST_USER valueFrom: secretKeyRef: name: op-secret key: username - name: TEST_PASSWORD valueFrom: secretKeyRef: name: op-secret key: password
1 2 3 4 5 $ kubectl exec -it pod-deployment-7cfd69dcf7-222q2 -- /bin/sh / admin / 1f2d1e2e67df
dockerconfigjson
用来存储私有 docker registry 的认证信息。
1、使用 Kuberctl 创建 docker registry 认证的 secret(注:针对私有仓库)
1 2 3 4 5 $ kubectl create secret docker-registry myregistrykey --docker-server=DOCKER_REGISTRY_SERVER --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD
2、在创建 Pod 的时候,通过 imagePullSecrets 来引用刚创建的 myregistrykey
1 2 3 4 5 6 7 8 9 10 11 apiVersion: v1 kind: Pod metadata: name: foo spec: containers: - name: foo image: hub.lemon.com/library/my_nginx:v1 imagePullSecrets: - name: myregistrykey
Volume
容器磁盘上的文件的生命周期是短暂的,这就使得在容器中运行重要应用时会出现一些问题。首先,当容器崩溃时,kubelet 会重启它,但是容器中的文件将丢失——容器以干净的状态(镜像最初的状态)重新启动。其次,在Pod中同时运行多个容器时,这些容器之间通常需要共享文件。Kubernetes 中的Volume抽象就很好的解决了这些问题。
Kubernetes 中的卷有明确的寿命 —— 与封装它的 Pod 相同。所f以,卷的生命比 Pod 中的所有容器都长,当这个容器重启时数据仍然得以保存。当然,当 Pod 不再存在时,卷也将不复存在。也许更重要的是,Kubernetes支持多种类型的卷,Pod 可以同时使用任意数量的卷。
Kubernetes 支持以下类型的卷:
awsElasticBlockStore、azureDisk、azureFile、cephfs、csi、downwardAPI、emptyDir
fc、flocker、gcePersistentDisk、gitRepo、glusterfs、hostPath、iscsi、local、nfs
persistentVolumeClaim、projected、portworxVolume、quobyte、rbd、scaleIO、secret
storageos、vsphereVolume
比较常用的两类 emptyDir、hostPath
emptyDir
当 Pod 被分配给节点时,首先创建emptyDir卷,并且只要该 Pod 在该节点上运行,该卷就会存在。正如卷的名字所述,它最初是空的。Pod 中的容器可以读取和写入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 apiVersion: v1 kind: Pod metadata: name: test-pd spec: containers: - name: test-container01 image: arminto/my_nginx:v1 volumeMounts: - mountPath: /cache01 name: cache-volume - name: test-container02 image: tomcat:latest volumeMounts: - mountPath: /cache02 name: cache-volume volumes: - name: cache-volume emptyDir: {}
1 2 3 4 5 6 7 8 9 10 11 12 $ kubectl exec -it test-pd -c test-container01 -- /bin/sh / /cache01 /cache01 test-pd $ kubectl exec -it test-pd -c test-container02 -- /bin/sh test.log test-pd
hostPath
hostPath卷将主机节点的文件系统中的文件或目录挂载到集群中。
hostPath 的用途如下:
运行需要访问 Docker 内部的容器;使用 /var/lib/docker 的 hostPath
在容器中运行 cAdvisor;使用 /dev/cgroups 的 hostPath
允许 pod 指定给定的 hostPath 是否应该在 pod 运行之前存在,是否应该创建,以及它应该以什么形式存在
除了所需的path属性之外,用户还可以为hostPath卷指定type
值
行为
空字符串(默认)用于向后兼容,这意味着在挂载 hostPath 卷之前不会执行任何检查。
DirectoryOrCreate
如果在给定的路径上没有任何东西存在,那么将根据需要在那里创建一个空目录,权限设置为 0755,与 Kubelet 具有相同的组和所有权。
Directory
给定的路径下必须存在目录。
FileOrCreate
如果在给定的路径上没有任何东西存在,那么会根据需要创建一个空文件,权限设置为 0644,与 Kubelet 具有相同的组和所有权。
File
给定的路径下必须存在文件。
Socket
给定的路径下必须存在 UNIX 套接字。
CharDevice
给定的路径下必须存在字符设备。
BlockDevice
给定的路径下必须存在块设备。
使用这种卷类型是请注意,因为:
由于每个节点上的文件都不同,具有相同配置(例如从 podTemplate 创建的)的 pod 在不同节点上的行为可能会有所不同。
当 Kubernetes 按照计划添加资源感知调度时,将无法考虑hostPath使用的资源。
在底层主机上创建的文件或目录只能由 root 写入。您需要在特权容器中以 root 身份运行进程,或修改主机上的文件权限以便写入hostPath卷。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 apiVersion: v1 kind: Pod metadata: name: h-pod spec: containers: - image: arminto/my_nginx:v1 name: test-container volumeMounts: - mountPath: /hv name: test-volume volumes: - name: test-volume hostPath: path: /data type: Directory
1 2 3 4 5 6 7 8 9 10 $ kubectl get pod -owide NAME READY STATUS RESTARTS AGE IP NODE h-pod 0/1 ContainerCreating 0 7s <none> k8s-node-192.168.2.22 $ hostname k8s-node-192.168.2.22 $ echo 'hv test' > /data/h-test.log $ kubectl exec -it h-pod -- cat /hv/h-test.log hv test
Persistent Volume(PV)
PersistentVolume(简称PV) 是 Volume 之类的卷插件,也是集群中的资源,但独立于Pod的生命周期(即不会因Pod删除而被删除),不归属于某个Namespace。
PersistentVolumeClaim(简称PVC)是用户存储的请求,PVC消耗PV的资源,可以请求特定的大小和访问模式,需要指定归属于某个Namespace,在同一个Namespace 的 Pod才可以指定对应的PVC。
PV (持久化卷),是对底层的共享存储的一种抽象,PV 由管理员进行创建和配置, 它和具体的底层的共享存储技术的实现方式有关,比如Ceph、GlusterFS、NFS等,都是通过插件机制完成与共享存储的对接。
PVC (持久化卷声明),PVC 是用户存储的一种声明,PVC 和 Pod 比较类型,Pod 是消耗节点,PVC 消耗的是 PV 资源,Pod 可以请求 CPU 的内存,而 PVC 可以请求特定的存储空间和访问模式。对于真正存储的用户不需要关心底层的存储实现细节,只需要直接使用PVC即可。
但是通过PVC请求一定的存储空间也很有可能不足以满足对于存储设备的各种需求,而且不同的应用程序对于存储性能的要求也能也不尽相同,比如读写速度、并发性能等,为了解决这一问题,Kubernetes又为我们引入了一个新的资源对象: StorageClass,通过StorageClass的定义,管理员可以将存储资源定义为某种类型的资源,比如快速存储、慢速存储等,用户根据StorageClass的描述就可以非常直观的知道各种存储资源特性了,这样就可以根据应用的特性去申请合适的存储资源了。
PV 和 PVC的生命周期
PV 可以看作可用的存储资源,PVC则是对存储资源的需求,PV 和 PVC的互相关系遵循如下图
资源供应 (Provisioning)
Kubernetes支持两种资源的供应模式:静态模式(Staic)和动态模式(Dynamic)。资源供应的结果就是创建好的PV。
静态模式:集群管理员手工创建许多 PV,在定义 PV 时需要将后端存储的特性进行设置。
动态模式:集群管理员无须手工创建 PV,而是通过 StorageClass 的设置对后端存储进行描述,标记为某种 “类型(Class)”。此时要求 PVC 对存储的类型进行声明,系统将自动完成 PV 的创建及 PVC 的绑定。
PVC 可以声明 Class 为””,说明该 PVC 禁止使用动态模式。
1、静态资源下,通过PV和PVC完成绑定,并供Pod使用的存储管理机制
2.动态资源下,通过StorageClass和PVC完成资源动态绑定 (系统自动生成PV,并供Pod使用的存储管理机制)
资源绑定 (Binding) 在用户定义好PVC后,系统将根据PVC对存储资源的请求 (存储空间和访问模式)在已存在的PV中选择一个满足PVC要求的PV,一旦找到,就将该PV与用户定义的PVC进行绑定,然后用户的应用就可以使用这个PVC了。如果系统中没有满足PVC要求的PV,PVC则会无限期处于Pending状态,直到等到系统管理员创建了一个符合要求的PV。PV一旦绑定在某个PVC上,就被这个PVC独占,不能再与其他PVC进行绑定了。在这种情况下,当PVC申请的存储空间比PV的少时,整个PV的空间都能够为PVC所用,可能会造成资源的浪费。如果资源供应使用的是动态模式,则系统在PVC找到合适的StorageClass后,将会自动创建PV并完成PVC的绑定。
资源使用 (Using) Pod 使用volume的定义, 将 PVC 挂载到容器内的某个路径进行使用。volume 的类型为 persistentVoulumeClaim , 在容器应用挂载了一个 PVC 后, 就能被持续独占使用。
不过, 多个Pod可以挂载同一个PVC, 应用程序需要考虑多个实例共同访问一块存储空间的问题。
资源释放 (Releasing) 当用户对存储资源使用哪个完毕后,用户可以删除PVC,与该PVC绑定的PV将会被标记为已释放,但还不能立刻与其他PVC进行绑定。通过之前PVC写入的数据可能还留在存储设备上,只有在清除之后该PV才能继续使用。
资源回收 (Reclaiming) 对于PV,管理员可以设定回收策略(Reclaim Policy)用于设置与之绑定的PVC释放资源之后,对于遗留数据如何处理。只有PV的存储空间完成回收,才能供新的PVC绑定和使用。
持久化卷声明的保护 PVC 保护的目的是确保由 pod 正在使用的 PVC 不会从系统中移除,因为如果被移除的话可能会导致数据丢失,当启用 PVC 保护 alpha 功能时,如果用户删除了一个 pod 正在使用的 PVC,则该 PVC 不会被立即删除。PVC 的删除将被推迟,直到 PVC 不再被任何 pod 使用。
PV 的类型 及 访问模式
PersistentVolume 类型以插件形式实现。以下仅列部分常用类型:
GCEPersistentDisk
AWSElasticBlockStore
NFS
RBD (Ceph Block Device)
CephFS、Glusterfs
PV 的访问模式 有三种 ReadWriteOnce 、 ReadOnlyMany 、 ReadWriteMany
PersistentVolume 可以以资源提供者支持的任何方式挂载到主机上。如下表所示,供应商具有不同的功能,每个PV 的访问模式都将被设置为该卷支持的特定模式。例如,NFS 可以支持多个读/写客户端,但特定的 NFS PV 可能以只读方式导出到服务器上。每个 PV 都有一套自己的用来描述特定功能的访问模式。
ReadWriteOnce——该卷可以被单个节点以读/写模式挂载(命令行缩写:RWO)
ReadOnlyMany——该卷可以被多个节点以只读模式挂载(命令行缩写:ROX)
ReadWriteMany——该卷可以被多个节点以读/写模式挂载(命令行缩写:RWX)
一个卷一次只能使用一种访问模式挂载,即使它支持很多访问模式。以下只列举部分常用插件
Volume 插件
ReadWriteOnce
ReadOnlyMany
ReadWriteMany
AWSElasticBlockStore
✓
-
-
CephFS
✓
✓
✓
GCEPersistentDisk
✓
✓
-
Glusterfs
✓
✓
✓
HostPath
✓
-
-
NFS
✓
✓
✓
RBD
✓
✓
-
PV 的回收策略 及 阶段状态
回收策略包括
Retain(保留)——手动回收
Recycle(回收)——基本擦除( rm -rf /thevolume/* )【ps 在新版本中该策略已被弃用】
Delete(删除)——关联的存储资产(例如 AWS EBS、GCE PD、Azure Disk 和 OpenStack Cinder 卷)将被删除。
当前,只有 NFS 和 HostPath 支持回收策略。AWS EBS、GCE PD、Azure Disk 和 Cinder 卷支持删除策略。
PV 可以处于以下的某种状态
Available(可用)——一块空闲资源还没有被任何声明绑定
Bound(已绑定)——卷已经被声明绑定
Released(已释放)——声明被删除,但是资源还未被集群重新声明
Failed(失败)——该卷的自动回收失败
命令行会显示绑定到 PV 的 PVC 的名称。
PV 的实验演练 - NFS 部署 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 $ yum install -y nfs-common nfs-utils rpcbind $ mkdir -p /nfs01 /nfs02 /nfs03 /nfs04 $ chmod 777 /nfs01 /nfs02 /nfs03 /nfs04 $ chown nfsnobody /nfs01 /nfs02 /nfs03 /nfs04 $ cat /etc/exports /nfs01 *(rw,no_root_squash,no_all_squash,sync) /nfs02 *(rw,no_root_squash,no_all_squash,sync) /nfs03 *(rw,no_root_squash,no_all_squash,sync) /nfs04 *(rw,no_root_squash,no_all_squash,sync) $ systemctl start rpcbind nfs $ showmount -e 192.168.245.44 Export list for 192.168.245.44: /nfs01 * /nfs02 * /nfs03 * /nfs04 * $ mkdir /test $ mount -t nfs 192.168.245.44:/nfs01 /test / $ cd /test / $ touch tset.txt $ echo lemon > test.txt $ cat test.txt lemon $ umount /test $ rm -rf /test
部署 PV 卷 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 apiVersion: v1 kind: PersistentVolume metadata: name: nfspv1 spec: capacity: storage: 1Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Retain storageClassName: nfs nfs: path: /nfs01 server: 192.168 .245 .44 --- apiVersion: v1 kind: PersistentVolume metadata: name: nfspv2 spec: capacity: storage: 10Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Retain storageClassName: nfs nfs: path: /nfs02 server: 192.168 .245 .44 --- apiVersion: v1 kind: PersistentVolume metadata: name: nfspv3 spec: capacity: storage: 20Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Retain storageClassName: nfs nfs: path: /nfs03 server: 192.168 .245 .44 --- apiVersion: v1 kind: PersistentVolume metadata: name: nfspv4 spec: capacity: storage: 30Gi accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Retain storageClassName: static nfs: path: /nfs04 server: 192.168 .245 .44
创建服务并使用 PVC 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 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 metadata: name: web spec: selector: matchLabels: app: nginx serviceName: "nginx" replicas: 3 template: metadata: labels: app: nginx spec: containers: - name: nginx image: arminto/my_nginx:v1 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
1 2 3 4 5 6 7 8 9 10 $ echo 'web-0' > /nfs01/index.html $ echo 'web-1' > /nfs02/index.html $ echo 'web-2' > /nfs03/index.html $ curl 10.244.2.24 web-0 $ curl 10.244.1.26 web-1 $ curl 10.244.1.27 web-2
关于 StatefulSet 的说明
匹配 Pod name ( 网络标识 ) 的模式为:$(statefulset名称)-$(序号),比如上面的示例:web-0,web-1,web-2 。
1 2 3 4 5 6 $ kubectl get pod NAME READY STATUS RESTARTS AGE h-pod 1/1 Running 0 23h web-0 1/1 Running 0 48m web-1 1/1 Running 0 48m web-2 1/1 Running 0 48m
StatefulSet 为每个 Pod 副本创建了一个 DNS 域名,这个域名的格式为: $(podname).(headless servername),也就意味着服务间是通过Pod域名来通信而非 Pod IP,因为当Pod所在Node发生故障时, Pod 会被飘移到其它 Node 上,Pod IP 会发生变化,但是 Pod 域名不会有变化。
1 2 3 4 5 6 7 $ kubectl exec -it h-pod -- /bin/sh / PING web-0.nginx (10.244.2.24): 56 data bytes 64 bytes from 10.244.2.24: seq=0 ttl=64 time=0.137 ms 64 bytes from 10.244.2.24: seq=1 ttl=64 time=0.056 ms 64 bytes from 10.244.2.24: seq=2 ttl=64 time=0.076 ms
StatefulSet 使用 Headless 服务来控制 Pod 的域名,这个域名的 FQDN 为:$(servicename).$(namespace).svc.cluster.local,其中,“cluster.local” 指的是集群的域名。
1 2 3 4 5 6 7 8 9 $ kubectl get pod -owide -n kube-system | grep coredns coredns-5c98db65d4-4v4kd 1/1 Running 13 47d 10.244.1.17 coredns-5c98db65d4-9k5xw 1/1 Running 7 47d 10.244.0.9 $ dig -t A nginx.default.svc.cluster.local. @10.244.0.9 ;; ANSWER SECTION: nginx.default.svc.cluster.local. 11 IN A 10.244.2.24 nginx.default.svc.cluster.local. 11 IN A 10.244.1.26 nginx.default.svc.cluster.local. 11 IN A 10.244.1.27
根据 volumeClaimTemplates,为每个 Pod 创建一个 pvc,pvc 的命名规则匹配模式:(volumeClaimTemplates.name)-(pod_name),比如上面的 volumeMounts.name=www, Podname=web-[0-2],因此创建出来的 PVC 是 www-web-0、www-web-1、www-web-2 。
1 2 3 4 5 $ kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE www-web-0 Bound nfspv1 1Gi RWO nfs 173m www-web-1 Bound nfspv2 10Gi RWO nfs 172m www-web-2 Bound nfspv3 20Gi RWO nfs 172m
删除 Pod 不会删除其 pvc,手动删除 pvc 将自动释放 pv
Statefulset的启停顺序
有序部署:部署StatefulSet时,如果有多个Pod副本,它们会被顺序地创建(从0到N-1)并且,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 $ kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS nfspv1 1Gi RWO Retain Available nfs nfspv2 10Gi RWO Retain Available static nfspv3 20Gi RWO Retain Available slow $ kubectl apply -f pvc.yaml service/nginx created statefulset.apps/web created $ kubectl get pod NAME READY STATUS RESTARTS AGE web-0 1/1 Running 0 19s web-1 0/1 Pending 0 16s
有序删除:当Pod被删除时,它们被终止的顺序是从N-1到0。
1 2 $ kubectl get pod -owide -n default -w $ kubectl delete statefulset web -n default
有序扩展:当对 Pod 执行扩展操作时,与部署一样,它前面的Pod必须都处于Running和Ready状态。
1 $ kubectl scale statefulset/web --replicas=4
StatefulSet使用场景
稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于 PVC 来实现。
稳定的网络标识符,即 Pod 重新调度后其 PodName 和 HostName 不变。
有序部署,有序扩展,基于 init containers 来实现。
有序收缩。
PV卷 的资源释放流程
生产环境下要再三确认后才可以进行如下操作
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 $ kubectl delete -f pvc.yaml $ kubectl delete pvc www-web-0 www-web-1 www-web-2 $ kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS nfspv1 1Gi RWO Retain Released default/www-web-0 nfs nfspv2 10Gi RWO Retain Released default/www-web-1 nfs nfspv3 20Gi RWO Retain Released default/www-web-2 nfs nfspv4 30Gi RWX Retain Available static $ kubectl get pv nfspv1 -o yaml claimRef: apiVersion: v1 kind: PersistentVolumeClaim name: www-web-0 namespace: default resourceVersion: "69260" uid: e175a86b-aa2c-4900-a7c4-faa1865315ab $ kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS nfspv1 1Gi RWO Retain Available nfs nfspv2 10Gi RWO Retain Available nfs nfspv3 20Gi RWO Retain Available nfs nfspv4 30Gi RWX Retain Available static