看完直接上手!K8s 存储 + 配置全套实操实验(含 NFS、负载均衡综合案例)
Kubernetes Volume
学习参考:卷
环境准备
root@master30 ~ 09:37:34# kubectl create ns storage
namespace/storage created
root@master30 ~ 09:38:07# kubectl config set-context --current --namespace storage
Context "kubernetes-admin@kubernetes" modified.
Volume 类型
Kubernetes 支持的 Volume 类型包含如下:
- emptyDir
- hostPath
- gcePersistentDisk
- awsElasticBlockStore
- nfs
- iscsi
- fc (fibre channel)
- flocker
- glusterfs
- rbd
- cephfs
- gitRepo
- secret
- persistentVolumeClaim
- downwardAPI
- projected
- azureFileVolume
- azureDisk
- vsphereVolume
- Quobyte
- PortworxVolume
- ScaleIO
- StorageOS
- local
emptyDir
当 Pod 调度至某一节点后,系统会自动创建 emptyDir 卷。只要该节点上的 Pod 持续运行,卷内数据就会保留;一旦 Pod 因任意原因被从节点移除,emptyDir 卷会同步删除,内部存储的数据将永久丢失。
实验说明:创建包含双容器的 Pod,挂载 emptyDir 卷完成验证。
root@master30 ~ 09:38:24# mkdir storage
root@master30 ~ 10:05:40# cd storage/
root@master30 storage 10:05:42# vim pod-with-emptyDir.yaml
apiVersion: v1
kind: Pod
metadata:
name: busybox
labels:
app: busybox
spec:
volumes:
- name: datavolume
emptyDir: {}
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
配置说明:
- spec.volumes:用于定义卷资源,此处采用默认卷类型。
- spec.containers.volumeMounts:在容器内引用已定义的卷
# 创建pod
root@master30 storage 10:06:31# kubectl apply -f pod-with-emptyDir.yaml
pod/busybox created
# 获取容器ID
root@master30 storage 10:06:46# kubectl describe pod busybox |egrep -o 'Container ID.*//.{12}'
Container ID: containerd://afa965ec70bd
Container ID: containerd://683042c7fc83
# 获取容器所在节点
root@master30 storage 10:07:32# kubectl describe pod busybox | grep Node;
Node: worker32.liu.cloud/10.1.8.32
Node-Selectors: <none>
# 登录到node查看容器挂载情况
root@worker32 ~ 09:41:13# crictl inspect afa965ec70bd|grep datavolume
"hostPath": "/var/lib/kubelet/pods/7c421ede-f8f0-4c33-adc2-2a5964920980/volumes/kubernetes.io~empty-dir/datavolume",
"host_path": "/var/lib/kubelet/pods/7c421ede-f8f0-4c33-adc2-2a5964920980/volumes/kubernetes.io~empty-dir/datavolume"
"source": "/var/lib/kubelet/pods/7c421ede-f8f0-4c33-adc2-2a5964920980/volumes/kubernetes.io~empty-dir/datavolume",
# 创建数据
root@master30 storage 10:09:52# kubectl exec busybox -c busybox1 -- touch /data/b1-f1
root@master30 storage 10:10:19# kubectl exec busybox -c busybox2 -- ls /data
b1-f1
# node上查看数据
root@worker32 ~ 10:09:10# ls /var/lib/kubelet/pods/7c421ede-f8f0-4c33-adc2-2a5964920980/volumes/kubernetes.io~empty-dir/datavolume
b1-f1
# 删除pod,验证emptyDir
root@master30 storage 10:10:27# 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@worker32 ~ 10:11:25# ls /var/lib/kubelet/pods/7c421ede-f8f0-4c33-adc2-2a5964920980/volumes/kubernetes.io~empty-dir/datavolume
ls: cannot access '/var/lib/kubelet/pods/7c421ede-f8f0-4c33-adc2-2a5964920980/volumes/kubernetes.io~empty-dir/datavolume': No such file or directory
# 容器删除后,临时卷的数据不会立刻清除,需等待一小段时间才会被系统回收。
hostPath
hostPath 允许 Pod 将宿主机文件系统内的指定目录挂载至容器内部;即便 Pod 被删除,hostPath 卷内的数据仍会保留在节点本地。
实验说明:创建 Pod 并挂载 hostPath 卷完成功能验证。
root@master30 storage 10:11:20# 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: 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,也就是读写访问。
readOnly: true
# 创建pod
root@master30 storage 10:11:48# kubectl apply -f pod-with-hostPath.yaml
pod/busybox created
# 获取容器ID
root@master30 storage 10:12:09# kubectl describe pod busybox |egrep -o 'Container ID.*//.{12}'
Container ID: containerd://a7463b36ec53
Container ID: containerd://f425d16ffe49
# 获取容器所在节点
root@master30 storage 10:12:39# kubectl describe pod busybox | grep Node;
Node: worker31.liu.cloud/10.1.8.31
Node-Selectors: <none>
# 登录到worker31查看容器挂载情况
root@worker31 ~ 10:13:25# crictl inspect a7463b36ec53|grep busyboxdir
"hostPath": "/busyboxdir",
"host_path": "/busyboxdir"
"source": "/busyboxdir",
# 创建数据
root@master30 storage 10:12:49# kubectl exec busybox -c busybox1 -- touch /data/b1-f1
root@master30 storage 10:14:15# kubectl exec busybox -c busybox2 -- ls /data/
b1-f1
root@master30 storage 10:15:55# kubectl exec busybox -c busybox2 -- touch /data/b2-f2
touch: /data/b2-f2: Read-only file system
# node上查看数据
root@master30 storage 10:16:49# ls /busyboxdir/
b1-f1
# 删除pod,验证hostPath
root@master30 storage 10:18:17# kubectl delete pod busybox --force
root@worker32:~10:19:27# ls /busyboxdir/
b1-f1
# pod删除后,hostPath卷数据保留。
NFS 存储
NFS 卷会将业务数据统一存储在 NFS 共享目录中,属于跨节点共享存储方案。
部署 NFS 共享服务
# 安装 NFS server
root@master30 storage 10:23:18# apt install -y nfs-kernel-server
# 安创建NFS目录 修改创建文件夹的权限
root@master30 storage 10:24:02# mkdir -m 777 /nfsshares
root@master30 storage 10:24:07# echo hello > /nfsshares/index.html
# 配置共享,允许所有客户端访问
root@master30 storage 10:28:36# cat << EOF > /etc/exports
/nfsshares *(rw)
EOF
# 重启 nfs server
root@master30 storage 10:34:36# systemctl restart nfs-server.service
# 客户端安装
root@worker31 ~ 10:28:24# apt install -y nfs-common
root@worker32:~ 10:28:26# apt install -y nfs-common
创建挂载 NFS 卷的 Pod
root@master30 storage 10:34:53# 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: hub.laoma.cloud/library/nginx
name: nginx
volumeMounts:
- name: nfs
mountPath: "/usr/share/nginx/html"
# 创建pod
root@master30 storage 10:37:11# kubectl apply -f pod-with-nfs.yaml
pod/nginx created
# 获取容器ID
root@master30 storage 10:38:07# kubectl describe pod nginx |egrep -o 'Container ID.*//.{12}'
Container ID: containerd://4ab4d3b478f0
# 获取容器所在节点
root@master30 storage 10:38:21# kubectl describe pod nginx |grep Node:
Node: worker32.liu.cloud/10.1.8.32
# 登录到worker32查看容器挂载情况
root@worker32 ~ 10:39:32# crictl inspect 4ab4d3b478f0|grep -e nfs -e html
"containerPath": "/usr/share/nginx/html",
"hostPath": "/var/lib/kubelet/pods/d0a4c35e-6385-478f-a57a-4320735dc182/volumes/kubernetes.io~nfs/nfs",
"container_path": "/usr/share/nginx/html",
"host_path": "/var/lib/kubelet/pods/d0a4c35e-6385-478f-a57a-4320735dc182/volumes/kubernetes.io~nfs/nfs"
"destination": "/usr/share/nginx/html",
"source": "/var/lib/kubelet/pods/d0a4c35e-6385-478f-a57a-4320735dc182/volumes/kubernetes.io~nfs/nfs",
root@worker32 ~ 10:39:43# df | grep nfsshare
10.1.8.30:/nfsshares 101590016 5643264 90740224 6% /var/lib/kubelet/pods/d0a4c35e-6385-478f-a57a-4320735dc182/volumes/kubernetes.io~nfs/nfs
# 访问容器
root@master30 storage 10:38:49# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 3m3s 10.224.218.130 worker32.liu.cloud <none> <none>
root@master30 storage 10:40:30# curl 10.224.218.130
hello
# 创建数据
root@master30 storage 10:40:44# kubectl exec nginx -- ls /usr/share/nginx/html
index.html
root@master30 storage 10:41:20# kubectl exec nginx -- touch /usr/share/nginx/html/test.html
root@master30 storage 10:41:42# ls /nfsshares
index.html test.html
# 删除pod,验证nfs卷数据
root@master30 storage 10:41:51# kubectl delete pod nginx --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 "nginx" force deleted
root@master30 storage 10:42:20# ls /nfsshares
index.html test.html
# pod删除后,nfs卷数据保留。
持久性存储
学习参考:持久卷
Kubernetes 通过 PV(持久卷)架构为集群提供持久化存储能力。
PV 和 PVC 架构
开发人员无需感知底层云存储、共享存储的具体实现细节,仅通过 PVC(持久卷申领)即可申请存储资源,实现标准化持久化存储。
- Persistent Volume:由 PersistentVolume API 对象定义,代表集群内已存在的后端存储资源。PV 的生命周期与使用它的 Pod 相互独立,属于集群级别的资源。
- Persistent Volume Claim:由 PersistentVolumeClaim API 对象定义,代表开发人员发起的存储资源申请。PVC 隶属于命名空间,是命名空间级别的资源。
创建 PV 和 PVC
创建 PV
集群管理员可根据后端存储的容量创建若干 PV。本次演示选用 NFS 作为后端存储。
PV 资源定义示例:
root@master30 storage 11:09:40# 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@master30storage 11:09:45# kubectl apply -f pv.yaml
root@master30storage 11:10:30# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
web 5Gi RWO Recycle Available 3s
创建 PVC
用户创建 PVC 时,可指定所需存储容量、访问模式及存储类别。集群控制平面会持续监听 PVC 对象,自动匹配符合条件的 PV;若无现成 PV,则等待存储插件动态制备,完成 PV 与 PVC 的绑定。
PVC 资源定义示例:
root@master30 storage 11:11:05# vim pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: webclaim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
root@master30 storage 11:11:26# kubectl apply -f pvc.yaml
persistentvolumeclaim/webclaim created
root@master30 storage 11:11:41# 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:11:53# 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:12:22# kubectl apply -f pod-with-pvc.yaml
pod/web created
root@master30 storage 11:13:06# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web 1/1 Running 0 14s 10.224.133.66 worker31.liu.cloud <none> <none>
# 测试:访问pod web页面
root@master30 storage 11:13:20# curl http://10.224.133.66
hello
root@master30 storage 11:13:34# echo test > /nfsshares/test.html
root@master30 storage 11:13:50# curl http://10.224.133.66/test.html
test
# 删除pod
root@master30 storage 11:14:02# kubectl delete pod web --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 "web" force deleted
PV accessModes
PersistentVolume 支持四种卷访问模式:
-
ReadWriteOnce:卷仅能挂载至单个节点读写,同一节点下允许多个 Pod 同时读写该卷。
-
ReadOnlyMany:卷可被多个节点挂载,所有节点仅拥有只读权限。
-
ReadWriteMany:卷支持多节点同时挂载,各节点均可读写数据。
-
ReadWriteOncePod:卷仅允许集群内单个 Pod 进行读写挂载。若需要全局唯一 Pod 独占读写存储,可选用该模式,仅兼容 CSI 存储驱动,最低要求 Kubernetes 1.22 版本。
该模式详细说明可参考官方博文
Introducing Single Pod Access Mode for PersistentVolumes。
命令行查看资源时,访问模式会使用简写标识:
- 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 卷,要么单节点读写挂载,要么多节点只读挂载,两种模式不能同时生效。
常规 NAS 类共享存储普遍兼容 ReadWriteOnce、ReadOnlyMany、ReadWriteMany 三种访问模式。
PV volumeModes
特性状态:Kubernetes v1.18 稳定可用
针对 PV 持久卷,Kubernetes 提供两种卷模式配置项 volumeModes:Filesystem(文件系统模式)与 Block(原始块设备模式)。该参数为可选配置,省略时默认采用 Filesystem。
- Filesystem 卷:以目录挂载形式交付 Pod 使用。若后端存储设备空白无文件系统,Kubernetes 会在首次挂载前自动格式化生成文件系统。
- Block 卷:以原始裸块设备形式交付 Pod,设备内部不存在文件系统。该模式可消除文件系统层开销,最大化读写性能,但业务应用必须具备直接操作裸块设备的能力。裸块卷的使用方式可查阅文档原始块卷支持。
PVC 与 PV 匹配规则
- 访问模式兼容:PV 的访问模式权限等级必须高于或等于 PVC 的申请要求;权限优先级可简单排序为 ROX < RWO < RWX。举例:PVC 申请 RWO 模式时,支持 RWX 的 NFS PV 能够成功匹配绑定。
- 存储容量满足:先将访问模式一致的 PV 分组,组内按照存储容量从小到大排序,优先匹配容量刚好满足 PVC 需求的 PV。
- 存储类匹配:StorageClass 用于对 PV 分类隔离;若 PV 定义了 storageClassName,则 PVC 申请时必须填写完全一致的 storageClassName 才能匹配。例如 PV 标记存储类 ns1-storage,对应命名空间下的 PVC 也需指定 ns1-storage。
PV 回收策略
当业务不再使用存储资源,删除 PVC 对象后,集群将按照 PV 配置的回收策略处理底层存储数据。PersistentVolume 共支持三种回收策略:Retain(保留)、Recycle(回收,已废弃)、Delete(自动删除)。
Retain(保留)
Retain 为 PV 默认回收策略,需管理员手动清理回收资源。删除 PVC 后,PV 状态变更为 Released,卷内原有数据完整保留,但 PV 会记录上一次绑定的 PVC 关联信息,无法直接分配给新 PVC;只有手动清除 PV 内的 claimRef 绑定信息,才能重新绑定其他 PVC。
资源定义示例:
root@master30 storage 11:14:30# 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 11:14:34# kubectl apply -f pv-pvc-Retain.yaml
root@master30 storage 11:14:40# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
web 5Gi RWO Retain Bound default/webclaim 22s
root@master30 storage 11:15:26# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
webclaim Bound web 5Gi RWO 23s
删除 PVC 验证回收逻辑
root@master30 storage 11:16:21# kubectl delete pvc webclaim
root@master30 storage 11:16:30# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
web 5Gi RWO Retain Released default/webclaim 3m4s
# 创建新pvc,无法绑定
root@master30 storage 11:16:45# vim pvc-db.yaml
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: dbclaim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
root@master30 storage 11:17:21# kubectl apply -f pvc-db.yaml
root@master30 storage 11:17:29# kubectl get pvc dbclaim
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
dbclaim Pending 11s
# 手动清理卷上claimRef信息,删除claimRef部分
root@master30 storage 11:18:21# kubectl edit pv web
......
spec:
accessModes:
- ReadWriteOnce
capacity:
storage: 5Gi
# 删除claimRef部分
# claimRef:
# apiVersion: v1
# kind: PersistentVolumeClaim
# name: webclaim
# namespace: test
# resourceVersion: "112157"
# uid: 0839cdc1-81bb-11e9-9ca8-52540000fa0a
# 删除claimRef部分
nfs:
path: /web
server: 10.1.8.30
......
# dbclaim 绑定成功
root@master30 storage 11:18:41# kubectl get pvc dbclaim
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
dbclaim Bound web 5Gi RWO 3m49s
清理测试环境
root@master30 storage 11:18:49# kubectl delete pvc dbclaim
root@master30 storage 11:19:30# kubectl delete pv web
root@master30 storage 11:19:36# ls /nfsshares/
index.html test.html
注意事项:直接删除 PV 资源不会同步清除后端存储内的业务数据。
Recycle(回收)
警告提示:Recycle 回收策略已正式废弃,官方推荐使用存储类动态制备方案替代。该策略行为逻辑与 Retain 保留策略基本一致。
Delete(删除)
仅兼容支持资源销毁接口的存储插件。删除 PVC 后,集群会同步完成两项操作:移除 Kubernetes 内 PV 对象、删除底层基础设施对应的存储资源(如 AWS EBS、GCE PD 云盘)。
资源定义示例:
root@master30 storage 11:20:04# 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 storage 11:20:15# kubectl apply -f pv-pvc-Delete.yaml
root@master30 storage 11:20:26# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
web 5Gi RWO Retain Bound default/webclaim 22s
root@master30 storage 11:20:46# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
webclaim Bound web 5Gi RWO 23s
删除 PVC 验证自动删除逻辑
root@master30 storage 11:21:04# kubectl delete pvc webclaim
root@master30 storage 11:21:16# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
web 5Gi RWO Retain Released default/webclaim 3m4s
# pv
root@master30 storage 11:21:18# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
web 5Gi RWO Delete Failed default/webclaim 33s
root@master30 storage 11:21:29# kubectl describe pv web
......
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning VolumeFailedDelete 50s persistentvolume-controller error getting deleter volume plugin for volume "web": no deletable volume plugin matched
集群输出报错日志 Error getting deleter volume plugin for volume "web": no deletable volume plugin matched,代表当前存储插件不支持自动销毁底层存储资源,因此无法执行删除动作。
此时可手动删除 PV 对象,但手动删除 PV 不会清理后端存储中留存的数据。
思考
问题:如何创建仅能绑定指定 PVC 的 PV?
答案:在 PV 定义中补充 claimRef 字段,绑定目标 PVC 的名称与命名空间。
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: web
spec:
capacity:
storage: 5Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
nfs:
path: /nfsshares
server: 10.1.8.30
claimRef:
name: webclaim
namespace: test
PV 卷的类型
PV 持久卷基于插件化机制实现,Kubernetes 当前支持以下存储插件:
csi- 容器存储标准接口 CSIfc- 光纤通道 FC 存储hostPath- 宿主机路径卷(仅适用于单节点测试环境,多节点集群不推荐使用;本地存储场景建议替换为 local 卷)iscsi- IP 网络 SCSI 存储 iSCSIlocal- 节点本地物理存储设备nfs- 网络文件系统 NFS
以下树内持久卷插件已标记废弃:当前版本仍可兼容运行,但后续 Kubernetes 大版本会彻底移除相关支持。
azureFile- Azure 文件存储(v1.21 版本废弃)flexVolume- FlexVolume 扩展存储(v1.23 版本废弃)gcePersistentDisk- GCE 云磁盘(v1.17 版本废弃)portworxVolume- Portworx 分布式存储(v1.25 版本废弃)vsphereVolume- vSphere 虚拟机磁盘(v1.19 版本废弃)cephfs- Ceph 文件存储(v1.28 版本废弃)rbd- Ceph 块存储 RBD(v1.28 版本废弃)
旧版 Kubernetes 曾经内置、现已下线的树内存储插件:
awsElasticBlockStore- AWS EBS 云盘(v1.27 起完全移除支持)azureDisk- Azure 云磁盘(v1.27 起完全移除支持)cinder- OpenStack Cinder 块存储(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 稳定可用
支持裸块设备模式(volumeMode: Block)的存储插件,同时兼容动态制备能力:
- 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
配置说明:使用原始块设备卷时,需通过 volumeDevices 指定容器内设备路径,而非文件挂载目录 volumeMounts。
环境清理
root@master30 storage 11:24:30# kubectl delete ns storage
Kubernetes Configure
环境准备
root@master30 ~ 13:52:17# kubectl create ns config
namespace/config created
root@master30 ~ 13:52:31# kubectl create ns configure
namespace/configure created
ConfigMap
学习参考:ConfigMap
ConfigMap 介绍
ConfigMap 是用于存放业务所需配置信息的标准 API 对象,通过 data、binaryData 两个字段存储键值对数据。
- data 字段:用于存储 UTF-8 格式普通字符串文本。
- binaryData 字段:用于存储二进制内容,存储值为 base64 编码字符串。
ConfigMap 名称必须符合 DNS 子域名规范;data、binaryData 下的键名仅允许包含大小写字母、数字、横杠 -、下划线 _、点 .;同一 ConfigMap 中 data 与 binaryData 不能存在重名键。
自 Kubernetes v1.19 起,可通过 immutable 字段创建不可变更 ConfigMap。
ConfigMap 使用规范
- ConfigMap 仅适合存放轻量配置文本,单资源上限 1 MiB,不适合存储业务数据或大体积文件。
- 若需存储大容量数据,建议使用持久化存储卷、独立数据库或文件存储服务。
ConfigMap 创建
命令基础用法
kubectl create configmap NAME [--from-file=[key=]source]
[--from-literal=key1=value1] [--dry-run=server|client|none] [options]
键值对创建方式
root@master30 configure 13:54:00# kubectl create configmap mysql --from-literal=password=123
configmap/mysql created
root@master30 configure 13:54:30# kubectl get configmaps mysql -o yaml | grep ^data -A1
data:
password: "123"
单个文件创建方式
root@master30 configure 13:55:09# echo hello > index.html
root@master30 configure 13:55:22# kubectl create configmap web1 --from-file=./index.html
configmap/web1 created
root@master30 configure 13:55:46# kubectl get configmaps web1 -o yaml | grep ^data -A2
data:
index.html: |
hello
目录批量创建方式
root@master30 configure 13:56:12# echo error > error.html
root@master30 configure 13:56:38# mkdir web2
root@master30 configure 13:56:42# mv index.html error.html web2
root@master30 configure 13:56:50# kubectl create configmap web2 --from-file=./web2
configmap/web2 created
root@master30 configure 13:57:13# kubectl get configmaps web2 -o yaml | grep ^data -A4
data:
error.html: |
error
index.html: |
hello
ConfigMap 引用方式
环境变量注入引用
注意:环境变量配置作用域为单个容器。
root@master30 configure 13:57:42# vim pod-with-env-from-configmap.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:
configMapKeyRef:
name: mysql
key: password
验证流程
root@master30 configure 13:58:05# kubectl apply -f pod-with-env-from-configmap.yaml
pod/mysql created
root@master30 configure 13:58:35# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
mysql 1/1 Running 0 37s 10.224.133.67 worker31.liu.cloud <none> <none>
root@master30 configure 13:58:54# kubectl exec -it mysql -- bash -c 'echo $MYSQL_ROOT_PASSWORD'
123
root@master30 configure 14:01:05# kubectl -it exec mysql -- bash
bash-5.1# exit
exit
command terminated with exit code 127
# 删除 pod
root@master30 configure 14:01:42# kubectl delete pod mysql --force
存储卷挂载引用
注意:volumes 定义作用域为整个 Pod,容器通过 volumeMounts 完成挂载。
完整挂载全部键值
root@master30 configure 14:01:59# vim pod-with-volume-from-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值是一个挂载点,将configuremap中所有键值对挂载过来
mountPath: "/usr/share/nginx/html"
volumes:
- name: webcontent
configMap:
name: web2
验证流程
root@master30 configure 14:02:26# kubectl apply -f pod-with-volume-from-configmap.yaml
pod/web created
root@master30 configure 14:02:41# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web 1/1 Running 0 11s 10.224.218.131 worker32.liu.cloud <none> <none>
root@master30 configure 14:02:52# kubectl exec web -- ls /usr/share/nginx/html
error.html
index.html
root@master30 configure 14:03:20# curl http://10.224.218.131
hello
root@master30 configure 14:03:36# curl http://10.224.218.131/error.html
error
仅挂载指定键
root@master30 configure 14:03:49# 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: "/usr/share/nginx/html"
volumes:
- name: webcontent
configMap:
name: web2
items:
- key: index.html
path: index.html
等效配置写法
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/index.html"
subPath: index.html
volumes:
- name: webcontent
configMap:
name: web2
自动更新机制:以卷形式挂载的 ConfigMap 支持动态更新。修改 ConfigMap 内容后,kubelet 会周期性同步配置,容器内挂载文件会自动刷新。
集群内置 ConfigMap 示例:kube-proxy
root@master30 configure 14:04:12# kubectl get configmaps -n kube-system
NAME DATA AGE
calico-config 4 47h
coredns 1 2d
extension-apiserver-authentication 6 2d
kube-apiserver-legacy-service-account-token-tracking 1 2d
kube-proxy 2 2d
kube-root-ca.crt 1 2d
kubeadm-config 1 2d
kubelet-config 1 2d
root@master30 configure 14:04:34# 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: ""
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-08-05T05:47:42Z"
labels:
app: kube-proxy
name: kube-proxy
namespace: kube-system
resourceVersion: "238"
uid: 105fc0de-2f99-4c76-b43c-8b7d17783ec4
# kube-proxy支持iptables、ipvs两种代理模式
# 当前配置mode字段为空,查看组件日志可知默认启用iptables模式。
root@master30 configure 14:08:38# kubectl logs -n kube-system kube-proxy-q6zt6
I0807 01:36:55.922413 1 server_linux.go:69] "Using iptables proxy"
I0807 01:36:55.937531 1 server.go:1062] "Successfully retrieved node IP(s)" IPs=["10.1.8.32"]
I0807 01:36:55.941673 1 conntrack.go:119] "Set sysctl" entry="net/netfilter/nf_conntrack_max" value=131072
I0807 01:36:55.941700 1 conntrack.go:59] "Setting nf_conntrack_max" nfConntrackMax=131072
I0807 01:36:55.941726 1 conntrack.go:119] "Set sysctl" entry="net/netfilter/nf_conntrack_tcp_timeout_established" value=86400
I0807 01:36:55.941736 1 conntrack.go:119] "Set sysctl" entry="net/netfilter/nf_conntrack_tcp_timeout_close_wait" value=3600
I0807 01:36:55.979668 1 server.go:659] "kube-proxy running in dual-stack mode" primary ipFamily="IPv4"
I0807 01:36:55.979725 1 server_linux.go:165] "Using iptables Proxier"
......
# 修改代理模式为ipvs
root@master30 configure 14:08:56# kubectl edit cm -n kube-system kube-proxy
......
mode: "ipvs"
......
# 删除原有Pod,等待控制器重建生效
root@master30 configure 14:09:40# kubectl get pod -n kube-system |grep kube-proxy
kube-proxy-bnsvk 1/1 Running 1 37d
kube-proxy-f58ks 1/1 Running 1 37d
kube-proxy-q6zt6 1/1 Running 1 37d
root@master30 configure 14:09:47# kubectl delete pod -n kube-system kube-proxy-{bnsvk,f58ks,q6zt6}
pod "kube-proxy-bnsvk" deleted
pod "kube-proxy-f58ks" deleted
pod "kube-proxy-q6zt6" deleted
# 查看更新后组件日志
root@master30 configure 14:10:32# kubectl get pod -n kube-system |grep kube-proxy
kube-proxy-8swmx 1/1 Running 0 18s
kube-proxy-948cw 1/1 Running 0 29s
kube-proxy-xlh7b 1/1 Running 0 24s
root@master30:~# kubectl logs -n kube-system kube-proxy-8swmx
I1019 03:41:12.288952 1 node.go:141] Successfully retrieved node IP: 10.1.8.31
I1019 03:41:12.290902 1 conntrack.go:52] "Setting nf_conntrack_max" nfConntrackMax=131072
I1019 03:41:12.325988 1 server.go:632] "kube-proxy running in dual-stack mode" primary ipFamily="IPv4"
I1019 03:41:12.364416 1 server_others.go:218] "Using ipvs Proxier"
......
# 实验完成后恢复原有代理模式
综合案例:haproxy+web
需求说明:
- 创建名为 haproxy 的 Pod,使用 haproxy 镜像;haproxy 配置文件存放于 ConfigMap,以卷形式挂载至容器内路径
/usr/local/etc/haproxy/haproxy.cfg,实现流量转发至 webapp-1、webapp-2 两个业务 Pod。
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 镜像;配套创建同名 ConfigMap webapp-1,包含两组配置:
-
index.html=“hello webapp-1”
-
error.html=“sorry, error.”
将 ConfigMap 以卷挂载至容器
/usr/share/nginx/html
- 创建第二个业务 Pod webapp-2,基于 nginx 镜像;配套创建同名 ConfigMap webapp-2,包含两组配置:
-
index.html=“hello webapp-2”
-
error.html=“sorry, error.”
将 ConfigMap 以卷挂载至容器
/usr/share/nginx/html
实现步骤:
- 部署 webapp-1 的 ConfigMap 与 Pod
root@master30 ~ 14:29:50# mkdir web && cd web
root@master30 web 14:29:56# kubectl create cm webapp-1 --from-literal=index.html="hello webapp-1" --from-literal=error.html="sorry, error."
configmap/webapp-1 created
root@master30 web 14:30:03# vim pod-webapp-1.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 web 14:30:23# kubectl apply -f pod-webapp-1.yaml
- 部署 webapp-2 的 ConfigMap 与 Pod
root@master30 web 14:30:38# kubectl create cm webapp-2 --from-literal=index.html="hello webapp-2" --from-literal=error.html="sorry,error."
configmap/webapp-2 created
root@master30 web 14:31:55# vim pod-webapp-2.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 web 14:32:17# kubectl apply -f pod-webapp-2.yaml
pod/webapp-2 created
- 构建 haproxy 配置并创建对应 ConfigMap、Pod
# 获取两个业务Pod的IP地址
root@master30 web 14:32:37# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web 1/1 Running 0 30m 10.224.218.131 worker32.liu.cloud <none> <none>
webapp-1 1/1 Running 0 2m8s 10.224.133.68 worker31.liu.cloud <none> <none>
webapp-2 1/1 Running 0 9s 10.224.133.69 worker31.liu.cloud <none> <none>
# 编写haproxy配置文件
root@master30 web 14:33:25# 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.133.68:80 check
server app2 10.224.133.69:80 check
# 将配置文件存入ConfigMap
root@master30 web 14:34:05# kubectl create cm haproxy.cfg --from-file=haproxy.cfg=./haproxy.cfg
configmap/haproxy.cfg created
# 创建haproxy负载均衡Pod
root@master30 web 14:34:33# vim 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 web 14:34:55# kubectl apply -f haproxy.yaml
pod/haproxy created
# 功能验证
root@master30 web 14:35:06# kubectl get pods haproxy -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
haproxy 1/1 Running 0 14s 10.224.218.132 worker32.liu.cloud <none> <none>
root@master30 web 14:35:20# curl http://10.224.218.132:8080
hello webapp-1
root@master30 web 14:35:41# curl http://10.224.218.132:8080
hello webapp-2
Secret
学习参考:Secret
Secret 介绍
Secret 是用于存储少量敏感数据的 Kubernetes 对象,常见存储内容包括密码、访问令牌、密钥等敏感凭证。这类数据若直接写入 Pod 定义或容器镜像中,极易发生泄露;借助 Secret 管理机密信息,可避免业务代码硬编码敏感数据。
Secret 的创建流程可与使用它的 Pod 完全解耦,能够有效降低在 Pod 创建、查看、编辑流程中机密信息暴露的风险。Kubernetes 集群与集群内运行的应用还能针对 Secret 执行额外安全防护策略,例如禁止将敏感数据持久写入非易失性存储介质。
Secret 的定位与 ConfigMap 相近,但 Secret 专门用于存放机密数据。
Secret 与 ConfigMap 的核心差异:Secret 会对存储数据做 Base64 编码处理,ConfigMap 不对数据进行编码。
Secret 类型
- generic:用于自定义键值对存储,内部数据经过编码,资源类型标识为 Opaque。
- docker-registry:存放镜像仓库访问凭证,资源类型标识为 kubernetes.io/dockerconfigjson。
- tls:存储 TLS 证书与私钥配对文件,资源类型标识为 Opaque。
Secret 创建
generic
# 帮助信息
root@master30 web 15:00:14# kubectl create secret generic --help
Create a secret based on a file, directory, or specified literal value.
A single secret may package one or more key/value pairs
......
基于键值对创建
该方式多用于向容器传递环境变量类敏感值。
root@master30 web 15:19:15# kubectl create secret generic mysecret1 --from-literal=usr=tom --from-literal=password1=r
edhat --from-literal=password2=redhat
secret/mysecret1 created
root@master30 web 15:19:52# kubectl get secrets
NAME TYPE DATA AGE
mysecret1 Opaque 3 12s
root@master30 web 15:20:04# kubectl get secrets mysecret1 -o yaml
apiVersion: v1
data:
password1: cmVkaGF0
password2: cmVkaGF0
user: dG9t
kind: Secret
metadata:
......
name: mysecret1
namespace: liu
resourceVersion: "24180"
selfLink: /api/v1/namespaces/liu/secrets/mysecret1
uid: d510d58f-8737-4d2f-bd00-dd9ce6b50a9b
type: Opaque
root@master30 web 15:24:37# kubectl get secrets mysecret1 -o jsonpath={.data} | json_reformat
{
"password1": "cmVkaGF0",
"password2": "cmVkaGF0",
"usr": "dG9t"
}
# json_reformat 工具由软件包 yajl-tools 提供
root@master30 web 15:24:39# kubectl get secrets mysecret1 -o jsonpath={.data.user}
amFjaw==
root@master30 web 15:25:29# kubectl describe secrets mysecret1
Name: mysecret1
Namespace: configure
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
password1: 6 bytes
password2: 6 bytes
usr: 3 bytes
# secret 通过 base64 编码
root@master30 web 15:25:48# echo -n tom | base64
dG9t
root@master30 web 15:30:33# echo -n redhat | base64
cmVkaGF0
root@master30 web 15:30:55# echo -n cmVkaGF0 | base64 -d
redhat
基于普通文件创建
该方式多用于向容器挂载配置文件类敏感资源。
root@master30 web 15:31:02# echo -n tom > user
root@master30 web 15:31:15# echo -n redhat > password1
root@master30 web 15:31:33# echo -n redhat > password2
root@master30 web 15:31:36# kubectl create secret generic mysecret2 --from-file=./user --from-file=./password1 --from-file=./password2
secret/mysecret2 created
# 文件名默认作为存储键名,文件内容作为对应键值;也可手动自定义键名
root@master30 web 15:32:37# kubectl create secret generic mysecret3 --from-file=username=./user --from-file=./password1 --from-file=./password2
secret/mysecret3 created
root@master30 web 15:33:45# kubectl get secrets mysecret2 -o yaml
apiVersion: v1
data:
password1: cmVkaGF0
password2: cmVkaGF0
user: dG9t
kind: Secret
metadata:
......
name: mysecret2
namespace: liu
resourceVersion: "25329"
selfLink: /api/v1/namespaces/liu/secrets/mysecret2
uid: 047366b6-e8d2-4cf0-b68c-d1f5f9999458
type: Opaque
root@master30web 15:34:10# kubectl get secrets mysecret3 -o yaml
apiVersion: v1
data:
password1: cmVkaGF0
password2: cmVkaGF0
username: dG9t
kind: Secret
metadata:
......
name: mysecret3
namespace: liu
resourceVersion: "25532"
selfLink: /api/v1/namespaces/liu/secrets/mysecret3
uid: e19df9a7-7ce3-4539-b6f5-92f83417ac1f
type: Opaque
基于键值对环境文件创建
root@master30 web 15:34:17# echo 'user=tom
password1=redhat
password2=redhat' > env.txt
root@master30 web 15:35:03# kubectl create secret generic mysecret4 --from-env-file=./env.txt
secret/mysecret4 created
root@master30 web 15:35:06# kubectl get secrets mysecret4 -o yaml
apiVersion: v1
data:
password1: cmVkaGF0
password2: cmVkaGF0
user: dG9t
kind: Secret
metadata:
......
name: mysecret4
namespace: liu
resourceVersion: "27240"
selfLink: /api/v1/namespaces/liu/secrets/mysecret3
uid: 935c0392-8e54-492d-b093-99021e0dc5bb
type: Opaque
基于目录创建
目录内所有文件的文件名为存储键名,文件内容作为对应键值。
root@master30 web 15:35:40 # mkdir config
root@master30 web 15:36:12 # mv user password1 password2 config
root@master30 web 15:37:05 # kubectl create secret generic mysecret5 --from-file=./config
secret/my-secret5 created
root@master30 web 15:38:21 # kubectl get secrets mysecret5 -o yaml
apiVersion: v1
data:
password1: cmVkaGF0
password2: cmVkaGF0
user: dG9t
kind: Secret
metadata:
......
name: mysecret5
namespace: liu
resourceVersion: "26687"
selfLink: /api/v1/namespaces/liu/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 清单中,data 字段下的取值必须填写经过 Base64 编码后的字符串。
docker-registry 类型 Secret
先回顾镜像仓库登录命令:
docker login DOCKER_REGISTRY_SERVER -u=DOCKER_USER -p=DOCKER_PASSWORD
执行登录后,Docker 认证信息默认存储于~/.dockercfg 文件,后续拉取、推送镜像时会自动读取该凭证访问镜像仓库。
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 web 15:40:11 # kubectl create secret docker-registry registry --docker-username=liu --docker-password=redhat --docker-email=admin@liu.cloud --docker-server=registry.liu.cloud
secret/docker-registry-secret created
root@master30 web 15:41:25 # kubectl get secrets docker-registry-secret -o yaml
apiVersion: v1
data:
.dockerconfigjson: eyJhdXRocyI6eyJyZWdpc3RyeS5yZWRoYXQuZnVuIjp7InVzZXJuYW1lIjoibGFvbWEiLCJwYXNzd29yZCI6InJlZGhhdCIsImVtYWlsIjoibGFvbWFAcmVkaGF0LmZ1biIsImF1dGgiOiJiR0Z2YldFNmNtVmthR0YwIn19fQ==
kind: Secret
metadata:
......
name: docker-registry-secret
namespace: liu
resourceVersion: "29629"
selfLink: /api/v1/namespaces/liu/secrets/docker-registry-secret
uid: 017f736a-11f5-42d8-a9ed-47dad254f2c2
type: kubernetes.io/dockerconfigjson
TLS 类型 Secret
用于创建存储 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 web 15:43:06 # mkdir certs && cd certs
root@master30 web 15:43:42 # openssl genrsa -out www.key 2048
#--2--生成证书请求文件csr
root@master30 web 15:45:18 # openssl req -new -key www.key -out www.csr -subj "/C=CN/ST=JS/L=NJ/O=LM/OU=DEVOPS/CN=www.liu.cloud/emailAddress=admin@liu.cloud"
# CN 字段的值必须与站点域名保持一致
#--3--使用本地私钥对证书请求文件签名,生成正式证书
root@master30 web 15:47:33 # openssl x509 -req -days 3650 -in www.csr -signkey www.key -out www.crt
基于证书与私钥创建 TLS 类型 Secret
root@master30 web 15:49:05 # kubectl create secret tls www-tls --cert=./www.crt --key=./www.key
Secret 引用方式
Pod 中使用 Secret 存在两种主流方式:以环境变量注入、以数据卷挂载。
环境变量方式引用 Secret
配置示例:
root@master30 web 15:52:10 # 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
功能验证
root@master30 web 15:54:22 # kubectl apply -f pod-with-env-from-secret.yaml
root@master30 web 15:55:08 # kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
mysql 1/1 Running 0 90s 10.224.51.131 worker31.liu.cloud <none> <none>
root@master30 web 15:56:41 # kubectl exec -it mysql -- bash -c 'echo $MYSQL_ROOT_PASSWORD'
redhat
root@master30 web 15:57:26 # mysql -u root -predhat -h 10.224.51.131 --ssl-mode=DISABLED
# 清理测试 Pod 资源
root@master30 web 15:59:13 # kubectl delete pod mysql --force
数据卷方式引用 Secret
root@master30 web 16:01:05 # echo Hello world > index.html
root@master30 web 16:01:42 # echo error > error.html
root@master30 web 16:02:18 # kubectl create secret generic web --from-file=./index.html --from-file=./error.html
挂载 Secret 全部键值
配置示例:
root@master30 web 16:04:33 # 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 为容器内挂载目录,Secret 内所有键值对会全部挂载至该目录
mountPath: "/usr/share/nginx/html"
volumes:
- name: webcontent
secret:
secretName: web
功能验证
root@master30 web 16:06:12 # kubectl apply -f pod-with-volume-from-secret.yaml
root@master30 web 16:06:58 # kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web 1/1 Running 0 15s 10.224.51.136 worker31.liu.cloud <none> <none>
root@master30 web 16:08:25 # curl 10.224.51.136
Hello World
root@master30 web 16:08:57 # curl 10.224.51.136/error.html
error
# 清理测试 Pod 资源
root@master30 web 16:10:14 # kubectl delete pod web --force
仅挂载 Secret 指定键
示例清单 nginx.yaml
root@master30 web 16:12:09 # 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: index.html
另一种实现方案:
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
# 将 Secret 内 index.html 单独挂载为容器内指定文件
mountPath: "/usr/share/nginx/html/index.html"
# subPath 参数标识本次挂载目标为单个文件而非目录
subPath: index.html
volumes:
- name: webcontent
secret:
secretName: web
功能验证
root@master30 web 16:14:26 # kubectl create -f pod-secret-volume-single.yaml
root@master30 web 16:15:03 # kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web 1/1 Running 0 21s 10.224.51.137 worker31.liu.cloud <none> <none>
root@master30 web 16:16:41 # curl 10.224.51.137/index.html
Hello World
# 容器挂载目录仅存在指定挂载的文件,不会包含未配置挂载的 error.html
root@master30 web 16:17:25 # kubectl exec web -- ls /usr/share/nginx/html
index.html
数据卷挂载模式支持 Secret 内容热更新:
root@master30 web 16:20:13 # echo 'Hello Nginx' |base64
SGVsbG8gbmdpbng=
# 修改 Secret 中 index 对应的编码值
root@master30 web 16:21:07 # kubectl edit secrets web
......
apiVersion: v1
data:
index: SGVsbG8gbmdpbng=
......
# 等待约30秒后访问验证更新结果
root@master30 web 16:23:45 # curl 10.224.51.137/index.html
Hello Nginx
挂载文件权限配置
Secret 挂载至容器后的文件默认权限为八进制 644,对应十进制数值 420。
全局设置 Secret 所有挂载文件权限,使用 defaultMode 字段:
volumes:
- name: foo
secret:
secretName: mysecret1
defaultMode: 256
十进制数值 256 转换为八进制为 400。由于 JSON 格式不支持八进制数值,权限配置统一使用十进制数字填写。
root@master30 web 16:26:19 # kubectl exec mypod1 -- ls -l /etc/foo/..data/user
-r-------- 1 root root 4 Aug 9 08:58 /etc/foo/..data/user
单独配置 Secret 内某一个键对应文件的权限:
volumes:
- name: foo
secret:
secretName: mysecret1
items:
- key: user
path: user
mode: 511
十进制数值 511 转换为八进制为 777。
root@master30 web 16:28:34 # kubectl exec mypod1 -- ls -l /etc/foo/..data/user
-rwxrwxrwx 1 root root 4 Aug 9 08:58 /etc/foo/..data/user
综合实验
基于 MySQL、WordPress 容器镜像部署博客业务,部署约束如下:
- MySQL 数据库密码、WordPress 站点证书均通过 Secret 资源存储;
- 博客站点虚拟主机配置文件由 ConfigMap 提供。
环境清理
root@master30 web 16:30:08 # kubectl delete ns configure
更多推荐



所有评论(0)