Во время деплоя приложения из 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]
Готово.