Kubernetes 存储
Zhongjun Qiu 元婴开发者

本文详细介绍了 Kubernetes 中的核心存储概念,涵盖了从临时数据卷到持久化存储方案,以及如何通过 ConfigMap 和 Secret 管理应用的配置与敏感数据,旨在为数据提供一个可靠的“家”。

存储分类

[cite_start]在 Kubernetes 中,存储根据其用途和特性可以分为两大类:元数据真实数据 [cite: 27, 31][cite_start]。理解它们的特性是高效使用 Kubernetes 存储的基础 [cite: 24]。

  • [cite_start]元数据 (Metadata) [cite: 27]

    • [cite_start]ConfigMap: 用于保存非敏感的配置数据,以明文形式存储 [cite: 28]。
    • [cite_start]Secret: 用于保存敏感数据,如密码、Token、密钥等,数据会进行 Base64 编码 [cite: 29]。
    • [cite_start]Downward API: 允许容器在运行时获取关于自身或其所在环境(如 Pod 名称、命名空间、资源限制等)的信息 [cite: 30]。
  • [cite_start]真实数据 (Real Data) [cite: 31]

    • [cite_start]Volume: Pod 级别的数据卷,生命周期与 Pod 绑定,用于存储临时或持久性数据 [cite: 32]。
    • [cite_start]PersistentVolume (PV): 集群级别的持久化存储资源,独立于 Pod 的生命周期,通过申请制(PVC)供应用使用 [cite: 33]。

ConfigMap

[cite_start]ConfigMap 是 Kubernetes 中用于存储和管理应用配置的核心组件,它将配置信息与容器镜像解耦,使得应用配置更加灵活和易于管理 [cite: 35, 53]。

创建 ConfigMap

可以通过命令行从文件或字面量快速创建 ConfigMap:

1
2
3
4
5
# 从文件创建,文件名会成为 key,文件内容成为 value
kubectl create configmap my-app-config --from-file=app.properties

# 从字面量创建,直接指定 key-value 对
kubectl create configmap db-config --from-literal=db.host=mysql --from-literal=db.port=3306

使用 ConfigMap

ConfigMap 中的数据可以通过多种方式注入到 Pod 中:

1. 作为环境变量

这是最常见的用法,可以将 ConfigMap 的键值对直接注入为容器的环境变量。

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: my-pod
spec:
containers:
- name: my-container
image: redis
env:
# 注入单个 key
- name: APP_COLOR
valueFrom:
configMapKeyRef:
name: my-app-config # ConfigMap 的名称
key: app.color # ConfigMap 中的 key
# 注入整个 ConfigMap 的所有 key-value
envFrom:
- configMapRef:
name: db-config

2. 作为启动命令参数

[cite_start]通过将 ConfigMap 的值注入环境变量,可以在容器的启动命令中引用这些变量 [cite: 58]。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: v1
kind: Pod
metadata:
name: my-command-pod
spec:
containers:
- name: my-container
image: busybox
command: [ "/bin/sh", "-c", "echo The database host is $(DB_HOST)" ]
env:
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: db-config
key: db.host

3. 作为文件挂载到 Volume

[cite_start]可以将 ConfigMap 的内容作为一个或多个文件挂载到容器的指定路径,这对于需要读取配置文件的应用非常有用 [cite: 59]。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: v1
kind: Pod
metadata:
name: my-volume-pod
spec:
containers:
- name: my-container
image: nginx
volumeMounts:
- name: config-volume
mountPath: /etc/nginx/conf.d # 将配置文件挂载到 Nginx 配置目录
volumes:
- name: config-volume
configMap:
name: nginx-config # 引用包含 nginx.conf 的 ConfigMap

热更新

ConfigMap 的更新机制是其核心特性之一,但不同使用方式下表现不同:

  • [cite_start]环境变量: 通过 envenvFrom 注入的环境变量不会在 ConfigMap 更新后自动更新 [cite: 65]。Pod 必须重启才能获取新的配置。
  • [cite_start]Volume 挂载: 通过 Volume 挂载的 ConfigMap 文件自动更新 [cite: 66][cite_start]。Kubelet 会定期同步,通常在几十秒内完成更新,无需重启 Pod [cite: 66]。

[cite_start]注意:仅更新 ConfigMap 不会触发 Deployment 的滚动更新。如果需要 Pod 使用新配置进行重建,可以通过修改 Pod 模板的注解来强制触发滚动更新 [cite: 62]。

1
kubectl patch deployment my-deploy --patch '{"spec": {"template": {"metadata": {"annotations": {"version/config": "v2" }}}}}'

不可变 ConfigMap (Immutable)

[cite_start]为了防止意外更新导致应用中断,并提升大规模集群的性能(通过减少 API Server 的监控负载),可以将 ConfigMap 设置为不可变 [cite: 68, 69, 70]。

1
2
3
4
5
6
7
apiVersion: v1
kind: ConfigMap
metadata:
name: my-immutable-config
immutable: true # 设置为 true 后,data 字段将无法修改
data:
key: "initial_value"

Secret

[cite_start]Secret 用于存储和管理敏感信息,如密码、OAuth 令牌和 SSH 密钥等 [cite: 75][cite_start]。相比于明文存储的 ConfigMap,Secret 提供了基本的安全保障 [cite: 72]。

安全特性

  • 数据以 Base64 编码形式存储,并非加密,但避免了明文暴露。
  • [cite_start]只分发到需要使用它的 Pod 所在的节点上 [cite: 77]。
  • [cite_start]在节点上,Secret 通常存储在内存中,而非持久化到磁盘 [cite: 78]。
  • [cite_start]在 Etcd 中默认是加密存储的(从 K8s 1.7 开始) [cite: 79]。

创建和使用

创建 Secret 时,值必须经过 Base64 编码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 准备数据
$ echo -n 'admin' | base64
YWRtaW4=
$ echo -n 'P@ssw0rd123' | base64
UEBzc3cwcmQxMjM=

# 创建 Secret YAML
apiVersion: v1
kind: Secret
metadata:
name: db-user-pass
type: Opaque # 最常见的类型,用于存储任意键值对
data:
username: YWRtaW4=
password: UEBzc3cwcmQxMjM=

Secret 的使用方式与 ConfigMap 完全相同,可以通过环境变量Volume 文件挂载注入到容器中,只是引用的对象从 configMapKeyRef 变为 secretKeyRef

热更新与不可变性

[cite_start]Secret 的热更新机制和不可变(Immutable)配置与 ConfigMap 完全一致。通过 Volume 挂载的 Secret 会自动更新 [cite: 87][cite_start],而注入为环境变量的则不会。同样可以设置 immutable: true 来锁定 Secret 的内容 [cite: 89]。


Downward API

[cite_start]Downward API 是一种特殊的机制,它不用于存储用户定义的数据,而是将 Pod 自身的元数据暴露给运行在其中的容器 [cite: 93, 96]。

使用场景

  • [cite_start]获取元数据: 容器需要知道自己的 Pod 名称、IP 地址、所在命名空间等 [cite: 97]。
  • [cite_start]动态配置: 应用根据其被分配的资源限制(CPU/Memory limits)来动态调整行为 [cite: 98]。

使用方式

与 ConfigMap 和 Secret 类似,Downward API 也通过环境变量Volume 文件两种方式提供信息。

1. 环境变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
env:
- name: MY_POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: MY_POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: MY_CPU_REQUEST
valueFrom:
resourceFieldRef:
containerName: my-container # 必须指定容器名称
resource: requests.cpu

2. Volume 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
volumeMounts:
- name: podinfo
mountPath: /etc/podinfo
volumes:
- name: podinfo
downwardAPI:
items:
- path: "labels"
fieldRef:
fieldPath: metadata.labels
- path: "annotations"
fieldRef:
fieldPath: metadata.annotations

[cite_start]优势对比: 使用 Volume 挂载 Downward API 的信息支持热更新(例如 Pod 的 label 或 annotation 发生变化时,文件内容会同步更新),而环境变量则是在 Pod 启动时一次性注入,不会改变 [cite: 103]。


Volume

[cite_start]Volume 是 Kubernetes 存储体系的基石,它解决了容器文件系统是临时的这一核心问题,提供了数据持久化和容器间文件共享的能力 [cite: 127]。Volume 的生命周期与 Pod 绑定。

Kubernetes 支持多种类型的 Volume,这里介绍两种最基础的:

emptyDir

[cite_start]一个临时的空目录,当 Pod 被分配到节点时创建,只要 Pod 在该节点上运行,它就一直存在。当 Pod 被删除时,emptyDir 中的数据也会被永久删除 [cite: 130]。

  • 特性:
    • Pod 内所有容器都可以挂载并读写同一个 emptyDir,实现容器间文件共享。
    • [cite_start]容器崩溃不会导致 emptyDir 数据丢失,因为它的生命周期与 Pod 相同 [cite: 131]。
  • 用途:
    • [cite_start]临时暂存空间,例如用于排序算法的中间数据 [cite: 133]。
    • [cite_start]一个容器从网络下载数据,另一个容器处理这些数据 [cite: 134]。
  • [cite_start]后端存储: 默认使用节点的磁盘,也可以配置使用内存(tmpfs)以获得更高性能 [cite: 137]。
    1
    2
    3
    4
    5
    volumes:
    - name: cache-volume
    emptyDir:
    medium: Memory
    sizeLimit: 1Gi # 使用内存时建议设置大小限制

hostPath

[cite_start]将主机(Node)文件系统上的文件或目录直接挂载到 Pod 中 [cite: 139]。

  • 警告: hostPath 是一种强大但危险的工具。

    • [cite_start]它将 Pod 与特定节点绑定,如果 Pod 被调度到其他节点,数据会丢失 [cite: 147]。
    • [cite_start]可能暴露节点上的敏感文件或系统目录,带来安全风险 [cite: 149]。
    • 通常仅用于需要访问节点底层资源的系统级 Pod,如日志收集代理或监控组件。
  • 示例:

    1
    2
    3
    4
    5
    volumes:
    - name: docker-sock
    hostPath:
    path: /var/run/docker.sock # 挂载节点的 Docker socket
    type: Socket # 指定路径类型,增强可靠性

PersistentVolume (PV) 与 PersistentVolumeClaim (PVC)

[cite_start]PV 和 PVC 是 Kubernetes 持久化存储的核心,它们将存储资源的定义(PV)与存储资源的使用(PVC)解耦,实现了存储的标准化管理 [cite: 152]。

  • [cite_start]PersistentVolume (PV): 由管理员创建和配置的一块网络存储。它是集群的资源,就像 CPU 和内存一样 [cite: 33]。PV 封装了底层存储的实现细节,如 NFS、Ceph、ISCSI 等。
  • PersistentVolumeClaim (PVC): 用户(应用开发者)创建的存储请求。它声明了需要的存储大小、访问模式等,但不关心底层如何实现。

工作流程

  1. 管理员预先创建多个 PV,定义好它们的容量、访问模式和存储类别。
  2. 用户在部署应用时,创建一个 PVC,声明所需的存储规格。
  3. Kubernetes 会在现有的 PV 中寻找一个能满足该 PVC 要求的 PV,并将它们**绑定(Bind)**在一起。
  4. Pod 在定义 Volume 时,直接引用 PVC 的名称,即可挂载并使用这块持久化存储。

核心概念

  • [cite_start]访问模式 (Access Modes)[cite: 168]:

    • [cite_start]ReadWriteOnce (RWO): 卷可以被单个节点以读写方式挂载 [cite: 169]。
    • [cite_start]ReadOnlyMany (ROX): 卷可以被多个节点以只读方式挂载 [cite: 170]。
    • [cite_start]ReadWriteMany (RWX): 卷可以被多个节点以读写方式挂载 [cite: 171]。
  • [cite_start]回收策略 (Reclaim Policy)[cite: 173]: 定义了当 PVC 被删除后,与之绑定的 PV 如何处理。

    • [cite_start]Retain (保留): PV 不会被删除,数据保留,状态变为 Released,需要管理员手动清理和回收 [cite: 174]。
    • [cite_start]Delete (删除): PV 会被自动删除,同时底层的存储资源(如云硬盘)也会被删除 [cite: 176]。
    • [cite_start]Recycle (回收): (已废弃) 会清空卷上的数据(rm -rf /thevolume/*)使其可被再次使用 [cite: 175]。

StorageClass

[cite_start]当使用静态方式(手动创建 PV)管理存储时,每次用户需要存储都得麻烦管理员。StorageClass 提供了一种动态存储供应 (Dynamic Provisioning) 的机制,彻底自动化了 PV 的创建过程 [cite: 194, 197]。

工作原理

  1. 管理员创建一个或多个 StorageClass 对象,每个对象定义了一种存储类型(如 fast-ssdslow-hdd)和用于创建它的驱动(Provisioner)。
  2. 用户在创建 PVC 时,通过 storageClassName 字段指定想要的 StorageClass。
  3. Kubernetes 收到这个 PVC 后,会调用该 StorageClass 指定的 Provisioner。
  4. Provisioner 会根据 PVC 的要求,在后端存储(如 NFS、Ceph、云厂商)上自动创建一个新的存储卷,并为其创建一个对应的 PV 对象。
  5. 新创建的 PV 会自动与用户的 PVC 绑定。

这个过程对用户完全透明,用户只需提交一个 PVC,就能在几秒钟内得到一块可用的持久化存储。

示例: 使用 NFS 的动态供应

[cite_start]社区提供了 nfs-client-provisioner 这样的工具,可以利用现有的 NFS 服务器实现动态存储供应 [cite: 208, 209]。管理员部署好 Provisioner 和一个指向它的 StorageClass 后,用户就可以通过 PVC 自动在 NFS 服务器上创建目录并挂载使用了。

1
2
3
4
5
6
7
8
9
10
11
12
# 用户提交的 PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-nfs-claim
spec:
storageClassName: "managed-nfs-storage" # 指定 StorageClass
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
 REWARD AUTHOR
 Comments
Comment plugin failed to load
Loading comment plugin