Kubernetes—Volume与Configure
Kubernetes Volume
环境准备
root@master30~ 16:45:06# kubectl create ns storage
namespace/storage created
root@master30~ 16:45:17# kubectl config set-context --current --namespace storage
Context "kubernetes-admin@kubernetes" modified.
一、Volume类型
Kubernetes支持Volume类型有:
root@master30~ 16:48:29# kubectl explain pod.spec.volumes | grep '^ [a-zA-Z]'
awsElasticBlockStore <AWSElasticBlockStoreVolumeSource>
azureDisk <AzureDiskVolumeSource>
azureFile <AzureFileVolumeSource>
cephfs <CephFSVolumeSource>
cinder <CinderVolumeSource>
configMap <ConfigMapVolumeSource>
csi <CSIVolumeSource>
downwardAPI <DownwardAPIVolumeSource>
emptyDir <EmptyDirVolumeSource>
ephemeral <EphemeralVolumeSource>
fc <FCVolumeSource>
flexVolume <FlexVolumeSource>
flocker <FlockerVolumeSource>
gcePersistentDisk <GCEPersistentDiskVolumeSource>
gitRepo <GitRepoVolumeSource>
glusterfs <GlusterfsVolumeSource>
hostPath <HostPathVolumeSource>
iscsi <ISCSIVolumeSource>
name <string> -required-
nfs <NFSVolumeSource>
persistentVolumeClaim <PersistentVolumeClaimVolumeSource>
photonPersistentDisk <PhotonPersistentDiskVolumeSource>
portworxVolume <PortworxVolumeSource>
projected <ProjectedVolumeSource>
quobyte <QuobyteVolumeSource>
rbd <RBDVolumeSource>
scaleIO <ScaleIOVolumeSource>
secret <SecretVolumeSource>
storageos <StorageOSVolumeSource>
vsphereVolume <VsphereVirtualDiskVolumeSource>
最常用的类型为:
- emptyDir:临时存储,Pod 内多容器共享缓存、日志、临时文件;Pod 删除数据直接清空。
- hostPath:直接挂载宿主机目录 / 文件到容器。缺点:绑定节点,Pod 漂移数据丢失。
- configMap / secret:配置类卷,放配置文件、环境变量、密码证书。
- configMap:普通明文配置
- secret:密码、密钥、证书加密存储
- persistentVolumeClaim(PVC):标准持久化存储,搭配 PV/StorageClass,真正持久化数据,Pod 重建、漂移数据不丢。
- nfs:传统共享网络存储,简单易用,多节点同时读写。
二、emptyDir
默认情况下,当Pod分配到Node上时,将会创建emptyDir,只要Node上的Pod一直运行,Volume就会一直存。当Pod(不管任何原因)从Node上被删除时,emptyDir也同时会删除,存储的数据也将永久删除。
示例:
root@master30~ 16:48:35# vim pod-with-emptyDir.yaml
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
配置说明:
- volumeMounts.name绑定关联 spec.volumes里定义的卷
- volumeMounts.mountPath为容器内挂载点
- spec.volumes:定义卷,是默认卷类型。
- spec.containers.volumeMounts:引用卷
#创建Pod
root@master30~ 17:06:48# kubectl apply -f pod-with-emptyDir.yaml
pod/busybox created
#获取容器ID
root@master30~ 17:07:05# kubectl describe pod busybox | grep 'Container ID'
Container ID: containerd://0f0b94abaf6146a2c753c57e8336ad86d5c8bd595a3aaa23a9ae4c11fee1673f
Container ID: containerd://47a4afa3c123fc1d780e1a03f197316ce5a366b97cd2c573533894c4d1987416
#查看容器所在节点
root@master30~ 17:07:29# kubectl describe pod busybox | grep Node
Node: worker32.hgq.cloud/10.1.8.32
Node-Selectors: <none>
#在worker32上查看容器挂载情况
root@worker32~ 17:10:04# crictl inspect 47 | grep datavolume
"hostPath": "/var/lib/kubelet/pods/d5896fbe-d5f5-4d45-a251-678c10b911a7/volumes/kubernetes.io~empty-dir/datavolume",
"host_path": "/var/lib/kubelet/pods/d5896fbe-d5f5-4d45-a251-678c10b911a7/volumes/kubernetes.io~empty-dir/datavolume"
"source": "/var/lib/kubelet/pods/d5896fbe-d5f5-4d45-a251-678c10b911a7/volumes/kubernetes.io~empty-dir/datavolume",
#创建并查看数据
root@master30~ 17:08:51# kubectl exec busybox -c busybox1 -- touch /data/test1
root@master30~ 17:12:20# kubectl exec busybox -c busybox2 -- ls /data
test1
#在worker32节点上查看数据
root@worker32~ 17:10:19# ls /var/lib/kubelet/pods/d5896fbe-d5f5-4d45-a251-678c10b911a7/volumes/kubernetes.io~empty-dir/datavolume
test1
#删除Pod,验证emptyDir
root@master30~ 17:12:34# kubectl delete pod busybox
pod "busybox" deleted
root@worker32~ 18:42:34# ls /var/lib/kubelet/pods/d5896fbe-d5f5-4d45-a251-678c10b911a7/volumes/kubernetes.io~empty-dir/datavolume
ls: cannot access '/var/lib/kubelet/pods/d5896fbe-d5f5-4d45-a251-678c10b911a7/volumes/kubernetes.io~empty-dir/datavolume': No such file or directory
三、hostPath
hostPath允许Pod将Node的文件系统中某个目录挂载到Pod内部。pod删除后,hostPath卷数据保留。
示例:
root@master30~ 18:45:44# vim pod-with-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~ 18:47:11# kubectl apply -f pod-with-hostPath.yaml
pod/busybox created
#查看容器ID
root@master30~ 18:47:25# kubectl describe pod busybox | grep 'Container ID'
Container ID: containerd://f8f2d8a1c55ac9504758ca70dc5776b3a63b2238f6a8bce34d9bca8811c9eaf3
Container ID: containerd://373e1bfdfbdad56f1c621d2d9567b8d3c48f0a3f3ea71a54fc80a60832696830
#获取容器所在节点
root@master30~ 18:47:55# kubectl describe pod busybox | grep Node
Node: worker31.hgq.cloud/10.1.8.31
Node-Selectors: <none>
#查看挂载详情
root@worker31~ 18:49:18# crictl inspect 37 | grep /data
"containerPath": "/data",
"container_path": "/data",
"destination": "/data",
root@worker31~ 18:49:44# crictl inspect 37 | grep busyboxdir
"hostPath": "/busyboxdir",
"host_path": "/busyboxdir",
"source": "/busyboxdir",
#创建数据
root@master30~ 18:48:31# kubectl exec busybox -c busybox1 -- touch /data/test1
root@master30~ 18:54:23# kubectl exec busybox -c busybox2 -- ls /data
test1
root@master30~ 18:54:38# kubectl exec busybox -c busybox2 -- touch /data/test2
touch: /data/test2: Read-only file system
command terminated with exit code 1
#因为busybox2被设置为只读
#在worker31上查看数据
root@worker31~ 18:49:53# ls /busyboxdir/
test1
#pod删除后,hostPath卷数据保留
root@master30~ 18:54:58# kubectl delete pod busybox
pod "busybox" deleted
root@worker31~ 18:56:19# ls /busyboxdir/
test1
四、NFS存储
NFS卷,将数据存储在NFS共享中。
准备NFS共享
#安装NFS server
root@worker30 18:58:04# apt install -y nfs-kernel-server
#创建NFS目录,修改文件夹权限
root@master30~ 19:06:45# mkdir -m 777 /nfsshares
root@master30~ 19:06:53# echo hello world > /nfsshares/index.html
#配置共享,允许所有客户端访问
root@master30~ 19:07:07# cat << EOF > /etc/exports
> /nfsshares/ *(rw)
> EOF
#重启服务
root@master30~ 19:07:40# systemctl restart nfs-server.service
#客户端安装
root@worker31~ 19:05:56# apt install -y nfs-kernel-server
root@worker32~ 18:44:04# apt install -y nfs-kernel-server
准备Pod
root@master30~ 19:08:00# vim pod-with-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: docker.io/library/nginx
name: nginx
imagePullPolicy: IfNotPresent
volumeMounts:
- name: nfs
mountPath: "/usr/share/nginx/html"
#创建Pod
root@master30~ 19:09:22# kubectl apply -f pod-with-nfs.yaml
pod/nginx created
#查看容器ID与所在节点
root@master30~ 19:12:46# kubectl describe pod nginx | grep 'Container ID'
Container ID: containerd://ba054b61f4d4a044d9c4b5abe38201c26c7410ef4cf8c28b24ad0dd220815fe0
root@master30~ 19:15:29# kubectl describe pod nginx | grep Node
Node: worker32.hgq.cloud/10.1.8.32
Node-Selectors: <none>
#查看挂载详情
root@worker32~ 19:18:01# crictl inspect ba | grep -e nfs -e html
"containerPath": "/usr/share/nginx/html",
"hostPath": "/var/lib/kubelet/pods/7fdcab39-2c3c-4e54-8a32-43f805e77cd5/volumes/kubernetes.io~nfs/nfs",
"container_path": "/usr/share/nginx/html",
"host_path": "/var/lib/kubelet/pods/7fdcab39-2c3c-4e54-8a32-43f805e77cd5/volumes/kubernetes.io~nfs/nfs"
"destination": "/usr/share/nginx/html",
"source": "/var/lib/kubelet/pods/7fdcab39-2c3c-4e54-8a32-43f805e77cd5/volumes/kubernetes.io~nfs/nfs",
#访问容器
root@master30~ 19:15:44# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 6m32s 10.224.96.217 worker32.hgq.cloud <none> <none>
root@master30~ 19:18:53# curl 10.224.96.217
hello world
#创建数据
root@master30~ 19:19:51# kubectl exec nginx -- touch /usr/share/nginx/html/test.html
root@master30~ 19:20:28# ls /nfsshares/
index.html test.html
#Pod删除后,nfs数据卷保留
pod "nginx" deleted
root@master30~ 19:21:26# ls /nfsshares/
index.html test.html
五、持久性存储
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,取决于后端存储,这里采用NFS后端存储。
示例:
root@master30~ 19:23:29# 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~ 19:23:54# kubectl apply -f pv.yaml
persistentvolume/web created
root@master30~ 19:33:00# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
web 5Gi RWO Retain Available <unset> 7s
创建 PVC
用户创建PVC,pod使用PVC申请特定容量、特定modes和特定存储类别的存储。master监控PVCs,查找匹配的PV或者等待后端存储创建相应PV,然后绑定PV和PVC。
示例:
root@master30~ 19:33:07# vim pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: webclaim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
root@master30~ 19:34:13# kubectl apply -f pvc.yaml
persistentvolumeclaim/webclaim created
root@master30~ 19:34:59# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
webclaim Bound web 5Gi RWO <unset> 4s
创建Pod使用PV
root@master30~ 19:41:41# vim pod-with-pvc.yaml
apiVersion: v1
kind: Pod
metadata:
name: web
labels:
name: web
spec:
containers:
- image: docker.io/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~ 19:44:36# kubectl apply -f pod-with-pvc.yaml
pod/web created
#测试
root@master30~ 19:47:47# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web 1/1 Running 0 49s 10.224.128.13 worker31.hgq.cloud <none> <none>
root@master30~ 19:47:55# curl 10.224.128.13
hello world
PV accessModes
PersistentVolume 卷访问模式有:
-
ReadWriteOnce,卷可以被一个节点以读写方式挂载,也允许同一节点上的多个 Pod 读写访问卷。
-
ReadOnlyMany,卷可以被多个节点以只读方式挂载。
-
ReadWriteMany,卷可以被多个节点以读写方式挂载。
-
ReadWriteOncePod,卷可以被单个 Pod 以读写方式挂载。
在命令行接口(CLI)中,访问模式也使用以下缩写形式:
- RWO - ReadWriteOnce
- ROX - ReadOnlyMany
- RWX - ReadWriteMany
- RWOP - ReadWriteOncePod
不同存储后端支持不同的模式:
| 卷插件 | ReadWriteOnce | ReadOnlyMany | ReadWriteMany | ReadWriteOncePod |
|---|---|---|---|---|
| AzureFile | ✓ | ✓ | ✓ | - |
| CephFS | ✓ | ✓ | ✓ | - |
| CSI | 取决于驱动 | 取决于驱动 | 取决于驱动 | 取决于驱动 |
| FC | ✓ | ✓ | - | - |
| FlexVolume | ✓ | ✓ | 取决于驱动 | - |
| GCEPersistentDisk | ✓ | ✓ | - | - |
| Glusterfs | ✓ | ✓ | ✓ | - |
| HostPath | ✓ | - | - | - |
| iSCSI | ✓ | ✓ | - | - |
| NFS | ✓ | ✓ | ✓ | - |
| RBD | ✓ | ✓ | - | - |
| VsphereVolume | ✓ | - | -(Pod 运行于同一节点上时可行) | - |
| PortworxVolume | ✓ | - | ✓ | - |
重要: 每个卷同一时刻只能以一种访问模式挂载,即使该卷能够支持多种访问模式。 例如,一个 GCEPersistentDisk 卷可以被某节点以 ReadWriteOnce 模式挂载,或者被多个节点以 ReadOnlyMany 模式挂载,但不可以同时以两种模式挂载。
常见的NAS存储(Network Attached Storage,网络附加存储)都支持三种存储模式: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 中运行的应用必须知道如何处理原始块设备。
PV 回收策略
当用户不再使用其存储卷时,他们可以从 API 中将 PVC 对象删除, 从而允许该资源被回收再利用。PersistentVolume 对象的回收策略告诉集群, 当其被从申领中释放时如何处理该数据卷。
PersistentVolume 回收策略支持:Retain(保留)、Recycle(回收)、Delete(删除)。
Retain(保留)
回收策略 Retain 使得用户可以手动回收资源。当 PersistentVolumeClaim 对象被删除时,PersistentVolume 卷仍然存在,对应的数据卷被视为"已释放(released)"。 由于卷上仍然保留上一次关联的pvc信息,清理掉上一次关联的pvc信息才可分配给其他pvc。Retain(保留)策略是默认策略。
示例:
root@master30~ 19:40:17# 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~ 19:48:51# kubectl apply -f pv-pvc-Retain.yaml
root@master30~ 19:51:07# kubectl get pv,pvc
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
persistentvolume/web 5Gi RWO Retain Bound storage/webclaim <unset> 18m
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
persistentvolumeclaim/webclaim Bound web 5Gi RWO <unset> 16m
#删除PVC验证
root@master30~ 19:51:10# kubectl delete pvc webclaim
persistentvolumeclaim "webclaim" deleted
root@master30~ 19:52:25# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
web 5Gi RWO Retain Released storage/webclaim <unset> 19m
#重新创建PVC也无法绑定,webclaim的状态为pending
root@master30~ 19:52:33# kubectl apply -f pvc.yaml
persistentvolumeclaim/webclaim created
root@master30~ 19:53:19# kubectl get pv,pvc
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
persistentvolume/web 5Gi RWO Retain Released storage/webclaim <unset> 20m
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
persistentvolumeclaim/webclaim Pending <unset> 17s
#清除claimRef部分
root@master30~ 20:01:44# kubectl edit pv web
.......
# 删除claimRef部分
# claimRef:
# apiVersion: v1
# kind: PersistentVolumeClaim
# name: webclaim
# namespace: test
# resourceVersion: "112157"
# uid: 0839cdc1-81bb-11e9-9ca8-52540000fa0a
# 删除claimRef部分
......
#此时二者已经绑定成功了
root@master30~ 20:03:13# kubectl get pv,pvc
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
persistentvolume/web 5Gi RWO Retain Bound storage/webclaim <unset> 36m
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
persistentvolumeclaim/webclaim Bound web 5Gi RWO <unset> 8m6s
Recycle(回收)
**警告:**回收策略 Recycle 已被废弃。取而代之的建议方案是使用动态制备。
Recycle(回收)策略的效果与 Retaine(保留)一致。
Delete(删除)
对于支持 Delete 回收策略的卷插件,删除动作会将 PersistentVolume 对象从 Kubernetes 中移除,同时也会从外部基础设施(如 AWS EBS 或 GCE PD 卷)中移除所关联的存储资产。
示例:
root@master30~ 20:12:28# vim pv-pvc-delete.yaml
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: web
spec:
capacity:
storage: 5Gi
persistentVolumeReclaimPolicy: Delete
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~ 20:14:46# kubectl apply -f pv-pvc-delete.yaml
persistentvolume/web created
persistentvolumeclaim/webclaim created
root@master30~ 20:15:01# kubectl get pv,pvc
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
persistentvolume/web 5Gi RWO Delete Bound storage/webclaim <unset> 15s
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
persistentvolumeclaim/webclaim Bound web 5Gi RWO <unset> 15s
#删除pvc验证
root@master30~ 20:15:16# kubectl delete pvc webclaim
#查看pv,pv自动变成delete
persistentvolumeclaim "webclaim" deleted
root@master30~ 20:15:47# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
web 5Gi RWO Delete Failed storage/webclaim <unset> 59s
root@master30~ 20:16:00# kubectl describe pv web
Name: web
Labels: <none>
Annotations: pv.kubernetes.io/bound-by-controller: yes
Finalizers: [kubernetes.io/pv-protection]
StorageClass:
Status: Failed
Claim: storage/webclaim
Reclaim Policy: Delete
Access Modes: RWO
VolumeMode: Filesystem
Capacity: 5Gi
Node Affinity: <none>
Message: error getting deleter volume plugin for volume "web": no deletable volume plugin matched
Source:
Type: NFS (an NFS mount that lasts the lifetime of a pod)
Server: 10.1.8.30
Path: /nfsshares
ReadOnly: false
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning VolumeFailedDelete 90s persistentvolume-controller error getting deleter volume plugin for volume "web": no deletable volume plugin matched
####由于NFS原生不支持自动删除后端存储,K8s找不到对应的删除插件,于是PV卡死在 Failed 状态,此时可以手动删除pv,手动删除pv,并不会删除后端存储数据。
PV卷的类型
通过指定PV卷的类型说明PV使用何种类型的后端存储。
PV 持久卷是用插件的形式来实现的。Kubernetes 目前支持以下插件:
csi- 容器存储接口 (CSI)fc- Fibre Channel (FC) 存储hostPath- HostPath 卷 (仅供单节点测试使用;不适用于多节点集群;请尝试使用local卷作为替代)iscsi- iSCSI (SCSI over IP) 存储local- 节点上挂载的本地存储设备nfs- 网络文件系统 (NFS) 存储
以下的持久卷已被弃用。这意味着当前仍是支持的,但是 Kubernetes 将来的发行版会将其移除。
azureFile- Azure File (于 v1.21 弃用)flexVolume- FlexVolume (于 v1.23 弃用)gcePersistentDisk- GCE Persistent Disk (于 v1.17 弃用)portworxVolume- Portworx 卷 (于 v1.25 弃用)vsphereVolume- vSphere VMDK 卷 (于 v1.19 弃用)cephfs- CephFS 卷 (于 v1.28 弃用)rbd- Rados Block Device (RBD) 卷 (于 v1.28 弃用)
旧版本的 Kubernetes 仍支持这些“树内(In-Tree)”持久卷类型:
awsElasticBlockStore- AWS Elastic Block Store (EBS) (v1.27 开始不可用)azureDisk- Azure Disk (v1.27 开始不可用)cinder- Cinder (OpenStack block storage) (v1.27 开始不可用)photonPersistentDisk- Photon 控制器持久化盘。(从 v1.15 版本开始将不可用)scaleIO- ScaleIO 卷(v1.21 之后不可用)flocker- Flocker 存储 (v1.25 之后不可用)quobyte- Quobyte 卷 (v1.25 之后不可用)storageos- StorageOS 卷 (v1.25 之后不可用)
原始块卷支持
特性状态: Kubernetes v1.18 [stable]
以下卷插件支持原始块卷,包括其动态制备(如果支持的话)的卷:
- CSI
- FC(光纤通道)
- GCEPersistentDisk(已弃用)
- iSCSI
- Local 卷
- OpenStack Cinder
- RBD(已弃用)
- RBD(Ceph 块设备,已弃用)
- VsphereVolume
PV示例
apiVersion: v1
kind: PersistentVolume
metadata:
name: block-pv
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
volumeMode: Block
persistentVolumeReclaimPolicy: Retain
fc:
targetWWNs: ["50060e801049cfd1"]
lun: 0
readOnly: false
PVC示例:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: block-pvc
spec:
accessModes:
- ReadWriteOnce
volumeMode: Block
resources:
requests:
storage: 10Gi
Pod示例
apiVersion: v1
kind: Pod
metadata:
name: pod-with-block-volume
spec:
containers:
- name: fc-container
image: fedora:26
command: ["/bin/sh", "-c"]
args: [ "tail -f /dev/null" ]
volumeDevices:
- name: data
devicePath: /dev/xvda
volumes:
- name: data
persistentVolumeClaim:
claimName: block-pvc
**说明:**向 Pod 中添加原始块设备时,你要在容器内设置设备路径而不是挂载路径。
注意:
两条互斥路线
路线 1:裸块设备 Block
PV:
volumeMode: BlockPVC:
volumeMode: BlockPod:
volumeDevices: [{name: xx, devicePath: /dev/xxx}]路线 2:文件系统 Filesystem(默认)
PV:省略 volumeMode /
volumeMode: FilesystemPVC:省略 volumeMode /
volumeMode: FilesystemPod:
volumeMounts: [{name: xx, mountPath: /xxx}]
六、PV 与 PVC 绑定匹配完整规则
核心匹配优先级总序(最高→最低)
- claimRef 强制锁定校验(最高优先级,独立分支,跳过常规匹配)
- StorageClass 存储类严格匹配
- PV 访问模式兼容匹配
- 存储容量满足 PVC 最低申请要求
- 同规则下最优 PV 筛选
前置说明
两种绑定分支互斥:
- 分支 A:PV 配置了
spec.claimRef→ 走强制绑定逻辑,不执行下方常规匹配流程; - 分支 B:PV 无
claimRef→ 依次执行 StorageClass、访问模式、容量等常规匹配规则。
一、分支 A:claimRef 强制一对一绑定(最高优先级)
只要 PV 定义claimRef,该 PV 脱离通用匹配池,仅服务指定 PVC,所有常规匹配规则失效。
1. 匹配细则
- PV 内
claimRef包含两个必填标识:name(PVC 名称)、namespace(PVC 所在命名空间); - 只有同名、同命名空间的 PVC 才能绑定该 PV;
- 其他 PVC 哪怕 StorageClass、访问模式、容量全部吻合,也无法占用此 PV;
- 强制绑定仍需满足基础硬条件(否则一直 Pending):
- PV 容量 ≥ PVC 申请容量
- PV 访问模式兼容 PVC 申请模式
- PV 状态为
Available空闲
- 一旦绑定成功,PV 状态变为
Bound,永久锁定该 PVC,不可被其他 PVC 抢占。
2. claimRef 示例 PV
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-fixed
spec:
capacity:
storage: 20Gi
accessModes:
- ReadWriteOnce
storageClassName: local-fast
# 强制绑定default命名空间下名为web-pvc的PVC
claimRef:
name: web-pvc
namespace: default
local:
path: /mnt/local-disk
3. 特殊场景约束
- PVC 无法反向指定绑定 PV,仅能在 PV 侧配置
claimRef锁定 PVC; - 若指定的 PVC 不存在,该 PV 会持续保持
Available等待,不会参与其他 PVC 匹配; - 若对应 PVC 删除,PV 根据自身
persistentVolumeReclaimPolicy决定回收逻辑(Retain/Delete/Recycle)。
二、分支 B:无 claimRef,常规自动匹配完整规则
规则一:StorageClass 存储类严格匹配(常规匹配第一道门槛)
StorageClass 是 PV、PVC 的分类标识,用于区分不同存储类型、不同存储介质、不同命名空间的存储资源,是第一道匹配门槛,规则为强一致匹配,不兼容则直接无法绑定。
- 有存储类场景:若 PV 配置了
storageClassName,PVC 必须配置完全一致字符串(大小写敏感)才可匹配; - 无存储类场景:PV 未配置 storageClassName(空),PVC 也不能配置该字段,双方均为空才能匹配;
- 禁止交叉匹配:带 SC 的 PV 只能匹配同 SC 的 PVC,无 SC PV 只能匹配无 SC PVC。
规则二:PV 访问模式兼容匹配(次优先级)
访问模式权限由低到高:ROX < RWO < RWX,高权限 PV 兼容低权限 PVC 需求,低权限无法兼容高权限申请。
- PVC 申请 ROX:ROX/RWO/RWX 所有 PV 均可匹配;
- PVC 申请 RWO:仅 RWO、RWX PV 可匹配,ROX 不满足;
- PVC 申请 RWX:仅 RWX PV 可匹配,ROX/RWO 权限不足。
规则三:存储容量满足 PVC 最低申请要求
- 仅保留通过前两层校验、容量≥PVC 申请容量的空闲 PV;
- 同分组(同 SC、同访问模式)PV 按容量升序排序;
- 最优分配策略:选择满足容量的最小 PV绑定,避免大容量存储浪费。
三、全局通用补充约束规则(同时适用于 claimRef 强制绑定、常规自动匹配)
- PV 状态约束:仅
Available空闲 PV 可参与绑定;Bound、Released、Failed 状态 PV 直接排除; - 一对一绑定约束:同一 PV 同一时间只能绑定一个 PVC,绑定后状态变为 Bound,不再参与任何匹配;
- 核心参数不可变更:绑定后 PV/PVC 的 StorageClass、accessModes、申请容量无法修改,变更需重建资源;
- volumeMode 约束:PV 与 PVC 的
volumeMode(Filesystem/Block)必须完全一致,否则匹配失败; - 回收策略不影响绑定逻辑:
persistentVolumeReclaimPolicy仅控制 PVC 删除后 PV 处理方式,不参与匹配校验。
四、完整绑定流程总览
流程 1:先判断 PV 是否存在 claimRef
-
PV 存在
claimRef:① 校验集群内是否存在同 namespace、同名 PVC;
② 校验 PV 容量、访问模式、volumeMode 是否满足该 PVC 需求;
③ 全部满足则直接绑定,跳过所有常规 SC / 容量筛选逻辑;
④ 不满足则 PV 持续等待,不参与其他 PVC 匹配。
流程 2:PV 无 claimRef,执行自动匹配流水线
- 过滤出 StorageClass 完全一致、状态 Available 的 PV 集合;
- 集合内过滤出访问模式权限≥PVC 申请模式、volumeMode 一致的 PV;
- 保留其中存储容量≥PVC 申请容量的 PV;
- 剩余 PV 按容量从小到大排序,选取最小容量 PV 完成绑定。
五、典型场景区分举例
- 静态 PV 配置 claimRef:运维提前固定存储给指定业务 PVC,隔离资源,防止其他应用抢占;
- 无 claimRef 静态 PV 池:多业务共享同类型存储,k8s 自动调度分配;
- StorageClass 动态供给:动态创建 PV,自动匹配逻辑与无 claimRef 静态 PV 完全一致,不会生成带 claimRef 的 PV。
Kubernetes Configure
环境准备:
root@master30~ 13:21:35# kubectl create ns configure
root@master30~ 13:21:48# kubectl config set-context --current --namespace configure
ConfigMap
ConfigMap 介绍
ConfigMap 是一个存储其他对象所需要使用的配置的 API 对象。 ConfigMap 使用 data 和 binaryData 字段,保存键值对数据。
data字段,用来保存 UTF-8 字符串binaryData字段,用来保存二进制数据(base64 编码的字串)
ConfigMap 的名字必须是一个合法的 DNS 子域名。data 或 binaryData 字段下面的每个键的名称都必须由字母、数字或者 -、_ 或 . 组成。在 data 下保存的键名不可以与在 binaryData 下出现的键名有重叠。
ConfigMap 使用建议
-
使用 ConfigMap 存储配置数据,不存数据 / 文件,上限 = 1 MiB。
-
**ConfigMap 在设计上不是用来保存大量数据的。**如果你需要保存大量数据,考虑使用挂载存储卷或者使用独立的数据库或者文件服务。
ConfigMap 创建
使用帮助
root@master30~/configure 13:27:51# kubectl create configmap --help | tail -4
Usage:
kubectl create configmap NAME [--from-file=[key=]source] [--from-literal=key1=value1] [--dry-run=server|client|none] [options]
Use "kubectl options" for a list of global command-line options (applies to all commands).
键值对类型
--from-literal=键名=值
root@master30~/configure 13:28:14# kubectl create configmap mysql --from-literal=password=redhat
root@master30~/configure 13:29:51# kubectl get cm mysql -o yaml | grep ^data -A1
data:
password: redhat
文件类型
--from-file 表示从本地文件导入内容生成 ConfigMap,文件名为 key,文件里全部文本为 value
root@master30~/configure 13:33:44# kubectl create configmap web1 --from-file=index.html
configmap/web1 created
root@master30~/configure 13:33:56# kubectl get cm web1 -o yaml | grep ^data -A2
data:
index.html: |
hello world
目录类型
root@master30~/configure 13:37:09# kubectl create configmap web2 --from-file=web2/
configmap/web2 created
root@master30~/configure 13:37:43# kubectl get cm web2 -o yaml | grep ^data -A4
data:
error.html: |
error
index.html: |
hello world
ConfigMap 引用
环境变量方式引用
注意:环境变量属于特定容器级别
root@master30~/configure 13:38:04# vim pod-cm-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
# 原先通过value设置环境变量值
# valuefrom代表不从固定 value 写死值,而是从外部资源读取变量值
valueFrom:
#configMapRef表示指定数据源是ConfigMap
configMapKeyRef:
#name: mysql,引用的ConfigMap资源名称
name: mysql
#key: password,取该ConfigMapdata.password对应的value作为环境变量的值。
key: password
验证:
root@master30~/configure 14:45:43# kubectl exec mysql -- bash -c 'echo $MYSQL_ROOT_PASSWORD'
redhat
root@master30~/configure 14:45:49# kubectl exec -it mysql -- bash -c 'mysql -uroot -predhat'
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>
或者
root@master30~/configure 14:47:02# apt install -y mysql-client
root@master30~/configure 14:49:46# kubectl get pods -o wide | awk '{print$6}'
IP
10.224.96.233
root@master30~/configure 14:48:26# mysql -uroot -predhat -h 10.224.96.233
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 10
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>
以volume方式引用
**注意:**volumes属于pod级别,通过volumeMounts挂载。
引用整体
root@master30~/configure 14:52: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值是一个挂载点,将configuremap中所有键值对挂载过来
mountPath: "/usr/share/nginx/html"
volumes:
- name: webcontent
configMap:
name: web2
验证:
root@master30~/configure 14:54:27# kubectl apply -f pod-cm-volume-all.yaml
root@master30~/configure 14:58:05# kubectl exec web -- ls /usr/share/nginx/html
error.html
index.html
root@master30~/configure 15:00:44# kubectl get pods -o wide | awk '{print$6}'
IP
10.224.128.32
root@master30~/configure 15:00:49# curl 10.224.128.32
hello world
root@master30~/configure 15:01:20# curl 10.224.128.32/error.html
error
引用特定key
root@master30~/configure 15:04:20# vim pod-cm-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值是一个挂载点,将configuremap中所有键值对挂载过来
mountPath: "/usr/share/nginx/html"
volumes:
- name: webcontent
configMap:
name: web2
验证:
root@master30~/configure 15:10:43# kubectl apply -f pod-cm-volume-single.yaml
pod/web created
root@master30~/configure 15:10:45# kubectl get pods -o wide | awk '{print$6}'
IP
10.224.128.33
root@master30~/configure 15:11:04# curl 10.224.128.33
hello world
root@master30~/configure 15:11:12# kubectl exec web -- ls /usr/share/nginx/html
index.html
🌟当卷中使用的 ConfigMap 被更新时,所投射的键最终也会被更新。 kubelet 组件会在每次周期性同步时检查所挂载的 ConfigMap 是否为最新。
⭐️ConfigMap 是 Kubernetes 集群用于存放非敏感明文内容的统一配置仓库,既支持存储端口、地址、功能开关这类单行键值参数,也支持完整配置文件、静态网页、脚本等多行文本;集群内 Pod、Deployment、StatefulSet、DaemonSet、Job、CronJob 等各类工作负载,以及初始化容器 initContainer、集群系统组件(CoreDNS、kube-proxy 等)均可使用,能够通过环境变量注入或数据卷挂载两种方式为容器交付配置,彻底实现配置与业务镜像解耦,支持多服务共享同一份配置、在线修改配置无需重新打包推送镜像,便于全集群统一管控、批量运维。
k8s中的ConfigMap
以kube-proxy为例
root@master30~/web 16:28:33# kubectl get cm -n kube-system
NAME DATA AGE
calico-config 4 3d23h
coredns 1 3d23h
extension-apiserver-authentication 6 3d23h
kube-apiserver-legacy-service-account-token-tracking 1 3d23h
kube-proxy 2 3d23h
kube-root-ca.crt 1 3d23h
kubeadm-config 1 3d23h
kubelet-config 1 3d23h
#列属性DATA表示当前这个ConfigMap内部存储了多少组 key-value 数据。
#查看kube-proxy的yaml文件
root@master30~/web 16:32:35# kubectl get configmaps kube-proxy -n kube-system -o yaml
apiVersion: v1
data:
config.conf: |-
apiVersion: kubeproxy.config.k8s.io/v1alpha1
bindAddress: 0.0.0.0
bindAddressHardFail: false
clientConnection:
acceptContentTypes: ""
burst: 0
contentType: ""
kubeconfig: /var/lib/kube-proxy/kubeconfig.conf
qps: 0
clusterCIDR: 10.224.0.0/16
configSyncPeriod: 0s
conntrack:
maxPerCore: null
min: null
tcpBeLiberal: false
tcpCloseWaitTimeout: null
tcpEstablishedTimeout: null
udpStreamTimeout: 0s
udpTimeout: 0s
detectLocal:
bridgeInterface: ""
interfaceNamePrefix: ""
detectLocalMode: ""
enableProfiling: false
healthzBindAddress: ""
hostnameOverride: ""
iptables:
localhostNodePorts: null
masqueradeAll: false
masqueradeBit: null
minSyncPeriod: 0s
syncPeriod: 0s
ipvs:
excludeCIDRs: null
minSyncPeriod: 0s
scheduler: ""
strictARP: false
syncPeriod: 0s
tcpFinTimeout: 0s
tcpTimeout: 0s
udpTimeout: 0s
kind: KubeProxyConfiguration
logging:
flushFrequency: 0
options:
json:
infoBufferSize: "0"
text:
infoBufferSize: "0"
verbosity: 0
metricsBindAddress: ""
######这里的mode值为空#########
mode: ""
nftables:
masqueradeAll: false
masqueradeBit: null
minSyncPeriod: 0s
syncPeriod: 0s
nodePortAddresses: null
oomScoreAdj: null
portRange: ""
showHiddenMetricsForVersion: ""
winkernel:
enableDSR: false
forwardHealthCheckVip: false
networkName: ""
rootHnsEndpointName: ""
sourceVip: ""
kubeconfig.conf: |-
apiVersion: v1
kind: Config
clusters:
- cluster:
certificate-authority: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
server: https://10.1.8.30:6443
name: default
contexts:
- context:
cluster: default
namespace: default
user: default
name: default
current-context: default
users:
- name: default
user:
tokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
kind: ConfigMap
metadata:
annotations:
kubeadm.kubernetes.io/component-config.hash: sha256:7575011b7a20fcf5b8c31230aa579b3bb2eb35c4bfd10454001184112165b9fa
creationTimestamp: "2026-06-23T09:05:07Z"
labels:
app: kube-proxy
name: kube-proxy
namespace: kube-system
resourceVersion: "263"
uid: 8c603ca7-f3de-43e2-b27d-84e48156a94a
# kube-proxy有两种工作模式ipvs和iptables
# 这里 mode 值为空,查看日志发现默认使用iptables。
root@master30~/web 16:35:21# kubectl logs -n kube-system kube-proxy-xzxkq
I0627 02:02:44.058959 1 server_linux.go:69] "Using iptables proxy"
......
#更改模式为ipvs
root@master30~/web 16:35:32# kubectl edit cm -n kube-system kube-proxy
......
mode: "ipvs"
......
# 删除pod,等待控制器重新创建
root@master30~/web 16:39:10# kubectl delete pods -n kube-system kube-proxy-pfbpn kube-proxy-smc9w kube-proxy-xzxkq
pod "kube-proxy-pfbpn" deleted
pod "kube-proxy-smc9w" deleted
pod "kube-proxy-xzxkq" deleted
root@master30~/web 16:39:42# kubectl get pods -n kube-system | grep kube-proxy kube-proxy-6vjf7 1/1 Running 0 6s
kube-proxy-jcsgn 1/1 Running 0 6s
kube-proxy-mkcrg 1/1 Running 0 7s
# 再次查看日志,mode变为ipvs
root@master30~/web 16:39:47# kubectl logs -n kube-system kube-proxy-6vjf7
I0627 08:39:42.021586 1 server.go:1062] "Successfully retrieved node IP(s)" IPs=["10.1.8.32"]
I0627 08:39:42.026965 1 conntrack.go:59] "Setting nf_conntrack_max" nfConntrackMax=131072
I0627 08:39:42.064501 1 server.go:659] "kube-proxy running in dual-stack mode" primary ipFamily="IPv4"
I0627 08:39:42.529531 1 server_linux.go:233] "Using ipvs Proxier"
......
综合案例:haproxy+web
需求:
-
创建一个名称为 haproxy 的pod,使用镜像 haproxy,pod haproxy 的配置文件保存在 configmap 中,通过 volume 方式挂载到 /usr/local/etc/haproxy/haproxy.cfg。将流量转发到pod webapp-1 和 webapp-2。
haproxy 的配置文件模板内容如下:
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.84.80:80 check server app2 10.224.149.25:80 check -
创建第一个pod,名称为webapp-1,使用镜像nginx。为该pod创建一个cm,名称为webapp-1,该cm保存两个值:
- index.html=“hello webapp-1”
- error.html=“sorry, error.”
这个两个值通过volume方式挂载到**/usr/share/nginx/html**目录。
-
创建第二个pod,名称为webapp-2,使用镜像nginx。为该pod创建一个cm,名称为webapp-2,该cm保存两个值:
- index.html=“hello webapp-2”
- error.html=“sorry, error.”
这个两个值通过volume方式挂载到**/usr/share/nginx/html**目录。
示例如下:
创建第一个cm和pod
root@master30~/web 15:26:29# kubectl create cm webapp-1 --from-literal=index.html="hello world" --from-literal=error.html="error"
root@master30~/web 15:28:24# kubectl get cm webapp-1 -o yaml | grep ^data -A2
data:
error.html: error
index.html: hello world
root@master30~/web 15:28:30# 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~/web 15:28:30# kubectl apply -f pod-webapp-1.yaml
创建第二个cm和pod
root@master30~/web 15:29:40# kubectl create cm webapp-2 --from-literal=index.html="hello webapp-2" --from-literal=error.html="sorry, error."
root@master30~/web 15:30:56# 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~/web 15:31:17# kubectl apply -f pod-webapp-2.yaml
创建haproxy配置文件。
#获取pod ip地址
root@master30~/web 15:32:55# kubectl get pods -o wide | awk '{print$1,$6}'
NAME IP
webapp-1 10.224.128.34
webapp-2 10.224.96.234
# haproxy内容如下:
root@master30~/web 15:32:57# 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.84.80:80 check
server app2 10.224.149.25:80 check
工作流程:客户端 → HAProxy 8080 端口 → 七层 HTTP 转发 → 轮询分发到 app1 /app2 两台 80 端口 Web 服务
# 创建haproxy.cfg configmap
root@master30~/web 15:32:57# kubectl create cm haproxy.cfg --from-file=haproxy.cfg=haproxy.cfg
# 创建pod haproxy
root@master30~/web 15:34:24# vim pod-haproxy.yaml
---
apiVersion: v1
kind: Pod
metadata:
name: haproxy
spec:
containers:
- name: haproxy
image: hub.laoma.cloud/library/haproxy:3.4
imagePullPolicy: IfNotPresent
securityContext:
allowPrivilegeEscalation: true
volumeMounts:
- name: config
mountPath: "/usr/local/etc/haproxy"
readOnly: true
volumes:
- name: config
configMap:
name: haproxy.cfg
root@master30~/web 16:20:14# kubectl apply -f pod-haproxy.yaml
验证:
root@master30~/web 16:20:34# kubectl get pods haproxy -o wide | awk '{print$6}'
IP
10.224.128.38
root@master30~/web 16:21:06# curl 10.224.128.38:8080
hello webapp-1
root@master30~/web 16:21:07# curl 10.224.128.38:8080
hello webapp-2
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 创建
generic
Usage:
kubectl create secret generic NAME [--type=string] [--from-file=[key=]source]
[--from-literal=key1=value1] [--dry-run=server|client|none] [options]
基于键值对
一般用于传递变量值。
root@master30~/web 18:43:40# kubectl create secret generic mysecret --from-literal=user=tom --from-literal=password1=redhat --from-literal=password2=redhat
secret/mysecret created
root@master30~/web 18:49:41# kubectl get secrets mysecret -o yaml | grep ^data -A3
data:
password1: cmVkaGF0
password2: cmVkaGF0
user: dG9t
或者
root@master30~/web 18:46:21# kubectl get secrets mysecret -o jsonpath={.data} | json_reformat
{
"password1": "cmVkaGF0",
"password2": "cmVkaGF0",
"user": "dG9t"
}
# json_reformat 工具由软件包 yajl-tools 提供
# secret 通过 base64 编码
root@master30~/web 18:50:23# echo -n tom | base64
dG9t
基于普通文件
一般用于传递配置文件。
root@master30~/web 18:51:19# echo -n tom > user
root@master30~/web 18:52:22# echo -n redhat > password1
root@master30~/web 18:52:33# echo -n redhat > password2
root@master30~/web 18:52:36# kubectl create secret generic mysecret2 --from-file=user --from-file=password1 --from-file=password2
root@master30~/web 18:54:10# kubectl get secrets
NAME TYPE DATA AGE
mysecret Opaque 3 8m55s
mysecret2 Opaque 3 5s
root@master30~/web 18:54:15# kubectl get secrets mysecret2 -o yaml
apiVersion: v1
data:
password1: cmVkaGF0
password2: cmVkaGF0
user: dG9t
kind: Secret
metadata:
creationTimestamp: "2026-06-27T10:54:10Z"
name: mysecret2
namespace: configure
resourceVersion: "229660"
uid: f372e931-7a3a-4d3b-91bd-693c19b2faac
type: Opaque
基于键值对内容的文件
root@master30~/web 18:56:43# echo 'user=tom
password1=redhat
password2=redhat' > env.txt
root@master30~/web 18:57:03# kubectl create secret generic mysecret4 --from-env-file=./env.txt
secret/mysecret4 created
root@master30~/web 18:57:09# kubectl get secrets mysecret4 -o yaml
apiVersion: v1
data:
password1: cmVkaGF0
password2: cmVkaGF0
user: dG9t
kind: Secret
metadata:
creationTimestamp: "2026-06-27T10:57:09Z"
name: mysecret4
namespace: configure
resourceVersion: "229929"
uid: 49d79a19-8dc4-4cde-886d-7d50c2fd7f75
type: Opaque
基于目录
文件名用作key名,文件内容用作value。
root@master30~/web 18:57:58# mkdir config
root@master30~/web 18:58:34# mv user password1 password2 config
root@master30~/web 19:00:10# kubectl create secret generic mysecret5 --from-file=config/
root@master30~/web 19:00:42# kubectl get secrets mysecret5 -o yaml
apiVersion: v1
data:
password1: cmVkaGF0
password2: cmVkaGF0
user: dG9t
kind: Secret
metadata:
creationTimestamp: "2026-06-27T11:00:37Z"
name: mysecret5
namespace: configure
resourceVersion: "230237"
uid: 6d1949a8-ba38-42d9-a8ca-c8fbae5d950c
type: Opaque
基于 yaml 文件
apiVersion: v1
kind: Secret
metadata:
name: mysecret6
type: Opaque
data:
user: dG9t
password1: cmVkaGF0
password2: cmVkaGF0
yaml格式中变量值使用转换后的值。
docker-registry
回顾docker login命令:
docker login DOCKER_REGISTRY_SERVER -u=DOCKER_USER -p=DOCKER_PASSWORD
docker 登录的信息默认保存在~/.dockercfg,后续docker pull和push使用该凭据文件访问registry。
Usage:
kubectl create secret docker-registry NAME --docker-username=user --docker-password=password --docker-email=email [--docker-server=string] [--from-literal=key1=value1] [--dry-run=server|client|none] [options]
示例:
[root@master30 ~]# kubectl create secret docker-registry registry --docker-username=laoma --docker-password=redhat --docker-email=laoma@laoma.cloud --docker-server=registry.laoma.cloud
secret/docker-registry-secret created
[root@master30 ~]# kubectl get secrets docker-registry-secret -o yaml
apiVersion: v1
data:
.dockerconfigjson: eyJhdXRocyI6eyJyZWdpc3RyeS5yZWRoYXQuZnVuIjp7InVzZXJuYW1lIjoibGFvbWEiLCJwYXNzd29yZCI6InJlZGhhdCIsImVtYWlsIjoibGFvbWFAcmVkaGF0LmZ1biIsImF1dGgiOiJiR0Z2YldFNmNtVmthR0YwIn19fQ==
kind: Secret
metadata:
......
name: docker-registry-secret
namespace: laoma
resourceVersion: "29629"
selfLink: /api/v1/namespaces/laoma/secrets/docker-registry-secret
uid: 017f736a-11f5-42d8-a9ed-47dad254f2c2
type: kubernetes.io/dockerconfigjson
TLS
创建 TLS secret 保存公钥/私钥对。 公钥证书必须是 .PEM 编码。
Usage:
kubectl create secret tls NAME --cert=path/to/cert/file --key=path/to/key/file [--dry-run=server|client|none] [options]
创建私钥和证书
#--1--生成私钥
[root@master30 ~]# mkdir certs && cd certs
[root@master30 certs]# openssl genrsa -out www.key 2048
#--2--生成请求文件csr
[root@master30 certs]# 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=webadmin@laoma.cloud"
# CN的值必须是网站域名
#--3--使用自己的私钥对请求文件签名,以生成证书
[root@master30 certs]# openssl x509 -req -days 3650 -in www.csr -signkey www.key -out www.crt
创建 tls 类型 secret
[root@master30 certs]# kubectl create secret tls www-tls --cert=./www.crt --key=./www.key
Secret 引用
Secrets可以以卷方式挂载给pods,也可以通过环境变量方式传递给pods。
以环境变量方式引用
示例:
root@master30~/web 19:04:57# vim pod-secret-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
# 原先通过value设置环境变量值
valueFrom:
secretKeyRef:
name: mysecret
key: password1
root@master30~/web 19:31:06# kubectl exec mysql -- bash -c 'echo $MYSQL_ROOT_PASSWORD'
redhat
以 volume 方式引用
root@master30~/web 19:33:11# echo hello world > index.html
root@master30~/web 19:33:28# echo error > error.html
root@master30~/web 19:33:38# kubectl create secret generic web --from-file=index.html --from-file=error.html
引用整体
示例:
root@master30~/web 19:34:36# vim pod-secret-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
secret:
secretName: web
验证:
root@master30~/web 19:38:35# kubectl apply -f pod-secret-volume-all.yaml
pod/web created
root@master30~/web 19:38:48# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web 1/1 Running 0 8s 10.224.128.39 worker31.hgq.cloud <none> <none>
root@master30~/web 19:38:56# curl 10.224.128.39
hello world
root@master30~/web 19:39:05# curl 10.224.128.39/error.html
error
引用特定 key
示例:
root@master30~/web 19:39:28# 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
验证:
root@master30~/web 19:40:20# kubectl apply -f pod-secret-volume-single.yaml
pod/web created
root@master30~/web 19:40:47# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web 1/1 Running 0 58s 10.224.128.40 worker31.hgq.cloud <none> <none>
root@master30~/web 19:41:45# curl 10.224.128.40
hello world
root@master30~/web 19:41:51# kubectl exec web -- ls /usr/share/nginx/html
index.html
以卷方式引用,支持动态更新secret内容:
root@master30~/web 19:42:47# echo 'hello nginx' |base64
aGVsbG8gbmdpbngK
root@master30~/web 19:42:54# kubectl edit secrets web
......
apiVersion: v1
data:
index: aGVsbG8gbmdpbngK
......
# 等30秒左右查看
root@master30~/web 19:44:29# curl 10.224.128.40
hello nginx
注意:
- Volume 挂载 Secret(和 ConfigMap 逻辑一样)
把 Secret 里每一个 key 变成容器内真实文件:
- secret 的 key = 文件名
- secret 的 value = 文件内容
- 程序读取本地文件拿密码,不会暴露在环境变量列表里。
- Secret 更新后,容器内文件会自动刷新。
- env 环境变量引用 Secret
只是把 Secret 的值提取出来,放进容器的环境变量里:
- 没有生成任何文件;
- 执行
env、ps命令就能明文看到密码,泄露风险高; - Secret 修改后,必须重启 Pod 才能加载新值。
文件权限
映射过去文件默认权限是644(8进制),对应10进制是420.。
设置映射的所有文件权限,defaultMode。
volumes:
- name: foo
secret:
secretName: mysecret1
defaultMode: 256
10进制是256转换成8进制是400。因为json格式不支持8进制,所以使用10进制。
更多推荐

所有评论(0)