一、现象

在使用Helm部署K8S应用时,发现helm install指令报错,报错内容如下:

Error: create: failed to create: Secret "sh.helm.release.v1.xxxxxx-xxxxxxxx.v1" is invalid:
data: Too long: must have at most 1048576 bytes

上述报错信息表明,在Helm部署K8S创建Secret时,其内容超过了1048576 bytes(即1MB),导致部署失败。基于上述报错,可能需要考虑以下几点:

  • Helm使用Secret的原因
  • 1MB上限的根源
  • Helm中Secret的内容

二、排查过程

(一)Helm使用Secret的原因以及1MB上限的根源

根据Helm官方文档,Helm在部署时会默认使用Secret保存Helm版本信息,该版本信息包含了位于HelmChart目录下的所有数据(包括挂载文件等内容),这里的Secret也可以根据配置替换为ConfigMap或PostgreSQL存储。

同时,根据K8S官方文档,K8S对Secret大小有所限制,其要求Secret不能大于1MB。

综上,Helm使用Secret的原因是:保存Helm版本信息。1MB上限的根源是:K8S本身的限制,与Helm没有关系。

针对这种情况,Helm提供了一种解决方案:使用PostgreSQL存储Helm版本信息,这种方案对Helm版本信息大小没有限制,允许大小超过1MB。但单独为Helm部署一套数据库成本相对较高,减少Secret中非必要的内容可能更符合K8S的规范。对此,需要先查看Helm中的Secret存在哪些内容,具体详见下文

(二)Helm中Secret的内容

首先,使用kubectl describe查看secret的概要信息:

kubectl get secret # 查看所有Secret
kubectl describe secret sh.helm.release.v1.xxxxxx-xxxxxxxx.v1 # 查看特定Secret的描述信息

由于前面Helm创建Secret报错,这里可以找一个类似的Secret查看里面的内容。

结果如下所示:

Name:         sh.helm.release.v1.xxxxxx-xxxxxxxx.v1
Namespace:    xxxxxx
Labels:       modifiedAt=1680513692
              name=xxxxxx-xxxxxxxx
              owner=helm
              status=deployed
              version=1
Annotations:  <none>

Type:  helm.sh/release.v1

Data
====
release:  999999 bytes

可以看到,这个Secret里面Data的内容大小是999999 bytes,但其具体内容并没有打印(kubectl describe ConfigMap是可以直接显示配置内容的)。对此,需要使用下述命令查看Secret的配置内容:

kubectl get secret sh.helm.release.v1.xxxxxx-xxxxxxxx.v1 -o jsonpath="{ .data.release }"

该命令会打印加密过后的配置内容,Helm采取gzip与base64的方式加密(参见此条helm github issue讨论记录)。对此,需要使用以下指令对配置内容进行解密:

kubectl get secret sh.helm.release.v1.xxxxxx-xxxxxxxx.v1 -o jsonpath="{ .data.release }" \
| base64 --decode \
| base64 --decode \
| gunzip -c \
> secret.json

该命令会将加密后的内容经过两次base64解密(第一次是K8S的base64,第二次是Helm的base64),随后再使用gunzip完成解压,最终输出格式如下所示:

{
    "name": "xxxxxx-xxxxxxxx",
    "chart": {
        "files": [
            {
                "name": ".helmignore",
                "data": ""
            }
            // 其它文件略
        ]
    }
}

data中的内容也是基于base64加密的,使用base64 --decode解密即可。

通过分析上述内容,发现有很多非必要的文件都被打入到了HelmChart Secret中,在.helmignore文件中加入不必要的文件路径后,问题最终得到解决。

三、结论

Helm部署时使用Secret保存版本信息,同时,K8S限制了Secret的内容不能大于1MB。因此,有以下两种解决方案:

  1. 将Helm中的存储系统由Secret替换为PostgreSQL,因为PostgreSQL没有存储大小的限制。该方案引入了新的数据库组件,改造成本相对较高,非必要不太建议使用。
  2. 通过base64gunzip获取secret中的内容,找到非必要的文件,并将其路径添加到.helmignore文件中,以避免将其打入HelmChart包。该方案改动较小,推荐使用。如果确实所有文件都必须打入HelmChart,且大小超过1MB,也可考虑采用上述PostgreSQL方案。

参考文档