存储

存储分类

元数据
  • configMap:用于保存配置数据(明文)
  • Secret:用于保存敏感性数据(密文编码)
  • Downward API:容器在运行时从 kubernetes API服务器获取有关它们自身的信息。
真实数据
  • Volume:用于存储临时或者持久性数据
    与Docker中的卷类似
  • PersistentVolume:申请制的持久化存储
    解决跨部门交流代价较高的问题

configMap

configMap模型
在这里插入图片描述
两种不同的数据共享方式:
在这里插入图片描述
服务内部文件达到一致。
两种方式在读取文件时的不同:
共享:在每一次读取文件的时候,都会发生网络IO
注入:configMap使用的方式。一次注入后,多次读取不在消耗网络IO。

创建

第一种创建方式:从文件中创建

$ kubectl create configmap game-config --from-file=hongfu.file

–from-file
要求:第一种:文件内部必须是一行一对的 k=v,该方式可以在使用的时候注入至Pod。
1.txt
name=zhangsan
passwd=123
第二种:文件内部不是一行一对的
第二种创建方式: 命令直接创建

$ kubectl create configmap literal-config --from-literal=name=dave --from-literal=password=pass

–from-literal 后面跟 k=v

文件创建的具体书写方式

以下是创建了两个configMap对应的配置yaml文件。
多个文件使用 — 进行分隔。

apiVersion: v1
kind: ConfigMap
metadata:
  name: literal-config
  namespace: default
data:
  name: dave
  password: pass
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: env-config
  namespace: default
data:
  log_level: INFO
Pod引用configMap

下面给出了两种引用方式:
①取出configMap中对应的key然后对当前值的key重新命名。

  env:
    - name: USERNAME
      valueFrom:
        configMapKeyRef:
          name: literal-config
          key: name

②直接应用对应的configMap全部。

      envFrom:
        - configMapRef:
            name: env-config

下面是完整的yaml引用清单,这里要特别注意configMap引用的层级关系,是在containers下的env或envFrom下。

apiVersion: v1
kind: Pod
metadata:
  name: cm-env-test-pod
spec:
  containers:
    - name: test-container
      image: wangyanglinux/myapp:v1
      command: [ "/bin/sh", "-c", "env" ]
      env:
        - name: USERNAME
          valueFrom:
            configMapKeyRef:
              name: literal-config
              key: name
        - name: PASSWORD
          valueFrom:
            configMapKeyRef:
              name: literal-config
              key: password
      envFrom:
        - configMapRef:
            name: env-config
  restartPolicy: Never
configMap作为启动命令
apiVersion: v1
kind: Pod
metadata:
  name: cm-command-pod
spec:
  containers:
    - name: myapp-container
      image: wangyanglinux/myapp:v1.0
      command: ["/bin/sh", "-c", "echo $(USERNAME) $(PASSWORD)"]
      env:
        - name: USERNAME
          valueFrom:
            configMapKeyRef:
              name: literal-config
              key: name
        - name: PASSWORD
          valueFrom:
            configMapKeyRef:
              name: literal-config
              key: password
  restartPolicy: Never
configMap文件

文件名为key,文件内容为value。
需要重点强调的是:
volumeMounts关键字代表卷绑定。意思是需要把哪个卷挂载在哪个目录下。如下配置就是将 config-volume 文件挂载到当前容器的/etc/config目录下。
volumes关键字代表声明卷。如下的配置基于名为literal-config的configMap做一个名为config-volume的卷。这里会将 configMap 转化为卷。

apiVersion: v1
kind: Pod
metadata:
  name: cm-volume-pod
spec:
  containers:
    - name: myapp-container
      image: wangyanglinux/myapp:v1.0
      volumeMounts:
        - name: config-volume
          mountPath: /etc/config
  volumes:
    - name: config-volume
      configMap:
        name: literal-config
  restartPolicy: Never
#命令补充:实时显示pod状态
kubectl get pod -w
实验热更新
#进入容器内部:
kubectl exec -it deploy-5566 -- /bin/bash
#找到容器中对应文件
cd /etc/nginx/conf.d
#使用死循环来显示config文件
while true;
do cat default.conf;
sleep 2s;
done;
#修改configMap
kubectl edit cm default-nginx;
#验证configMap
kubectl get cm default-nginx -o yaml;

traefic web服务器,可以监控到文件的热更新的变化。
由于并是不通过挂载的方式来实现文件共享的,所以修改不会立即发生变化。但是会在一段时间后发生改变。不过由于实验中使用的Nginx并不支持热更新,所以需要重启Nginx才能验证配置生效。

注意:更新configMap不会触发相关Pod的滚动更新,不过可以通过修改Pod annotations的方式强制触发滚动更新。
触发滚动更新的补丁命令如下:

kubectl patch deployment nginx-test --patch '{"spec": {"template": {"metadata": {"annotations": {"version/config": "66666666" }}}}}'

上述中的version/config在配置需要注意,应该为String类型。

更新 ConfigMap 后:

  • 使用该 ConfigMap 挂载的 Env 不会同步更新
  • 使用该 ConfigMap 挂载的 Volume 中的数据需要一段时间(实测大概 10 秒)才能同步更新
热更新缺点

需要对kube-apiserver进行监视来判断configMap文件是否需要更新。这里会造成kube-apiserver的负载。

不可更改

Kubernetes 给不可变的 ConfigMap 和 Secret 提供了一种可选配置,可以设置各个 Secret 和 ConfigMap 为不可变的。对于大量使用 configmap 的集群(至少有成千上万各不相同的 configmap 供 Pod 挂载),禁止变更它们的数据有下列好处:

  • ✅ 防止意外(或非预期的)更新导致应用程序中断
  • ✅ 通过将 configmap 标记为不可变来关闭 kube-apiserver 对其的监视,从而显著降低 kube-apiserver 的负载,提升集群性能。

字段说明:
KIND: ConfigMap
VERSION: v1
FIELD: immutable < boolean >
DESCRIPTION:
Immutable, if set to true, ensures that data stored in the ConfigMap cannot
be updated (only object metadata can be modified). If not set to true, the
field can be modified at any time. Defaulted to nil.
这里修改完成后,更改configMap是无法正常保存的。也就是不可逆的,一旦配置为不可改变,就一直为不可改变。

Secret

Secret 对象类型用来保存敏感信息,例如密码、OAuth 令牌和 SSH 密钥。将这些信息放在 secret 中比放在 Pod 的定义或者容器镜像中来说更加安全和灵活。
注意:Secret中保存的值必须经过base64编码。

Secret类型
内置类型用法
Opaque用户定义的任意数据
kubernetes.io/service-account-token服务账号令牌
kubernetes.io/dockerCFG~/.dockercfg 文件的序列化形式
kubernetes.io/dockerconfigjson~/.docker/config.json 文件的序列化形式
kubernetes.io/basic-auth用于基本身份认证的凭据
kubernetes.io/ssh-auth用于 SSH 身份认证的凭据
kubernetes.io/tls用于 TLS 客户端或者服务器端的数据
bootstrap.kubernetes.io/token启动引导令牌数据
Opaque

当 Secret 配置文件中未作显式设定时,默认的 Secret 类型是 Opaque。当你使用 kubectl 来创建一个 Secret 时,你会使用 generic 子命令来标明要创建的是一个 Opaque 类型 Secret。

Opaque-创建

在 Kubernetes 中,Opaque 类型的 Secret 用于存储任意敏感数据(如密码、用户名等),其内容以 Base64 编码形式保存。

  1. 将明文数据编码为 Base64
echo -n "admin" | base64
YWRtaW4=
echo -n "lf2d1e2e67df" | base64
MWYyZDFlMmU2N2Rm
#解码命令
echo "MWYyZDFlMmU2N2Rm" | base64 -d
  1. 定义 Secret 的 YAML 配置文件
apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  password: MWYyZDFlMmU2N2Rm
  username: YWRtaW4=
  1. 创建配置文件并创建Secret
vim 7.secret.yaml
kubectl create -f 7.secret.yaml
#查看Secret
kubectl describe secret mysecret
#获取Secret并以yaml形式输出
kubectl get secret mysecret -o yaml

上述kubectl get secret mysecret -o yaml命令的输出如下:

Name:         mysecret
Namespace:    default
Labels:       <none>
Annotations:  <none>

Type:  Opaque

Data
====
username:  5 bytes
password:  12 bytes
[root@k8s-master01 7]# kubectl get  secret mysecret -o yaml
apiVersion: v1
data:
  password: MWYyZDFlMmU2N2Rm
  username: YWRtaW4=
kind: Secret
metadata:
  creationTimestamp: "2025-09-27T07:46:33Z"
  name: mysecret
  namespace: default
  resourceVersion: "369712"
  uid: 1b3b9e9d-19c8-4ffb-b24a-39639d519659
type: Opaque

Opaque-使用 - env方案

Secret与configMap使用方法类似,可以在yaml中的containers的标签下的env标签中来引用并使用。

  1. 定义 deploy并设置env为Secret的 YAML 配置文件
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: opaque-secret-env
  name: opaque-secret-env-deploy
spec:
  replicas: 5
  selector:
    matchLabels:
      app: op-se-env-pod
  template:
    metadata:
      labels:
        app: op-se-env-pod
    spec:
      containers:
      - name: myapp-continaer
        image: wangyanglinux/myapp:v1.0
        ports:
        - containerPort: 80
        env:
        - name: TEST_USER
          valueFrom:
            secretKeyRef:
              name: mysecret
              key: username
        - name: TEST_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysecret
              key: password
  1. 创建配置文件并创建deploy
vim 8.deploy.yaml
kubectl create -f 8.deploy.yaml
  1. 查看部署的deploy及容器的env
kubectl get pod
#执行结果
NAME                                        READY   STATUS    RESTARTS   AGE
opaque-secret-env-deploy-7f868565fb-cwdz7   1/1     Running   0          6m15s
opaque-secret-env-deploy-7f868565fb-fjd68   1/1     Running   0          6m15s
opaque-secret-env-deploy-7f868565fb-kgn4k   1/1     Running   0          6m15s
opaque-secret-env-deploy-7f868565fb-s6ffq   1/1     Running   0          6m15s
opaque-secret-env-deploy-7f868565fb-xkrgx   1/1     Running   0          6m15s
#进入容器查看对应环境变量
[root@k8s-master01 7]# kubectl exec -it opaque-secret-env-deploy-7f868565fb-cwdz7 -- /bin/bash
opaque-secret-env-deploy-7f868565fb-cwdz7:/# env
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_SERVICE_PORT=443
NGINX_NOSELECTT_PORT_6666_TCP_PROTO=tcp
CHARSET=UTF-8
HOSTNAME=opaque-secret-env-deploy-7f868565fb-cwdz7
NGINX_NOSELECTT_PORT=tcp://10.3.177.211:6666
MYAPP_SERVICE_PORT_80_TCP=tcp://10.8.142.55:80
PWD=/
MYAPP_SERVICE_PORT_80_TCP_PROTO=tcp
NGINX_NOSELECTT_SERVICE_PORT=6666
HOME=/root
LANG=C.UTF-8
KUBERNETES_PORT_443_TCP=tcp://10.0.0.1:443
MYAPP_SERVICE_PORT_80_TCP_PORT=80
NGINX_NOSELECTT_PORT_6666_TCP=tcp://10.3.177.211:6666
TEST_USER=admin
MYAPP_SERVICE_PORT=tcp://10.8.142.55:80
NGINX_NOSELECTT_PORT_6666_TCP_PORT=6666
MYAPP_SERVICE_SERVICE_HOST=10.8.142.55
TERM=xterm
NGINX_NOSELECTT_SERVICE_HOST=10.3.177.211
SHLVL=1
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP_ADDR=10.0.0.1
MYAPP_SERVICE_SERVICE_PORT=80
NGINX_NOSELECTT_PORT_6666_TCP_ADDR=10.3.177.211
KUBERNETES_SERVICE_HOST=10.0.0.1
TEST_PASSWORD=1f2d1e2e67df
KUBERNETES_PORT=tcp://10.0.0.1:443
KUBERNETES_PORT_443_TCP_PORT=443
LC_COLLATE=C
MYAPP_SERVICE_PORT_80_TCP_ADDR=10.8.142.55
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
_=/usr/bin/env
opaque-secret-env-deploy-7f868565fb-cwdz7:/#

TEST_USER=admin
TEST_PASSWORD=1f2d1e2e67df
容器中的env中的Secret都经过了base64解码。

Opaque-使用 - volume方案
  1. 创建Pod的yaml文件以使用对应的Secret
apiVersion: v1
kind: Pod
metadata:
  name: secret-volume-pod
  labels:
    name: secret-volume
spec:
  volumes:
  - name: volumes12
    secret:
      secretName: mysecret
  containers:
  - name: myapp-container
    image: wangyanglinux/myapp:v1.0
    volumeMounts:
    - name: volumes12
      mountPath: "/data"

上面是只将Secret挂载到/data目录下。
下面是其他的一些写法,以实现不同的功能。

  • 写法1:修改文件权限
    这里的defaultMode字段对应Linux文件的权限:
    256对应400
    420对应644
volumes:
  - name: volumes12
    secret:
      secretName: mysecret
      defaultMode: 256
  • 写法2:部分数据挂载
    只使用Secret部分字段。这种方式不支持热更新。
volumes:
  - name: volumes12
    secret:
      secretName: mysecret
      items:
        - key: username
          path: my-group/my-username
  1. 创建配置文件创建Pod并检验
vim 9.pod.yaml
kubectl create -f 9.pod.yaml
kubectl exec -it secret-volume-pod -- /bin/bash
secret-volume-pod:/# cd /data
secret-volume-pod:/data# ls
password  username
secret-volume-pod:/data# cat password
1f2d1e2e67dfsecret-volume-pod:/data#
Opaque-使用 - volume方案 - 热更新

当已经存储于卷中被使用的 Secret 被更新时,被映射的键也将最终被更新。组件 kubelet 在周期性同步时会检查被挂载的 Secret 是否是最新的。但是,它会使用其本地缓存的数值作为 Secret 的当前值。
注意:※ 使用 Secret 作为子路径卷挂载的容器不会收到 Secret 更新。

Opaque-使用 - volume方案 - 不可更改

Kubernetes 给不可变的 Secret 和 ConfigMap 提供了一种可选配置,可以设置各个 Secret 和 ConfigMap 为不可变的。对于大量使用 Secret 的集群(至少有成千上万各不相同的 Secret 供 Pod 挂载),禁止变更它们的数据有下列好处:

  • ✅ 防止意外(或非预期的)更新导致应用程序中断
  • ✅ 通过将 Secret 标记为不可变来关闭 kube-apiserver 对其的监视,从而显著降低 kube-apiserver 的负载,提升集群性能。
apiVersion: v1
kind: Secret
metadata:
  ...
data:
  ...
immutable: true

Downward API

Downward API - 作用

容器在运行时从 Kubernetes API 服务器获取有关它们自身的信息。

Downward API 是 Kubernetes 中的一个功能,它允许容器在运行时从 Kubernetes API 服务器获取有关它们自身的信息。这些信息可以作为容器内部的环境变量或文件注入到容器中,以便容器可以获取有关其运行环境的各种信息,如 Pod 名称、命名空间、标签等。

◆ 提供容器元数据

◆ 动态配置

◆ 与 Kubernetes 环境集成

Downward API - 使用 - env方案

使用Downward API和其他类型相同,也是通过使用containers下的env标签。

  1. 在Pod中使用Downward API的yaml文件
apiVersion: v1
kind: Pod
metadata:
  name: downward-api-env-example
spec:
  containers:
    - name: my-container
      image: wangyanglinux/myapp:v1.0
      env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: POD_IP
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
        - name: CPU_REQUEST
          valueFrom:
            resourceFieldRef:
              resource: requests.cpu
        - name: CPU_LIMIT
          valueFrom:
            resourceFieldRef:
              resource: limits.cpu
        - name: MEMORY_REQUEST
          valueFrom:
            resourceFieldRef:
              resource: requests.memory
        - name: MEMORY_LIMIT
          valueFrom:
            resourceFieldRef:
              resource: limits.memory
  restartPolicy: Never
Downward API - 使用 - volume方案

和其他类型一样,也是在对应的containers标签下的volumeMounts标签下,进行卷挂载以及卷命名。
在volumes标签下的downwardAPI标签下指定对应属性名称。

  1. 在Pod中使用Downward API的yaml文件
apiVersion: v1
kind: Pod
metadata:
  name: downward-api-volume-example
spec:
  containers:
    - name: my-container
      image: wangyanglinux/myapp:v1.0
      resources:
        limits:
          cpu: "1"
          memory: "512Mi"
        requests:
          cpu: "0.5"
          memory: "256Mi"
      volumeMounts:
        - name: downward-api-volume
          mountPath: /etc/podinfo
  volumes:
    - name: downward-api-volume
      downwardAPI:
        items:
          - path: "annotations"
            fieldRef:
              fieldPath: metadata.annotations
          - path: "labels"
            fieldRef:
              fieldPath: metadata.labels
          - path: "name"
            fieldRef:
              fieldPath: metadata.name
          - path: "namespace"
            fieldRef:
              fieldPath: metadata.namespace
          - path: "uid"
            fieldRef:
              fieldPath: metadata.uid
          - path: "cpuRequest"
            resourceFieldRef:
              containerName: my-container
              resource: requests.cpu
          - path: "memoryRequest"
            resourceFieldRef:
              containerName: my-container
              resource: requests.memory
          - path: "cpuLimit"
            resourceFieldRef:
              containerName: my-container
              resource: limits.cpu
          - path: "memoryLimit"
            resourceFieldRef:
              containerName: my-container
              resource: limits.memory
  restartPolicy: Never

热更新
传递一个容器的资源字段到另一个容器中

Downward API - 使用 - 扩展 - 基于API server

Downward API 提供了一种简单的方式,将 pod 和容器的元数据传递给在它们内部运行的进程。但这种方式其实仅仅可以暴露一个 pod 自身的元数据传递给在它们内部运行的进程。这种方式其实仅仅可以暴露一个 pod 自身的元数据,而且只可以暴露部分元数据。
所以看一下另外一种方式:

  • 容器 内部的 App Process 通过 Downward API 从 API 服务器 获取相关信息。
  • API 服务器 中包含多个 API 对象,用于存储和提供 Kubernetes 资源的元数据。
  • 该机制允许应用程序动态获取其运行环境的上下文信息,如 Pod 名称、命名空间、标签等。

通过 ServiceAccount 调用 API:在 Pod 中使用 ServiceAccount 通过 curl 直接调用 Kubernetes API Server 获取当前命名空间下的 Pod 列表。

  1. 创建 ClusterRoleBinding(赋予管理员权限)
# 1.RBAC.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: test-api-cluster-admin-binding
subjects:
  - kind: ServiceAccount
    name: test-api
    namespace: default
roleRef:
  kind: ClusterRole
  name: cluster-admin
  apiGroup: rbac.authorization.k8s.io
  1. 创建 Pod 并指定 ServiceAccount
apiVersion: v1
kind: Pod
metadata:
  name: curl
spec:
  serviceAccountName: test-api  # 使用具有权限的 ServiceAccount
  containers:
    - name: main
      image: tutum/curl
      command: ["sleep", "9999"]

3.在 Pod 中执行命令调用 Kubernetes API

# 获取 ServiceAccount 的 JWT token
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)

# 指定 CA 证书路径(用于验证 API Server 证书)
CAPATH="/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"

# 获取当前 Pod 所在的命名空间
NS=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)

# 调用 Kubernetes API 获取当前命名空间下的所有 Pods
curl -H "Authorization: Bearer $TOKEN" --cacert $CAPATH \
  https://kubernetes/api/v1/namespaces/$NS/pods
  1. 查看接口组版本并使用Swagger UI做展示
### 1. 启动 kubectl 代理服务
```bash
$ kubectl proxy --port=8080
$ curl localhost:8080/openapi/v2 > k8s-swagger.json
$ docker run \
  --rm \
  -d \
  -p 80:8080 \
  -e SWAGGER_JSON=/k8s-swagger.json \
  -v $(pwd)/k8s-swagger.json:/k8s-swagger.json \
  swaggerapi/swagger-ui
  1. 访问部署IP的8080端口就可以通过Swagger来查看接口了

volume(卷)

K8s_node2
K8s_node1
/mfs
/mfs
Pod
MFS
Meta File System

通过卷挂载方式来是实现不同Node间的文件共享。

volume - 作用

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

CSI:container storage interface 容器存储接口
hostPath:主机路径,后端共享存储。

volume类型 - emptyDir

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

容器崩溃不会从节点中移除 pod,因此 emptyDir 卷中的数据在容器崩溃时是安全的。因为emptyDir绑定的级别是Pod级别,所以生命周期是和Pod保持一致。

emptyDir用法
  • 暂存空间,例如用于基于磁盘的合并排序、用作长时间计算崩溃恢复时的检查点(判断当前的Pod是否有过崩溃)。
  • Web 服务器容器提供数据时,保存内容管理器容器提取的文件。在通过intiC拉取git仓库中的代码,而mainC实际来执行的代码。由于mainC启动后IntiC已经消亡了,所有需要一个空间来暂存一些数据(共享卷)。那么就可以使用emptyDir。
emptyDir用法-基于文件
  1. 创建emptyDir的Pod的yaml文件
apiVersion: v1
kind: Pod
metadata:
  name: volume-emptydir-disk-pod
  namespace: default
spec:
  containers:
    - name: myapp
      image: wangyanglinux/myapp:v1.0
      ports:
        - containerPort: 80
      volumeMounts:
        - name: logs-volume
          mountPath: /usr/local/nginx/logs
    - name: busybox
      image: wangyanglinux/tools:busybox
      command: ["/bin/sh", "-c", "touch /logs/access.log && tail -f /logs/access.log"]
      volumeMounts:
        - name: logs-volume
          mountPath: /logs
  volumes:
    - name: logs-volume
      emptyDir: {}
  1. 以上实现的基于文件本身的共享策略。

  2. Kubernetes 中 emptyDir 卷的存储路径说明
    kubelet 的工作目录(由 root-dir 参数控制,默认为 /var/lib/kubelet)中,会为每个使用了 emptyDir: {} 的 Pod 创建一个专用目录。
    /var/lib/kubelet/pods/{podid}/volumes/kubernetes.io~empty-dir/

  • 所有存放在 emptyDir 卷中的数据,最终都会被存储在上述路径中。
  • 该路径位于节点本地文件系统上,因此数据仅在当前节点有效。
  • 当 Pod 被删除时,emptyDir 中的数据也会被自动清除。
特性说明
生命周期与 Pod 生命周期一致,Pod 删除则数据丢失
存储位置节点本地磁盘,不跨节点共享
用途临时存储、缓存、中间处理数据等
性能高速读写,适合临时数据

💡 提示:emptyDir 是一种临时性卷类型,适用于不需要持久化的场景。若需跨节点或持久化存储,请考虑使用 hostPathPersistentVolume 等其他卷类型。

emptyDir用法-基于内存
  1. 创建emptyDir的Pod的yaml文件
apiVersion: v1
kind: Pod
metadata:
  name: volume-emptydir-mem
  namespace: default
spec:
  containers:
  - name: myapp
    image: wangyanglinux/myapp:v1.0
    ports:
    - containerPort: 80
    resources:
      limits:
        cpu: "1"
        memory: 1024Mi
      requests:
        cpu: "1"
        memory: 1024Mi
    volumeMounts:
    - name: mem-volume
      mountPath: /data
  volumes:
  - name: mem-volume
    emptyDir:
      medium: Memory
      sizeLimit: 500Mi
volume类型 - hostPath

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

hostPath 用途
  • 运行需要访问 Docker 内部的容器;使用 /var/lib/dockerhostPath
  • 在容器中运行 cAdvisor;使用 /dev/cgroupshostPath
  • 允许 pod 指定给定的 hostPath 是否应该在 pod 运行之前存在,是否应该创建,以及它应该以什么形式存在

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

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

创建带有hostPath的Pod。

apiVersion: v1
kind: Pod
metadata:
  name: hostpath-pod
spec:
  containers:
  - image: wangyanglinux/myapp:v1.0
    name: myapp
    volumeMounts:
    - mountPath: /test-pd
      name: test-volume
  volumes:
  - name: test-volume
    hostPath:
      # directory location on host
      path: /data
      # this field is optional
      type: Directory

注意

  • 由于每个节点上的文件都不同,具有相同配置(例如从 podTemplate 创建的)的 pod 在不同节点上的行为可能会有所不同

  • 当 Kubernetes 按照计划添加资源感知调度时,将无法考虑 hostPath 使用的资源

  • 在底层主机上创建的文件或目录只能由 root 写入。您需要在特权容器中以 root 身份运行进程,或修改主机上的文件权限以便写入 hostPath

PV/PVC(持久卷/持久卷申请)

PV和PVC关系

storage
k8s_infrastructure
EMC2
CEPH
NFS
PVC
Pod-1
Pod
pv1
pv2
pv3
  1. 容量
    PV 的值不小于 PVC 要求,可以大于,最好一致
  2. 读写策略
  • 单节点读写 – ReadWriteOnce – RWO
  • 多节点只读 – ReadOnlyMany – ROX
  • 多节点读写 – ReadWriteMany – RWX
    举例:
    使用iSCSI /aɪˈskʌzi/ (Internet Small Computer Systems Interface,互联网小型计算机系统接口)是一种基于 IP 网络的存储网络技术,实现跨网络的块设备访问。
    其可使用 EKT4 或 XFS 文件格式系统。其只支持本地锁不支持网络锁。
    GFS 支持网络锁。
  1. 存储类
    PV 的类与 PVC 的类必须一致,不存在包容降级关系

PV和PVC的回收策略

  • Retain(保留):手动回收(优先级更高)
  • Recycle(回收):基本擦除(rm -rf /thevolume/*
  • Delete(删除):关联的存储资产(例如 AWS EBS、GCE PD、Azure Disk 和 OpenStack Cinder 卷)将被删除

当前,只有 NFS 和 HostPath 支持回收策略。AWS EBS、GCE PD、Azure Disk 和 Cinder 卷支持删除策略。

PV和PVC的回收策略

  • Available(可用):一块空闲资源还没有被任何声明所绑定
  • Bound(已绑定):卷已经被声明绑定
  • Released(已释放):声明被删除,但是资源还未被集群重新声明 ,不可被重新绑定。
    让release变为available
    ①删除当前PV并重新创建,这样就变为未可用状态了。
    ②移除PV的yaml中的Claim
  • Failed(失败):该卷的自动回收失败

命令行会显示绑定到 PV 的 PVC 的名称

PVC保护

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

注意:当 pod 状态为 Pending 并且 pod 已经分配给节点,或 pod 为 Running 状态时,PVC 处于活动状态。

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

statefulSet 有状态服务的部署类型

  • 有序的创建回收:有序创建,上一个Pod没有Running状态,下一个Pod不允许创建。有序回收,倒序回收。
  • 数据持久化 Pod级别:Pod > PVC > PC > NFS
  • 稳定的网络访问方式:域名不会发生改变,域名和内部IP映射有DNS完成,以此来实现IP变化但是域名不变的特性。
    PodName.headlessSvcName.nsName.svc.domainName.

storageClass

  • StorageClass 介绍
    StorageClass 是一种资源对象,用于定义持久卷(Persistent Volumes)的动态供给(Dynamic Provisioning)策略。把PV抽象一个存储类。

  • 解决PVC和PV匹配不精准问题。
    StorageClass 允许管理员定义不同类型的存储,并指定如何动态创建持久卷以供应应用程序使用。
    这使得 Kubernetes 集群中的存储管理更加灵活和自动化。

NFS的PV存储实验

  1. 在每一个节点中都运行如下命令,安装对应的nfs-utils 和 rpcbind
yum install -y nfs-utils rpcbind
  1. 在对应节点部署NFS服务,并配置对应的共享路径
#创建nfsdata
mkdir /nfsdata
#给nfsdata赋值权限
chmod 666 /nfsdata
#赋值给nobody用户权限
chown nobody /nfsdata
#打开nfs的管理文件
vim /etc/exports
#将nfs路径共享到
/nfsdata *(rw,no_root_squash,no_all_squash,sync)
#在对应路径下建立对应的目录
mkdir {1..10}
#并在每一个目录中放置一个对应的主页
echo "1" > 1/index.html
...
#或者使用for循环的方式进行创建
for i in `seq 10`;
do mkdir $i;
echo ${i} > ${i}/index.html;
done;

#将对应的共享路径换为对应的路径
/nfsdata/1 *(rw,no_root_squash,no_all_squash,sync)
...

#将NFS的Server做重启
systemctl restart nfs-server

#重启rpcbind
systemctl restart rpcbind
#查看共享结果
showmount -e
  1. 在IP为12的Node上做测试
    下面命令在 12 Node 上执行
#创建测试目录
mkdir /nfstest
#对应的目录挂载到nfstest下的
mount -t nfs 192.168.253.11:/nfsdata/1 /nfstest/
#使用echo追加到对应的文件中
会发现两边的文件都能显示追加信息,表示共享成功
#最后解除掉共享文件的挂载
umount /nfstest

注:如果发现在卸载时提示
umount.nfs4: /nfstest: device is busy
可以使用 lsof 命令查看哪些进程正在使用该挂载点。

yum install lsof
lsof /nfstest
kill -9 PID #对应使用挂载点的PID给结束掉
  1. 创建PV使用对应的NFS的共享路径
    使用下面的yaml完成部署
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfspv1
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: nfs
  nfs:
    path: /nfsdata/1 #这里是共享目录也需要修改
    server: 192.168.253.11 #这里要做对应的修改
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfspv2
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: nfs
  nfs:
    path: /nfsdata/2 #这里是共享目录也需要修改
    server: 192.168.253.11 #这里要做对应的修改
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfspv3
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: nfs
  nfs:
    path: /nfsdata/3 #这里是共享目录也需要修改
    server: 192.168.253.11 #这里要做对应的修改
  1. 创建服务以及对应的PVC
    首先穿件一个headless的服务,名字是Nginx
    把clusterIP设置为None,但是没有VIP,由于底层使用的是IPVS的模式,对于IPVS来说,需要指定集群地址,这里为none,也就是证明当前不会进行负载均衡的创建。
    无头服务是专门供给给StatefulSet来使用的。
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: wangyanglinux/myapp:v1.0
       ports:
       - containerPort: 80
         name: web
       volumeMounts:
       - name: www
         mountPath: /usr/local/nginx/html
 volumeClaimTemplates:
 - metadata:
     name: www
   spec:
     accessModes: [ "ReadWriteOnce" ]
     storageClassName: "nfs"
     resources:
       requests:
         storage: 1Gi

部署成功后
[root@k8s-master01 7]# kubectl get pod
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 14s
web-1 1/1 Running 0 10s
web-2 1/1 Running 0 4s

Pod创建选择PV的策略:先预选后再优选。如果PV相同,那么会随机选择。

  1. stateFulSet回收
#移除statefulSet
kubectl  delete -f 7/19.svc.ss.yaml
#以上的删除并不会删除模板文件中的PVC
#pvc需要手动删除
kubectl delete pvc --all
#pv就会变为已释放状态,但已释放的PV是不能再被PVC直接使用的
kubectl edit pv nfspv1

NFS Client Provisioner 实验

nfs-client-provisioner 介绍

nfs-client-provisioner 是一个 Kubernetes 供应商,用于动态提供由 NFS(Network File System)共享支持的持久卷。在 Kubernetes 中,持久卷是独立于 pod 存在的存储资源,可以在 pod 重新启动或重新调度时持久地存储数据。
nfs-client-provisioner 自动化了根据需要创建持久卷的过程,通过与 NFS 服务器交互。在需要在 Kubernetes 集群中为应用程序动态分配存储而无需手动管理 NFS 共享和持久卷创建的情况下,这尤其有用。

工作流程

以下是 nfs-client-provisioner 的工作流程:

  1. 配置
  2. 动态供应
  3. 卷创建
  4. 绑定
  5. 使用

实验部分

1.在NFS的共享目录配置中新增 /nfsdata/share

vim /etc/exports
yyp #复制一行内容
# 创建对应的目录
mkdir -p /nfsdata/share
# 对文件夹的相应权限做限制
chown -R nobody /nfsdata/share
systemctl restart nfs-server
#设置开机自启
systemctl enable nfs-server && systemctl start nfs-server
#
showmount -e
  1. 创建deployment
kind: Deployment
apiVersion: apps/v1
metadata:
  name: nfs-client-provisioner
  namespace: nfs-storageclass
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nfs-client-provisioner
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: nfs-client-provisioner
    spec:
      serviceAccountName: nfs-client-provisioner
      containers:
        - name: nfs-client-provisioner
          # image: registry.k8s.io/sig-storage/nfs-subdir-externalprovisioner:v4.0.2
          image: k8s.dockerproxy.com/sig-storage/nfs-subdir-externalprovisioner:v4.0.2
          volumeMounts:
            - name: nfs-client-root
              mountPath: /persistentvolumes
          env:
            - name: PROVISIONER_NAME
              value: k8s-sigs.io/nfs-subdir-external-provisioner
            - name: NFS_SERVER
              # value: <YOUR NFS SERVER HOSTNAME>
              value: 192.168.253.11
            - name: NFS_PATH
              # value: /var/nfs
              value: /nfsdata/share
      volumes:
        - name: nfs-client-root
          nfs:
            # server: <YOUR NFS SERVER HOSTNAME>
            server: 192.168.253.11
            # share nfs path
            path: /nfsdata/share
  1. 创建SA
apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-client-provisioner
  # replace with namespace where provisioner is deployed
  namespace: nfs-storageclass
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: nfs-client-provisioner-runner
rules:
  - apiGroups: [""]
    resources: ["nodes"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["persistentvolumes"]
    verbs: ["get", "list", "watch", "create", "delete"]
  - apiGroups: [""]
    resources: ["persistentvolumeclaims"]
    verbs: ["get", "list", "watch", "update"]
  - apiGroups: ["storage.k8s.io"]
    resources: ["storageclasses"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["events"]
    verbs: ["create", "update", "patch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: run-nfs-client-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    # replace with namespace where provisioner is deployed
    namespace: nfs-storageclass
roleRef:
  kind: ClusterRole
  name: nfs-client-provisioner-runner
  apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner
  # replace with namespace where provisioner is deployed
  namespace: nfs-storageclass
rules:
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner
  # replace with namespace where provisioner is deployed
  namespace: nfs-storageclass
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    # replace with namespace where provisioner is deployed
    namespace: nfs-storageclass
roleRef:
  kind: Role
  name: leader-locking-nfs-client-provisioner
  apiGroup: rbac.authorization.k8s.io
Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐