K8S 配置与调度-叩丁狼

3.4 配置与存储

3.4.1 配置管理

Kubernetes(K8s)的配置管理是其核心能力之一,它旨在将应用配置(如配置文件、环境变量、命令行参数等)从容器镜像中解耦,从而实现 "一次构建,到处部署" 的云原生理念

在传统模式下,配置通常被"硬编码"到应用代码或Docker镜像中,导致不同环境(开发、测试、生产)需要不同的镜像,管理复杂且易出错。K8s的配置管理通过一系列原生API对象解决了这个问题。

K8s的配置管理的核心是:将配置数据作为独立的K8s资源进行管理,并通过卷(Volume)或环境变量等方式动态注入到Pod容器中,实现配置与应用的分离。

3.4.1.1 configMap

管理非敏感的配置数据,例如配置文件、命令行参数、环境变量、端口号等。

  • 数据以键值对(key-value)形式存储。

  • Value可以是简单的字符串,也可以是完整的配置文件内容(如JSON、XML、Properties、YAML等)。

  • 存储大小有限制(默认约1MB in etcd)。

    配置configMap的资源

  1. 命令行形式 : kubectl creat configmap <资源名称> --from-literal=< ? 文件别名>=文件地址/文件夹路径(整个文件夹下所有文件)

  2. 配置形式:

    apiVersion: v1
    kind: ConfigMap
    metadata:
    name: game-config
    namespace: default
    labels:
    app: game
    tier: backend
    data:

    简单键值对

    game.type: "mmorpg"
    game.difficulty: "hard"

    配置文件内容(整个文件作为值)

    server.properties: |
    enemy.types=aliens,monsters
    player.maximum-lives=5
    level.complexity=high

    另一个配置文件

    ui.properties: |
    color.good=purple
    color.bad=yellow
    allow.textmode=true

    JSON 配置

    config.json: |
    {
    "logging": {
    "level": "DEBUG",
    "file": "/var/log/app.log"
    },
    "features": {
    "enableCache": true,
    "maxItems": 100
    }
    }

    在 Pod 中使用 configmap
    - 作为环境变量 : 将整个 configmap 的特定键注入容器环境变量
    - 作为文件挂载 : 支持热更新
    - 作为命令行参数 : 通过环境变量间接传递

    apiVersion: v1
    kind: Pod
    metadata:
    name: configmap-env-pod
    spec:
    containers:

    • name: test-container
      image: busybox
      command: [ "/bin/sh", "-c", "env" ]
      env:

      定义环境变量 LOG_LEVEL,值来自 ConfigMap

      • name: LOG_LEVEL
        valueFrom: # 将整个 configmap 的特定键注入容器环境变量
        configMapKeyRef:
        name: app-config # ConfigMap 名称 ,单写这一个,指将所有键值对作为环境变量
        key: log-level # ConfigMap 中的键
        optional: false # 可选,如果为 true,ConfigMap 不存在时 Pod 也能启动

    apiVersion: v1
    kind: Pod
    metadata:
    name: configmap-volume-pod
    spec:
    containers:

    • name: test-container
      image: busybox
      command: [ "/bin/sh", "-c", "ls -la /etc/config && cat /etc/config/*" ]
      volumeMounts:
      • name: config-volume
        mountPath: /etc/config # ConfigMap 中的每个键都会成为一个文件
        readOnly: true
        volumes: # 作为文件挂载
    • name: config-volume
      configMap:
      name: game-config # 引用的 ConfigMap

    挂载效果

    /etc/config/
    ├── game.type # 文件内容: "mmorpg"
    ├── game.difficulty # 文件内容: "hard"
    ├── server.properties # 文件内容: "enemy.types=aliens..."
    └── ui.properties # 文件内容: "color.good=purple..."

3.4.1.2 Secret

专门用于管理和存储敏感信息 ,如密码、OAuth令牌、SSH密钥、TLS证书等。

使用方式与ConfigMap几乎完全相同(也支持环境变量和Volume挂载)。

K8s会对Secret数据进行Base64编码(仅是一种简单的混淆,并非加密)。在Master节点上,etcd中存储的数据可以启用静态加密。

Secret有三种主要类型:

  • Opaque(通用,默认)
  • kubernetes.io/tls(TLS证书)
  • kubernetes.io/dockerconfigjson(镜像仓库认证)。
场景 使用 ConfigMap 使用 Secret
数据库连接字符串 ❌ 敏感 ✅ 使用 Secret
日志级别 ✅ 非敏感
API 端点 URL ✅ 非敏感
TLS 证书 ❌ 敏感 ✅ 使用 Secret
特性开关 ✅ 非敏感
3.4.1.3 subPath

使用 ConfigMap 或 Secret 挂载到目录的时候,会将容器中源目录给覆盖掉,此时我们可能只想覆盖目录中的某一个文件,但是这样的操作会覆盖整个文件,因此需要使用到 SubPath

复制代码
# 假设你有一个容器镜像,其目录结构如下:
/opt/app/
├── app.jar
├── config/
│   ├── application.yaml    # 需要被替换的配置
│   └── security.yaml       # 需要保留的默认配置
└── logs/

# 直接将 ConfigMap 挂载到 `/opt/app/config`
volumeMounts:
- name: config-volume
  mountPath: /opt/app/config  # 这会覆盖整个 config 目录!
  
# `security.yaml` 文件会消失!因为整个目录被 ConfigMap 的内容覆盖了。
# 这时候subPath就派出用场了:

volumeMounts:
- name: config-volume
  mountPath: /opt/app/config/application.yaml  # 挂载到特定文件路径
  subPath: application.yaml                     # 只挂载这个文件 volume 的 path 配置中 不要以 / 开头,且
3.4.1.4 配置热更新

我们通常会将项目的配置文件作为 configmap 然后挂载到 pod,那么如果更新 configmap 中的配置,会不会更新到 pod 中呢?这得分成几种情况:

默认方式:会更新,更新周期是更新时间 + 缓存时间

subPath:不会更新

变量形式:如果 pod 中的一个变量是从 configmap 或 secret 中得到,同样也是不会更新的

对于 subPath 的方式,我们可以取消 subPath 的使用,将配置文件挂载到一个不存在的目录,避免目录的覆盖,然后再利用软连接的形式,将该文件链接到目标位置,但是如果目标位置原本就有文件,可能无法创建软链接,此时可以基于 postStart 操作执行删除命令,将默认的吻技安删除即可

  • 热更新方式
    • 通过 edit 命令直接修改 configmap
    • 通过 replace 替换
      • 如果 configmap 基于文件创建,不会有 yaml 文件,无法直接替换.
      • kubectl create cm --from-file=nginx.conf<文件> --dry-run -oyaml | kubelctl replace -f- # f 后面的 - 表示接收控制台的输出变成另一个命令的输入.
        • 该命令重点在于 --dry-run 参数(打印 yaml 文件单不将文件发给 apiserver 再结合 -oyaml 输出 yaml 文件,就得到一个配置好但没发送给 apiserver 的文件,然后再结合 replace 监听控制台输出得到 yaml 数据可实现替换)

3.4.1.5 不可变的 Secrte 和 configmap

对于一些敏感的服务配置文件,在线上是不允许被修改的,此时配置 configmap 可以设置 immutable:true 来禁止修改.

3.4.2 持久化存储

3.4.2.1 Volumes
复制代码
HostPath :  将节点上的文件或目录挂载到 Pod 上,此时该目录会变成持久化存储目录,即使 Pod 被删除后重启,也可以加载到该目录,该目录下文件不会丢失

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: nginx
    name: nginx-volume
    volumeMounts:
    - mountPath: /test-pd # 挂载到容器的哪个目录
      name: test-volume # 挂载哪个 volume
  volumes:
  - name: test-volume
    hostPath: # 与主机共享目录,加载主机中指定目录到容器
      path: /data # 节点中的目录
      type: Directory # 检查类型,在挂载前对挂载目录做什么检查操作,有多种选项,默认为空字符串,不做任何检查


类型:
空字符串:默认类型,不做任何检查
DirectoryOrCreate:如果给定的 path 不存在,就创建一个 755 的空目录
Directory:这个目录必须存在
FileOrCreate:如果给定的文件不存在,则创建一个空文件,权限为 644
File:这个文件必须存在
Socket:UNIX 套接字,必须存在
CharDevice:字符设备,必须存在
BlockDevice:块设备,必须存在

emptyDir : 临时目录,Pod删除数据就没了,管理同一个 Pod 的不同容器之间的文件管理

# 定义一个使用 EmptyDir 临时存储卷的 Pod 资源
apiVersion: v1          # API版本:Pod 属于核心组资源,v1 是稳定版本
kind: Pod               # 资源类型:声明该YAML文件定义的是 Pod 资源
metadata:               # 元数据段:用于描述 Pod 的基础信息(名称、标签、注解等)
  name: test-pd         # Pod名称:在所属命名空间内唯一标识该Pod
spec:                   # 规格段:定义 Pod 的核心配置(容器、存储、网络、调度等)
  containers:           # 容器列表:一个Pod可包含多个容器,此处是数组形式(- 开头表示元素)
  - image: nginx        # 容器镜像:使用官方 nginx 镜像(默认拉取最新版)
    name: nginx-emptydir # 容器名称:在Pod内唯一标识该容器
    volumeMounts:       # 容器挂载配置:定义该容器需要挂载的 Volume 列表
    - mountPath: /cache # 挂载路径:将 Volume 挂载到容器内的 /cache 目录
      name: cache-volume # 挂载的Volume名称:关联下方 spec.volumes 中定义的 cache-volume(名称必须一致)
  volumes:              # Pod级Volume列表:定义该Pod可用的存储卷(所有容器可共享)
  - name: cache-volume  # Volume名称:与容器 volumeMounts.name 对应,作为挂载关联标识
    emptyDir: {}        # Volume类型:emptyDir(空目录卷),{} 表示使用默认配置
                        # emptyDir特性:Pod调度到节点后自动创建,Pod删除/迁移时数据永久丢失
                        # 默认存储在节点磁盘,可配置 medium: Memory 改为内存存储(tmpfs)
3.4.2.2 NFS 网络文件系统

nfs 卷能将 NFS (网络文件系统)挂载到你的 Pod 中,不像 emptyDir 那样会被当 Pod 删除时也会别删除, NFS 卷的内容在删除 Pod 时会被保存,卷只是被卸载.

复制代码
独立服务器部署

# 在专用服务器上(假设IP:192.168.1.100)
# 1. 安装NFS服务
sudo apt-get update
sudo apt-get install nfs-kernel-server -y  # Ubuntu/Debian
# 或
sudo yum install nfs-utils -y  # CentOS/RHEL

# 2. 创建共享目录
sudo mkdir -p /data/nfs-share
sudo chmod 777 /data/nfs-share  # 简化权限,生产环境需细化

# 3. 配置共享目录
sudo vim /etc/exports

在 K8S 上部署

# nfs-server.yaml
apiVersion: apps/v1          # 使用Deployment资源的API版本
kind: Deployment             # 定义资源类型为部署控制器
metadata:                    # 资源的元数据部分
  name: nfs-server           # 部署的名称,用于标识和引用
spec:                        # 部署的具体规格配置
  replicas: 1                # Pod副本数量,设为1表示只运行一个NFS服务器实例
  selector:                  # 标签选择器,用于选择要管理的Pod
    matchLabels:             # 匹配标签的条件
      app: nfs-server        # 必须匹配标签app=nfs-server的Pod
  template:                  # Pod模板定义,描述如何创建Pod
    metadata:                # Pod的元数据
      labels:                # 给Pod设置的标签
        app: nfs-server      # 标签键值对,与上面的选择器匹配
    spec:                    # Pod的具体规格
      containers:            # 容器定义列表
      - name: nfs-server     # 容器名称
        image: gcr.io/google_containers/volume-nfs:0.8  # NFS服务器容器镜像
        ports:               # 容器暴露的端口列表
        - name: nfs          # 端口名称,便于识别
          containerPort: 2049  # NFS协议主端口,用于文件传输
        - name: mountd       # 端口名称
          containerPort: 20048 # NFS挂载守护进程端口,处理挂载请求
        - name: rpcbind      # 端口名称
          containerPort: 111   # RPC绑定端口,NFS依赖的服务端口
        securityContext:     # 容器的安全上下文配置
          privileged: true   # 授予容器特权模式,NFS服务需要访问内核功能
        volumeMounts:        # 容器内的卷挂载配置
        - mountPath: /exports  # 容器内的挂载点路径,NFS将共享此目录
          name: nfs-data     # 引用的卷名称,必须与下面的volumes名称匹配
      volumes:               # Pod级别的存储卷定义
      - name: nfs-data       # 卷名称,供容器引用
        hostPath:            # 卷类型:使用宿主机路径
          path: /data/nfs    # 宿主机上的实际目录路径
---
apiVersion: v1              # 使用Service资源的API版本
kind: Service               # 定义资源类型为服务
metadata:                   # 服务的元数据
  name: nfs-server          # 服务的名称
spec:                       # 服务的规格配置
  ports:                    # 服务暴露的端口列表
  - name: nfs               # 端口名称
    port: 2049              # 服务端口,集群内部访问时使用的端口
    targetPort: 2049        # 容器端口,流量转发到容器的这个端口
  - name: mountd            # 端口名称
    port: 20048             # 服务端口
    targetPort: 20048       # 容器端口
  - name: rpcbind           # 端口名称
    port: 111               # 服务端口
    targetPort: 111         # 容器端口
  selector:                 # 标签选择器,指定后端Pod
    app: nfs-server         # 选择标签为app=nfs-server的Pod作为后端
  clusterIP: None           # 设置为Headless Service,不分配集群IP
                           # 这样Pod可以直接通过DNS记录访问,适合有状态服务

宿主机存储 Kubernetes集群 DNS查询 返回Pod IP 读写数据 宿主机 /data/nfs nfs-server Service NFS Server Pod NFS客户端Pod

复制代码
使用 NFS

# nfs-pod-direct.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nfs-web
spec:
  containers:
  - name: web-server
    image: nginx
    volumeMounts:
    - name: nfs-storage 
      mountPath: /usr/share/nginx/html  # Nginx默认网页目录
      readOnly: false
  volumes:
  - name: nfs-storage 
    nfs:
      server: 192.168.1.100  # NFS服务器IP
      path: /data/nfs-share  # 共享目录
      readOnly: false  # 读写模式
      

 -----------------------------------------------------------------
 
 # 高性能NFS配置示例
volumes:
- name: nfs-optimized
  nfs:
    server: 192.168.1.100
    path: /data/nfs
    readOnly: false
    mountOptions:
    - hard
    - intr
    - noatime
    - nodiratime
    - nfsvers=4.1
    - rsize=1048576  # 1MB读缓冲
    - wsize=1048576  # 1MB写缓冲
    - tcp           # 使用TCP协议
    - timeo=600     # 60秒超时
    - retrans=2     # 重试2次
3.4.2.3 PV 与 PVC
复制代码
 PV : 资源存储池 ->
  • 管理员创建:运维人员提前准备好

  • 集群级别:整个K8s集群都能用

  • 独立存在:不依赖任何Pod

  • 多种类型:NFS、云硬盘、本地磁盘等

  • 状态 :

    • Available : 空闲未被绑定
    • bound : 已经被 PVC 绑定
    • Released : PVC 被删除,资源已回收,但 PV 未被重新使用
    • Failed : 自动回失败
  • 存储资源池(PV们):

    ├── PV1:100G SSD云盘(快速)

    ├── PV2:500G HDD本地盘(慢速)

    ├── PV3:1TB NFS共享存储(共享)

    └── PV4:50G 高速SSD(超快)\

    PVC : 资源申请单

  • 开发人员创建:应用需要存储时申请

  • 命名空间级别:只在当前项目/命名空间有效

  • 声明需求:只要说"我要多大的、什么性能的"

  • 自动匹配:系统自动找合适的PV给你

  • 存储申请(PVC们):

    ├── 申请1:我要30G,快点就行(给MySQL)

    ├── 申请2:我要100G,能共享的(给文件服务器)

    └── 申请3:我要10G,最便宜的(给测试环境)

3.4.2.3.1 部署一个容器实现示例
  1. 管理员准备创建

    管理员准备存储(创建PV)

    运维人员创建两个"饭菜"(PV)

    apiVersion: v1
    kind: PersistentVolume
    metadata:
    name: fast-ssd-pv # PV名字:高速SSD
    spec:
    capacity:
    storage: 100Gi # 容量:100GB
    accessModes:
    - ReadWriteOnce # 访问模式:一次只能一个Pod读写
    storageClassName: fast # 存储类型标签:快速型
    hostPath:
    path: "/data/ssd" # 实际存储位置


    apiVersion: v1
    kind: PersistentVolume
    metadata:
    name: slow-hdd-pv # PV名字:慢速HDD
    spec:
    capacity:
    storage: 500Gi # 容量:500GB
    accessModes:
    - ReadWriteMany # 访问模式:多个Pod可以同时读写
    storageClassName: slow # 存储类型标签:慢速型
    nfs:
    server: 192.168.1.100
    path: "/data/nfs"

  2. 开发人员申请 pvc

    WordPress开发人员申请:

    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: wp-storage-pvc # PVC名字:WordPress存储申请
    spec:
    accessModes:
    - ReadWriteOnce # 需求:单Pod读写 / 权限需要与对应的 pv 相同
    resources:
    requests:
    storage: 20Gi # 需求:20GB空间
    storageClassName: fast # 需求:快速存储(匹配fast标签)

  1. 系统自动匹配

    K8s系统自动匹配过程:

    1. 收到PVC申请:"要20G,快速,单Pod读写"
    2. 查找PV池:
      • fast-ssd-pv:100G,fast类型,单Pod读写 ✅ 匹配!
      • slow-hdd-pv:500G,slow类型,多Pod读写 ❌ 类型不匹配
    3. 绑定:把fast-ssd-pv分配给wp-storage-pvc
    4. 状态变更:
      • fast-ssd-pv状态:Available → Bound(已绑定)
      • wp-storage-pvc状态:Pending → Bound(已满足)
  2. 应用使用存储 (Pod 挂载)

    apiVersion: v1
    kind: Pod
    metadata:
    name: wordpress-pod
    spec:
    containers:

    • name: wordpress
      image: wordpress:latest
      volumeMounts:
      • name: data-volume # 给卷起个名字
        mountPath: /var/www/html # 挂载到容器这个路径
        volumes:
    • name: data-volume # 卷的名字
      persistentVolumeClaim: # 卷类型:使用PVC
      claimName: wp-storage-pvc # 使用哪个PVC(重点是这一行!)
3.4.2.3.2 重要概念详解
  1. 访问模式 : 就像文件的打开方式
模式 说明 比喻 适用场景
ReadWriteOnce (RWO) 单节点读写 单人间,一次只能一个人住 数据库(MySQL)
ReadOnlyMany (ROX) 多节点只读 图书馆,很多人可以同时看 配置文件、静态资源
ReadWriteMany (RWX) 多节点读写 会议室白板,多人可写可读 文件共享、用户上传目录
  1. 回收策略 : 用完后怎么处理
策略 说明 比喻 何时使用
Retain 保留数据 退房后房间保持原样 重要数据(数据库)
Delete 删除数据 退房后清空房间 临时数据(测试环境)
Recycle 清理数据(已废弃) 退房后简单打扫 基本不用了
  1. StorageClass : 代购,自动售货机

每次都要管理员提前创建PV,太麻烦了!StorageClass应运而生,他相当于一个自动创建PV的模板

复制代码
# StorageClass定义:一个"自动创建PV的模板"
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-ssd-class      # 存储类名字
provisioner: kubernetes.io/aws-ebs  # 提供者:AWS云硬盘 / 制备器 : 用什么形式来创建 PV
parameters:
  type: gp3                 # 硬盘类型:通用型SSD
  fsType: ext4              # 文件系统:ext4
  archiveOnDelete           # 是否存档(不存档 会删除 old 下面的数据,存档 会重命名路径)
reclaimPolicy: Ratain       # 

实际应用中云厂商的 StorageClass 已经内置好了,不用自己创建

复制代码
# 前提:管理员已经创建了StorageClass
#(通常云厂商的K8s已经内置了)

# 开发人员一步到位:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-auto-pvc
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: standard  # 使用标准的StorageClass
  resources:
    requests:
      storage: 20Gi
3.4.2.3.3 常见问题
  • pv 与 PVC 一定要异议对应吗
    • 是的!一个PVC只能绑定一个PV,一个PV也只能绑定一个PVC。就像:一个停车位(PV)只能停一辆车(PVC),一辆车也只能占一个停车位。
  • PVC 申请的容量可以大于 PV 吗
    • 不可以
  • 删除 Pod 后数据会丢失吗?
    • 不会,数据在 PV 里面,不在 Pod.
  • 删除 PVC 后 pv 会怎样
    • 看配置的回收策略,保留还是删除
  • SorageClass 是必须的吗?
    • 不是,但推荐.
3.4.2.3.4 常见命令
复制代码
# 1. 创建PV
kubectl apply -f mysql-pv.yaml

# 2. 查看PV状态
kubectl get pv
# 应该看到:STATUS = Available(可用)

# 3. 创建PVC
kubectl apply -f mysql-pvc.yaml

# 4. 查看绑定情况
kubectl get pv
kubectl get pvc
# 应该看到:STATUS = Bound(已绑定)

# 5. 创建MySQL Pod
kubectl apply -f mysql-pod.yaml 

# 6. 测试数据持久化
kubectl exec -it mysql -- mysql -ppassword -e "CREATE DATABASE test;"
kubectl delete pod mysql
kubectl apply -f mysql-pod.yaml
kubectl exec -it mysql -- mysql -ppassword -e "SHOW DATABASES;"
# 应该还能看到test数据库
相关推荐
掘根2 小时前
【微服务即时通讯】用户管理子服务1
微服务·云原生·架构
恋红尘2 小时前
K8S Pod 基础解析-分篇-叩丁狼
云原生·容器·kubernetes·pod
阿里云云原生3 小时前
极速导入,便捷无忧!LoongCollector 一次性文件采集能力上线
云原生
丘桔3 小时前
k8s01:容器运行时之争
云原生·容器·kubernetes
2401_891655813 小时前
开源项目吐槽大会技术文章大纲
数据库·云原生
步步为营DotNet4 小时前
#.NET Aspire在云原生应用部署与管理中的深度实践
云原生·.net
学不完的5 小时前
Docker 的安全优化
运维·安全·docker·容器·eureka
恋红尘5 小时前
K8S 服务发现-叩丁狼
linux·docker·kubernetes
sleP4o5 小时前
[Docker] ‘s Basic Usage
docker·容器·eureka