K8s的配置存储与实战
配置与存储:应用“吃喝”的保障
ConfigMap:存储配置文件
Secret:存放密码或密钥
PV/PVC:持久化存储的资源与资源绑定

K8s存储:临时存储与持久化存储
临时存储
emptyDir:在同一个Pod内的不同容器间共享数据,和Pod生命周期相同,Pod创建时创建,Pod销毁时销毁,临时存储;
configMap:(配置映射)存储配置文件(如:my.cnf/redis.conf/nginx.conf);
secret:存储敏感数据(如密码、私钥、证书等);
downwardAPI:存储Pod与容器元数据(如名称、IP)。
存储类型 | 生命周期 | 共享范围 | 典型用途 |
emptyDir | 与 Pod 一致 | 同一 Pod 内容器 | 临时数据共享 |
configMap | 独立于 Pod | 跨 Pod 配置共享 | 配置文件挂载 |
secret | 独立于 Pod | 跨 Pod 敏感信息 | 密码、证书挂载 |
PersistentVolume | 独立于 Pod | 跨 Pod/命名空间 | 持久化数据(如数据库) |
持久化存储:将数据存储到磁盘空间;
hostpath :让Pod中容器直接挂载当前Node节点的目录;
PV :持久化存储卷,用于关联存储介质(后台存储),相当于“硬盘”;每个PV都有权限、容量;
PVC :在Pod中设置的持久化存储需求,启动Pod时,根据需求自动匹配相应的PV,从而实现数据目录的映射;在容器中的目录上挂载存储介质;
StorageClass :存储类,根据PVC的需求自动创建PV,使用时更加方便快捷。
维度 | PV (Persistent Volume) | PVC (Persistent Volume Claim) |
角色定位 | 集群中的"物理硬盘" | 用户的"硬盘申请单" |
创建者 | 集群管理员创建 | 应用开发者/用户创建 |
作用范围 | 集群级别资源 | Namespace级别资源 |
核心功能 | 定义具体存储属性: | 声明存储需求: |
生命周期状态 | Available → Bound → Released → Failed | Pending → Bound → Deleted |
绑定关系 | 被PVC绑定使用 | 主动绑定到合适的PV |
使用方式 | Pod通过PVC间接使用PV | Pod直接引用PVC名称 |
工作流程简化:
管理员创建 PV → 用户创建 PVC → 系统自动绑定 → Pod 使用 PVC
存储方式:
NFS
CEPH
MINIO
……
存算分离:
将存储与运算分离,集中存储,方便管理;
存储实战
emptyDir
# 示例一:基础共享(磁盘介质)
# 两个容器通过磁盘介质的emptyDir共享日志文件
vim 13-emptydir.yaml
apiVersion: v1
kind: Pod
metadata:
name: log-share-pod
spec:
volumes:
- name: log-storage
emptyDir:
sizeLimit: 512Mi
containers:
- name: app
image: busybox:latest
command: ["/bin/sh", "-c", "mkdir -p /var/log/app && while true; do echo \"$(date) 访问日志: ...\" >> /var/log/app/app.log; sleep 3; done"]
volumeMounts:
- name: log-storage
mountPath: /var/log/app
- name: log-collector
image: busybox:latest
command: ["/bin/sh", "-c", "tail -f /logs/app.log"]
volumeMounts:
- name: log-storage
mountPath: /logs
# 创建Pod,然后查看log-collector容器的日志输出
kubectl apply -f 13-emptydir.yaml

# 通过路径验证两个容器中的文件是否相同,如果两个MD5值相同证明文件相同
# 在app容器中检查文件
kubectl exec log-share-pod -c app -- md5sum /var/log/app/app.log
# 在log-collector容器中检查文件
kubectl exec log-share-pod -c log-collector -- md5sum /logs/app.log

示例二:内存介质(tmpfs)+ 只读权限
vim 14-emptydir-tmpfs.yaml
apiVersion: v1
kind: Pod
metadata:
name: cache-share-pod
spec:
volumes:
- name: cache-volume
emptyDir:
medium: Memory
sizeLimit: 256Mi
containers:
- name: cache-generator
image: busybox:latest
command: ["/bin/sh", "-c", "while true; do echo \"缓存数据: $(date +%s)\" > /cache/data; sleep 5; done"]
volumeMounts:
- name: cache-volume
mountPath: /cache
- name: cache-reader
image: busybox:latest
command: ["/bin/sh", "-c", "while true; do echo \"读取到: $(cat /read-only-cache/data)\"; sleep 5; done"]
volumeMounts:
- name: cache-volume
mountPath: /read-only-cache
readOnly: true
# 创建Pod
kubectl apply -f 14-emptydir-tmpfs.yaml
# 查看Pod状态
kubectl get pod cache-share-pod

## 验证内存emptyDir功能
# 查看cache-generator容器的日志(写入端)(什么都不会出现是正常的,因为输出被重定向到了文件,可以通过检查文件内容来验证数据是否正确写入和共享)
kubectl logs cache-share-pod -c cache-generator -f
# 查看cache-reader容器的日志(读取端)
kubectl logs cache-share-pod -c cache-reader -f

## 验证只读权限
# 在cache-reader容器中尝试写入(应该失败)
kubectl exec -it cache-share-pod -c cache-reader -- sh -c "echo '测试写入' > /read-only-cache/test.txt"

# 在cache-generator容器中尝试写入(应该成功)
kubectl exec -it cache-share-pod -c cache-generator -- sh -c "echo '测试写入' > /cache/test.txt && echo '写入成功'"

Configmap
vim 15-emptydir-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config01
data:
app.conf: |
# 应用基础配置
app.name=my-application
app.version=1.0.0
server.port=8080
debug.mode=false
logback.xml: |
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>
environment.properties: |
# 环境配置
database.url=jdbc:postgresql://localhost:5432/testdb
cache.enabled=true
feature.toggle.new-ui=true
# 应用ConfigMap
kubectl apply -f 15-emptydir-configmap.yaml
# 查看ConfigMap,或者可以用describe验证创建
kubectl get configmaps

# 创建Pod
vim 16-dapi-pod-configmap.yaml
apiVersion: v1
kind: Pod
metadata:
name: dapi-test-pod
spec:
containers:
- name: test-container
image: busybox:latest
command: [ "/bin/sh", "-c", "sleep 3600" ]
volumeMounts:
- name: app-config
mountPath: /etc/app-config/
volumes:
- name: app-config
configMap:
name: app-config01
# 应用Pod
kubectl apply -f 16-dapi-pod-configmap.yaml
# 查看Pod状态
kubectl get pod

## 验证ConfigMap挂载
# 查看Pod日志
kubectl logs dapi-test-pod
# 进入Pod验证文件
root@master:~/yaml# kubectl exec -it dapi-test-pod -- /bin/sh
# 然后就变成
/ #
# 在容器内检查
ls /etc/app-config/

cat /etc/app-config/app.conf
cat /etc/app-config/logback.xml
cat /etc/app-config/environment.properties
# 退出容器
exit
Secret
1. Service Account Secret(自动创建)
vim 17-service-account-secret-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: sa-test-pod
spec:
automountServiceAccountToken: true #显式启用
containers:
- name: test-container
image: busybox:latest
command: ["/bin/sh", "-c", "echo '检查Service Account目录...'; sleep 3; find /run -name '*serviceaccount*' 2>/dev/null || find /var -name '*serviceaccount*' 2>/dev/null || echo '未找到serviceaccount目录'; sleep 3600"]
# 创建Pod并等待Pod运行起来
kubectl apply -f 17-service-account-secret-pod.yaml
kubectl get pod

# 进入Pod里验证
kubectl exec -it sa-test-pod -- /bin/sh
/ # find / -name "*serviceaccount*" 2>/dev/null
显示/var/run/secrets/kubernetes.io/serviceaccount
/ # ls /var/run/secrets/kubernetes.io/serviceaccount
显示ca.crt namespace token
/ # cat /var/run/secrets/kubernetes.io/serviceaccount/namespace
显示default/ # 然后输入exit退出即可
2. Opaque Secret(存储密码密钥)
## 创建Opaque Secret
# 通过命令行创建Secret
kubectl create secret generic app-secrets \
--from-literal=username=admin \
--from-literal=password=secret123 \
--from-literal=database-url="db://admin:secret@db.example.com/app"
# 验证Secret内容
kubectl get secret app-secrets -o jsonpath='{.data}' | base64 -d
显示base64: invalid input
## 创建使用Opaque Secret的Pod
cat > 19-opaque-test-pod.yaml << 'EOF'
apiVersion: v1
kind: Pod
metadata:
name: opaque-test-pod
spec:
containers:
- name: test-container
image: busybox:latest
command: ["/bin/sh", "-c", "sleep 3600"]
env:
- name: USERNAME
valueFrom:
secretKeyRef:
name: app-secrets
key: username
- name: PASSWORD
valueFrom:
secretKeyRef:
name: app-secrets
key: password
volumeMounts:
- name: secret-volume
mountPath: /etc/secrets
readOnly: true
volumes:
- name: secret-volume
secret:
secretName: app-secrets
EOF
kubectl apply -f 19-opaque-test-pod.yaml

3. Docker Registry Secret(私有仓库认证)(kubernetes.io/dockerconfigjson 类型)
## 创建Docker Registry Secret
# 创建用于私有Docker仓库的secret
kubectl create secret docker-registry my-registry-key \
--docker-server=192.168.57.200:8099 \
--docker-username=admin \
--docker-password=password123 \
--docker-email=admin@example.com
# 查看创建的secret详情
kubectl get secret my-registry-key -o yaml
# 或者查看类型
kubectl get secret my-registry-key -o jsonpath='{.type}'
# 输出会是: kubernetes.io/dockerconfigjson
## 创建使用Registry Secret的Pod
cat > 18-registry-test-pod.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
name: registry-test-pod
spec:
containers:
- name: test-container
image: busybox:latest
command: ["/bin/sh", "-c", "echo '这个Pod使用了Docker Registry Secret'; sleep 3600"]
imagePullSecrets:
- name: my-registry-key
EOF
# 创建Pod
kubectl apply -f 18-registry-test-pod.yaml
## 验证
# 查看所有secret
kubectl get secrets

# 查看各个Pod状态
kubectl get pods

# 进入Pod查看详细内容
kubectl exec -it opaque-test-pod -- /bin/sh
# 在容器内查看:
env | grep USERNAME
env | grep PASSWORD
ls -la /etc/secrets/
cat /etc/secrets/database-url
exit

### 清理资源
# 删除所有测试资源
kubectl delete pod sa-test-pod opaque-test-pod registry-test-pod
kubectl delete secret app-secrets my-registry-key
K8s存储之volume
卷的类型:
emptyDir:与pod的生命周期是一致的,它的最实际实用是提供Pod内多容器的volume数据共享;
用法有:
1、暂存空间,例如用于基于磁盘的合并排序
2、用作长时间计算崩溃恢复时的检查点
3、Web服务器容器提供数据时,保存内容管理器容器提取的文件
hostPath:将主机文件系统上的某个目录挂载到 Pod 中,可以使用本地节点的磁盘资源;
具有持久化能力,但仅限固定节点。
适合需要在特定节点上存储数据的场景,daemonset控制器。
存储卷只能在节点本地使用,无法跨节点共享(缺点)。
hostPath 卷的使用需要谨慎,因为它会与节点的文件系统直接交互,可能引发数据一致性和安全性问题。
PersistentVolume(PV) & PersistentVolumeClaim(PVC)
StorageClass
emptyDir
## 验证两个容器如何通过共享卷来交换数据(容器间的数据共享和通信)
vim 20-volume-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: test-volume
spec:
containers:
- name: volume-pod-1
image: 192.168.57.200:8099/library/nginx:latest
imagePullPolicy: IfNotPresent
volumeMounts:
- mountPath: /test-1
name: volume
- name: volume-pod-2
image: 192.168.57.200:8099/library/busybox:latest
imagePullPolicy: IfNotPresent
command: ["/bin/sh", "-c", "sleep 600"]
volumeMounts:
- mountPath: /test-2
name: volume
volumes:
- name: volume
emptyDir: {} # 必须指定卷类型
kubectl apply -f 20-volume-pod.yaml

# 进入busybox容器验证共享卷
kubectl exec -it test-volume -c volume-pod-2 -- /bin/sh
# 在busybox容器中创建文件 → 测试写入功能:
/ # ls -la /test-2/
total 8
drwxrwxrwx 2 root root 4096 Nov 14 03:26 .
drwxr-xr-x 1 root root 4096 Nov 14 03:26 ..
/ # echo "hello from pod-2" > /test-2/shared-file.txt
/ # exit
# 进入nginx容器验证
kubectl exec -it test-volume -c volume-pod-1 -- /bin/bash
# 在nginx容器中读取文件 → 验证共享是否成功:
root@test-volume:/# ls -la /test-1/
total 12
drwxrwxrwx 2 root root 4096 Nov 14 03:28 .
drwxr-xr-x 1 root root 4096 Nov 14 03:26 ..
-rw-r--r-- 1 root root 17 Nov 14 03:28 shared-file.txt
root@test-volume:/# cat /test-1/shared-file.txt
应该能看到hello from pod-2
root@test-volume:/# exit
后续实验与之前相似就不做了。
补充hostPath
# 创建Pod YAML文件
vim 21-hostpath-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: hostpath
spec:
containers:
- name: test-container
image: 192.168.57.200:8099/library/busybox:latest
command: ["/bin/sh", "-c", "sleep 3600"]
volumeMounts:
- mountPath: /host-logs
name: host-volume
volumes:
- name: host-volume
hostPath:
path: /tmp/hostpath-test
type: DirectoryOrCreate
kubectl apply -f 21-hostpath-pod.yaml
# 查看Pod状态
kubectl get pods
# 查看Pod是在哪个节点上执行的,方便一会儿验证
kubectl get pod hostpath -o wide
# 进入容器
kubectl exec -it hostpath -- /bin/sh
# 在容器内操作
ls -la /host-logs/
echo "Hello from container!" > /host-logs/test-file.txt
cat /host-logs/test-file.txt
exit
# 在主机上验证(在node节点上执行)
ls -la /tmp/hostpath-test/
cat /tmp/hostpath-test/test-file.txt

