Kubernetes Volume

环境准备

[root@master30 ~ 09:46:38]# kubectl create namespace storage
namespace/storage created
[root@master30 ~ 10:07:02]# kubectl config set-context --current --namespace storage
Context "kubernetes-admin@kubernetes" modified.
[root@master30 ~ 10:07:32]# mkdir storage

emptyDir

默认情况下,当Pod分配到Node上时,将会创建emptyDir,只要Node上的Pod一直运行,Volume就会一直存。当Pod(不管任何原因)从Node上被删除时,emptyDir也同时会删除,存储的数据也将永久删除。

准备一个包含2个容器的pod,使用emptyDir。

apiVersion: v1
kind: Pod
metadata:
  name: busybox
  labels:
    app: busybox
spec:
  volumes:
  - name: datavolume
    emptyDir: {} 
  containers:
  - name: busybox1
    image: docker.io/library/busybox
    imagePullPolicy: IfNotPresent
    command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']
    volumeMounts:
    - mountPath: /data
      name: datavolume
  - name: busybox2
    image: docker.io/library/busybox
    imagePullPolicy: IfNotPresent
    command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']
    volumeMounts:
    - mountPath: /data
      name: datavolume
[root@master30 ~ 10:07:46]# cd storage/
[root@master30 storage 10:07:57]# vim pod-emptyDir.yaml  ##文件如上
[root@master30 storage 10:08:34]# kubectl apply -f pod-emptyDir.yaml 
pod/busybox created

##查看在那个主机
[root@master30 storage 10:08:59]# kubectl get pod -o wide
NAME      READY   STATUS    RESTARTS   AGE   IP             NODE       NOMINATED NODE   READINESS GATES
busybox   2/2     Running   0          25s   10.224.137.5   worker31   <none>           <none>


# 创建数据
[root@master30 storage 10:11:42]# kubectl exec -it busybox -c busybox2 sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
/ # ls /data/
/ # exit
[root@master30 storage 10:12:10]# kubectl exec -it busybox -c busybox1 sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
/ # ls /data/
/ # cd /data/
/data # touch f1-from-b1
/data # exit
[root@master30 storage 10:16:01]# kubectl exec -it busybox -c busybox2 sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
/ # ls /data/
f1-from-b1
/ # cd /data/
/data # touch f2-from-b2
/data # exit
##发现俩个容器之间都会有相同的文件,因为都是挂载同一个目录

##查看文件来源于宿主机的哪里
##获取容器id
[root@worker31 ~ 10:10:15]# crictl ps
CONTAINER           IMAGE               CREATED             STATE               NAME                ATTEMPT             POD ID              POD
f35e75dc69581       c6348fa86ba0f       10 minutes ago      Running             busybox2            0                   927073f4d41f1       busybox
1005d7803e287       c6348fa86ba0f       10 minutes ago      Running             busybox1            0                   927073f4d41f1       busybox
418ed90fc8fed       637146d27f660       48 minutes ago      Running             calico-node         3                   fe3424c654b77       calico-node-gcz8k
4cafdf0a7dbd7       53c535741fb44       48 minutes ago      Running             kube-proxy          3                   ed3ab523c794c       kube-proxy-7z8tj
##查看目录
[root@worker31 ~ 10:20:25]# crictl inspect 1005d7803e287 | grep data
    "metadata": {
        "containerPath": "/data",
        "hostPath": "/var/lib/kubelet/pods/cfbeedc4-f63e-4f6c-a6e2-44289931bf8b/volumes/kubernetes.io~empty-dir/datavolume",
      "metadata": {
          "container_path": "/data",
          "host_path": "/var/lib/kubelet/pods/cfbeedc4-f63e-4f6c-a6e2-44289931bf8b/volumes/kubernetes.io~empty-dir/datavolume"
          "destination": "/data",
          "source": "/var/lib/kubelet/pods/cfbeedc4-f63e-4f6c-a6e2-44289931bf8b/volumes/kubernetes.io~empty-dir/datavolume",

##宿主机的这个目录下面存在这容器创建的文件
[root@worker31 ~ 10:20:51]# ls /var/lib/kubelet/pods/cfbeedc4-f63e-4f6c-a6e2-44289931bf8b/volumes/kubernetes.io~empty-dir/datavolume
f1-from-b1  f2-from-b2


# 删除pod,验证emptyDir
[root@master30 storage 10:22:34]# kubectl delete pod busybox --force 
Warning: Immediate deletion does not wait for confirmation that the running resource has been terminated. The resource may continue to run on the cluster indefinitely.
pod "busybox" force deleted

[root@worker31 ~ 10:22:56]# ls /var/lib/kubelet/pods/cfbeedc4-f63e-4f6c-a6e2-44289931bf8b/volumes/kubernetes.io~empty-dir/datavolume
ls: cannot access '/var/lib/kubelet/pods/cfbeedc4-f63e-4f6c-a6e2-44289931bf8b/volumes/kubernetes.io~empty-dir/datavolume': No such file or directory
[root@worker31 ~ 10:23:28]# ls /var/lib/kubelet/pods/cfbeedc4-f63e-4f6c-a6e2-44289931bf8b/
ls: cannot access '/var/lib/kubelet/pods/cfbeedc4-f63e-4f6c-a6e2-44289931bf8b/': No such file or directory
##发现podid已经没有了

hostPath

hostPath允许Pod将Node的文件系统中某个目录挂载到Pod内部。pod删除后,hostPath卷数据保留。

准备一个pod,使用hostPath。

[root@master30 storage 10:32:35]# vim pod-hostPath.yaml
apiVersion: v1
kind: Pod
metadata:
  name: busybox
  labels:
    app: busybox
spec:
  volumes:
  - name: datavolume
    hostPath:
      path: /busyboxdir 
  containers:
  - name: busybox1
    image: docker.io/library/busybox
    imagePullPolicy: IfNotPresent
    command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']
    volumeMounts:
    - mountPath: /data
      name: datavolume
  - name: busybox2
    image: docker.io/library/busybox
    imagePullPolicy: IfNotPresent
    command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']
    volumeMounts:
    - mountPath: /data
      name: datavolume
      # 设置readOnly,控制读写,默认值是false,也就是读写访问。
      readOnly: true
#创建pod
[root@master30 storage 10:38:56]# kubectl apply -f pod-hostPath.yaml 
pod/busybox created
##获取容器所在节点
[root@master30 storage 10:39:07]# kubectl get pod busybox -o wide
NAME      READY   STATUS    RESTARTS   AGE   IP             NODE       NOMINATED NODE   READINESS GATES
busybox   2/2     Running   0          27s   10.224.137.6   worker31   <none>           <none>

##获取容器id,查看目录
[root@worker31 ~ 10:39:52]# crictl  ps
CONTAINER           IMAGE               CREATED             STATE               NAME                ATTEMPT             POD ID              POD
165a2171d17ee       c6348fa86ba0f       57 seconds ago      Running             busybox2            0                   bf4bf16ffe0cd       busybox
1ff42035ca413       c6348fa86ba0f       57 seconds ago      Running             busybox1            0                   bf4bf16ffe0cd       busybox
418ed90fc8fed       637146d27f660       About an hour ago   Running             calico-node         3                   fe3424c654b77       calico-node-gcz8k
4cafdf0a7dbd7       53c535741fb44       About an hour ago   Running             kube-proxy          3                   ed3ab523c794c       kube-proxy-7z8tj
[root@worker31 ~ 10:40:06]# crictl inspect 1ff42035ca413 | grep busyboxdir
        "hostPath": "/busyboxdir",
          "host_path": "/busyboxdir"
          "source": "/busyboxdir",
   
##测试
[root@master30 storage 10:40:44]# kubectl exec -it busybox -c busybox1 -- sh
/ # touch /data/b1-f1
/ # exit
[root@master30 storage 10:41:33]# kubectl exec -it busybox -c busybox2 -- sh
/ # ls /data/
b1-f1
/ # touch /data/b2-f2
touch: /data/b2-f2: Read-only file system
/ # 
##发现busybox2只读不可创建  验证了 yaml文件的 readOnly: true
##节点确实存在目录且有先前创建的文件
[root@worker31 ~ 10:40:34]# ls /busyboxdir/
b1-f1

##删除pod容器用来验证hostpath
[root@master30 storage 10:45:31]# kubectl get pods
NAME      READY   STATUS    RESTARTS   AGE
busybox   2/2     Running   0          7m56s
[root@master30 storage 10:47:03]# kubectl delete pod busybox --force 
Warning: Immediate deletion does not wait for confirmation that the running resource has been terminated. The resource may continue to run on the cluster indefinitely.
pod "busybox" force deleted

# pod删除后,hostPath卷数据保留。
[root@worker31 ~ 10:49:26]# ls /busyboxdir/
b1-f1
##过了几分钟目录还存在

NFS 存储

NFS卷,将数据存储在NFS共享中。

准备NFS共享

# 安装 NFS server
[root@master30 storage 11:15:31]# apt install -y nfs-kernel-server

# 安创建NFS目录 修改创建文件夹的权限
[root@master30 storage 11:16:01]# mkdir -m 777 /nfsshares
[root@master30 storage 11:16:10]# echo hello  > /nfsshares/index.html

# 配置共享,允许所有客户端访问
[root@master30 storage 11:16:20]# cat << EOF > /etc/exports
/nfsshares *(rw)
EOF

# 重启 nfs server
[root@master30 storage 11:16:29]# systemctl restart nfs-server.service

# 客户端安装
[root@worker31 ~ 10:50:04]# apt install -y nfs-kernel-server
[root@worker31 ~ 11:16:13]# showmount -e master30
Export list for master30:
/nfsshares *
[root@worker31 ~ 11:16:54]# mount master30:/nfsshares /mnt
[root@worker31 ~ 11:17:30]# ls /mnt
index.html
[root@worker31 ~ 11:17:37]# cat /mnt/index.html 
hello

准备 pod

[root@master30 storage 11:17:56]# vim pod-nfs.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"
#创建容器
[root@master30 storage 11:18:31]# kubectl apply -f pod-nfs.yaml 
pod/nginx created

#查看容器ip
[root@master30 storage 11:18:42]# kubectl get pod -o wide
NAME    READY   STATUS    RESTARTS   AGE   IP            NODE       NOMINATED NODE   READINESS GATES
nginx   1/1     Running   0          10s   10.224.45.6   worker32   <none>           <none>
[root@master30 storage 11:18:52]# curl 10.224.45.6
hello

持久性存储

Kubernetes 使用 persistent volume(PV)架构为集群提供永久存储。

PV 和 PVC 架构

开发人员不知道特定云环境的细节的情况下,只需要使用persistentVolumeClaim(PVC)请求PV资源,实现持久化存储。

  • Persistent Volume,由PersistentVolume API对象定义,代表集群中现有存储。PV的生命周期与使用其的pod无关。Persistent Volume 是集群级别资源。
  • Persistent Volume Claim,由PersistentVolumeClaim API对象定义,代表开发人员请求PV。Persistent Volume Claim 是 namespace 级别资源。

创建 PV 和 PVC

创建 PV

集群管理员可以创建任意数量PV,取决于后端存储。

[root@master30 storage 11:19:17]# 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
[root@master30 storage 11:42:45]# kubectl apply -f pv.yaml 
persistentvolume/web created
[root@master30 storage 11:43:31]# kubectl get pv
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
web    5Gi        RWO            Retain           Available                          <unset>                          15s
创建 PVC

用户创建PVC,pod使用PVC申请特定容量、特定modes和特定存储类别的存储。master监控PVCs,查找匹配的PV或者等待后端存储创建相应PV,然后绑定PV和PVC。

[root@master30 storage 11:44:41]# vim pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: webclaim
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
[root@master30 storage 11:45:35]# kubectl apply -f pvc.yaml 
persistentvolumeclaim/webclaim created
[root@master30 storage 11:46:20]# kubectl get pvc
NAME       STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
webclaim   Bound    web      5Gi        RWO                           <unset>                 12s
创建Pod使用PV
[root@master30 storage 11:46:32]# vim pod-with-pvc.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
##创建
[root@master30 storage 11:47:37]# kubectl apply -f pod-with-pvc.yaml 
pod/web created
##查看ip
[root@master30 storage 11:48:16]# kubectl get pod -o wide
NAME   READY   STATUS    RESTARTS   AGE   IP            NODE       NOMINATED NODE   READINESS GATES
web    1/1     Running   0          11s   10.224.45.7   worker32   <none>           <none>
ce'shi
[root@master30 storage 11:48:27]# curl 10.224.45.7
hello

PV accessModes

PersistentVolume 卷访问模式有:

  • ReadWriteOnce,卷可以被一个节点以读写方式挂载,也允许同一节点上的多个 Pod 读写访问卷。
  • ReadOnlyMany,卷可以被多个节点以只读方式挂载。
  • ReadWriteMany,卷可以被多个节点以读写方式挂载。
  • ReadWriteOncePod,卷可以被单个 Pod 以读写方式挂载。 如果你想确保整个集群中只有一个 Pod 可以读取或写入该 PVC, 请使用 ReadWriteOncePod 访问模式。这只支持 CSI 卷以及需要 Kubernetes 1.22 以上版本。

在命令行接口(CLI)中,访问模式也使用以下缩写形式:

  • RWO - ReadWriteOnce
  • ROX - ReadOnlyMany
  • RWX - ReadWriteMany
  • RWOP - ReadWriteOncePod

不同存储后端支持不同的模式:

卷插件ReadWriteOnceReadOnlyManyReadWriteManyReadWriteOncePod
AzureFile✓✓✓-
CephFS✓✓✓-
CSI取决于驱动取决于驱动取决于驱动取决于驱动
FC✓✓--
FlexVolume✓✓取决于驱动-
GCEPersistentDisk✓✓--
Glusterfs✓✓✓-
HostPath✓---
iSCSI✓✓--
NFS✓✓✓-
RBD✓✓--
VsphereVolume✓--(Pod 运行于同一节点上时可行)-
PortworxVolume✓-✓-

重要: 每个卷同一时刻只能以一种访问模式挂载,即使该卷能够支持多种访问模式。 例如,一个 GCEPersistentDisk 卷可以被某节点以 ReadWriteOnce 模式挂载,或者被多个节点以 ReadOnlyMany 模式挂载,但不可以同时以两种模式挂载。

常见的NAS存储都支持三种存储模式:ReadWriteOnce、ReadOnlyMany、ReadWriteMany。

PV volumeModes

特性状态: Kubernetes v1.18 [stable]

针对 PV 持久卷,Kubernetes 支持两种卷模式(volumeModes):Filesystem(文件系统) 和 Block(块)。 volumeMode 是一个可选的 API 参数。 如果该参数被省略,默认的卷模式是 Filesystem。

  • Filesystem 卷,会被 Pod 挂载(Mount) 到某个目录。 如果卷的存储来自某块设备而该设备目前为空,Kuberneretes 会在第一次挂载卷之前在设备上创建文件系统。
  • Block 卷,会被作为原始块设备来使用。 这类卷以块设备的方式交给 Pod 使用,其上没有任何文件系统。 这种模式对于为 Pod 提供一种使用最快可能方式来访问卷而言很有帮助, Pod 和卷之间不存在文件系统层。另外,Pod 中运行的应用必须知道如何处理原始块设备。 关于如何在 Pod 中使用 volumeMode: Block 的卷, 可参阅原始块卷支持。

PVC与PV匹配规则

  • PV的mode必须高于PVC申请的最低要求:mode 优先级,可简单理解为ROX<RWO<RWX。例如,用户请求RWO模式PV,但是目前只有NFS PV(RWO+ROX+RWX),PVC将匹配NFS。
  • 容量满足最低要求:具有相同modes卷会被分组,然后根据size分类(由小到大)。
  • pv storage classes:用于对pv进行分类,pvc可以根据storageClassName参数申请特定类型pv。如果pv设置了storageClassName,那么 pvc 申请资源的时候也要指定storageClassName。例如,storageClassName指定为 ns1-storage,ns1中pvc申请也指定storageClassName为ns1-storage。

PV 回收策略

当用户不再使用其存储卷时,他们可以从 API 中将 PVC 对象删除, 从而允许该资源被回收再利用。PersistentVolume 对象的回收策略告诉集群, 当其被从申领中释放时如何处理该数据卷。

PersistentVolume 回收策略支持:Retain(保留)、Recycle(回收)、Delete(删除)。

Retain(保留)

回收策略 Retain 使得用户可以手动回收资源。当 PersistentVolumeClaim 对象被删除时,PersistentVolume 卷仍然存在,对应的数据卷被视为"已释放(released)"。 由于卷上仍然保留上一次关联的pvc信息,清理掉上一次关联的pvc信息才可分配给其他pvc。Retain(保留)策略是默认策略。

示例:

[root@master30 storage 13:49:52]# vim pv-pvc-Retain.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: web
spec:
  capacity:
    storage: 5Gi
  #persistentVolumeReclaimPolicy: Retain
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  nfs:
    path: /nfsshares
    server: 10.1.8.30
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: webclaim
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
[root@master30 storage 13:55:45]# kubectl apply -f pv-pvc-Retain.yaml 
persistentvolume/web unchanged
persistentvolumeclaim/webclaim unchanged
[root@master30 storage 13:56:25]# kubectl get pv
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM              STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
web    5Gi        RWO            Retain           Bound    storage/webclaim                  <unset>                          133m
[root@master30 storage 13:56:36]# kubectl get pvc
NAME       STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
webclaim   Bound    web      5Gi        RWO                           <unset>                 130m

删除 pvc 验证

[root@master30 storage 13:57:30]# kubectl delete pvc webclaim 
persistentvolumeclaim "webclaim" deleted
##显示一直在删除,不回显示
[root@master30 ~ 13:57:59]# kubectl get pv
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM              STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
web    5Gi        RWO            Retain           Bound    storage/webclaim                  <unset>                          134m
[root@master30 ~ 13:58:17]# vim pvc-db.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: dbclaim
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
[root@master30 ~ 13:59:49]# kubectl apply -f pvc-db.yaml 
persistentvolumeclaim/dbclaim created
##无法绑定
[root@master30 ~ 14:00:02]# kubectl get pvc dbclaim 
NAME      STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
dbclaim   Pending                                                     <unset>                 13s

# 手动清理卷上claimRef信息,删除claimRef部分
[root@master30 ~]# kubectl edit pv web
# 删除claimRef部分
  # claimRef:
  #   apiVersion: v1
  #   kind: PersistentVolumeClaim
  #   name: webclaim
  #   namespace: test
  #   resourceVersion: "112157"
  #   uid: 0839cdc1-81bb-11e9-9ca8-52540000fa0a
  # 删除claimRef部分
# dbclaim 绑定成功
[root@master30 ~]# kubectl get pvc dbclaim 
NAME      STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
dbclaim   Bound    web      5Gi        RWO                           3m49s

**注意:**删除pv,并不会删除后端存储中数据。

Recycle(回收)

**警告:**回收策略 Recycle 已被废弃。取而代之的建议方案是使用动态制备。

Recycle(回收)策略的效果与 Retaine(保留)一致。

Delete(删除)

对于支持 Delete 回收策略的卷插件,删除动作会将 PersistentVolume 对象从 Kubernetes 中移除,同时也会从外部基础设施(如 AWS EBS 或 GCE PD 卷)中移除所关联的存储资产。

Kubernetes Configure

环境准备

[root@master30 ~ 14:11:47]# kubectl create ns configure
namespace/configure created
[root@master30 ~ 14:15:00]# kubectl config set-context --current --namespace configure
Context "kubernetes-admin@kubernetes" modified.

ConfigMap

ConfigMap 介绍

ConfigMap 是一个存储其他对象所需要使用的配置的 API 对象。 ConfigMap 使用 data 和 binaryData 字段,保存键值对数据。

  • data 字段,用来保存 UTF-8 字符串。
  • binaryData 字段,用来保存二进制数据(base64 编码的字串) 。

ConfigMap 的名字必须是一个合法的 DNS 子域名。data 或 binaryData 字段下面的每个键的名称都必须由字母、数字或者 -、_ 或 . 组成。在 data 下保存的键名不可以与在 binaryData 下出现的键名有重叠。

从 v1.19 开始,你可以添加一个 immutable 字段到 ConfigMap 定义中, 创建不可变更的 ConfigMap

ConfigMap 使用建议

  • 使用 ConfigMap 存储配置数据,不存数据 / 文件,上限 = 1 MiB。
  • **ConfigMap 在设计上不是用来保存大量数据的。**如果你需要保存大量数据,考虑使用挂载存储卷或者使用独立的数据库或者文件服务。

ConfigMap 创建

键值对类型
[root@master30 ~ 14:27:55]# kubectl create configmap mysql --from-literal=password=redhat
configmap/mysql created
[root@master30 ~ 14:28:12]# kubectl get configmaps mysql -o yaml
apiVersion: v1
data:
  password: redhat <<<<<
kind: ConfigMap
metadata:
  creationTimestamp: "2026-06-25T06:28:12Z"
  name: mysql
  namespace: configure
  resourceVersion: "21786"
  uid: db5484bb-29a0-4295-91e5-00daddab68ad
文件类型
[root@master30 ~ 14:28:27]# echo hello > index.html
[root@master30 ~ 14:29:10]# kubectl create configmap web1 --from-file=./index.html 
configmap/web1 created

[root@master30 ~ 14:30:24]# kubectl get configmaps web1 -o yaml
apiVersion: v1
data:
  index.html: |
    hello    <<<<<<<<
kind: ConfigMap
metadata:
  creationTimestamp: "2026-06-25T06:29:32Z"
  name: web1
  namespace: configure
  resourceVersion: "21905"
  uid: 5e9e9171-388d-4bde-b3bb-a59d778d138a
目录类型
[root@master30 ~ 14:30:28]# echo error > error.html
[root@master30 ~ 14:31:28]# mkdir web2
[root@master30 ~ 14:31:32]# mv error.html index.html web2
[root@master30 ~ 14:31:46]# kubectl create configmap web2 --from-file=./web2
configmap/web2 created
[root@master30 ~ 14:32:07]# kubectl get configmaps web2 -o yaml
apiVersion: v1
data:
  error.html: |  
    error     <<<<<<<<<
  index.html: |
    hello      <<<<<<<<<<
kind: ConfigMap
metadata:
  creationTimestamp: "2026-06-25T06:32:07Z"
  name: web2
  namespace: configure
  resourceVersion: "22138"
  uid: b89f5f62-9df1-4d34-b8b0-23ef97df0d74

ConfigMap 引用

环境变量方式引用

**注意:**环境变量属于特定容器级别。

[root@master30 ~ 14:39:02]# vim pod-cm-env.yaml
apiVersion: v1
kind: Pod
metadata:
  name: mysql
  labels:
    name: mysql
spec:
  containers:
  - image: docker.io/library/mysql:latest
    imagePullPolicy: IfNotPresent
    name: mysql
    ports:
    - containerPort: 3306
      name: mysql
    env:
    - name: MYSQL_ROOT_PASSWORD
      # 原先通过value设置环境变量值
      valueFrom:
        configMapKeyRef:
          name: mysql
          key: password
[root@master30 ~ 14:39:58]# kubectl apply -f pod-cm-env.yaml 
pod/mysql created
[root@master30 ~ 14:41:03]# kubectl get pod -o wide
NAME    READY   STATUS    RESTARTS   AGE   IP             NODE       NOMINATED NODE   READINESS GATES
mysql   1/1     Running   0          15s   10.224.137.1   worker31   <none>           <none>
[root@master30 ~ 14:41:18]# kubectl exec -it mysql -- bash -c 'echo $MYSQL_ROOT_PASSWORD'
redhat
[root@master30 ~ 14:41:58]# apt install -y mysql-client
[root@master30 ~ 14:46:38]# mysql -uroot -predhat -h 10.224.137.1
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 9
Server version: 9.6.0 MySQL Community Server - GPL

Copyright (c) 2000, 2026, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
4 rows in set (0.00 sec)

mysql> quit
Bye
[root@master30 ~ 14:47:17]# kubectl exec -it mysql -- bash
bash-5.1# echo $MYSQL_ROOT_PASSWORD
redhat
bash-5.1# 
以 volume 方式引用

**注意:**volumes属于pod级别,通过volumeMounts挂载。

引用整体
[root@master30 ~ 14:48:59]# vim pod-cm-volume-all.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
    configMap:
      name: web2
[root@master30 ~ 15:08:50]# kubectl apply -f pod-cm-volume-all.yaml 
pod/web created
[root@master30 ~ 15:08:59]# kubectl get pod web -o wide
NAME   READY   STATUS    RESTARTS   AGE   IP            NODE       NOMINATED NODE   READINESS GATES
web    1/1     Running   0          17s   10.224.45.1   worker32   <none>           <none>

[root@master30 ~ 15:10:00]# cat ./web2/error.html
error
[root@master30 ~ 15:10:37]# cat ./web2/index.html 
hello
[root@master30 ~ 15:10:41]# curl 10.224.45.1
hello
[root@master30 ~ 15:10:51]# curl 10.224.45.1/error.html
error

综合案例:haproxy+web

  1. 创建一个名称为 haproxy 的pod,使用镜像 haproxy,pod haproxy 的配置文件保存在 configmap 中,通过 volume 方式挂载到 /usr/local/etc/haproxy/haproxy.cfg。将流量转发到pod webapp-1 和 webapp-2。

创建第一个pod,名称为webapp-1,使用镜像nginx。为该pod创建一个cm,名称为webapp-1,该cm保存两个值

[root@master30 ~ 15:14:05]# kubectl create cm webapp-1 --from-literal=index.html="hello webapp-1" --from-literal=error.html="sorry,error"
configmap/webapp-1 created
[root@master30 ~ 15:19:03]# vim pod-webapp-1.yaml
apiVersion: v1
kind: Pod
metadata:
  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 ~ 15:19:33]# kubectl apply -f pod-webapp-1.yaml 
pod/webapp-1 created

创建第二个pod,名称为webapp-2,使用镜像nginx。为该pod创建一个cm,名称为webapp-2,该cm保存两个值

[root@master30 ~ 15:19:44]# kubectl create cm webapp-2 --from-literal=index.html="hello webapp-2" --from-literal=error.html="sorry,error"
configmap/webapp-2 created
[root@master30 ~ 15:20:07]# vim pod-webapp-2.yaml
apiVersion: v1
kind: Pod
metadata:
  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 ~ 15:20:27]# kubectl apply -f pod-webapp-2.yaml 
pod/webapp-2 created

创建haproxy配置文件

[root@master30 ~ 15:21:12]# vim haproxy.cfg
global
    daemon
    maxconn 256

defaults
    mode http
    timeout connect 5000ms
    timeout client 50000ms
    timeout server 50000ms

frontend http-in
    bind *:8080
    default_backend servers

backend servers
    server app1 10.224.137.2:80 check
    server app2 10.224.45.2:80 check
[root@master30 ~ 15:22:09]# kubectl create cm haproxy.cfg --from-file=haproxy.cfg=./haproxy.cfg 
configmap/haproxy.cfg created

#创建 HAProxy Pod
[root@master30 ~ 15:22:42]# 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 ~ 15:23:29]# kubectl apply -f pod-haproxy.yaml 
pod/haproxy created

#查看IP
[root@master30 ~ 15:23:45]# kubectl get pod -o wide
NAME       READY   STATUS    RESTARTS   AGE     IP             NODE       NOMINATED NODE   READINESS GATES
haproxy    1/1     Running   0          11s     10.224.45.3    worker32   <none>           <none>
webapp-1   1/1     Running   0          4m12s   10.224.137.2   worker31   <none>           <none>
webapp-2   1/1     Running   0          3m16s   10.224.45.2    worker32   <none>           <none>

##轮询访问测试
[root@master30 ~ 15:23:56]# curl 10.224.45.3:8080
hello webapp-1[root@master30 ~ 15:24:07]# curl 10.224.45.3:8080
hello webapp-1[root@master30 ~ 15:24:09]# curl 10.224.45.3:8080
hello webapp-2[root@master30 ~ 15:24:10]# 

Secret

Secret 介绍

Secret 是一种包含少量敏感信息的对象,例如密码、令牌或密钥。 这样的信息可能会被放在 Pod 规约中或者镜像中。 使用 Secret 意味着你不需要在应用程序代码中包含机密数据。

创建 Secret 可以独立于使用它们的 Pod, 减少在创建、查看和编辑 Pod 的工作流程中暴露 Secret(及其数据)的风险。 Kubernetes 和在集群中运行的应用程序也可以对 Secret 采取额外的预防措施, 例如避免将敏感数据写入非易失性存储。

Secret 类似于 ConfigMap 但专门用于保存机密数据。

Secret 与ConfigMap的区别在于:Secret对数据编码,ConfigMap不对数据编码。

Secret 类型

  • generic:定义键值对,对变量值加密,类型为Opaque。
  • docker-registry:用于访问registry仓库的凭据,类型kubernetes.io/dockerconfigjson。
  • tls:保存TLS公钥和私钥,类型为Opaque。

Secret 创建

基于键值对

一般用于传递变量值。

[root@master30 ~ 15:44:36]# kubectl create secret generic mysecret1 --from-literal=user=tom --from-literal=password1=redhat --from-literal=password2=redhat
secret/mysecret1 created
[root@master30 ~ 16:37:16]# kubectl get secret
NAME        TYPE     DATA   AGE
mysecret1   Opaque   3      11s
[root@master30 ~ 16:37:27]# kubectl get secret mysecret1 -o yaml
apiVersion: v1
data:
  password1: cmVkaGF0
  password2: cmVkaGF0
  user: dG9t
kind: Secret
metadata:
  creationTimestamp: "2026-06-25T08:37:16Z"
  name: mysecret1
  namespace: configure
  resourceVersion: "33563"
  uid: fccc93e3-d20b-49b7-a8b2-a3cf691d6ebc
type: Opaque
[root@master30 ~ 16:37:52]# echo -n tom | base64
dG9t
[root@master30 ~ 16:39:33]# echo -n redhat | base64
cmVkaGF0
[root@master30 ~ 16:40:02]# echo -n cmVkaGF0 | base64 -d
redhat
基于普通文件

一般用于传递配置文件。

[root@master30 ~ 16:41:16]# echo -n tom > user
[root@master30 ~ 16:41:33]# echo -n redhat > password1
[root@master30 ~ 16:41:45]# echo -n redhat > password2
[root@master30 ~ 16:41:47]# kubectl create secret generic mysecret2 --from-file=./user --from-file=./password1 --from-file=./password2
secret/mysecret2 created
[root@master30 ~ 16:43:05]# kubectl get secret mysecret2 -o yaml
apiVersion: v1
data:
  password1: cmVkaGF0
  password2: cmVkaGF0
  user: dG9t
kind: Secret
metadata:
  creationTimestamp: "2026-06-25T08:43:05Z"
  name: mysecret2
  namespace: configure
  resourceVersion: "34089"
  uid: aaf6b4bc-a8c8-4dd8-9df2-38b13fb465d8
type: Opaque
基于键值对内容的文件
[root@master30 ~ 16:48:16]#  echo 'user=tom
password1=redhat
password2=redhat' > env.txt
[root@master30 ~ 16:48:33]# kubectl create secret generic mysecret1 --from-env-file=./env.txt 
secret/mysecret1 created
[root@master30 ~ 16:49:07]# kubectl get secret mysecret1 -o yaml
apiVersion: v1
data:
  password1: cmVkaGF0
  password2: cmVkaGF0
  user: dG9t
kind: Secret
metadata:
  creationTimestamp: "2026-06-25T08:49:07Z"
  name: mysecret1
  namespace: configure
  resourceVersion: "34641"
  uid: a5402999-c2b1-4e06-a051-5e454059aa7d
type: Opaque
基于目录

文件名用作key名,文件内容用作value。

[root@master30 ~]# kubectl create secret generic mysecret5 --from-file=./config
secret/my-secret5 created

[root@master30 ~]# kubectl get secrets mysecret5 -o yaml
apiVersion: v1
data:
  password1: cmVkaGF0
  password2: cmVkaGF0
  user: dG9t
kind: Secret
metadata:
......
  name: mysecret5
  namespace: laoma
  resourceVersion: "26687"
  selfLink: /api/v1/namespaces/laoma/secrets/mysecret4
  uid: 04eccd8b-8686-4856-82c3-0ac697d75746
type: Opaque
基于 yaml 文件
apiVersion: v1
kind: Secret
metadata:
  name: mysecret6
type: Opaque
data:
  user: dG9t
  password1: cmVkaGF0
  password2: cmVkaGF0

yaml格式中变量值使用转换后的值。

更多推荐