K8s存储与配置核心实战

万字干货|K8s存储与配置核心全家桶:emptyDir/hostPath/NFS/PV/PVC/ConfigMap/Secret 一站式实战

摘要:本文系统梳理 Kubernetes 存储卷与配置管理的核心知识点,从最基础的 emptyDir、hostPath,到 NFS 网络存储,再到 PV/PVC 持久化架构,最后覆盖 ConfigMap 配置管理与 Secret 敏感信息加密。每个知识点都配有完整的 YAML 示例、实操命令和验证结果,适合作为 K8s 二阶段考核复习资料,也可直接作为运维手册查阅。

关键词:Kubernetes、Volume、emptyDir、hostPath、NFS、PersistentVolume、PVC、ConfigMap、Secret、容器存储


一、Kubernetes Volume 基础

1.1 环境准备

在开始实验前,先创建专用命名空间并切换上下文:

bash 复制代码
root@master30:~# kubectl create ns storage
root@master30:~# kubectl config set-context --current --namespace storage

1.2 Volume 类型总览

Kubernetes 支持非常多的 Volume 类型,常见的有:

Volume 类型 说明
emptyDir 临时目录,Pod 删除即消失
hostPath 挂载 Node 主机上的目录
nfs NFS 网络文件系统
persistentVolumeClaim 绑定 PV 持久卷
secret 存储敏感信息
configMap 存储配置数据
downwardAPI 暴露 Pod 元数据
cephfs / rbd Ceph 存储(已逐步弃用,转向 CSI)
local 节点本地持久存储

二、emptyDir:临时卷

2.1 原理说明

emptyDir 是最基础的临时卷:

  • 当 Pod 被调度到 Node 上时,自动创建 emptyDir 目录
  • 只要 Pod 在该 Node 上运行,卷就一直存在
  • Pod 被删除时,emptyDir 数据永久删除
  • 同一个 Pod 内的多个容器可以共享 emptyDir

典型场景:同一个 Pod 中的两个容器需要共享文件(比如一个生成日志,另一个采集日志)。

2.2 实验:多容器共享 emptyDir

编写 pod-with-emptyDir.yaml:

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: busybox                                                     # Pod资源名称,namespace下不能重名
  labels:
    app: busybox                                                    # 标签,用于筛选、匹配Service等
spec:
  volumes:
  - name: datavolume                                                # emptyDir卷名称
    emptyDir: {}                                                    # 临时空卷,Pod生命周期绑定
  containers:
  - name: busybox1                                                  # 容器1名称
    image: hub.laoma.cloud/library/busybox
    imagePullPolicy: IfNotPresent                                   # 本地存在镜像则不远程拉取
    command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']
    volumeMounts:
    - mountPath: /data                                              # 挂载到容器内/data路径
      name: datavolume                                              # 关联卷(必须与上面的定义的名字一样)
  - name: busybox2                                                  # 容器2名称,同Pod共享emptyDir
    image: hub.laoma.cloud/library/busybox
    imagePullPolicy: IfNotPresent
    command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']
    volumeMounts:
    - mountPath: /data                                              # 同样挂载/data,实现容器间共享
      name: datavolume

配置说明:

  • spec.volumes:定义卷,类型为 emptyDir
  • spec.containers.volumeMounts:容器内挂载点,通过 name 关联卷

创建 Pod:

bash 复制代码
root@master30:~# kubectl apply -f pod-with-emptyDir.yaml

查看容器 ID 和所在节点:

bash 复制代码
root@master30:~# kubectl describe pod busybox |egrep -o 'Container ID.*//.{12}'
Container ID:  containerd://cb9d649a0c15
Container ID:  containerd://cc7b82600fd4

root@master30:~# kubectl describe pod busybox |grep Node
Node:             worker31.laoma.cloud/10.1.8.31
Node-Selectors:              <none>

登录到对应 Node,用 crictl 查看挂载路径:

bash 复制代码
root@worker31:~# crictl inspect cb9d649a0c15 |grep datavolume
        "hostPath": "/var/lib/kubelet/pods/c2dbcc63-e1ac-45cc-88c8-94e7e510086e/volumes/kubernetes.io~empty-dir/datavolume",
          "host_path": "/var/lib/kubelet/pods/c2dbcc63-e1ac-45cc-88c8-94e7e510086e/volumes/kubernetes.io~empty-dir/datavolume"
          "source": "/var/lib/kubelet/pods/c2dbcc63-e1ac-45cc-88c8-94e7e510086e/volumes/kubernetes.io~empty-dir/datavolume",

可以看到 emptyDir 实际位于:

复制代码
/var/lib/kubelet/pods/<PodUID>/volumes/kubernetes.io~empty-dir/datavolume

验证数据共享:在 busybox1 中创建文件,busybox2 中能看到:

bash 复制代码
root@master30:~# kubectl exec busybox -c busybox1 -- touch /data/b1-f1
root@master30:~# kubectl exec busybox -c busybox2 -- ls /data
b1-f1

验证 Pod 删除后数据消失:

bash 复制代码
root@master30:~# kubectl delete pod busybox --force

登录 Node 查看对应目录,已经不存在。

注意:容器删除后,临时卷数据会延迟一段时间才被清理。


三、hostPath:节点主机目录映射

3.1 原理说明

hostPath 将 Node 文件系统中的单个文件或者目录 挂载到 Pod 内部,靠 type 字段控制:

type 含义
Directory 路径必须是已经存在的目录,不存在 Pod 起不来
DirectoryOrCreate 目录不存在就自动新建目录
File 路径必须是已经存在的文件,不存在 Pod 起不来
FileOrCreate 如果文件不存在,kubelet 自动创建一个空文件(权限 0644)
  • Pod 删除后,hostPath 卷数据保留在 Node 上
  • 适合需要访问 Node 上文件的场景(如采集容器日志)
  • 不推荐生产环境使用,存在安全风险和可移植性问题

⚠️ 注意:FileOrCreate 只会创建文件,不会自动创建上级目录 。比如 /etc/test/abc.txt,如果 /etc/test 文件夹不存在,Pod 依然启动失败!

3.2 实验:挂载节点目录并验证读写权限

编写 pod-with-hostPath.yaml:

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: busybox
  labels:
    app: busybox
spec:
  volumes:
  - name: datavolume
    hostPath:
      path: /busyboxdir
  containers:
  - name: busybox1
    image: hub.laoma.cloud/library/busybox
    imagePullPolicy: IfNotPresent
    command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']
    volumeMounts:
    - mountPath: /data
      name: datavolume
  - name: busybox2
    image: hub.laoma.cloud/library/busybox
    imagePullPolicy: IfNotPresent
    command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']
    volumeMounts:
    - mountPath: /data
      name: datavolume
      readOnly: false

创建 Pod:

bash 复制代码
root@master30:~# kubectl apply -f pod-with-hostpath.yaml

验证数据写入和共享:

bash 复制代码
root@master30:~# kubectl exec busybox -c busybox1 -- touch /data/b1-f1
root@master30:~# kubectl exec busybox -c busybox2 -- ls /data
b1-f1

登录 Node 查看目录:

bash 复制代码
root@worker32:~# ls /busyboxdir
b1-f1

删除 Pod 后验证数据保留:

bash 复制代码
root@master30:~# kubectl delete pod busybox --force

root@worker32:~# ls /busyboxdir
b1-f1

Pod 删除后,hostPath 卷数据依然保留在 Node 上。


四、NFS 网络存储

4.1 搭建 NFS Server

在 master 节点安装 NFS 服务端:

bash 复制代码
root@master30:~# apt install -y nfs-kernel-server

创建共享目录并写入测试文件:

bash 复制代码
root@master30:~# mkdir -m 777 /nfsshares
root@master30:~# echo hello fb > /nfsshares/index.html

配置 NFS 导出,允许所有客户端访问:

bash 复制代码
root@master30:~# vim /etc/exports
/nfsshares *(rw)

重启 NFS 服务:

bash 复制代码
root@master30:~# systemctl restart nfs-server.service

所有 Worker 节点安装 NFS 客户端并验证:

bash 复制代码
root@work31和32:~# apt install -y nfs-common
root@worker31和32:~# showmount -e master30
Export list for master30:
/nfsshares *

4.2 Pod 直接挂载 NFS

编写 pod-with-nfs.yaml:

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: nginx
  name: nginx
spec:
  volumes:
  - name: nfs
    nfs:
      server: 10.1.8.30
      path: "/nfsshares"
  containers:
  - image: hub.laoma.cloud/library/nginx
    name: nginx
    volumeMounts:
    - name: nfs
      mountPath: "/usr/share/nginx/html"

创建 Pod:

bash 复制代码
root@master30:~# kubectl apply -f pod-with-nfs.yaml

查看 Pod IP 并访问测试:

bash 复制代码
root@master30:~# kubectl get pod -o wide
NAME    READY   STATUS    RESTARTS   AGE   IP            NODE                   NOMINATED NODE   READINESS GATES
nginx   1/1     Running   0          55s   10.224.19.2   worker31.laoma.cloud   <none>           <none>
root@master30:~# curl 10.224.19.2
hello fb

在 Pod 内创建文件,NFS 服务端同步可见:

bash 复制代码
root@master30:~# kubectl exec nginx -- touch /usr/share/nginx/html/test.html
root@master30:~# ls /nfsshares
index.html  test.html

删除 Pod 后,NFS 上的数据依然保留:

bash 复制代码
root@master30:~# kubectl delete pod nginx --force
root@master30:~# ls /nfsshares
index.html  test.html

五、持久化存储 PV 与 PVC

5.1 PV/PVC 架构原理

Kubernetes 使用 PV(Persistent Volume)架构为集群提供持久化存储,核心思想是分离管理员与开发人员的职责:

  • PV(Persistent Volume):集群级别资源,由管理员创建,代表实际存储,只能绑定1个PVC
  • PVC(Persistent Volume Claim):命名空间级别资源,由用户创建,存储申请
  • Pod:通过 PVC 声明存储需求,不关心底层存储细节

开发人员只需要写 PVC 申请多少存储,不需要关心底层是 NFS、Ceph 还是云盘。

5.2 创建 PV 和 PVC

创建 PV(NFS 后端):

yaml 复制代码
root@master30:~# vim pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: web
spec:
  capacity:
    storage: 5Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  nfs:
    path: /nfsshares
    server: 10.1.8.30

应用并查看:

bash 复制代码
root@master30:~# kubectl apply -f pv.yaml 
root@master30:~# kubectl get pv
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
web    5Gi        RWO            Retain           Available                          <unset>     

创建 PVC:

yaml 复制代码
root@master30:~# vim pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: webclaim
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi

应用并查看:

bash 复制代码
root@master30:~# kubectl apply -f pvc.yaml 
root@master30:~# kubectl get pvc
NAME       STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
webclaim   Bound    web      5Gi        RWO                           <unset>                 11s

5.3 Pod 使用 PVC

编写 Pod YAML:

yaml 复制代码
root@master30:~# vim pod-web.yaml
apiVersion: v1
kind: Pod
metadata:
  name: web
  labels:
    name: web
spec:
  containers:
    - image: hub.laoma.cloud/library/nginx
      name: web
      ports:
        - containerPort: 80
          name: web-port
      volumeMounts:
        - name: web-persistent-storage
          mountPath: /usr/share/nginx/html
  volumes:
    - name: web-persistent-storage
      persistentVolumeClaim:
        claimName: webclaim

创建并验证:

bash 复制代码
root@master30:~# kubectl apply -f pod-web.yaml 
root@master30:~# kubectl get pod web -o wide
NAME   READY   STATUS    RESTARTS   AGE   IP            NODE                   NOMINATED NODE   READINESS GATES
web    1/1     Running   0          25s   10.224.19.3   worker31.laoma.cloud   <none>           <none>
root@master30:~# curl 10.224.19.3
hello fb

5.4 PV 访问模式 accessModes

缩写 全称 说明
RWO ReadWriteOnce 卷可被一个节点以读写方式挂载
ROX ReadOnlyMany 卷可被多个节点以只读方式挂载
RWX ReadWriteMany 卷可被多个节点以读写方式挂载
RWOP ReadWriteOncePod 卷只能被单个 Pod 读写(K8s 1.22+)

重要:每个卷同一时刻只能以一种访问模式挂载,即使它支持多种模式。

常见存储后端支持情况:

存储类型 RWO ROX RWX
NFS ✓ ✓ ✓
CephFS ✓ ✓ ✓
Ceph RBD ✓ ✓ -
HostPath ✓ - -
iSCSI ✓ ✓ -

5.5 PV 卷模式 volumeModes

两种卷模式:

  • Filesystem(默认):文件系统模式,挂载到 Pod 目录下
  • Block:原始块设备模式,直接使用块设备,无需文件系统,适合数据库等高性能场景

5.6 PVC 与 PV 匹配规则

PVC 绑定 PV 的匹配条件:

  1. 存储容量:PV 的容量 ≥ PVC 请求容量
  2. 访问模式(AccessModes):PV 必须包含 PVC 要求的访问模式

访问模式没有 ROX<RWO<RWX 的优先级排序!只是能力集合匹配,不是大小比较。

举例:PV 访问模式只有 RWO,PVC 要 RWX → 不能绑定。

  • PVC 申请 RWO → PV 必须支持 RWO
  • PVC 申请 RWX → PV 必须支持 RWX
  • PVC 申请 ROX → PV 必须支持 ROX
  1. StorageClass 一致
    • PV 定义里的 storageClassName 和 PVC 的 storageClassName 必须相同;
    • 两者都为空,代表都不指定 SC,也可以匹配;
    • 一个有 SC、一个为空 → 不匹配。
  2. PV 的 Selector(标签选择器,可选)
    如果 PV 配置了 spec.selector(标签选择器),那么PVC 必须带有匹配的标签,否则无法绑定。

5.7 PV 回收策略

回收策略 说明
Retain(保留) PVC 删除后,PV 变为 Released 状态,数据保留,需手动清理
Recycle(回收) 已废弃,建议使用动态制备
Delete(删除) 删除 PV 同时删除后端存储资源(如云盘)
Retain 策略详解

删除 PVC 后,PV 状态变为 Released:

bash 复制代码
root@master30:~# kubectl delete pod web
root@master30:~# kubectl delete pvc webclaim
root@master30:~# kubectl get pv
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS     CLAIM    STORAGECLASS   VOLUMEATTRIBUTESCLASS  REASON  AGE
web    5Gi        RWO            Retain           Released   storage/webclaim        <unset>                        19m

此时 PV 不会自动绑定新的 PVC,需要手动编辑 PV,删除 claimRef 部分才能重新分配。

如何让 PV 只绑定给特定 PVC?

在 PV 定义中指定 claimRef:

yaml 复制代码
root@master30:~# kubectl edit pv web 
spec:
  claimRef:
    apiVersion: v1
    kind: PersistentVolumeClaim
    name: webclaim                      #  指定PVC 名字
    namespace: storage                  #  指定PVC 所在命名空间(PVC 是命名空间资源,必须写 ns)      
    resourceVersion: "14992"
    uid: e2c9cc0e-65f2-477e-ad62-38ed90ca679f 

六、ConfigMap:配置管理

6.1 ConfigMap 介绍

ConfigMap 用于存储非敏感的配置数据,以键值对形式保存:

  • data:UTF-8 字符串
  • binaryData:二进制数据(base64 编码)
  • 单个 ConfigMap 上限 1 MiB
  • 设计上不适合存大量数据,配置文件和环境变量是最佳场景
  • 挂载默认只读

6.2 ConfigMap 创建方式

方式一:键值对(--from-literal)

bash 复制代码
root@master30:~# kubectl create configmap mysql --from-literal=password=redhat

方式二:单文件(--from-file)

bash 复制代码
root@master30:~# echo Hello World > index.html
root@master30:~# kubectl create configmap web1 --from-file=./index.html

方式三:目录(--from-file=目录)

bash 复制代码
root@master30:~# mkdir web2
root@master30:~# echo error > web2/error.html
root@master30:~# cp index.html web2/
root@master30:~# kubectl create configmap web2 --from-file=./web2

方式四:变量文件(--from-env-file=文件)

bash 复制代码
root@master30:~# vim mysql-env.yaml
MYSQL_ROOT_PASSWORD=123
MYSQL_USER=blog
MYSQL_PASSWORD=123
MYSQL_DATABASE=blog

root@master30:~# kubectl create secret generic mysql --from-env-file=./mysql-env.yaml

6.3 环境变量方式引用

Pod YAML 示例:

yaml 复制代码
root@master30:~# vim pod-env.yaml
apiVersion: v1
kind: Pod
metadata:
  name: mysql
  labels:
    name: mysql
spec:
  containers:
  - image: hub.laoma.cloud/library/mysql:latest
    imagePullPolicy: IfNotPresent
    name: mysql
    ports:
    - containerPort: 3306
      name: mysql
    env:
    - name: MYSQL_ROOT_PASSWORD
      valueFrom:
        configMapKeyRef:
          name: mysql
          key: password

验证环境变量:

bash 复制代码
root@master30:~# kubectl apply -f pod-env.yaml
root@master30:~# kubectl exec -it mysql -- bash -c 'echo $MYSQL_ROOT_PASSWORD'
redhat

6.4 Volume 方式引用

引用整个 ConfigMap:

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: web
  name: web
spec:
  containers:
  - image: hub.laoma.cloud/library/nginx:latest
    imagePullPolicy: IfNotPresent
    name: web
    volumeMounts:
    - name: webcontent
      mountPath: "/usr/share/nginx/html"
  volumes:
  - name: webcontent
    configMap:
      name: web2

只引用特定 Key(items 方式):

yaml 复制代码
volumes:
- name: webcontent                     # 卷名称,和volumeMounts的name对应关联
  configMap:
    name: web2                         # 指定要引用的ConfigMap资源名称
    items:                             # 只挑选CM中部分key进行挂载
      - key: index.html                # 数据源:ConfigMap内部data下的键名
        path: index.html               # 在容器挂载目录下生成的文件名(相对路径)

ConfigMap 以 Volume 方式挂载时,内容会自动更新(约 30 秒同步周期)。


七、Secret:敏感信息加密

7.1 Secret 介绍

Secret 与 ConfigMap 类似,但专门用于保存敏感信息(密码、令牌、密钥):

  • Secret 数据使用 base64 编码(不是加密!只是编码)
  • 避免在 Pod 定义或镜像中硬编码敏感信息
  • etcd 中默认以 base64 编码存储,可开启加密静态数据

7.2 Secret 三种类型

1. generic(通用键值对)
bash 复制代码
root@master30:~# kubectl create secret generic mysecret1 \
  --from-literal=user=tom \
  --from-literal=password1=redhat

查看 Secret 内容(base64 编码):

bash 复制代码
root@master30:~# kubectl get secrets mysecret1 -o yaml
apiVersion: v1
data:
  password1: cmVkaGF0
  user: dG9t
kind: Secret
metadata:
  creationTimestamp: "2026-10-21T15:44:58Z"
  name: mysecret1
  namespace: storage
  resourceVersion: "20330"
  uid: 3577c1c4-3303-4af0-80fa-3a74e251a815
type: Opaque

手动解码验证:

bash 复制代码
root@master30:~# echo -n redhat | base64
cmVkaGF0

redhatroot@master30:~# echo -n cmVkaGF0 | base64 -d
redhat

其他创建方式:

bash 复制代码
# 文件准备
root@master30:~# echo -n tom > user
root@master30:~# echo -n redhat > password1
# 从文件创建
root@master30:~# kubectl create secret generic mysecret2 --from-file=./user --from-file=./password1

# 从 env 文件创建
root@master30:~# echo 'user=tom password=redhat' > env.txt
root@master30:~# kubectl create secret generic mysecret4 --from-env-file=./env.txt
2. docker-registry(镜像仓库凭据)

用于拉取私有 Harbor 镜像:

bash 复制代码
root@master30:~# kubectl create secret docker-registry registry \
  --docker-username=laoma \
  --docker-password=redhat \
  --docker-email=admin@laoma.cloud \
  --docker-server=hub.laoma.cloud
3. tls(TLS 证书)

先生成证书:

bash 复制代码
root@master30:~# mkdir certs && cd certs

# 1. 生成私钥
root@master30:~# openssl genrsa -out www.key 2048

# 2. 生成 CSR
root@master30:~# openssl req -new -key www.key -out www.csr \
  -subj "/C=CN/ST=JS/L=NJ/O=LM/OU=DEVOPS/CN=www.laoma.cloud/emailAddress=admin@laoma.cloud"

# 3. 自签名证书
root@master30:~# openssl x509 -req -days 3650 -in www.csr -signkey www.key -out www.crt

创建 TLS Secret:

bash 复制代码
root@master30:~# kubectl create secret tls www-tls --cert=./www.crt --key=./www.key

7.3 Secret 引用方式

与 ConfigMap 类似,支持环境变量和 Volume 两种方式:

环境变量方式:

yaml 复制代码
root@master30:~# vim pod-with-env-from-secret.yaml
---
apiVersion: v1
kind: Pod
metadata:
  name: mysql
  labels:
    name: mysql
spec:
  containers:
  - image: hub.laoma.cloud/library/mysql:latest
    imagePullPolicy: IfNotPresent
    name: mysql
    ports:
    - containerPort: 3306
      name: mysql
    env:
    - name: MYSQL_ROOT_PASSWORD
      # 原先通过value设置环境变量值
      valueFrom:
        secretKeyRef:
          name: mysecret1
          key: password1

Volume 方式引用整个 Secret:

yaml 复制代码
root@master30:~# echo Hello world > index.html
root@master30:~# echo error > error.html
root@master30:~# kubectl create secret generic web --from-file=./index.html --from-file=./error.html

root@master30:~# vim pod-with-volume-from-secret.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: web
  name: web
spec:
  containers:
  - image: hub.laoma.cloud/library/nginx:latest
    imagePullPolicy: IfNotPresent
    name: web
    volumeMounts:
    - name: webcontent
      # mountPath值是一个挂载点,将secrete中所有键值对挂载过来
      mountPath: "/usr/share/nginx/html"
  volumes:
  - name: webcontent
    secret:
      secretName: web

Volume 方式只引用特定 Key:

yaml 复制代码
root@master30:~# vim pod-secret-volume-single.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: web
  name: web
spec:
  containers:
  - image: hub.laoma.cloud/library/nginx:latest
    imagePullPolicy: IfNotPresent
    name: web
    volumeMounts:
    - name: webcontent
      mountPath: "/usr/share/nginx/html"
  volumes:
  - name: webcontent
    secret:
      secretName: web
      items:
      - key: index.html
        # 通过path设置挂载的文件名,这里我们path值与key一致。
        path: index.html

7.4 文件权限控制

Secret 挂载到容器后,文件默认权限是 644(八进制)。

设置所有文件默认权限(注意:JSON 不支持八进制,用十进制):

yaml 复制代码
volumes:
- name: foo
  secret:
    secretName: mysecret1
    defaultMode: 256    # 十进制 256 = 八进制 400(只读)

设置单个文件权限:

yaml 复制代码
volumes:
- name: foo
  secret:
    secretName: mysecret1
    items:
      - key: user
        path: user
        mode: 511        # 十进制 511 = 八进制 777

八、综合实战:haproxy + 多 Web 后端

需求:

  1. 创建 webapp-1、webapp-2 两个 nginx Pod,各自用 ConfigMap 提供首页内容
  2. 创建 haproxy Pod,配置文件用 ConfigMap 挂载,反向代理到两个 Web Pod

第一步:创建 webapp-1 的 ConfigMap 和 Pod

bash 复制代码
root@master30:~# kubectl create cm webapp-1 \
  --from-literal=index.html="hello webapp-1" \
  --from-literal=error.html="sorry, error."
yaml 复制代码
root@master30:~# vim pod-web1.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: webapp-1
  name: webapp-1
spec:
  containers:
  - name: nginx
    image: hub.laoma.cloud/library/nginx:latest
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - name: config
      mountPath: "/usr/share/nginx/html"
      readOnly: true
  volumes:
  - name: config
    configMap:
      name: webapp-1
      
root@master30:~# kubectl apply -f pod-web1.yaml

第二步:创建 webapp-2 的 ConfigMap 和 Pod

bash 复制代码
root@master30:~# kubectl create cm webapp-2 \
  --from-literal=index.html="hello webapp-2" \
  --from-literal=error.html="sorry, error."
yaml 复制代码
root@master30:~# vim pod-web2.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: webapp-2
  name: webapp-2
spec:
  containers:
  - name: nginx
    image: hub.laoma.cloud/library/nginx:latest
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - name: config
      mountPath: "/usr/share/nginx/html"
      readOnly: true
  volumes:
  - name: config
    configMap:
      name: webapp-2
      
root@master30:~# kubectl apply -f pod-web2.yaml

第三步:获取 Pod IP,编写 haproxy.cfg

bash 复制代码
root@master30:~# kubectl get pods -o wide
NAME       READY   STATUS    RESTARTS   AGE     IP               NODE                   NOMINATED NODE   READINESS GATES
webapp-1   1/1     Running   0          2m14s   10.224.113.131   worker32.laoma.cloud   <none>           <none>
webapp-2   1/1     Running   0          3s      10.224.19.5      worker31.laoma.cloud   <none>           <none>

创建 haproxy 配置文件:

bash 复制代码
root@master30:~# vim haproxy.cfg
global
    daemon                    # 后台守护进程方式运行haproxy
    maxconn 256               # 全局最大并发连接数256

defaults
    mode http                 # 默认工作模式为HTTP七层代理
    timeout connect 5000ms    # 连接后端服务器超时:5秒
    timeout client 50000ms    # 客户端空闲超时:50秒
    timeout server 50000ms    # 后端服务空闲超时:50秒

frontend http-in             # 前端:接收客户端请求
    bind *:8080               # 监听本机所有网卡的8080端口
    default_backend servers   # 默认把请求转发给后端组 servers

backend servers              # 后端服务器池,真实业务节点
    server app1 10.224.113.131:80 check   # 后端节点1,填写webapp‑1的PodIP,check开启健康检查
    server app2 10.224.19.5:80 check   # 后端节点2,填写webapp‑2的PodIP,check开启健康检查

创建 haproxy ConfigMap:

bash 复制代码
root@master30:~# kubectl create cm haproxy.cfg --from-file=haproxy.cfg=./haproxy.cfg

第四步:创建 haproxy Pod

yaml 复制代码
root@master30:~# vim pod-haproxy.yaml
apiVersion: v1
kind: Pod
metadata:
  name: haproxy
spec:
  containers:
  - name: haproxy
    image: hub.laoma.cloud/library/haproxy
    imagePullPolicy: IfNotPresent
    securityContext:
      allowPrivilegeEscalation: true
    volumeMounts:
    - name: config
      mountPath: "/usr/local/etc/haproxy"
      readOnly: true
  volumes:
  - name: config
    configMap:
      name: haproxy.cfg
      
root@master30:~# kubectl apply -f pod-haproxy.yaml

验证负载均衡效果:

bash 复制代码
root@master30:~# kubectl get pod haproxy -o wide
NAME      READY   STATUS    RESTARTS   AGE   IP               NODE                   NOMINATED NODE   READINESS GATES
haproxy   1/1     Running   0          27s   10.224.113.132   worker32.laoma.cloud   <none>           <none>

root@master30:~# curl http://10.224.113.132:8080
root@master30:~# curl http://10.224.113.132:8080

多次访问会轮询返回:

复制代码
hello webapp-1
hello webapp-2

九、总结

本文覆盖了 Kubernetes 存储与配置管理的核心知识点:

类别 核心对象 生命周期 典型用途
临时存储 emptyDir 随 Pod 删除 同 Pod 多容器共享数据
节点存储 hostPath 数据保留在 Node 访问 Node 本地文件(测试用)
网络存储 NFS Volume 数据保留在 NFS 多 Pod 共享网络文件
持久化存储 PV + PVC 由回收策略决定 生产环境持久化数据
配置管理 ConfigMap 独立于 Pod 应用配置、环境变量
敏感信息 Secret 独立于 Pod 密码、证书、仓库凭据

考试重点速记:

  1. emptyDir 随 Pod 删除而消失,hostPath 和 NFS 数据保留
  2. PV 是集群级资源,PVC 是命名空间级资源
  3. PVC 匹配 PV 看三个条件:访问模式、容量、StorageClass
  4. PV 回收策略:Retain(默认保留)、Recycle(已废弃)、Delete(云盘)
  5. ConfigMap 不加密,Secret 用 base64 编码(注意:不是加密!)
  6. ConfigMap 和 Secret 都支持环境变量和 Volume 两种引用方式
  7. Volume 方式挂载 ConfigMap/Secret 支持动态更新,环境变量方式不支持
相关推荐
A-刘晨阳2 小时前
【云原生】nerdctl 部署及使用指南
运维·docker·kubernetes·云计算·1024程序员节·nerdctl
bigbig_ben8 小时前
yearning安装docker
运维·docker·容器
IT小小猪16 小时前
从零搭建K8s 集群
云原生·容器·kubernetes
吴声子夜歌17 小时前
Docker入门与实战——热门镜像介绍
运维·docker·容器
方便面不加香菜17 小时前
容器虚拟化基础--CGroups资源控制
运维·docker·容器
吴声子夜歌20 小时前
Docker入门与实战——Docker命令查询
docker·容器
时间售卖铺21 小时前
MySQL DML 与 DQL 语句详解
运维·数据库·mysql·云原生
孔令飞1 天前
1024程序员节,OneX平台送3套免费Go实战项目
云原生·go·ai编程
吴声子夜歌1 天前
Docker入门与实战——常见问题总结
运维·docker·容器