Helm模板遍历可选值遇类型错误 求探针配置优化方案
Helm模板探针配置报错修复
问题说明
执行helm template时触发错误:
Error: template: deployment.yaml:70:54: executing "templates/deployment.yaml" at <.probes>: wrong type for value; expected map[string]interface {}; got interface {}
原因是kupkake-inator2服务仅配置了readiness探针,未配置liveness探针,模板中直接访问.probes.liveness时出现类型不匹配。
方案a:强制校验探针完整性
在模板开头新增校验逻辑,当服务配置了.probes但未同时定义readiness和liveness探针时,直接抛出错误终止模板渲染,强制用户补全配置。
修改后的模板核心代码:
{{- $root := . }} {{- range $service, $val := .Values.services }} --- apiVersion: apps/v1 kind: Deployment {{- /* 新增校验:若配置probes则必须包含readiness和liveness */}} {{- if (hasKey $val "probes") }} {{- if not (hasKey $val.probes "readiness") }} {{- fail (printf "服务 %s 的probes配置缺少readiness探针" $service) }} {{- end }} {{- if not (hasKey $val.probes "liveness") }} {{- fail (printf "服务 %s 的probes配置缺少liveness探针" $service) }} {{- end }} {{- end }} {{- /* 原有readiness探针渲染逻辑保持不变 */}} {{- if and (hasKey (.probes) "readiness") (hasKey (.probes).readiness "enabled") ((.probes).readiness.enabled) }} readinessProbe: {{- if .probes.readiness.custom }} {{- toYaml .probes.readiness.custom | nindent 12 }} {{- else }} httpGet: {{- if (hasKey .probes.readiness "port") }} port: {{ required "probes.readiness.port is required" .probes.readiness.port }} {{- end }} {{- if (hasKey .probes.readiness "endpoint") }} path: "{{ required "probes.readiness.endpoint is required" .probes.readiness.endpoint }}" {{- end }} {{- if (hasKey .probes.readiness "scheme") }} scheme: {{ .probes.readiness.scheme }} {{- end }} {{- if (hasKey .probes.readiness.initialDelaySeconds) }} initialDelaySeconds: {{ .probes.readiness.initialDelaySeconds }} {{- end }} {{- if (hasKey .probes.readiness.periodSeconds) }} periodSeconds: {{ .probes.readiness.periodSeconds }} {{- end }} {{- if (hasKey .probes.readiness.timeoutSeconds) }} timeoutSeconds: {{ .probes.readiness.timeoutSeconds }} {{- end }} {{- if (hasKey .probes.readiness.successThreshold) }} successThreshold: {{ .probes.readiness.successThreshold }} {{- end }} {{- if (hasKey .probes.readiness.failureThreshold) }} failureThreshold: {{ .probes.readiness.failureThreshold }} {{- end }} {{- end }} {{- else if $root.Values.global.probes.readiness.enabled }} readinessProbe: {{- if $root.Values.global.probes.readiness.custom }} {{- toYaml $root.Values.global.probes.readiness.custom | nindent 12 }} {{- else }} httpGet: {{- if (hasKey $root.Values.global.probes.readiness "port") }} port: {{ required "probes.readiness.port is required" $root.Values.global.probes.readiness.port }} {{- end }} {{- if (hasKey $root.Values.global.probes.readiness "endpoint") }} path: "{{ required "probes.readiness.endpoint is required" $root.Values.global.probes.readiness.endpoint }}" {{- end }} {{- if (hasKey $root.Values.global.probes.readiness "scheme") }} scheme: {{ $root.Values.global.probes.readiness.scheme }} {{- end }} {{- if (hasKey $root.Values.global.probes.readiness.initialDelaySeconds) }} initialDelaySeconds: {{ $root.Values.global.probes.readiness.initialDelaySeconds }} {{- end }} {{- if (hasKey $root.Values.global.probes.readiness.periodSeconds) }} periodSeconds: {{ $root.Values.global.probes.readiness.periodSeconds }} {{- end }} {{- if (hasKey $root.Values.global.probes.readiness.timeoutSeconds) }} timeoutSeconds: {{ $root.Values.global.probes.readiness.timeoutSeconds }} {{- end }} {{- if (hasKey $root.Values.global.probes.readiness.successThreshold) }} successThreshold: {{ $root.Values.global.probes.readiness.successThreshold }} {{- end }} {{- if (hasKey $root.Values.global.probes.readiness.failureThreshold) }} failureThreshold: {{ $root.Values.global.probes.readiness.failureThreshold }} {{- end }} {{- end }} {{- end }} {{- /* 原有liveness探针渲染逻辑保持不变 */}} {{- if and (hasKey (.probes) "liveness") (hasKey (.probes).liveness "enabled") ((.probes).liveness.enabled) }} livenessProbe: {{- if .probes.liveness.custom }} {{- toYaml .probes.liveness.custom | nindent 12 }} {{- else }} httpGet: {{- if (hasKey .probes.liveness "port") }} port: {{ required "probes.liveness.port is required" .probes.liveness.port }} {{- end }} {{- if (hasKey .probes.liveness "endpoint") }} path: "{{ required "probes.liveness.endpoint is required" .probes.liveness.endpoint }}" {{- end }} {{- if (hasKey .probes.liveness "scheme") }} scheme: {{ .probes.liveness.scheme }} {{- end }} {{- if (hasKey .probes.liveness.initialDelaySeconds) }} initialDelaySeconds: {{ .probes.liveness.initialDelaySeconds }} {{- end }} {{- if (hasKey .probes.liveness.periodSeconds) }} periodSeconds: {{ .probes.liveness.periodSeconds }} {{- end }} {{- if (hasKey .probes.liveness.timeoutSeconds) }} timeoutSeconds: {{ .probes.liveness.timeoutSeconds }} {{- end }} successThreshold: 1 {{- if (hasKey .probes.liveness.failureThreshold) }} failureThreshold: {{ .probes.liveness.failureThreshold }} {{- end }} {{- end }} {{- else if $root.Values.global.probes.liveness.enabled }} livenessProbe: {{- if $root.Values.global.probes.liveness.custom }} {{- toYaml $root.Values.global.probes.liveness.custom | nindent 12 }} {{- else }} httpGet: {{- if (hasKey $root.Values.global.probes.liveness "port") }} port: {{ required "probes.liveness.port is required" $root.Values.global.probes.liveness.port }} {{- end }} {{- if (hasKey $root.Values.global.probes.liveness "endpoint") }} path: "{{ required "probes.liveness.endpoint is required" $root.Values.global.probes.liveness.endpoint }}" {{- end }} {{- if (hasKey $root.Values.global.probes.liveness "scheme") }} scheme: {{ $root.Values.global.probes.liveness.scheme }} {{- end }} {{- if (hasKey $root.Values.global.probes.liveness.initialDelaySeconds) }} initialDelaySeconds: {{ $root.Values.global.probes.liveness.initialDelaySeconds }} {{- end }} {{- if (hasKey $root.Values.global.probes.liveness.periodSeconds) }} periodSeconds: {{ $root.Values.global.probes.liveness.periodSeconds }} {{- end }} {{- if (hasKey $root.Values.global.probes.liveness.timeoutSeconds) }} timeoutSeconds: {{ $root.Values.global.probes.liveness.timeoutSeconds }} {{- end }} successThreshold: 1 {{- if (hasKey $root.Values.global.probes.liveness.failureThreshold) }} failureThreshold: {{ $root.Values.global.probes.liveness.failureThreshold }} {{- end }} {{- end }} {{- end }} {{- end }}
方案b:自动补全缺失探针
通过Helm的merge函数,将服务自定义的探针配置与全局探针配置合并,自动补全缺失的探针类型,避免类型错误。
修改后的模板核心代码:
{{- $root := . }} {{- range $service, $val := .Values.services }} --- apiVersion: apps/v1 kind: Deployment {{- /* 合并全局探针与服务自定义探针,补全缺失的探针类型 */}} {{- $mergedProbes := merge $root.Values.global.probes (default dict $val.probes) }} {{- /* 将合并后的探针赋值给当前上下文,后续逻辑直接使用 */}} {{- $val = merge $val (dict "probes" $mergedProbes) }} {{- /* readiness探针渲染逻辑 */}} {{- if and (hasKey .probes.readiness "enabled") .probes.readiness.enabled }} readinessProbe: {{- if .probes.readiness.custom }} {{- toYaml .probes.readiness.custom | nindent 12 }} {{- else }} httpGet: {{- if (hasKey .probes.readiness "port") }} port: {{ required "probes.readiness.port is required" .probes.readiness.port }} {{- end }} {{- if (hasKey .probes.readiness "endpoint") }} path: "{{ required "probes.readiness.endpoint is required" .probes.readiness.endpoint }}" {{- end }} {{- if (hasKey .probes.readiness "scheme") }} scheme: {{ .probes.readiness.scheme }} {{- end }} {{- if (hasKey .probes.readiness.initialDelaySeconds) }} initialDelaySeconds: {{ .probes.readiness.initialDelaySeconds }} {{- end }} {{- if (hasKey .probes.readiness.periodSeconds) }} periodSeconds: {{ .probes.readiness.periodSeconds }} {{- end }} {{- if (hasKey .probes.readiness.timeoutSeconds) }} timeoutSeconds: {{ .probes.readiness.timeoutSeconds }} {{- end }} {{- if (hasKey .probes.readiness.successThreshold) }} successThreshold: {{ .probes.readiness.successThreshold }} {{- end }} {{- if (hasKey .probes.readiness.failureThreshold) }} failureThreshold: {{ .probes.readiness.failureThreshold }} {{- end }} {{- end }} {{- end }} {{- /* liveness探针渲染逻辑 */}} {{- if and (hasKey .probes.liveness "enabled") .probes.liveness.enabled }} livenessProbe: {{- if .probes.liveness.custom }} {{- toYaml .probes.liveness.custom | nindent 12 }} {{- else }} httpGet: {{- if (hasKey .probes.liveness "port") }} port: {{ required "probes.liveness.port is required" .probes.liveness.port }} {{- end }} {{- if (hasKey .probes.liveness "endpoint") }} path: "{{ required "probes.liveness.endpoint is required" .probes.liveness.endpoint }}" {{- end }} {{- if (hasKey .probes.liveness "scheme") }} scheme: {{ .probes.liveness.scheme }} {{- end }} {{- if (hasKey .probes.liveness.initialDelaySeconds) }} initialDelaySeconds: {{ .probes.liveness.initialDelaySeconds }} {{- end }} {{- if (hasKey .probes.liveness.periodSeconds) }} periodSeconds: {{ .probes.liveness.periodSeconds }} {{- end }} {{- if (hasKey .probes.liveness.timeoutSeconds) }} timeoutSeconds: {{ .probes.liveness.timeoutSeconds }} {{- end }} successThreshold: 1 {{- if (hasKey .probes.liveness.failureThreshold) }} failureThreshold: {{ .probes.liveness.failureThreshold }} {{- end }} {{- end }} {{- end }} {{- end }}
merge函数会优先保留服务自定义的探针配置,全局配置仅作为缺失项的补充。比如kupkake-inator2只配置了readiness,liveness会自动使用global中的配置,避免模板访问.probes.liveness时出现类型错误。
内容的提问来源于stack exchange,提问作者gurpsone
相关产品推荐
相关产品推荐

