Kubernetes中的存储方式

一、存储方式

  • ConfigMap
  • Secret
  • Volume
  • Persistent Volume

1.1 ConfigMap

ConfigMap功能在Kubernetes1.2版本中引入,许多应用程序会从配置文件命令行参数环境变量中读取配置信息。ConfigMap API给我们提供了向容器中注入配置信息的机制,ConfigMap可以被用来保存单个属性,也可以用来保存整个配置文件或者JSON二进制大对象

1.1.1 ConfigMap的创建

1、使用目录创建

$ ls docs/user-guide/configmap/kubectl/
game.properties
ui.properties

$ cat docs/user-guide/configmap/kubectl/game.properties
$ cat docs/user-guide/configmap/kubectl/ui.properties
// 创建configmap
$ kubectl create configmap game-config --from-file=docs/user-guide/configmap/kubectl

其中,–from-file 指定在目录下的所有文件都会被用在ConfigMap里面创建一个键值对,键的名字就是文件名,值就是文件的内容

2、使用文件创建
只要指定为一个文件就可以从单个文件中创建ConfigMap

// 与上面的区别是一个使用的是目录,一个使用的是文件
$ kubectl create configmap game-config-2 --from-file=docs/user-guide/configmap/kubectl/game.properties
$ kubectl get configmap game-config-2 -o yaml

其中,–from-file 这个参数可以使用多次,你可以使用两次分别指定上个实例中的那两个配置文件,效果就跟指定整个目录是一样的

3、使用字面值创建
使用文字值创建,利用 –from-literal 参数传递配置信息,该参数可以使用多次,格式如下:

$ kubectl create configmap special-config --from-literal=special.how=very --from-literal=special.type=charm
$ kubectl get configmaps special-config -o yaml

1.1.2 Pod中使用ConfigMap

1、使用ConfigMap来替代环境变量

apiVersion:v1
kind:ConfigMap
metadata:
  name:special-config
  namespace:default
data:
  special.how:very
  special.type:charm
apiVersion:v1
kind:ConfigMap
metadata:
  name:env-config
  namespace:default
data:
  log.level:INFO
apiVersion:v1
kind:Pod
metadata:
  name:dapi-test-pod
spec:
  containers:
  - name:test-container
    image: www.baidu.com/library/myapp:v1
    # 打印环境变量
    command: ["/bin/sh", "-c", "env"]
    # 以下为导入special ConfigMap
    env:
    - name:SPECIAL_LEVEL_KEY
      valueFrom:
        configMapKeyRef:
          name:special-config
          key:special.how
    - name:SPECIAL_TYPE_KEY
      valueFrom:
        configMapKeyRef:
          name:special-config
          key:special.type
    # 以下为导入env ConfigMap
    envFrom:
      - configMapRef:
          name:env-config
restartPolicy:Never

2、使用ConfigMap设置命令行参数

apiVersion:v1
kind:ConfigMap
metadata:
  name:special-config
  namespace:default
data:
  special.how:very
  special.type:charm
apiVersion:v1
kind:Pod
metadata:
  name:dapi-test-pod
spec:
  containers:
  - name:test-container
    image: www.baidu.com/library/myapp:v1
    # 打印环境变量
    command: ["/bin/sh", "-c", "echo ${SPECIAL_LEVEL_KEY} ${SPECIAL_TYPE_KEY}"]
    # 以下为导入special ConfigMap
    env:
    - name:SPECIAL_LEVEL_KEY
      valueFrom:
        configMapKeyRef:
          name:special-config
          key:special.how
    - name:SPECIAL_TYPE_KEY
      valueFrom:
        configMapKeyRef:
          name:special-config
          key:special.type
restartPolicy:Never

3、通过数据卷插件使用ConfigMap

apiVersion:v1
kind:ConfigMap
metadata:
  name:special-config
  namespace:default
data:
  special.how:very
  special.type:charm

在数据卷里面使用这个ConfigMap,有不同的选项。最基本的就是将文件填入数据卷,在这个文件中,键就是文件名,键值就是文件内容

1.1.3 ConfigMap的热更新

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:www.baidu.com/library/myapp:v1
        ports:
        - containerPort:80
        volumeMounts:
        - name:config-volume
          mountPath:/etc/config
      volumes:
        - name:config-volume
          configMap:
            name:log-config

修改ConfigMap

$ kubectl edit configmap log-config

修改 log_level 的值为DEBUG,等待大概10秒钟时间,再次查看环境变量的值

⚠️注意:ConfigMap如果以 ENV 的方式挂载至容器,修改 ConfigMap 并不会实现热更新

ConfigMap更新后滚动更新Pod

更新ConfigMap目前并不会触发相关Pod的滚动更新,可以通过修改pod annotations的方式强制触发滚动更新

kubectl path deployment my-nginx --patch '{"spec":{"template":{"metadata":{"annotations":{"version/config":"20200101"}}}}}'

在这个例子里,.spec.template.metadata.annotations中添加 version/config,每次通过修改 version/config 来触发滚动更新

⚠️注意:更新ConfigMap后:
(1)使用该ConfigMap挂载的Env不会同步更新
(2)使用该ConfigMap挂载的Volume中的数据需要一段时间(大约10秒)才能同步更新

1.2 Secret

1.2.1 Secret存在的意义

Secret解决了密码、token、密钥等敏感数据的配置问题,而不需要把这些数据暴露到镜像或者Pod Spec中。Secret可以以Volume或者环境变量的方式使用

1.2.2 Secret三种类型

  • Service Account:用来访问 Kubernetes API,由 Kubernetes 自动创建,并且会自动挂载到Pod的 /run/secrets/kubernetes.io/serviceaccount 目录中
  • Opaque:base64编码格式的Secret,用来存储密码、密钥等
  • kubernetes.io/dockerconfigjson:用来存储私有docker registry的认证信息

Service Account

Opaque Secret

1、创建说明
Opaque类型的数据是一个map,要求value是base64编码

// Linux下使用base64命令加密字符串
$ echo -n "admin" | base64
YWRtaW4=
$ echo -n "1f2d1e2e67df" | base64
NWYyZDF1MmU2N2Rm

secrets.yml

apiVersion:v1
kind:Secret
metadata:
  name:mysecret
type:Opaque
data:
  password:NWYyZDF1MmU2N2Rm
  username:YWRtaW4=

2、使用方式
(1)将Secret挂载到Volume中,使用时会自动解密

apiVersion:v1
kind:Pod
metadata:
  labels:
    name:secret-test
  name:secret-test
spec:
   volumes:
     - name:secrets
     secret:
       secretName:mysecret
   containers:
     name:db
     image:www.baidu.com/library/myapp:v1
     volumeMounts:
     - name:secrets
       mountPath:"/etc/secrets"
       readOnly:true

(2)将Secret导出到环境变量中

apiVersion:extensions/v1beta1
kind:Deployment
metadata:
  name:pod-deployment
spec:
  replicas:2
  template:
    metadata:
      labels:
        app:pod-deployment
    spec:
      containers:
      - name:pod-1
        image:www.baidu.com/library/myapp:v1
        ports:
        - containerPort:80
		env:
		- name:TEST_USER
		  valueFrom:
		    secretKeyRef:
		      name:mysecret
		      key:username
		- name:TEST_PASSWORD
		    valueFrom:
		      secretKeyRef:
		        name:mysecret
		        key:password

kubernetes.io/dockerconfigjson

1、使用 kubectl 创建 docker registry 认证的secret

$ kubectl create secret docker-registry myregistrykey --docker-server=DOCKER_REGISTRY_SERVER --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_EMAIL

2、创建一个Pod

apiVersion:v1
kind:Pod
metadata:
  name:foo
spec:
  containers:
    name:foo
    image:www.baidu.com/library/myapp:v1
  imagePullSecrets:
    - name:myregistrykey

1.3 Volume

1.3.1 产生背景

容器磁盘上的文件的生命周期是短暂的,这就使得在容器中运行重要应用时会出现一些问题。首先,当容器崩溃时,kubelet会重启它,但是容器中的文件将会丢失(即容器以初始化的状态重新启动);其次,在Pod中同时运行多个容器时,这些容器之间通常需要共享文件。Kubernetes中的volume抽象就很好的解决了上述问题

1.3.2 Volume简介

Kubernetes中的卷有明确的寿命,与封装它的Pod相同。所以,卷的生命比Pod中的所有容器都长,当这些容器重启时数据会被保留下来。当然,当Pod不存在时,卷也不会存在。也许更重要的是,Kubernetes支持多种类型的卷,Pod可以同时使用任意数量的卷

1.3.3 卷的类型

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

1、emptyDir
当Pod被分配给节点时,首先创建 emptyDir 卷,并且该Pod只有在该节点上运行时,该卷才会存在。如同卷的名字一样,它最初是空的。Pod中的容器可以读取和写入 emptyDir 卷中的相同文件,尽管该卷可以挂载到每个容器中相同或不同路径上。当出于任何原因从节点中删除Pod时,emptyDir中的数据将被永久删除

⚠️注意:容器崩溃不会从节点中移除Pod,即 emptyDir 卷中的数据在容器崩溃时是安全的

emptyDir的用法:

  • 临时空间,例如用于基于磁盘的合并排序
  • 用作长时间计算崩溃恢复时的检查点
  • web服务器容器提供数据时,保存内容管理器容器提取的文件
apiVersion:v1
kind:Pod
metadata:
  name:test-pd
spec:
  containers:
    name:test-container
    image:www.baidu.com/library/myapp:v1
    volumeMounts:
      - mountPath: /cache
        name:cache-volume
  volumes:
  - name:cache-volume
    emptyDir:{}

2、hostPath
hostPath卷将主机节点的文件系统中的文件或者目录挂载到集群中

hostPath的用法:

  • 运行需要访问Docker内部的容器,使用/var/lib/docker的 hostPath
  • 在容器中运行cAdvisor,使用/dev/cgroups的hostPath

除了所需的path属性之外,用户还可以为hostPath卷指定type

  • 空字符串:空字符串(默认)用于向后兼容,这意味着在挂载hostPath卷之前不会执行任何检查
  • DirectoryOrCreate:如果在给定的路径上没有任何东西存在,那么将根据需要在那里创建一个空目录,权限设置为0755,与Kubelet具有相同的组和所有权
  • Directory:给定的路径下必须存在目录
  • FileOrCreate:如果在给定的路径上没有任何东西存在,那么会根据需要创建一个空文件,权限设置为0644,与Kubelet具有相同的组和所有权
  • File:给定的路径下必须存在文件
  • Socket:给定的路径下必须存在UNIX套接字
  • CharDevice:给定的路径下必须存在字符设备
  • BlockDevice:给定的路径下必须存在块设备

使用这种卷类型时需要注意

  • 由于每个节点上的文件都是不同的,具有相同配置的Pod在不同节点上的行为可能会有所不同
  • 当Kubernetes按照计划添加资源感知调度时,将无法考虑 hostPath 使用的资源
  • 在底层主机上创建的文件或目录只能由root写入,需要在特权容器中以root身份运行进程,或修改主机上的文件权限以便写入 hostPath 卷

举例:

apiVersion:v1
kind:Pod
metadata:
  name:test-pd
spec:
  containers:
    - image:www.baidu.com/library/myapp:v1
      name:test-container
      volumeMounts:
      - mountPath: /test-pd
        name:test-volume
  volumes:
  - name:test-volume
    hostPath:
      # 主机本地目录
      path:/data
      type:Directory

1.4 Persistent Volume(PV)

1.4.1 概念

是由管理员设置的存储,它是集群的一部分,就像节点是集群中的资源一样,PV也是集群中的资源。PV是Volume类的卷插件,但是具有独立于使用PV的Pod的生命周期。此API对象包含存储实现的细节,即NFS、iSCSI或特定于云供应商的存储系统

1.4.2 PersistentVolumeClaim(PVC)

是用户存储的请求,它与Pod相似。Pod消耗节点资源,PVC消耗PV资源。Pod可以请求特定级别的资源(CPU和内存)。PVC可以请求特定的大小和访问模式(例如,可以以读/写一次 或 只读多次模式挂载)

1.4.3 静态PV

集群管理员创建一些PV。他们带有可供集群用户使用的实际存储的细节。它们存在于Kubernetes API中,可用于消费

1.4.4 动态

当管理员创建的静态PV都不匹配用户的 PVC 时,集群可能会尝试动态为PVC创建卷。此配置基于 StorageClasses:PVC必须请求【存储类】,并且管理员必须创建并配置该类才能进行动态创建。声明该类为“”可以有效地禁用其动态配置

要启动基于存储级别的动态配置,集群管理员需要启动API Server上的DefaultStorageClass【准入控制器】。例如,通过确保DefaultStorageClass位于API Server组件的 --admission-control标志,使用逗号分隔的有序值列表中,可以完成此操作

1.4.5 绑定

master中的控制环路监视新的PVC,寻找匹配的PV,并将他们绑定在一起。如果为新的PVC动态调配PV,则该环路将始终将该PV绑定到PVC。否则,用户总会得到他们所请求的存储,但是容量可能超出要求的数量。一旦PV和PVC绑定后,PVC绑定是排他性的,不管它们是如何绑定的。PVC和PV绑定是一对一的映射

1.4.6 持久卷声明的保护

PVC保护的目的是确保由Pod正在使用的PV不会从系统中移除,因为如果被移除的话可能会导致数据丢失

⚠️:当Pod状态为Pending,并且Pod已经分配给节点或Pod为Running状态时,PVC处于活动状态

当启动PVC保护alpha功能时,如果用户删除了一个Pod正在使用的PVC,则该PVC不会被立即删除。PVC的删除将被推迟,直到PVC不再被任何Pod使用

1.4.7 持久化卷类型

PV类型以插件形式实现。Kubernetes目前支持以下插件类型

  • GCEPersistentDisk AWSElasticBlockStore AzureFile AzureDisk FC(Fibre Channel)FlexVolume Flocker NFS iSCSI RBD CephFS等等

1.4.8 PV访问模式

PV可以以资源提供者支持的任何方式挂载到主机上。供应商具有不同的功能,每个PV的访问模式都将被设置为该卷支持的特定模式。例如,NFS可以支持多个读/写客户端,但特定的NFS PV可能以只读方式导出到服务器上,每个PV都有一套自己的用来描述特定功能的访问模式

  • ReadWriteOnce(RWO) ----该卷可以被单个节点以读/写模式挂载
  • ReadOnlyMany(ROX) ----该卷可以被多个节点以只读模式挂载
  • ReadWriteMany(RWX) ----该卷可以被多个节点以读/写模式挂载

⚠️:一个卷一次只能使用一种访问模式挂载,即使它支持很多访问模式。例如,GCEPersistentDisk可以由单个节点作为 ReadWriteOnce 模式挂载,或由多个节点以 ReadOnlyMany 模式挂载,但不能同时挂载

1.4.9 回收策略

  • Retain(保留):手动回收
  • Recycle(回收):基本删除(rm -rf /thevolume/*)
  • Delete(删除):关联的存储资产(例如:AWS EBS、GCE PD)将被删除
    当前,只有NFS和HostPath支持回收策略,AWS EBS、GCE PD、Azure Disk和Cinder卷支持删除策略

1.4.10 状态

卷可以处于以下的某种状态

  • Available(可用)---- 一块空闲资源还没有被任何声明绑定
  • Bound(已绑定)---- 卷已经被声明绑定
  • Released(已释放)---- 声明被删除,但是资源还未被集群重新声明
  • Failed(失效)---- 该卷的自动回收失败

1.4.11 关于StatefulSet

  • 匹配Pod name(网络标识)的模式为: ( s t a t e f u l 名 称 ) − (stateful名称)- (stateful)(序号),比如上面的示例:web-0,web-1,web-2
  • StatefulSet 为每个Pod副本创建了一个DNS域名,这个域名的格式为:$(podname).(headless server name),也就是意味着服务间是通过Pod域名来通信而非Pod IP,因为当Pod所在Node发生故障时,Pod会被转移到其他Node上,Pod IP会发生变化,但Pod域名不会有变化
  • StatefulSet使用Headless服务来控制Pod的域名,这个域名的FQDN为:(servicename).(namespace).svc.cluster.local。其中,cluster.local指的是集群的域名
  • 根据volumeClaimTemplates,为每个Pod创建一个PVC,PVC的命名规则匹配模式:(volumeClaimTemplates.name)-(pod_name),比如上面的volumeMounts.name=www,Pod name=web-0,创建出来的PVC是www.web-0
  • 删除Pod不会删除其PVC,手动删除PVC将自动释放PV

猜你喜欢

转载自blog.csdn.net/sinat_34241861/article/details/113309128