Во время деплоя приложения из Helm-чарта, описанного в посте Istio: общий Ingress/AWS ALB, Helm-чарт с условиями, Istio и ExternalDNS, возникает ошибка «spec.ports[0].nodePort: Forbidden: may not be used when `type` is ‘ClusterIP’«.
Воспроизведём ошибку вручную, и рассмотрим способы обхода проблемы с помощью kubectl и при использовании Helm.
Содержание
Ошибка spec.ports[0].nodePort: Forbidden: may not be used when `type` is ‘ClusterIP’
Создаём Service с типом NodePort:
---
apiVersion: v1
kind: Service
metadata:
name: test-svc
spec:
type: NodePort
ports:
- port: 80
targetPort: 80
protocol: TCP
selector:
app: test
Деплоим, проверяем:
[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]
И порты этого Сервиса:
[simterm]
$ kubectl get svc test-svc -o json | jq '.spec.ports[]'
{
"nodePort": 31231,
"port": 80,
"protocol": "TCP",
"targetPort": 80
}
[/simterm]
Обновляем манифест, меняем тип на ClusterIP:
---
apiVersion: v1
kind: Service
metadata:
name: test-svc
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 80
protocol: TCP
selector:
app: test
Ещё раз деплоим, и:
[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]
Собственно, из-за nodePort и возникает проблема, так как в спецификации ClusterIP его нет.
Решения
kubectl apply --force
Первый вариант — выполнить apply --force — тогда сначала Service будет удалён, а потом создан новый:
[simterm]
$ kubectl apply -f test-svc.yaml --force service/test-svc configured
[/simterm]
Проверяем порты:
[simterm]
$ kubectl get svc test-svc -o json | jq '.spec.ports[]'
{
"port": 80,
"protocol": "TCP",
"targetPort": 80
}
[/simterm]
kubectl edit service
Ещё вариант — выполнить kubectl edit service вручную, а потом накатить манифест:
[simterm]
$ kubectl edit svc test-svc
Удаляем — nodePort: 32729 и type: NodePort:
Применяем с apply, но уже без --force:
[simterm]
$ kubectl apply -f test-svc.yaml service/test-svc configured
[/simterm]
Снова проверяем порты:
[simterm]
$ kubectl get svc test-svc -o json | jq '.spec.ports[]'
{
"port": 80,
"protocol": "TCP",
"targetPort": 80
}
[/simterm]
Решение для Helm-чарта
Но основная задача — делать это автоматически во время Helm-деплоя.
Сделаем тестовый чарт:
[simterm]
$ helm create test-svc Creating test-svc
[/simterm]
Удаляем шаблоны:
[simterm]
$ cd test-svc $ rm -rf templates/* values.yaml
[/simterm]
Пишем свой values.yaml и манифест для Service:
[simterm]
$ vim -p values.yaml templates/svc.yaml
[/simterm]
В values зададим тип NodePort:
serviceType: NodePort
И опишем Service с этим типом:
---
apiVersion: v1
kind: Service
metadata:
name: test-svc
spec:
type: {{ .Values.serviceType }}
ports:
- port: 80
targetPort: 80
protocol: TCP
selector:
app: test
Деплоим:
[simterm]
$ helm upgrade --install test-svc .
[/simterm]
Проверим тип:
[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]
Ещё раз воспроизводим ошибку — меняем в values тип на ClusterIP:
#serviceType: NodePort serviceType: ClusterIP
Запускаем инстал, и:
[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]
А теперь добавим костыль — проверяем передаваемый тип, и если он ClusterIP, то задаём 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
Повторяем деплой:
[simterm]
$ helm upgrade --install test-svc .
[/simterm]
Проверяем:
[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]
Готово.

