Thank you for reading this post, don't forget to subscribe!
ClusterIP
Endpoint
Сервисы без селекторов
EndpointSlice
Headless Service
ExternalName
External IPs
NodePort
Поды – это не постоянные сущности кластера. В любой момент времени вы можете добавить новый под или удалить не нужный. При перемещении пода между нодами кластера, под создается на новой ноде и удаляется на старой. При этом у пода меняется IP адрес. Именно поэтому не стоит обращаться к поду по ip адресу.
В Kubernetes, для доступа к поду (наборам подов) используются сервисы (service). Сервис – это абстракция, определяющая набор подов и политику доступа к ним.
Предположим, что в системе есть приложение, производящее обработку запросов. Приложение работает без сохранения состояния и поэтому может легко горизонтально масштабироваться. Для обработки потока запросов нам потребовалось несколько экземпляров приложения (подов) и нам необходимо распределить нагрузку между ними.
Если бы мы не использовали Kubernetes, нам бы пришлось ставить перед приложениями какую-то программу, занимающуюся распределением запросов. Например nginx. И каждый раз при изменении количества приложений, при переезде приложения с одного сервера на другой перенастраивать nginx.
В Kubernetes заботу о распределении нагрузки или доступа к группе приложений ложится на сервис. При определении сервиса обычно достаточно указать селектор, определяющий выбор подов, на которые будут пересылаться запросы. Так же существует возможность определения сервисов без селекторов
ClusterIP
Утилита kubectl может создать сервис используя аргументы командной строки. Но мы будем пользоваться yaml файлами.
1 2 3 4 5 6 7 8 9 10 11 12 |
--- apiVersion: v1 kind: Service metadata: name: service-name spec: selector: app: pod-selector ports: - protocol: TCP port: 80 targetPort: 8080 |
В приведённом выше примере создаётся сервис с именем service-name. Kubernetes присваивает сервису ip адрес и добавляет его данные в DNS сервер. После этого вы можете обратиться к сервису по его имени. В нашем случае сервис принимает запросы на 80-м порту и, если мы находимся в том же неймспейсе, мы можем обратиться к нему http://service-name. Если в другом, то с указанием неймспейса: http://service-name.namespace.svc
Приходящие запросы сервис будет пересылать на порт 8080 подам с метками (label) app: selector. Если в системе будет несколько подов с таким селектором, сервис будет перераспределять запросы между ними. По умолчанию по алгоритму round robbin.
В качестве значения targetPort можно использовать имена портов. Конечно, если вы его описали при определении пода. Это удобно, если вы ссылаетесь на поды, у которых определены разные номера портов, но под одним именем.
Рассмотрим пример. Deployment для сервера Tomcat.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
--- apiVersion: apps/v1 kind: Deployment metadata: name: tomcat labels: app: tomcat spec: replicas: 2 revisionHistoryLimit: 3 selector: matchLabels: app: tomcat template: metadata: labels: app: tomcat spec: containers: - name: tomcat image: tomcat:10-jdk15 imagePullPolicy: IfNotPresent resources: requests: cpu: "0.2" memory: "200Mi" limits: cpu: "0.5" memory: "500Mi" ports: - containerPort: 8080 name: tomcat protocol: TCP |
Файл 01-deployment.yaml.
В deployment указано наличие двух реплик (подов) приложения. Объявляется порт приложения 8080, с названием tomcat. Приложению присваивается метка: app: tomcat.
Сервис, предоставляющий доступы к этим подам можно объявить следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 |
--- apiVersion: v1 kind: Service metadata: name: tomcat-main spec: selector: app: tomcat ports: - protocol: TCP port: 80 targetPort: tomcat |
Файл 02-service.yaml
В разделе selector мы указываем метку приложения, на которые мы будем ссылаться. В разделе ports говорим, что к сервису нужно обращаться на 80-й порт. Запрос будет переслан приложению на порт, имеющий имя tomcat.
Применим файлы манифеста.
kubectl apply -f 01-deployment.yaml
kubectl apply -f 02-service.yaml
Примечание. Кластер kuberntes устанавливался со следующими параметрами сети:
1 2 3 4 |
networking: dnsDomain: cluster.local podSubnet: 10.234.0.0/18 serviceSubnet: 10.233.0.0/18 |
Посмотрим информацию о запущенных подах. Вывод программы немного обрезан.
1 2 3 4 |
# kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP tomcat-56db4566fd-8ffjg 1/1 Running 0 2m40s 10.234.9.2 tomcat-56db4566fd-xdctz 1/1 Running 0 2m40s 10.234.8.196 |
Если обратиться к любому поду напрямую, мы увидим ответ сервера tomcat.
1 2 3 4 |
# curl 10.234.9.2:8080 <!doctype html> Тут было много символов <h3>Apache Tomcat/10.0.0-M10</h3></body></html> |
Cписок сервисов в namespace default:
1 2 3 4 |
# kubectl get svc -o wide NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR kubernetes ClusterIP 10.233.0.1 <none> 443/TCP 173m <none> tomcat-main ClusterIP 10.233.47.171 <none> 80/TCP 114s app=tomcat |
Посмотрим подробнее на сервис.
# kubectl get svc tomcat-main -o yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
apiVersion: v1 kind: Service … name: tomcat-main namespace: default … spec: clusterIP: 10.233.47.171 ports: - port: 80 protocol: TCP targetPort: tomcat selector: app: tomcat sessionAffinity: None type: ClusterIP |
Система выделила сервису виртуальный ip адрес:
clusterIP: 10.233.47.171
Адрес виртуальный. Это значит, что вы не найдете на машине Linux интерфейса с таким ip. Следует отметить, что за выдачу ip для сервисов отвечает kube-proxy, а не модуль IPAM драйвера сети Kubernetes.
Обратите внимание, что ip адреса сервисам выдаются из диапазона, определенного при помощи serviceSubnet, заданном при установке кластера.
В принципе, ip адрес можно определять сразу в файле манифеста. Если это ip занят – то вы получите сообщение об ошибке. Но нет особого смысла заниматься явным указанием ip, поскольку к сервису мы всегда будем обращаться по имени, а не по ip.
Так же следует обратить внимание на:
type: ClusterIP
По умолчанию создаются сервисы типа ClusterIP.
После определения сервиса мы можем обратиться к нему и получить ответ одного из tomcat. Да, тут мы обращаемся к сервису по его ip адресу, но только потому, что делаем это в консоли машины Linux, а не внутри какого-либо пода Kubernetes. Linux машина не использует DNS Kubernetes и её клиент не может разрешать внутренние имена Kubernetes в ip адреса.
1 2 3 4 |
# curl 10.233.47.171 <!doctype html> Тут было много символов <h3>Apache Tomcat/10.0.0-M10</h3></body></html> |
Endpoint
Каким образом происходит связь между сервисом и подами? «За кадром» остался еще один элемент – endpoint.
1 2 3 4 |
# kubectl get ep NAME ENDPOINTS AGE kubernetes 192.168.218.171:6443,192.168.218.172:6443,192.168.218.173:6443 174m tomcat-main 10.234.8.196:8080,10.234.9.2:8080 3m17s |
Посмотрим на него подробнее.
# kubectl get ep tomcat-main -o yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
apiVersion: v1 kind: Endpoints metadata: … name: tomcat-main namespace: default … subsets: - addresses: - ip: 10.234.8.196 nodeName: worker1.kryukov.local targetRef: kind: Pod name: tomcat-56db4566fd-xdctz namespace: default … - ip: 10.234.9.2 nodeName: worker2.kryukov.local targetRef: kind: Pod name: tomcat-56db4566fd-8ffjg namespace: default … ports: - port: 8080 protocol: TCP |
В случае сервисов, использующих селектор, такие endpoints создаются автоматически. Система сама находит ip адреса подов, имеющих соответствующие метки, и формирует записи в endpoint.
Как такие связи будут выглядеть с точки зрения Linux машины, зависит от режима работы kube-proxy. Ведь именно он управляет сервисами. Обычно используют iptables или ipvs. С точки зрения быстродействия предпочтительнее использовать режим ipvs.
1 2 3 4 |
# ipvsadm -L -n | grep -A 2 10.233.47.171 TCP 10.233.47.171:80 rr -> 10.234.8.196:8080 Masq 1 0 0 -> 10.234.9.2:8080 Masq 1 0 0 |
По своей сути перед нами nat преобразование.
Сервисы без селекторов.
Сервисы без селекторов обычно используются для обращения за пределы кластера по ip адресу к какому либо приложению.
В качестве примера возьмем mail.ru. Конечно, лучше в качестве примера использовать какую либо базу данных, но у таковой базы не оказалось под рукой
# host mail.ru
mail.ru has address 217.69.139.200
mail.ru has address 94.100.180.200
mail.ru has address 217.69.139.202
mail.ru has address 94.100.180.201
Определение сервиса:
1 2 3 4 5 6 7 8 9 10 |
--- apiVersion: v1 kind: Service metadata: name: mail-ru spec: ports: - protocol: TCP port: 8080 targetPort: 80 |
Файл 03-service-mail-ru.yaml
Порт 8080 у сервиса был поставлен в качестве эксперимента. Можно оставить его значение равным 80.
Определение endpoint. Имя сервиса и endpoint должны совпадать.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
--- apiVersion: v1 kind: Endpoints metadata: name: mail-ru subsets: - addresses: - ip: 217.69.139.200 - ip: 94.100.180.200 - ip: 217.69.139.202 - ip: 94.100.180.201 ports: - port: 80 |
Файл 04-endpoint-mail-ru.yaml
IP-адреса конечных точек не должны быть: loopback (127.0.0.0/8 для IPv4, :: 1/128 для IPv6) или локальными для ссылки (169.254.0.0/16 и 224.0.0.0/24 для IPv4, fe80 :: / 64 для IPv6).
IP-адреса конечных точек не могут быть IP-адресами других служб кластера kubernetes, потому что kube—proxy не поддерживает виртуальные IP-адреса в качестве пункта назначения.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# kubectl apply -f 03-service-mail-ru.yaml # kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.233.64.1 <none> 443/TCP 73m mail-ru ClusterIP 10.233.99.119 <none> 8080/TCP 21s ClusterIP 10.233.107.242 <none> 80/TCP 61m # kubectl apply -f 04-endpoint-mail-ru.yaml # kubectl get ep NAME ENDPOINTS AGE kubernetes 192.168.218.171:6443 73m mail-ru 217.69.139.200:80,94.100.180.200:80,217.69.139.202:80 + 1 more… 24s tomcat-main 10.233.8.193:8080,10.233.9.1:8080 61m # ipvsadm -L -n | grep -A 4 10.233.99.119 TCP 10.233.99.119:8080 rr -> 94.100.180.200:80 Masq 1 0 0 -> 94.100.180.201:80 Masq 1 0 0 -> 217.69.139.200:80 Masq 1 0 0 -> 217.69.139.202:80 Masq 1 0 0 |
Проверим, можем ли мы обращаться к новому сервису:
1 2 3 4 5 6 7 8 |
# curl 10.233.99.119:8080 <html> <head><title>301 Moved Permanently</title></head> <body bgcolor="white"> <center><h1>301 Moved Permanently</h1></center> <hr><center>nginx/1.14.1</center> </body> </html> |
Работать с ip адресами не удобно. Запустим в кластере какой ни будь под и обратимся к сервисам по их именам.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# kubectl run -it --rm --restart=Never --image=infoblox/dnstools:latest dnstools If you don't see a command prompt, try pressing enter. dnstools# curl mail-ru:8080 <html> <head><title>301 Moved Permanently</title></head> <body bgcolor="white"> <center><h1>301 Moved Permanently</h1></center> <hr><center>nginx/1.14.1</center> </body> </html> dnstools# curl tomcat-main <!doctype html> Тут было много символов <h3>Apache Tomcat/10.0.0-M10</h3></body></html> |
EndpointSlice
У связки сервис + endpoint существовала большая проблема: все endpoint кластера – это объекты API. Все объекты API хранятся в базе данных etcd. И самое страшное, что записи объектов endpoints хранились в базе в одном ресурсе. При увеличении количества endpoints (читаем подов), ресурс приобретал просто огромные размеры. Представьте себе что в одной записи в БД хранятся ip адреса почти всех подов кластера!
Проблемы возникли в больших кластерах. При изменении endpoint, приходилось перечитывать весь объект из базы, что приводило к большому сетевому трафику.
Начиная с версии 1.17 в Kubernetes добавили EndpointSlice. При помощи него объект содержащий endpoint системы разбили на куски (слайсы). Теперь endpoints хранятся в EndpointSlice. Разбиение происходит автоматически. По умолчанию в одном slice хранится около 100 endpoints.
1 2 3 4 5 |
# kubectl get endpointslice NAME ADDRESSTYPE PORTS ENDPOINTS kubernetes IPv4 6443 192.168.218.171,192.168.218.172,192.168.218.173 mail-ru-fq7jd IPv4 80 217.69.139.200,217.69.139.202,94.100.180.200 + 1 more... tomcat-main-jf6dq IPv4 8080 10.234.9.2,10.234.8.196 |
# kubectl get endpointslice tomcat-main-jf6dq -o yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
addressType: IPv4 apiVersion: discovery.k8s.io/v1beta1 kind: EndpointSlice metadata: ... generateName: tomcat-main- generation: 1 labels: endpointslice.kubernetes.io/managed-by: endpointslice-controller.k8s.io kubernetes.io/service-name: tomcat-main ... name: tomcat-main-jf6dq namespace: default ownerReferences: - apiVersion: v1 blockOwnerDeletion: true controller: true kind: Service name: tomcat-main ... endpoints: - addresses: - 10.234.9.2 conditions: ready: true targetRef: kind: Pod name: tomcat-56db4566fd-8ffjg namespace: default ... topology: kubernetes.io/hostname: worker2.kryukov.local - addresses: - 10.234.8.196 conditions: ready: true targetRef: kind: Pod name: tomcat-56db4566fd-xdctz namespace: default ... topology: kubernetes.io/hostname: worker1.kryukov.local ports: - name: "" port: 8080 protocol: TCP |
Вывод команд я немного сократил.
Как видно из описания, EndpointSlice содержит набор портов, которые применяются ко всем endpoints. Мы можем в одном сервисе определить несколько портов. В результате на один сервис может быть создано несколько EndpointSlices.
Headless Service
нексус я запускаю следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
--- apiVersion: v1 kind: Service metadata: name: nexus namespace: nexus labels: app: nexus section: tools spec: ports: - name: http-main port: 8081 protocol: TCP targetPort: 8081 selector: app: nexus section: tools clusterIP: None --- apiVersion: apps/v1 kind: StatefulSet metadata: name: nexus namespace: nexus labels: app: nexus section: tools spec: selector: matchLabels: app: nexus section: tools serviceName: nexus template: metadata: labels: app: nexus section: tools spec: containers: - name: nexus image: sonatype/nexus3:3.27.0 imagePullPolicy: IfNotPresent ports: - containerPort: 8081 protocol: TCP - containerPort: 8083 protocol: TCP env: - name: INSTALL4J_ADD_VM_PARAMS value: "-Xms2g -Xmx2g -XX:MaxDirectMemorySize=3g" # - name: NEXUS_CONTEXT # value: nexus readinessProbe: httpGet: path: / port: 8081 initialDelaySeconds: 30 periodSeconds: 30 failureThreshold: 6 livenessProbe: httpGet: path: / port: 8081 initialDelaySeconds: 30 periodSeconds: 30 failureThreshold: 6 resources: requests: memory: "2Gi" cpu: "1" limits: memory: "2Gi" cpu: "2" volumeMounts: - mountPath: /nexus-data name: nexus-data volumeClaimTemplates: - metadata: name: nexus-data spec: accessModes: [ "ReadWriteOnce" ] storageClassName: "managed-nfs-storage" resources: requests: storage: 8Gi --- apiVersion: v1 kind: Service metadata: name: nexus-0 namespace: nexus labels: app: nexus section: tools spec: ports: - name: http-main port: 8081 protocol: TCP targetPort: 8081 - name: http-docker-rep port: 8083 protocol: TCP targetPort: 8083 selector: app: nexus section: tools statefulset.kubernetes.io/pod-name: nexus-0 --- apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: annotations: nginx.ingress.kubernetes.io/ssl-redirect: "false" nginx.ingress.kubernetes.io/proxy-body-size: "0" name: nexus namespace: nexus labels: app: nexus section: tools spec: rules: - host: nexus.kryukov.local http: paths: - backend: serviceName: nexus-0 servicePort: 8081 path: / - host: n.kryukov.local http: paths: - backend: serviceName: nexus-0 servicePort: 8083 path: / tls: - hosts: - n.kryukov.local secretName: nexus-tls |
Выше мы запустили Nexus в нашем кластере. В файле манифеста было определено два сервиса
Нас интересует сервис под названием nexus
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
--- apiVersion: v1 kind: Service metadata: name: nexus labels: app: nexus section: tools spec: ports: - name: http-main port: 8081 protocol: TCP targetPort: 8081 selector: app: nexus section: tools clusterIP: None |
Обратите внимание на последнюю строку: clusterIP: None. Мы говорим системе, что у данного сервиса не будет виртуального ip адреса. Таким образом, мы определяем «безголовый» сервис.
Вот так он отображается в командной строке при запросе списка сервисов.
1 2 3 4 5 |
# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) ... nexus ClusterIP None <none> 8081/TCP ... |
В системе для данного сервиса создаётся endpoint. Без этого никак.
1 2 3 4 5 |
# kubectl get endpoints NAME ENDPOINTS ... nexus 10.234.9.3:8081 ... |
Что же такое headless сервис? Это просто запись А в системе DNS. Т.е. имя сервиса преобразуется не в виртуальный ip, его (ip) у нас нет, а сразу в ip пода.
Увеличим количество подов nexus:
# kubectl scale --replicas=2 statefulset nexus
Немного подождем. Записи в системе DNS появляются с небольшой задержкой.
Посмотрим список endpoints.
1 2 3 4 5 |
# kubectl get endpoints NAME ENDPOINTS ... nexus 10.234.8.199:8081,10.234.9.3:8081 ... |
Запустим dnstools и пошлем запрос к DNS кластера.
1 2 3 4 5 6 7 8 9 10 |
# kubectl run -it --rm --restart=Never --image=infoblox/dnstools:latest dnstools If you don't see a command prompt, try pressing enter. dnstools# dig nexus.default.svc.cluster.local ... ;; ANSWER SECTION: nexus.default.svc.cluster.local. 5 IN A 10.234.9.3 nexus.default.svc.cluster.local. 5 IN A 10.234.8.199 ... |
Мы видим, что у нас появились две записи типа А, указывающие на ip адреса подов. Поскольку не создаётся виртуальный ip сервиса, т.е. не создаётся NAT преобразование. Мы можем по имени сервиса напрямую обратиться к поду (подам) без затрат времени на лишние преобразования.
Таким образом, если работе вашего приложения противопоказаны NAT преобразования – используйте headless сервисы. Правда равномерного распределения нагрузки вы не получите, DNS не умеет этого делать. Но чем-то приходится жертвовать.
ExternalName
В предыдущих примерах у нас немного неудачно происходила ссылка на mail.ru. В общем то была поставлена простая задача: обратиться внутри кластера к mail.ru не по его имени, а при помощи сервиса mail-ru. Что-то типа: http://mail-ru
Решим задачу правильно. Сначала удалите старый сервис.
# kubectl delete svc mail-ru
Применим манифест из файла 06-external-name.yaml.
1 2 3 4 5 6 7 8 9 |
apiVersion: v1 kind: Service metadata: name: mail-ru labels: post: mail-ru spec: type: ExternalName externalName: mail.ru |
Сервис типа ExternalName добавляет запись типа CNAME во внутренний DNS сервер Kubernetes.
1 2 3 |
# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) mail-ru ExternalName <none> mail.ru <none> |
Для этого сервиса не создаётся endpoint. Поэтому сразу переходим к запросам к DNS.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# kubectl run -it --rm --restart=Never --image=infoblox/dnstools:latest dnstools dnstools# dig mail-ru.default.svc.cluster.local ;; ANSWER SECTION: mail-ru.default.svc.cluster.local. 5 IN CNAME mail.ru. mail.ru. 5 IN A 217.69.139.202 mail.ru. 5 IN A 94.100.180.200 mail.ru. 5 IN A 94.100.180.201 mail.ru. 5 IN A 217.69.139.200 dnstools# ping mail-ru PING mail-ru (94.100.180.200): 56 data bytes 64 bytes from 94.100.180.200: seq=0 ttl=55 time=47.865 ms 64 bytes from 94.100.180.200: seq=1 ttl=55 time=47.892 ms |
External IPs
Не советую вам экспериментировать с полем externalIPs. Внимательно прочитайте что будет написано ниже. Если решитесь на эксперименты — не подставляете ip адреса, на которых висит API сервер кластера. Используйте multimaster установку кластера, если ошибётесь, можно переключиться на другую ноду и исправить ошибку. И не говорите, что я вас не предупреждал.
Почти в любом определение сервиса можно добавить поле externalIPs, в котором можно указать ip машины кластера. При обращении на этот ip и указанный в сервисе порт, запрос будет переброшен на соответствующий сервис.
В качестве примера добавьте алиас на сетевой интерфейс. Например вот так:
# ifconfig ens33:ext 192.168.218.178
оставим один под nexus.
# kubectl scale --replicas=1 statefulset nexus
Применим манифест из файла 07-external-ip.yaml.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
--- apiVersion: v1 kind: Service metadata: name: external-svc labels: app: nexus section: tools spec: ports: - name: http-main port: 8888 protocol: TCP targetPort: 8081 selector: app: nexus section: tools externalIPs: - 192.168.218.178 |
Посмотрим список сервисов.
1 2 3 |
# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) external-svc ClusterIP 10.233.55.223 192.168.218.178 8888/TCP |
Посмотрим таблицу преобразований.
1 2 3 4 5 6 |
# ipvsadm -L -n | grep -A 1 8888 TCP 192.168.218.178:8888 rr -> 10.234.8.203:8081 Masq 1 0 0 -- TCP 10.233.55.223:8888 rr -> 10.234.8.203:8081 Masq 1 0 |
Попробуем обратиться по указанному ip.
# curl 192.168.218.178:8888
Тут будет большой ответ nexus
Очень похоже на NodePort. Но только похоже.
NodePort
Сервисы типа NodePort открывают порт на каждой ноде кластера на сетевых интерфейсах хоста. Все запросы, приходящие на этот порт, будут пересылаться на endpoints, связанные с данным сервисом.
Диапазон портов, который можно использовать в NodePort — 30000-32767. Но его можно изменить при конфигурации кластера.
Пример сервиса типа NodePort.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
--- apiVersion: v1 kind: Service metadata: labels: app: tomcat name: tomcat-nodeport spec: type: NodePort # externalTrafficPolicy: Local selector: app: tomcat ports: - protocol: TCP port: 80 targetPort: tomcat # nodePort: 30080 |
При описании сервиса необходимо явно указать его тип. Если не указать значение порта, при помощи параметра spec.ports.nodePort, порт присваивается автоматически из стандартного диапазона.
1 2 3 |
# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) tomcat-nodeport NodePort 10.233.43.49 <none> 80:31504/TC |
Порт открывается на всех нодах кластера.
Посмотрим ноду из control plane кластера.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
root@control1 # ipvsadm -L -n | grep -A 2 31504 TCP 169.254.25.10:31504 rr -> 10.234.8.205:8080 Masq 1 0 0 -> 10.234.9.8:8080 Masq 1 0 0 TCP 192.168.218.171:31504 rr -> 10.234.8.205:8080 Masq 1 0 0 -> 10.234.9.8:8080 Masq 1 0 0 -- TCP 10.234.56.192:31504 rr -> 10.234.8.205:8080 Masq 1 0 0 -> 10.234.9.8:8080 Masq 1 0 0 TCP 127.0.0.1:31504 rr -> 10.234.8.205:8080 Masq 1 0 0 -> 10.234.9.8:8080 Masq 1 0 0 |
На остальных нодах кластера ситуация с портом 31504 будет аналогичной.
Существует возможность открыть порт только на тех нодах, на которых запущены поды на которые ссылается данный сервер. Для этого в описании сервиса необходимо добавить параметр externalTrafficPolicy: Local.