During applications deploy from a Helm chart described in the Istio: shared Ingress/AWS ALB, Helm chart with conditions, Istio, and ExternalDNS we are getting the “spec.ports[0].nodePort: Forbidden: may not be used when `type` is ‘ClusterIP’” error.
Let’s reproduce it and find solutions with kubectl
and Helm to solve it.
The “spec.ports[0].nodePort: Forbidden: may not be used when `type` is ‘ClusterIP'” error – reproduce
Create a Service with the type: NodePort
:
--- apiVersion: v1 kind: Service metadata: name: test-svc spec: type: NodePort ports: - port: 80 targetPort: 80 protocol: TCP selector: app: test
Deploy it and check:
[simterm]
$ kubectl get svc test-svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE test-svc NodePort 172.20.129.242 <none> 80:31853/TCP 22s
[/simterm]
Ports of this Service:
[simterm]
$ kubectl get svc test-svc -o json | jq '.spec.ports[]' { "nodePort": 31231, "port": 80, "protocol": "TCP", "targetPort": 80 }
[/simterm]
Now, update the manifest and change its type
to the ClusterIP
:
--- apiVersion: v1 kind: Service metadata: name: test-svc spec: type: ClusterIP ports: - port: 80 targetPort: 80 protocol: TCP selector: app: test
Deploy again, and:
[simterm]
$ kubectl apply -f test-svc.yaml The Service "test-svc" is invalid: spec.ports[0].nodePort: Forbidden: may not be used when `type` is 'ClusterIP'
[/simterm]
Actually, the issue here happens because if the nodePort
as it’s not present in the ClusterIP
specification.
Solutions
kubectl apply --force
The first way is to use kubectl
with the apply --force
option. In this way, a Service will be deleted first and then a new one will be created:
[simterm]
$ kubectl apply -f test-svc.yaml --force service/test-svc configured
[/simterm]
Check the ports:
[simterm]
$ kubectl get svc test-svc -o json | jq '.spec.ports[]' { "port": 80, "protocol": "TCP", "targetPort": 80 }
[/simterm]
kubectl edit service
Another way could be to run kubectl edit service
, update a Service manually and then apply an updated manifest with a new Service type
:
[simterm]
$ kubectl edit svc test-svc
Remove the nodePort: 32729
and type: NodePort
:
Deploy with apply
but at this time without --force
:
[simterm]
$ kubectl apply -f test-svc.yaml service/test-svc configured
[/simterm]
Check ports again:
[simterm]
$ kubectl get svc test-svc -o json | jq '.spec.ports[]' { "port": 80, "protocol": "TCP", "targetPort": 80 }
[/simterm]
A solution for Helm
But our main goal is to find a solution for a Helm chart that can do everything in an automated way during deployments.
Create a test chart:
[simterm]
$ helm create test-svc Creating test-svc
[/simterm]
Delete generated manifests:
[simterm]
$ cd test-svc $ rm -rf templates/* values.yaml
[/simterm]
Crate own values.yaml
and a manifest for a Service:
[simterm]
$ vim -p values.yaml templates/svc.yaml
[/simterm]
In the values set a Service’s type to the NodePort
:
serviceType: NodePort
And describe this Service using the serviceType
parameter:
--- apiVersion: v1 kind: Service metadata: name: test-svc spec: type: {{ .Values.serviceType }} ports: - port: 80 targetPort: 80 protocol: TCP selector: app: test
Deploy:
[simterm]
$ helm upgrade --install test-svc .
[/simterm]
Check the type:
[simterm]
$ kk get svc test-svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE test-svc NodePort 172.20.107.220 <none> 80:30506/TCP 3m7s
[/simterm]
Reproduce the error again – update values and set the ClusterIP
:
#serviceType: NodePort serviceType: ClusterIP
Run install, and:
[simterm]
$ helm upgrade --install test-svc . Error: UPGRADE FAILED: cannot patch "test-svc" with kind Service: Service "test-svc" is invalid: spec.ports[0].nodePort: Forbidden: may not be used when `type` is 'ClusterIP'
[/simterm]
What we can do here is to add a “dirty hack”: let’s check if the type passed contains the “ClusterIP” string, and if it does, then we will specify nodePort == null
:
--- apiVersion: v1 kind: Service metadata: name: test-svc spec: type: {{ .Values.serviceType }} ports: - port: 80 targetPort: 80 protocol: TCP {{- if (eq .Values.serviceType "ClusterIP") }} nodePort: null {{- end }} selector: app: test
Deploy it again, no errors this time:
[simterm]
$ helm upgrade --install test-svc .
[/simterm]
Check the Service:
[simterm]
$ kk get svc test-svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE test-svc ClusterIP 172.20.107.220 <none> 80:30506/TCP 3m7s
[/simterm]
Done.