一、从一个莫名的变更事故说起

1.1 我们遇到了什么坑

上周业务组的同学慌慌张张找到我,说他们刚上线的微服务流量突然切分异常,原本配置的网关实例应该是3个,结果自动扩缩容把实例拉成了10个,还把请求的CPU配额改乱了,导致服务偶尔宕机。排查了一圈,发现他们同时用了Helm部署Istio,又用Kustomize做定制化补丁,这俩工具的配置顺序撞在一起,搞出了大乌龙。

这种情况不是个例——很多开发者为了兼顾部署效率和定制灵活性,会同时用Helm和Kustomize,却忽略了两者的配置覆盖规则完全不一样,稍有不慎就会出现“看似改对了,实际全乱了”的意外变更。今天我们就把这个坑的前因后果捋清楚,帮大家避开这类麻烦。

二、先搞懂Helm和Kustomize的基本玩法

要解决冲突,得先知道这俩工具分别怎么管配置。别被术语吓到,我用奶茶点单举例子就懂了:

2.1 Helm的“值覆盖”玩法

Helm就像连锁奶茶店的标准菜单(官方Chart),每个参数都有默认值(比如默认三分糖),你可以自己改参数(自定义values),还能下单时临时加备注(--set命令)。它的规则是:临时备注优先级最高,自定义值次之,默认值最后。比如你要改糖度,要么改自己的备注,要么不碰默认值直接提要求。

举个Istio网关的Helm配置示例:

# 这是Helm的自定义values.yaml,用来配置Istio ingress网关
gateways:
  istio-ingressgateway:
    autoscaleMin: 2  # 自动扩缩容最小实例数
    resources:
      requests:
        cpu: 500m    # 网关的CPU请求配额

默认是自动扩缩容,不手动改副本数,这样集群会自动调实例数量。

2.2 Kustomize的“补丁合并”玩法

Kustomize就像奶茶店的定制辅料包,你不用改整个菜单,只需要提“加珍珠”“少冰”这类补丁要求,它会自动把补丁合并到基础配置里。它的规则是:补丁会直接替换对应字段,如果是嵌套配置(比如resources),会合并子字段,简单字段(比如副本数)会直接覆盖

同样是配置网关,用Kustomize做补丁的示例:

# Kustomize的kustomization.yaml,引用Helm生成的基础配置
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - ./istio-helm-rendered.yaml  # 这里是Helm渲染后的清单文件
patchesStrategicMerge:
  - patch-gateway.yaml          # 自定义补丁文件

补丁文件里想改网关配置:

# patch-gateway.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: istio-ingressgateway
spec:
  replicas: 3  # 要求改固定3个实例,不用自动扩缩容
  template:
    spec:
      containers:
      - name: istio-proxy
        resources:
          requests:
            cpu: 1000m  # 把CPU配额改成1核

看起来没问题吧?但问题就出在这儿。

三、当两者碰到一起,覆盖顺序乱套了

3.1 事故现场的复现

刚才的配置看起来是要改副本数和CPU,实际为什么会出问题?因为Helm的values里配置的是autoscaleMin:2,属于自动扩缩容的触发条件,而Kustomize的patch里直接改了replicas,这两个都是控制扩缩容的核心字段,只是级别不一样。

Helm渲染后的清单里,网关的Deployment会同时有spec.replicas(默认是空,因为没配置固定副本数)和触发自动扩缩容的参数,而Kustomize的patch直接把spec.replicas改成了3,但Helm的自动扩缩容逻辑还在生效,这就导致K8s里的网关配置出现了“固定副本数+自动扩缩容”的冲突,集群发现扩缩容的配置不匹配,就会默认把副本数拉到最大值,CPU配额也和预期不符,最后流量异常。

3.2 覆盖顺序的真面目

把这个逻辑拆成大白话:Helm管的是“规则”,Kustomize管的是“结果”,当两者都碰扩缩容这个领域时,规则和结果打架,K8s只能取最乱的结果。具体的优先级是:Helm的values是“规则”层,Kustomize的patch是“结果”层,同一维度的配置不能同时用两个工具管,否则就会打架

四、我们该怎么避坑

4.1 明确两种工具的边界

核心规则很简单:同一资源的同一维度配置,只能让Helm或Kustomize其中一个管。比如扩缩容要么全用Helm的autoscale参数,要么全用Kustomize改replicas,不能两边都碰。

4.2 正确的配置示例

修正后的配置应该这样:如果用Kustomize管扩缩容,就别让Helm碰这个维度,把Helm的values改成支持Kustomize patch的方式,或者反过来。比如改成: 技术栈:Istio 1.18,Helm 3.12,Kustomize 5.1 修正后的Helm values:

# 改Helm values,让它不碰固定副本数,只留自动扩缩容的参数
gateways:
  istio-ingressgateway:
    autoscaleMin: 2
    autoscaleMax: 5  # 加上最大实例数
    # 去掉replicas字段,让Kustomize可以安全改
    resources:
      requests:
        cpu: 500m

修正后的Kustomize patch,这次只改CPU配额,不碰副本数(把副本数的管理交给Helm的自动扩缩容):

# 安全的patch-gateway.yaml,不碰扩缩容维度
apiVersion: apps/v1
kind: Deployment
metadata:
  name: istio-ingressgateway
spec:
  template:
    spec:
      containers:
      - name: istio-proxy
        resources:
          requests:
            cpu: 1000m  # 只改CPU,不碰副本数,冲突就消失了

这样Helm管自动扩缩容,Kustomize管非核心的资源调整,两者的配置维度不重叠,就不会再出冲突。

五、应用场景、技术优缺点和注意事项

5.1 适用场景

Helm+Kustomize的组合,适合这几种情况:

  • 用官方Chart快速部署Istio这类复杂组件,不用从零写清单
  • 只需要做少量定制化(比如改资源配额、小参数调整),不用重写整个Chart
  • 团队已经熟悉Helm的Chart体系,又需要灵活的补丁修改能力

5.2 技术优缺点

Helm的优点是:官方维护的Chart生态成熟,值覆盖规则清晰,依赖管理方便,适合快速部署标准化组件;缺点是定制化程度有限,复杂的场景(比如跨命名空间的特殊配置)很难灵活修改。 Kustomize的优点是:原生适配Kubernetes资源,不需要依赖Chart,补丁合并规则清晰,适合做轻量定制;缺点是大项目里多个补丁容易混乱,没有像Helm那样的版本管理和依赖能力。 两者结合的优点是兼顾效率和灵活,缺点是配置边界容易模糊,需要明确分工。

5.3 注意事项

  • 绝对不能让同一资源的同一字段同时被Helm values和Kustomize patch修改,比如扩缩容、端口、镜像版本这类核心字段
  • 每次修改配置后,一定要看Helm渲染后的清单和Kustomize合并后的结果,确认没有冲突
  • 复杂场景下,尽量用Helm的values完成大部分配置,Kustomize只做少量补丁,减少边界模糊的可能

六、总结

很多时候,配置的意外变更不是工具本身的问题,而是我们没搞清楚工具的分工。Helm和Kustomize各有优势,结合使用时只要明确好各自负责的配置维度,避开“同一字段两边改”的坑,就能把两者的优势发挥出来,避免生产环境的莫名故障。希望这篇文章能帮大家避开踩过的坑,让Istio的配置管理更稳定。