Helm: Helm 3 - 升级 nginx - spec.clusterIP:无效值:“”:字段不可变

创建于 2019-09-06  ·  67评论  ·  资料来源: helm/helm

我使用以下内容来安装/升级图表:

./舵升级--安装
--set rbac.create=false
--set controller.replicaCount=2
--set controller.service.loadBalancerIP=$ip
--wait main-ingress stable/nginx-ingress

(其中 $ip 是一个 IP,例如 10.0.0.1)

这是在 CI/CD 管道中完成的,所以想法是第一次安装,下次升级。

它安装得很好。 在第二次运行时,它输出以下内容:

_client.go:339:无法修补服务:“main-ingress-nginx-ingress-controller”(服务“main-ingress-nginx-ingress-controller”无效:spec.clusterIP:无效值:“”:字段不可变)
client.go:358: 使用 --force 强制重新创建资源
client.go:339:无法修补服务:“main-ingress-nginx-ingress-default-backend”(服务“main-ingress-nginx-ingress-default-backend”无效:spec.clusterIP:无效值:“” : 字段是不可变的)
client.go:358: 使用 --force 强制重新创建资源
错误:升级失败:服务“main-ingress-nginx-ingress-controller”无效:spec.clusterIP:无效值:“”:字段不可变&服务“main-ingress-nginx-ingress-default-backend”无效:spec.clusterIP:无效值:“”:字段是不可变的_

我也在掌舵列表中得到了这个:

名称命名空间修订更新状态图表
main-ingress default 1 2019-09-06 13:17:33.8463781 -0400 EDT 部署 nginx-ingress-1.18.0
main-ingress default 2 2019-09-06 13:21:11.6428945 -0400 EDT 失败 nginx-ingress-1.18.0

所以,发布失败了。

我在 Helm 2 中没有这个问题。是由于 helm 3 中的行为改变还是错误? 如果是前者,我如何更改命令才不会出现该问题?

helm version :version.BuildInfo{Version:"v3.0.0-beta.2", GitCommit:"26c7338408f8db593f93cd7c963ad56f67f662d4", GitTreeState:"clean", GoVersion:"go1.12.9"}

kubectl version :客户端版本:version.Info{Major:"1", Minor:"12", GitVersion:"v1.12.0", GitCommit:"0ed33881dc4355495f623c6f22e7dd0b7632b7c0", GitTreeState:"clean “2018-09-27T17:05:32Z”,GoVersion:“go1.10.4”,编译器:“gc”,平台:“linux/amd64”}
服务器版本:version.Info{Major:"1", Minor:"13", GitVersion:"v1.13.10", GitCommit:"37d169313237cb4ceb2cc4bef300f2ae3053c1a2", GitTreeState:"clean", BuildDate:"20194T0- 49Z", GoVersion:"go1.11.13", 编译器:"gc", 平台:"linux/amd64"}

云提供商/平台(AKS、GKE、Minikube 等):AKS

最有用的评论

我有同样的问题,即使我没有使用 helm v3.0.0-rc.2 设置服务类型或 clusterIP,如果我在 helm update --install 命令中使用 --force 选项。 没有 --force 它工作正常

所有67条评论

这可能与 Helm 3 最近的变化有关,它现在使用类似于 kubectl 的三向合并补丁策略。 见#6124

如果您能提供有关如何重现此操作的步骤,那就太好了。 谢谢!

当然!

我创建了一个 AKS 集群。

我在 MC_* 资源组中创建了一个公共 IP。

我将该公共 IP 的 IP 地址存储在 $ip 中。

然后基本上运行该命令两次:

./舵升级--安装
--set rbac.create=false
--set controller.replicaCount=2
--set controller.service.loadBalancerIP=$ip
--wait main-ingress stable/nginx-ingress

这类似于https://docs.microsoft.com/en-us/azure/aks/ingress-static-ip 中所做的

不同之处在于我做了两次 helm upgrade --install 。 这样做的目的是在我的 CI/CD 中有一个命令行(无条件)。

如果您需要更多细节来重现,请告诉我。

这足以重现吗? 如果有帮助,我可以提供一个 bash 脚本。

抱歉,本周在欧盟 Helm 峰会上休假,所以我还没有时间做出回应。

啊……不用担心。 享受峰会!

我也遇到这个问题

$ helm version --short
v3.0.0-beta.3+g5cb923e

nginx-ingress 图表在第一次运行时安装良好,但是在升级时...

$ helm upgrade --install first-chart stable/nginx-ingress --namespace infra
client.go:357: Cannot patch Service: "first-chart-nginx-ingress-controller" (Service "first-chart-nginx-ingress-controller" is invalid: spec.clusterIP: Invalid value: "": field is immutable)
client.go:376: Use --force to force recreation of the resource
client.go:357: Cannot patch Service: "first-chart-nginx-ingress-default-backend" (Service "first-chart-nginx-ingress-default-backend" is invalid: spec.clusterIP: Invalid value: "": field is immutable)
client.go:376: Use --force to force recreation of the resource
Error: UPGRADE FAILED: Service "first-chart-nginx-ingress-controller" is invalid: spec.clusterIP: Invalid value: "": field is immutable && Service "first-chart-nginx-ingress-default-backend" is invalid: spec.clusterIP: Invalid value: "": field is immutable
$ helm ls -n infra
NAME            NAMESPACE       REVISION        UPDATED                                 STATUS          CHART               
first-chart     infra           1               2019-09-17 16:15:25.513997106 -0500 CDT deployed        nginx-ingress-1.20.0
first-chart     infra           2               2019-09-17 16:15:30.845249671 -0500 CDT failed          nginx-ingress-1.20.0

我相信这是 nginx-ingress 图表的问题,而不是 helm3。 默认情况下,图表将始终尝试传递controller.service.clusterIP = ""defaultBackend.service.clusterIP = ""除非您设置controller.service.omitClusterIP=truedefaultBackend.service.omitClusterIP=true

来源链接:
https://github.com/helm/charts/blob/master/stable/nginx-ingress/values.yaml#L321
https://github.com/helm/charts/blob/master/stable/nginx-ingress/templates/controller-service.yaml#L22

解决方法:

$ helm upgrade --install ingress-test stable/nginx-ingress --set controller.service.omitClusterIP=true --set defaultBackend.service.omitClusterIP=true

我试过这样做,但我仍然遇到同样的错误

helm upgrade --install ingx stable/nginx-ingress -f ingx-values.yaml                                             1 ↵
client.go:357: Cannot patch Service: "ingx-nginx-ingress-controller" (Service "ingx-nginx-ingress-controller" is invalid: spec.clusterIP: Invalid value: "": field is immutable)
client.go:376: Use --force to force recreation of the resource
client.go:357: Cannot patch Service: "ingx-nginx-ingress-default-backend" (Service "ingx-nginx-ingress-default-backend" is invalid: spec.clusterIP: Invalid value: "": field is immutable)
client.go:376: Use --force to force recreation of the resource
Error: UPGRADE FAILED: Service "ingx-nginx-ingress-controller" is invalid: spec.clusterIP: Invalid value: "": field is immutable && Service "ingx-nginx-ingress-default-backend" is invalid: spec.clusterIP: Invalid value: "": field is immutable

ingx-values.yaml

rbac:
  create: true
controller:
  service:
    externalTrafficPolicy: Local
    omitClusterIP: true
  autoscaling:
    enabled: true
    minReplicas: 2
    maxReplicas: 100
    targetCPUUtilizationPercentage: "70"
    targetMemoryUtilizationPercentage: "70"
defaultBackend:
  service:
    omitClusterIP: true

正如您在下面看到的,模板中没有 clusterIP

掌舵模板 ingx stable/nginx-ingress -f ingx-values.yaml

---
# Source: nginx-ingress/templates/controller-serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  labels:
    app: nginx-ingress
    chart: nginx-ingress-1.20.0
    heritage: Helm
    release: ingx
  name: ingx-nginx-ingress
---
# Source: nginx-ingress/templates/default-backend-serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  labels:
    app: nginx-ingress
    chart: nginx-ingress-1.20.0
    heritage: Helm
    release: ingx
  name: ingx-nginx-ingress-backend
---
# Source: nginx-ingress/templates/clusterrole.yaml
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  labels:
    app: nginx-ingress
    chart: nginx-ingress-1.20.0
    heritage: Helm
    release: ingx
  name: ingx-nginx-ingress
rules:
  - apiGroups:
      - ""
    resources:
      - configmaps
      - endpoints
      - nodes
      - pods
      - secrets
    verbs:
      - list
      - watch
  - apiGroups:
      - ""
    resources:
      - nodes
    verbs:
      - get
  - apiGroups:
      - ""
    resources:
      - services
    verbs:
      - get
      - list
      - update
      - watch
  - apiGroups:
      - extensions
      - "networking.k8s.io" # k8s 1.14+
    resources:
      - ingresses
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - ""
    resources:
      - events
    verbs:
      - create
      - patch
  - apiGroups:
      - extensions
      - "networking.k8s.io" # k8s 1.14+
    resources:
      - ingresses/status
    verbs:
      - update
---
# Source: nginx-ingress/templates/clusterrolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  labels:
    app: nginx-ingress
    chart: nginx-ingress-1.20.0
    heritage: Helm
    release: ingx
  name: ingx-nginx-ingress
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: ingx-nginx-ingress
subjects:
  - kind: ServiceAccount
    name: ingx-nginx-ingress
    namespace: default
---
# Source: nginx-ingress/templates/controller-role.yaml
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: Role
metadata:
  labels:
    app: nginx-ingress
    chart: nginx-ingress-1.20.0
    heritage: Helm
    release: ingx
  name: ingx-nginx-ingress
rules:
  - apiGroups:
      - ""
    resources:
      - namespaces
    verbs:
      - get
  - apiGroups:
      - ""
    resources:
      - configmaps
      - pods
      - secrets
      - endpoints
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - ""
    resources:
      - services
    verbs:
      - get
      - list
      - update
      - watch
  - apiGroups:
      - extensions
      - "networking.k8s.io" # k8s 1.14+
    resources:
      - ingresses
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - extensions
      - "networking.k8s.io" # k8s 1.14+
    resources:
      - ingresses/status
    verbs:
      - update
  - apiGroups:
      - ""
    resources:
      - configmaps
    resourceNames:
      - ingress-controller-leader-nginx
    verbs:
      - get
      - update
  - apiGroups:
      - ""
    resources:
      - configmaps
    verbs:
      - create
  - apiGroups:
      - ""
    resources:
      - endpoints
    verbs:
      - create
      - get
      - update
  - apiGroups:
      - ""
    resources:
      - events
    verbs:
      - create
      - patch
---
# Source: nginx-ingress/templates/controller-rolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
  labels:
    app: nginx-ingress
    chart: nginx-ingress-1.20.0
    heritage: Helm
    release: ingx
  name: ingx-nginx-ingress
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: ingx-nginx-ingress
subjects:
  - kind: ServiceAccount
    name: ingx-nginx-ingress
    namespace: default
---
# Source: nginx-ingress/templates/controller-service.yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    app: nginx-ingress
    chart: nginx-ingress-1.20.0
    component: "controller"
    heritage: Helm
    release: ingx
  name: ingx-nginx-ingress-controller
spec:
  externalTrafficPolicy: "Local"
  ports:
    - name: http
      port: 80
      protocol: TCP
      targetPort: http
    - name: https
      port: 443
      protocol: TCP
      targetPort: https
  selector:
    app: nginx-ingress
    component: "controller"
    release: ingx
  type: "LoadBalancer"
---
# Source: nginx-ingress/templates/default-backend-service.yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    app: nginx-ingress
    chart: nginx-ingress-1.20.0
    component: "default-backend"
    heritage: Helm
    release: ingx
  name: ingx-nginx-ingress-default-backend
spec:
  ports:
    - name: http
      port: 80
      protocol: TCP
      targetPort: http
  selector:
    app: nginx-ingress
    component: "default-backend"
    release: ingx
  type: "ClusterIP"
---
# Source: nginx-ingress/templates/controller-deployment.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  labels:
    app: nginx-ingress
    chart: nginx-ingress-1.20.0
    component: "controller"
    heritage: Helm
    release: ingx
  name: ingx-nginx-ingress-controller
spec:
  replicas: 1
  revisionHistoryLimit: 10
  strategy:
    {}
  minReadySeconds: 0
  template:
    metadata:
      labels:
        app: nginx-ingress
        component: "controller"
        release: ingx
    spec:
      dnsPolicy: ClusterFirst
      containers:
        - name: nginx-ingress-controller
          image: "quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.25.1"
          imagePullPolicy: "IfNotPresent"
          args:
            - /nginx-ingress-controller
            - --default-backend-service=default/ingx-nginx-ingress-default-backend
            - --election-id=ingress-controller-leader
            - --ingress-class=nginx
            - --configmap=default/ingx-nginx-ingress-controller
          securityContext:
            capabilities:
                drop:
                - ALL
                add:
                - NET_BIND_SERVICE
            runAsUser: 33
            allowPrivilegeEscalation: true
          env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
          livenessProbe:
            httpGet:
              path: /healthz
              port: 10254
              scheme: HTTP
            initialDelaySeconds: 10
            periodSeconds: 10
            timeoutSeconds: 1
            successThreshold: 1
            failureThreshold: 3
          ports:
            - name: http
              containerPort: 80
              protocol: TCP
            - name: https
              containerPort: 443
              protocol: TCP
          readinessProbe:
            httpGet:
              path: /healthz
              port: 10254
              scheme: HTTP
            initialDelaySeconds: 10
            periodSeconds: 10
            timeoutSeconds: 1
            successThreshold: 1
            failureThreshold: 3
          resources:
            {}
      hostNetwork: false
      serviceAccountName: ingx-nginx-ingress
      terminationGracePeriodSeconds: 60
---
# Source: nginx-ingress/templates/default-backend-deployment.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  labels:
    app: nginx-ingress
    chart: nginx-ingress-1.20.0
    component: "default-backend"
    heritage: Helm
    release: ingx
  name: ingx-nginx-ingress-default-backend
spec:
  replicas: 1
  revisionHistoryLimit: 10
  template:
    metadata:
      labels:
        app: nginx-ingress
        component: "default-backend"
        release: ingx
    spec:
      containers:
        - name: nginx-ingress-default-backend
          image: "k8s.gcr.io/defaultbackend-amd64:1.5"
          imagePullPolicy: "IfNotPresent"
          args:
          securityContext:
            runAsUser: 65534
          livenessProbe:
            httpGet:
              path: /healthz
              port: 8080
              scheme: HTTP
            initialDelaySeconds: 30
            periodSeconds: 10
            timeoutSeconds: 5
            successThreshold: 1
            failureThreshold: 3
          readinessProbe:
            httpGet:
              path: /healthz
              port: 8080
              scheme: HTTP
            initialDelaySeconds: 0
            periodSeconds: 5
            timeoutSeconds: 5
            successThreshold: 1
            failureThreshold: 6
          ports:
            - name: http
              containerPort: 8080
              protocol: TCP
          resources:
            {}
      serviceAccountName: ingx-nginx-ingress-backend
      terminationGracePeriodSeconds: 60
---
# Source: nginx-ingress/templates/controller-hpa.yaml
apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
  labels:
    app: nginx-ingress
    chart: nginx-ingress-1.20.0
    component: "controller"
    heritage: Helm
    release: ingx
  name: ingx-nginx-ingress-controller
spec:
  scaleTargetRef:
    apiVersion: apps/v1beta1
    kind: Deployment
    name: ingx-nginx-ingress-controller
  minReplicas: 2
  maxReplicas: 100
  metrics:
    - type: Resource
      resource:
        name: cpu
        targetAverageUtilization: 70
    - type: Resource
      resource:
        name: memory
        targetAverageUtilization: 70

我怀疑这是因为我最初在没有 omitClusterIP 参数的情况下部署了它,并且 helm v3 正在尝试与原始清单进行 3 向合并,其中确实有clusterIP: ""

helm get manifest ingx --revision 1 | grep "clusterIP"
  clusterIP: ""
  clusterIP: ""

我能够通过首先删除现有图表来修复它,然后使用omitClusterIP选项重新创建它。 最重要的是, @bambash建议的解决方法只有在您安装图表时,这些选项从一开始就设置为 true

$ helm upgrade --install ingress-test stable/nginx-ingress --set controller.service.omitClusterIP=true --set defaultBackend.service.omitClusterIP=true

如果 helm v3 中有一种方法可以跳过与现有清单的合并,那就太好了

抱歉,我应该指定在最初安装发行版时需要设置这些值。 更新现有版本可能会更棘手...

我在使用 metric-server-2.8.8 时遇到了这个问题,它的值中没有任何 clusterIP,还有一些其他图表,使用 helm v3.0.0-rc.2。 有什么建议吗? 我不知道如何继续。

我的问题似乎与 helmfile v0.95.0 有关。 我会在那里追求它:)

我有同样的问题,即使我没有使用 helm v3.0.0-rc.2 设置服务类型或 clusterIP,如果我在 helm update --install 命令中使用 --force 选项。 没有 --force 它工作正常

@johannges ,我正要发布相同的内容。 :+1:

设置omitClusterIP: true似乎适用于defaultBackend控制器服务,但不适用于指标服务。

我认为这是升级过程中带有--force选项的 helm 的问题。
Helm 正在尝试重新创建服务,但它也替换了 spec.clusterIP,因此会引发错误。
我可以使用我自己的自定义图表来确认这一点。
Error: UPGRADE FAILED: failed to replace object: Service "litespeed" is invalid: spec.clusterIP: Invalid value: "": field is immutable

实际上这是我的错误,在服务(或图表)的初始化中省略了 clusterIP 定义工作正常👍

对于现有部署的 _kafka_ 和 _redis_ 图表版本,我也遇到了这个错误。 删除--force确实解决了这个问题。

现在我从 _redis_ 版本中收到一个新错误:
Error: UPGRADE FAILED: release redis failed, and has been rolled back due to atomic being set: cannot patch "redis-master" with kind StatefulSet: StatefulSet.apps "redis-master" is invalid: spec: Forbidden: updates to statefulset spec for fields other than 'replicas', 'template', and 'updateStrategy' are forbidden

同意@bacongobbler ,这看起来与 Helm v3 三路合并补丁策略有关,这可能导致将字段(即使具有与以前相同的值)传递给 Kubernetes 在首次创建后认为不可变/不可更改的更新/补丁。

如果有人最终通过 terraform 使用 helm v3,因为您不能直接告诉它不要使用--force我成功地使用helm delete手动删除图表,然后重新运行 terraform。 这很糟糕,但确实有效。

编辑:整个错误:(“nginx-ingress-singleton-controller”是我设置的版本名称。它没有具体含义)

Error: cannot patch "nginx-ingress-singleton-controller" with kind Service: Service "nginx-ingress-singleton-controller" is invalid: spec.clusterIP: Invalid value:
"": field is immutable && cannot patch "nginx-ingress-singleton-default-backend" with kind Service: Service "nginx-ingress-singleton-default-backend" is invalid: sp
ec.clusterIP: Invalid value: "": field is immutable

  on .terraform/modules/app_dev/nginx-ingress.tf line 1, in resource "helm_release" "nginx_ingress":
   1: resource "helm_release" "nginx_ingress" {

@zen4everhttps://github.com/helm/helm/issues/6378#issuecomment -532766512 中解决了这个问题。 我将尝试更详细地解释它......

正如其他人指出的那样,当图表使用空字符串定义 clusterIP 时,就会出现问题。 安装 Service 后,Kubernetes 会使用分配给 Service 的 clusterIP 填充此字段。

当调用helm upgrade ,图表要求删除clusterIP ,因此错误消息为spec.clusterIP: Invalid value: "": field is immutable

发生这种情况是由于以下行为:

  1. 在安装时,图表指定它希望clusterIP为空字符串
  2. Kubernetes 自动为 Service 分配了clusterIP 。 我们将在这个例子中使用172.17.0.1
  3. helm upgrade ,图表希望clusterIP为空字符串(或者在上述@zen4ever情况下,它被省略)

在生成三路补丁时,它看到旧状态为"" ,当前状态为"172.17.0.1" ,建议状态为"" 。 Helm 检测到用户请求将clusterIP从“172.17.0.1”更改为“”,因此提供了补丁。

在 Helm 2 中,它忽略了实时状态,因此它没有看到任何变化(旧状态: clusterIP: ""到新状态: clusterIP: "" ),并且没有生成补丁,绕过了这种行为。

我的建议是更改模板输出。 如果没有提供clusterIP作为值,则不要将该值设置为空字符串...完全省略该字段。

例如在stable/nginx-ingress的情况下:

spec:
{{- if not .Values.controller.service.omitClusterIP }}
  clusterIP: "{{ .Values.controller.service.clusterIP }}"
{{- end }}

应改为:

spec:
{{- if not .Values.controller.service.omitClusterIP }}
  {{ with .Values.controller.service.clusterIP }}clusterIP: {{ quote . }}{{ end }}
{{- end }}

这也是--set controller.service.omitClusterIP=true在这种情况下起作用的原因。

TL;DR 不要在您的服务模板中执行此操作:

clusterIP: ""

否则,Helm 将尝试将服务的 clusterIP 从自动生成的 IP 地址更改为空字符串,因此会出现错误消息。

希望这可以帮助!

作为临时解决方案,如果您在解决此问题的同时尝试让它立即工作,我发现如果我执行以下操作,我可以执行更新:

  1. 通过以下方式获取控制器和默认后端的 clusterIP 值:
    kubectl get svc | grep ingress
  2. 将以下覆盖添加到现有的 helm 值文件中:
    controller: service: clusterIP: <cluster-ip-address-for-controller> defaultBackend: service: clusterIP: <cluster-ip-address-for-default-backend>
  3. 执行更新。

我已经为我正在运行的集群测试了这个,它不需要任何娱乐。

这也有效。 好电话@treacher。 通过--set或在您的值文件中设置相同的值不会生成补丁,因为升级不想更改clusterIP

按照上述三路合并补丁行为有意关闭。 操作项是让这些图表遵循 https://github.com/helm/helm/issues/6378#issuecomment-557746499 中提供的建议。 在 Helm 结束时,这里无事可做。 :)

@zen4ever#6378(评论)中解决了这个问题。 我将尝试更详细地解释它......

正如其他人指出的那样,当图表使用空字符串定义 clusterIP 时,就会出现问题。 安装 Service 后,Kubernetes 会使用分配给 Service 的 clusterIP 填充此字段。

当调用helm upgrade ,图表要求删除clusterIP ,因此错误消息为spec.clusterIP: Invalid value: "": field is immutable

发生这种情况是由于以下行为:

  1. 在安装时,图表指定它希望clusterIP为空字符串
  2. Kubernetes 自动为 Service 分配了clusterIP 。 我们将在这个例子中使用172.17.0.1
  3. helm upgrade ,图表希望clusterIP为空字符串(或者在上述@zen4ever情况下,它被省略)

在生成三路补丁时,它看到旧状态为"" ,当前状态为"172.17.0.1" ,建议状态为"" 。 Helm 检测到用户请求将clusterIP从“172.17.0.1”更改为“”,因此提供了补丁。

在 Helm 2 中,它忽略了实时状态,因此它没有看到任何变化(旧状态: clusterIP: ""到新状态: clusterIP: "" ),并且没有生成补丁,绕过了这种行为。

我的建议是更改模板输出。 如果没有提供clusterIP作为值,则不要将该值设置为空字符串...完全省略该字段。

例如在stable/nginx-ingress的情况下:

spec:
{{- if not .Values.controller.service.omitClusterIP }}
  clusterIP: "{{ .Values.controller.service.clusterIP }}"
{{- end }}

应改为:

spec:
{{- if not .Values.controller.service.omitClusterIP }}
  {{ with .Values.controller.service.clusterIP }}clusterIP: {{ quote . }}{{ end }}
{{- end }}

@bacongobbler ,我认为因为如果没有提供价值,我们仍然会以clusterIP: "" ......更好的是价值clusterIP: ""在值文件中完全注释掉。 这会在设置时从呈现的清单中省略它,并且应该可以避免将来的麻烦。 但是,如果使用 helm3 并且当前的 helm 状态设置了clusterIP: "" ,则需要在值文件中对 clusterIP 地址进行硬编码。

这也是--set controller.service.omitClusterIP=true在这种情况下起作用的原因。

TL;DR 不要在您的服务模板中执行此操作:

clusterIP: ""

否则,Helm 将尝试将服务的 clusterIP 从自动生成的 IP 地址更改为空字符串,因此会出现错误消息。

希望这可以帮助!

@bacongobbler ,我们在将 helm v2 版本迁移到 helm v3 期间遇到了同样的问题。 我们在 Service 中使用type: ClusterIP但完全省略ClusterIP ,我们得到:

Error: UPGRADE FAILED: failed to replace object: Service "dummy" is invalid: spec.clusterIP: Invalid value: "": field is immutable

我们的 helm 模板中没有spec.clusterIP:但是在通过 helm 2to3 迁移发布后我们得到了这个错误

服务模板:

apiVersion: v1
kind: Service
metadata:
  name: {{ .Release.Name }}
  labels:
    app: {{ .Values.image.name }}
    chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "-" }}
    cluster: {{ default "unknown" .Values.cluster }}
    region: {{ default "unknown" .Values.region }}
    datacenter: {{ default "unknown" .Values.datacenter }}
    release: {{ .Release.Name }}
    heritage: {{ .Release.Service }}
spec:
  type: ClusterIP
  ports:
    - port: {{ .Values.service.port }}
      targetPort: {{ .Values.service.port }}
      protocol: TCP
      name: http
  selector:
    app: {{ .Values.image.name }}
    release: {{ .Release.Name }}

同样的问题在这里。 问题是我们还没有触及服务。 升级前更改的是 Ingress。

如果从helm upgrade --install删除--force标志并且不接触不可变字段,则它会影响具有不可变字段的资源,一切正常。 但是,如果你想增加资源的 apiversion ??? 您需要重新创建资源,但 helm 3 不会升级资源....
@bacongobbler ^^^

尝试通过 helm 3 使用新的 apiVersion 更新 hpa:
Error: UPGRADE FAILED: rendered manifests contain a new resource that already exists. Unable to continue with update: existing resource conflict: kind: HorizontalPodAutoscaler, namespace: stage, name: dummy-stage

@bacongobbler@kritcher722拇指clusterIP: ""是个好主意。

看起来微软是项目的导师。 我看到了风格。 :)

请重新打开。 问题未解决。 nasseemkullah 建议的这种“hack”是不合适的。 不要让人们跳到头上。 修好就行了非常糟糕的迁移路径。 头盔很烂。

@antonakv开始这一年的方式是什么:)
我认为一般来说,我们在提供 clusterIP 作为图表中的可配置值时是在玩火,不能完全责怪一个工具/人/PR。
如果 clusterIP 需要是一个可配置的值,默认情况下它不应该在呈现的模板中,这就是我按照https://github.com/helm/charts/blob/270172836fd8cf56d787cf7d04d938856de0c794/stable在值文件中注释掉的想法

如果我没记错的话,这应该可以防止那些在更改后安装图表的人在未来遇到麻烦。 但是对于我们这些之前安装了它然后迁移到 helm3 的人(包括我自己),恐怕在我们的值文件中硬记录当前 clusterIP 值或卸载并重新安装图表(导致停机!)是唯一的选择看。

意见是我自己的,我没有报酬来掌舵,只是像你这样的最终用户。 那些全职工作的人可能能够提供更多的见解。

新年快乐,祝你好运! 不要放弃掌舵,我们可以一起让它变得更好。

@bacongobbler ,我们在将 helm v2 版本迁移到 helm v3 期间遇到了同样的问题。 我们在 Service 中使用type: ClusterIP但完全省略ClusterIP ,我们得到:

Error: UPGRADE FAILED: failed to replace object: Service "dummy" is invalid: spec.clusterIP: Invalid value: "": field is immutable

我们的 helm 模板中没有spec.clusterIP:但是在通过 helm 2to3 迁移发布后我们得到了这个错误

服务模板:

apiVersion: v1
kind: Service
metadata:
  name: {{ .Release.Name }}
  labels:
    app: {{ .Values.image.name }}
    chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "-" }}
    cluster: {{ default "unknown" .Values.cluster }}
    region: {{ default "unknown" .Values.region }}
    datacenter: {{ default "unknown" .Values.datacenter }}
    release: {{ .Release.Name }}
    heritage: {{ .Release.Service }}
spec:
  type: ClusterIP
  ports:
    - port: {{ .Values.service.port }}
      targetPort: {{ .Values.service.port }}
      protocol: TCP
      name: http
  selector:
    app: {{ .Values.image.name }}
    release: {{ .Release.Name }}

我们有同样的问题。 我们根本没有在图表中定义clusterIP ,它也没有出现在最终模板中。 但是,我们仍然遇到相同的错误,并且只有--force标志。

我们遇到了同样的问题:

apiVersion: v1
kind: Service
{{ include "mde.metadata" $ }}
spec:
  ports:
  - name: {{ include "mde.portName" $ | quote }}
    port: {{ include "mde.port" $ }}
    protocol: TCP
    targetPort: {{ include "mde.port" $ }}
  selector:
    app: {{ include "mde.name" $ }}
  sessionAffinity: None
  type: ClusterIP

spec.clusterIP不是服务模板的一部分,但在 Helm 3.0.2 和helm upgrade ... --force --install调用中,我们还看到:

错误:升级失败:无法替换对象:服务“虚拟”无效:spec.clusterIP:无效值:“”:字段不可变

请重新打开。

@tomcruise81请参阅https://github.com/helm/helm/issues/7350以了解--force上的线程。 这会导致相同的错误,但这是由于kubectl replace工作方式造成的。 这是一个与此处描述的不同的问题,它与 Service clusterIPs 和三路合并补丁策略( helm upgrade没有--force标志)有关。

@bacongobbler - 感谢您的快速回复和澄清。 看着:

https://github.com/helm/helm/blob/a963736f6675e972448bf7a5fd141628fd0ae4df/pkg/kube/client.go#L405 -L411

使用https://github.com/kubernetes/cli-runtime/blob/master/pkg/resource/helper.go#L155 -L181,对helper.Replace的调用似乎没有与kubectl replace -f ... --force (注意末尾的--force )。

我猜这是很多混乱的地方。

我知道我对helm upgrade ... --force期望,它使用的替代策略是它会做与kubectl replace -f ... --force相同的事情。

@bacongobbler ,我们在将 helm v2 版本迁移到 helm v3 期间遇到了同样的问题。 我们在 Service 中使用type: ClusterIP但完全省略ClusterIP ,我们得到:
Error: UPGRADE FAILED: failed to replace object: Service "dummy" is invalid: spec.clusterIP: Invalid value: "": field is immutable
我们的 helm 模板中没有spec.clusterIP:但是在通过 helm 2to3 迁移发布后我们得到了这个错误
服务模板:

apiVersion: v1
kind: Service
metadata:
  name: {{ .Release.Name }}
  labels:
    app: {{ .Values.image.name }}
    chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "-" }}
    cluster: {{ default "unknown" .Values.cluster }}
    region: {{ default "unknown" .Values.region }}
    datacenter: {{ default "unknown" .Values.datacenter }}
    release: {{ .Release.Name }}
    heritage: {{ .Release.Service }}
spec:
  type: ClusterIP
  ports:
    - port: {{ .Values.service.port }}
      targetPort: {{ .Values.service.port }}
      protocol: TCP
      name: http
  selector:
    app: {{ .Values.image.name }}
    release: {{ .Release.Name }}

我们有同样的问题。 我们根本没有在图表中定义clusterIP ,它也没有出现在最终模板中。 但是,我们仍然遇到相同的错误,并且只有--force标志。

我还检查了发布清单中没有clusterIP

$ helm get manifest paywall-api-ee | grep clusterIP
$

同样在这里 - 我们没有在任何地方定义ClusterIP但仍然看到错误

再纠结这个,我观察到:

  • helm upgrade ... --force --install - 导致_The Service "dummy" is
  • helm template ... | kubectl apply -f - - 有效
  • helm template ... | kubectl replace -f - - 导致_The Service "dummy" is
  • helm template ... | kubectl replace --force -f - - 有效

kubectl 版本 - 1.14.6
掌舵版本 - 3.0.2

@tomcruise81您可以尝试使用 helm 插件 2to3 并从 helm2 迁移到 helm3 版本并删除--force如果您以前使用过它)。
这是我们的工作。
至于我,看起来像另一个人--force有错误的行为,应该像我一样用不可变的字段处理这种情况

@alexandrsemak - 感谢您的推荐。 在我的例子中,我在仅使用 helm 3 安装或升级的图表上看到了这一点。

对我来说同样的问题! 使用

$ helm install my-release xxxxx
$ helm upgrade --install --force my-release xxxxx

就我而言,我没有在图表上使用的任何服务上定义ClusterIP ,但我面临同样的问题(请参阅下面的规范):

spec:
  type: {{ .Values.service.type }}
  {{- if and (eq .Values.service.type "LoadBalancer") (not (empty .Values.service.loadBalancerIP)) }}
  loadBalancerIP: {{ .Values.service.loadBalancerIP }}
  {{- end }}
  ports:
    - name: htttp-XXX
      port: {{ .Values.service.port }}
      targetPort: XXX
      {{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePort)) }}
      nodePort: {{ .Values.service.nodePort }}
      {{- else if eq .Values.service.type "ClusterIP" }}
      nodePort: null
      {{- end }}
  selector:
    app.kubernetes.io/name: XXX
    app.kubernetes.io/instance: {{ .Release.Name }}

正如其他用户之前所说,原因是 Kubernetes 第一次自动为 Service 分配集群IP(例如。 clusterIP: 10.96.26.65 )并且在您尝试升级时与clusterIP: ""冲突。 请注意,我没有在我的模板上生成这个: clusterIP: ""

请重新打开这个@bacongobbler

我有同样的问题。

@juan131 @Ronsevet : remove --force意思改变了。

在自定义图表上面临同样的问题。
我们没有在任何地方定义 clusterip。
掌舵 v3.0.2
kubectl 1.14.8

问题是,即使 pod 已创建并正在运行,有时图表仍处于失败状态。 如果我们尝试升级同一个版本,它不会在没有力量的情况下工作。
由于 Pod 正在运行,因此无法删除和重新创建发布。
必须有某种方式来使用“强制”

对我来说也是一样 - 我刚刚为服务添加了额外的标签并遇到了这个错误。 我也没有在任何地方定义 ClusterIP - 请重新打开问题

@bacongobbler我们正在部署StorageClasses作为我们图表的一部分,StorageClass 的参数是不可变的。 所以在下一个版本中,当我们更新某些 StorageClass 参数的值时, helm upgrade --force也会失败。
不确定如何处理 StorageClasses 更新的这种情况。 有什么建议?

Error: UPGRADE FAILED: failed to replace object: StorageClass.storage.k8s.io "ibmc-s3fs-standard-cross-region" is invalid: parameters: Forbidden: updates to parameters are forbidden.

它在 helm v2 中运行良好,因为helm upgrade --force用于强制删除和重新创建 StorageClass

如果有人遇到不是由https://github.com/helm/helm/issues/6378#issuecomment -557746499 中提供的解释导致的症状,请您打开一个新问题,说明您的发现以及我们如何重现它对我们结束了吗?

OP 提出的问题是由于上面提供的场景,其中图表在安装时将 ClusterIP 设置为空字符串。 完全有可能在其他情况下会出现这种特殊情况,例如其他人在使用--force标志时提到的情况。 这些病例应单独讨论,因为诊断和解决方案可能与之前提供的建议不同。

谢谢!

@mssachan请参阅 #7082 和 #7431 中的草案提案以了解您的用例。 该提案旨在实现kubectl replace —force ,这类似于 Helm 2 的helm install —force行为。

@mssachan请参阅 #7082 和 #7431 中的草案提案以了解您的用例。 该提案旨在实现kubectl replace —force ,这类似于 Helm 2 的helm install —force行为。

发生这种情况很好。 即使省略了--force标志,我在升级图表时仍然会收到错误消息。 例如,使用cert-manager

2020-03-05 12:15:19 CRITICAL: Command returned [ 1 ] exit code and error message [ Error: UPGRADE FAILED: cannot patch "cert-manager-cainjector" with kind Deployment: Deployment.apps "cert-manager-cainjector" is invalid: spec.selector: Invalid value: v1.LabelSelector{MatchLabels:map[string]string{"app":"cainjector", "app.kubernetes.io/instance":"cert-manager", "app.kubernetes.io/managed-by":"Helm", "app.kubernetes.io/name":"cainjector"}, MatchExpressions:[]v1.LabelSelectorRequirement(nil)}: field is immutable && cannot patch "cert-manager" with kind Deployment: Deployment.apps "cert-manager" is invalid: spec.selector: Invalid value: v1.LabelSelector{MatchLabels:map[string]string{"app":"cert-manager", "app.kubernetes.io/instance":"cert-manager", "app.kubernetes.io/managed-by":"Helm", "app.kubernetes.io/name":"cert-manager"}, MatchExpressions:[]v1.LabelSelectorRequirement(nil)}: field is immutable && cannot patch "cert-manager-webhook" with kind Deployment: Deployment.apps "cert-manager-webhook" is invalid: spec.selector: Invalid value: v1.LabelSelector{MatchLabels:map[string]string{"app":"webhook", "app.kubernetes.io/instance":"cert-manager", "app.kubernetes.io/managed-by":"Helm", "app.kubernetes.io/name":"webhook"}, MatchExpressions:[]v1.LabelSelectorRequirement(nil)}: field is immutable

@mssachan请参阅 #7082 和 #7431 中的草案提案以了解您的用例。 该提案旨在实现kubectl replace —force ,这类似于 Helm 2 的helm install —force行为。

发生这种情况很好。 即使省略了--force标志,我在升级图表时仍然会收到错误消息。 例如,使用cert-manager

2020-03-05 12:15:19 CRITICAL: Command returned [ 1 ] exit code and error message [ Error: UPGRADE FAILED: cannot patch "cert-manager-cainjector" with kind Deployment: Deployment.apps "cert-manager-cainjector" is invalid: spec.selector: Invalid value: v1.LabelSelector{MatchLabels:map[string]string{"app":"cainjector", "app.kubernetes.io/instance":"cert-manager", "app.kubernetes.io/managed-by":"Helm", "app.kubernetes.io/name":"cainjector"}, MatchExpressions:[]v1.LabelSelectorRequirement(nil)}: field is immutable && cannot patch "cert-manager" with kind Deployment: Deployment.apps "cert-manager" is invalid: spec.selector: Invalid value: v1.LabelSelector{MatchLabels:map[string]string{"app":"cert-manager", "app.kubernetes.io/instance":"cert-manager", "app.kubernetes.io/managed-by":"Helm", "app.kubernetes.io/name":"cert-manager"}, MatchExpressions:[]v1.LabelSelectorRequirement(nil)}: field is immutable && cannot patch "cert-manager-webhook" with kind Deployment: Deployment.apps "cert-manager-webhook" is invalid: spec.selector: Invalid value: v1.LabelSelector{MatchLabels:map[string]string{"app":"webhook", "app.kubernetes.io/instance":"cert-manager", "app.kubernetes.io/managed-by":"Helm", "app.kubernetes.io/name":"webhook"}, MatchExpressions:[]v1.LabelSelectorRequirement(nil)}: field is immutable

@sc250024将 helm v2 升级到 v3 后,我遇到了完全相同的问题。 升级过程顺利,没有错误,然后我尝试从 helm 升级证书管理器,失败并输出相同。

# helm upgrade cert-manager jetstack/cert-manager --namespace cert-manager --atomic --cleanup-on-fail

# helm version
version.BuildInfo{Version:"v3.1.1", GitCommit:"afe70585407b420d0097d07b21c47dc511525ac8", GitTreeState:"clean", GoVersion:"go1.13.8"}

不使用 --force 时的任何变通方法,或者没有设置clusterIP周围的任何选项。 这是我的服务清单:

apiVersion: v1
kind: Service
metadata:
  name: "{{ .Values.deploymentBaseName }}-{{ .Values.skaffoldUser }}"
  labels:
    name: "{{ .Values.deploymentBaseName }}-{{ .Values.skaffoldUser }}"
spec:
  ports:
    - port: {{ .Values.servicePort }}
      targetPort: {{ .Values.containerPort }}
      protocol: TCP
      name: http
    - name: debugger-http
      port: {{ .Values.debuggerPort }}
      targetPort: {{ .Values.debuggerPort }}
      protocol: TCP
  selector:
    app: "{{ .Values.deploymentBaseName }}-{{ .Values.skaffoldUser }}"
  type: ClusterIP

@davidfernandezm你有没有找到解决方案? 我看到了同样的情况,我的服务定义与您的完全相同。 没有设置clusterIP选项,但 Helm 仍然无法升级。

同样在这里

也得到这个,re:以上两条评论。

请提供更多信息。 如果不了解原因或此问题如何在您的案例中出现,我们将无法为您提供帮助。 谢谢。

@antonakv此问题与7956重复
@bacongobbler更多信息

我有同样的问题,即使我没有使用 helm v3.0.0-rc.2 设置服务类型或 clusterIP,如果我在 helm update --install 命令中使用 --force 选项。 没有 --force 它工作正常

凉爽的! 我从你的回答中得到启发,我必须在 helmfile yaml 中评论force: ..行:

helmDefaults:
  tillerless: true
  verify: false
  wait: true
  timeout: 600
  # force: true <---- THI ONE IS COMMENTED

它有效🎉

我尝试了上述所有方法,没有一个对我有用。 我不得不从我的图表中禁用nginx-ingress ,进行升级,再次启用,然后再次升级。 这导致云提供商分配的 IP 地址发生变化,但没有造成任何伤害。

我有同样的问题,即使我没有使用 helm v3.0.0-rc.2 设置服务类型或 clusterIP,如果我在 helm update --install 命令中使用 --force 选项。 没有 --force 它工作正常

最佳解决方案,它对我有用,谢谢!

我们遇到了同样的问题,找不到任何解决方法。
重现非常简单

helm install in stable/inbucket 
helm upgrade in stable/inbucket 
Error: UPGRADE FAILED: cannot patch "in-inbucket" with kind Service: Service "in-inbucket" is invalid: spec.clusterIP: Invalid value: "": field is immutable

我想知道为什么--force在这里不起作用,如果这是替换策略,它不应该force resource updates through a replacement strategy那么应该删除然后替换该服务吗?

@bacongobbler在检查了https://github.com/helm/helm/issues/7956后,我得到了这个线程

与之前的所有评论者一样:我们在模板中根本没有“clusterIP”,但如果使用 --force 标志,最新的 Helm 仍然存在错误。

头盔版本:3.4.1

“helm -n kube-system get manifest CHART_NAME | grep clusterIP”没有显示结果。

错误:
field is immutable && failed to replace object: Service "SERVICE_NAME" is invalid: spec.clusterIP: Invalid value: "": field is immutable

https://github.com/helm/helm/issues/6378#issuecomment -557746499 中提供的相同解释也适用于您的案例@nick4fake。 不同之处在于,使用--force ,您要求 Kubernetes 获取完全渲染的清单并强制覆盖当前活动对象。 由于您的清单不包含clusterIP字段,Kubernetes 会接受并假设您正在尝试从活动对象中删除clusterIP字段,因此会出现错误Invalid value: "": field is immutable

@bacongobbler如果我在这里遗漏了一些东西,我真的很抱歉,也许我只是对 Helm 内部了解不够。

“我的建议是更改模板输出。如果没有提供 clusterIP 作为值,则不要将该值设置为空字符串......完全省略该字段。”

那么解决方案是什么? 这是否意味着如果 clusterIP 字段未设置为某个静态值,则根本无法使用“--force”标志?

就 Kubernetes 而言:是的。

根据我的理解,这是 Kubernetes 的一个问题,因为“强行覆盖”与“删除并重新创建”的行为方式不同。 是否有任何上游错误?

另一方面,Helm 也具有误导性,因为--force被描述为“通过替换策略强制资源更新”。 虽然实际上它不做任何替换,它只是试图强行覆盖资源(最好将标志命名为--force-overwrite )。 强制替换看起来像是再次删除和重新创建(可能有一个标志--force-recreate )。 当然,将--force-recreate用于某些资源可能有点危险,但它总会成功。

无论如何,Helm 可以为此类问题实施后备解决方法。 如果当前行为(描述为--force-overwrite )失败并检测到不可变字段错误,它应该删除并重新创建资源(如--force-recreate )。

此页面是否有帮助?
0 / 5 - 0 等级