4. Kubernetes进阶:ConfigMap与Secret管理、Ingress控制器、持久化存储(PV/PVC)、Helm包管理

好,咱们继续往下走。前面几章我们把Kubernetes的基础概念和核心工作负载都过了一遍。说实话,光会部署个无状态应用,在真正的生产环境里是远远不够的。这一章,我带你看看几个真正让Kubernetes变得“好用”的关键组件。说白了,就是解决配置管理、外部访问、数据持久化和应用打包这几个痛点。

4.1 ConfigMap与Secret:配置与敏感信息管理

先聊聊配置管理。你想想看,一个应用通常有各种配置:数据库地址、日志级别、功能开关……以前我们怎么做?要么写死在代码里,要么丢在环境变量里。但到了Kubernetes里,容器是随时可能被销毁重建的,配置必须和镜像解耦。

ConfigMap 就是干这个的。它用来存储非敏感的配置数据,比如配置文件内容、命令行参数、环境变量等。

我举个例子,创建一个ConfigMap,里面放一个应用配置文件:

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  app.properties: |
    database.url=jdbc:mysql://db-service:3306/crm
    log.level=INFO
    feature.flag.new-ui=true

然后,在Pod里通过卷挂载的方式使用它:

apiVersion: v1
kind: Pod
metadata:
  name: crm-app
spec:
  containers:
  - name: crm
    image: crm-app:latest
    volumeMounts:
    - name: config-volume
      mountPath: /etc/config
  volumes:
  - name: config-volume
    configMap:
      name: app-config

这样,配置文件和镜像就彻底分开了。改配置?更新ConfigMap,重启Pod就行。我在项目中遇到过,有一次线上需要紧急调整日志级别排查问题,直接改了ConfigMap,滚动更新一下Pod,几分钟就搞定了,不用重新构建镜像,省大事了。

Secret 和ConfigMap很像,但它是用来存敏感信息的,比如密码、API密钥、TLS证书。它的数据是Base64编码的,但这只是序列化方式,不是加密。Kubernetes本身会对Secret进行加密存储(如果开启了加密配置)。

注意: 千万不要把Secret的YAML文件直接提交到Git仓库里!我曾经见过有人把生产环境的数据库密码Base64编码后放在GitLab上,结果整个团队都能看到。正确的做法是用Sealed Secrets、External Secrets Operator这类工具,或者结合Vault来管理。

创建Secret的示例:

apiVersion: v1
kind: Secret
metadata:
  name: db-secret
type: Opaque
data:
  username: Y3JtX3VzZXI=  # crm_user
  password: cGFzc3dvcmQxMjM=  # password123

在Pod里引用时,可以挂载成文件,也可以直接作为环境变量注入。我个人习惯用文件挂载的方式,因为环境变量在Pod启动后如果Secret更新了,环境变量不会自动刷新,而挂载的文件会自动更新(虽然应用需要自己监听文件变化)。

4.2 Ingress控制器:统一入口管理

好,配置管理说完了。接下来是外部访问的问题。你的CRM系统肯定要对外提供服务吧?用NodePort?可以,但端口管理太乱了,而且不支持7层路由。用LoadBalancer?每个服务一个公网IP,成本太高。

Ingress 就是来解决这个问题的。它提供了一种统一的方式,把外部HTTP/HTTPS流量路由到集群内部的服务。你可以把它理解成一个智能的“大门”,根据请求的域名和路径,把流量分发给不同的后端服务。

但注意,Ingress只是一个API对象,真正干活的是 Ingress Controller。常见的实现有Nginx Ingress Controller、Traefik、HAProxy等。我用的最多的是Nginx Ingress Controller,稳定,社区活跃,坑也少。

部署一个Ingress的YAML示例:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: crm-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
  - host: crm.example.com
    http:
      paths:
      - path: /api
        pathType: Prefix
        backend:
          service:
            name: crm-api-service
            port:
              number: 8080
      - path: /web
        pathType: Prefix
        backend:
          service:
            name: crm-web-service
            port:
              number: 80

这个配置的意思是:访问 crm.example.com/api 的请求,转发到 crm-api-service 的8080端口;访问 /web 的,转发到 crm-web-service 的80端口。

避坑指南: 我曾经在配置Ingress时,忘记加 ingressClassName 字段,结果Ingress Controller根本不认这个资源,流量一直404。后来查了半天文档才发现,新版本的Kubernetes里,这个字段是必须的。另外,rewrite-target 注解也很容易搞错,不配置的话,后端服务收到的请求路径会包含前缀,比如 /api/users,如果你的应用不期望这个前缀,就会报404。

4.3 持久化存储(PV/PVC):让数据不随Pod消失

接下来是存储。Pod是临时性的,重启后里面的数据就没了。但CRM系统里的客户数据、附件、日志,这些都是要持久保存的。怎么办?用 PersistentVolume(PV)PersistentVolumeClaim(PVC)

简单理解:PV是集群管理员预先准备好的存储资源,比如一块NFS共享目录、一个Ceph RBD块设备、或者云服务商的云盘。PVC是用户(开发者)对存储资源的“需求申请”,比如“我需要5GB的读写一次存储”。Kubernetes会负责把PVC和合适的PV绑定起来。

举个例子,先创建一个PV:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: crm-data-pv
spec:
  capacity:
    storage: 10Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  nfs:
    path: /data/crm
    server: 192.168.1.100

然后,在Pod里通过PVC来申请使用:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: crm-data-pvc
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: crm-db
spec:
  replicas: 1
  selector:
    matchLabels:
      app: crm-db
  template:
    metadata:
      labels:
        app: crm-db
    spec:
      containers:
      - name: mysql
        image: mysql:8.0
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
      volumes:
      - name: data
        persistentVolumeClaim:
          claimName: crm-data-pvc

这样,即使Pod被删除了,数据还在PV里。下次重建Pod,只要PVC还在,数据就能恢复。我在项目中遇到过,有一次集群节点宕机,Pod被调度到其他节点,但因为PVC绑定的PV是NFS共享存储,数据一点没丢,服务自动恢复了。嗯,这就是持久化存储的价值。

关键点: PV的 persistentVolumeReclaimPolicy 字段决定了PVC被删除后PV的行为。Retain表示保留数据,需要管理员手动清理;Delete表示自动删除底层存储;Recycle表示清空数据后重新可用。生产环境我建议用Retain,安全第一。

4.4 Helm包管理:一键部署复杂应用

最后,我们聊聊Helm。你有没有觉得,每次部署一个稍微复杂点的应用,都要写一堆YAML文件?Deployment、Service、ConfigMap、Secret、Ingress……而且每个环境(开发、测试、生产)的配置还不一样。手动改来改去,很容易出错。

Helm 就是Kubernetes的包管理器。它把一组相关的Kubernetes资源打包成一个 Chart,通过模板化的方式,让你可以用不同的配置值来生成不同的YAML文件。说白了,就是“一次编写,到处部署”。

Helm的核心概念有三个:

  • Chart:一个Helm包,包含了一组Kubernetes资源的模板和默认配置。
  • Release:一个Chart在某个集群中的一次部署实例。同一个Chart可以部署多次,生成多个Release。
  • Repository:存放Chart的仓库,类似Docker Hub。

安装一个Helm Chart的示例:

# 添加仓库
helm repo add bitnami https://charts.bitnami.com/bitnami

# 安装MySQL
helm install my-mysql bitnami/mysql \
  --set auth.rootPassword=secretpassword \
  --set persistence.size=20Gi

就这么一行命令,Kubernetes里就多了一个MySQL实例,包含了Deployment、Service、PVC、Secret等一堆资源。是不是很方便?

我自己写Chart时,通常会这样组织目录结构:

crm-chart/
├── Chart.yaml          # Chart的元数据
├── values.yaml         # 默认配置值
├── templates/          # 模板文件
│   ├── deployment.yaml
│   ├── service.yaml
│   ├── ingress.yaml
│   ├── configmap.yaml
│   └── _helpers.tpl    # 辅助模板函数
└── charts/             # 子Chart依赖

values.yaml 里定义好所有可配置的参数,然后在模板里用 {{ .Values.replicaCount }} 这样的语法引用。部署不同环境时,只需要提供不同的 values.yaml 文件即可。

个人经验: 刚开始用Helm时,我犯过一个错误:把所有配置都写在 values.yaml 里,导致文件变得非常庞大。后来我学会了用 --set 参数覆盖少量配置,用 -f 指定环境特定的values文件。另外,helm template 命令可以预览生成的YAML,调试时非常有用,建议多用。

好了,这一章的内容就到这里。ConfigMap和Secret帮你管理配置和敏感信息,Ingress解决了外部访问的统一入口问题,PV/PVC让数据有了“家”,Helm则让整个部署过程变得标准化和可重复。这些工具组合起来,你的CRM系统才能真正做到“云原生”——灵活、可靠、易于管理。