Введение
В предыдущих лабораторных работах вы узнали, что Pod могут создаваться и удаляться. Если Pod завершается и его заменяет новый, новый Pod получает совершенно другой IP-адрес. Представьте, что вы звоните другу, но после каждого разговора его номер телефона меняется!
Для решения этой проблемы Kubernetes использует Service.
Сервис предоставляет рабочей нагрузке стабильный сетевой идентификатор, даже если обслуживающие её Pod могут меняться. Такое разделение жизненного цикла Pod и конечной точки сервиса — фундаментальная идея сетевого взаимодействия в Kubernetes.
Это одна из важнейших концепций сетевого взаимодействия в Kubernetes: Pod должны легко заменяться, но клиентам по-прежнему нужен надёжный адрес. Service устраняет это несоответствие: он располагается перед изменяющимися Pod и предоставляет стабильное имя и виртуальный IP-адрес.
В этой лабораторной работе вы:
- Создадите Service, чтобы предоставить приложению стабильный адрес.
- Узнаете, как Service определяет, к каким Pod направлять запросы (с помощью Selector).
- Получите доступ к приложению извне кластера через специальный тип сервиса NodePort.
Создайте YAML-файл Service
На этом шаге вы создадите файл конфигурации для Service. Мы будем использовать специальный тип сервиса — NodePort.
Service типа NodePort открывает определённый порт на каждом Node (сервере) кластера. Это похоже на то, как если бы вы открыли в доме определённую дверь, чтобы внутрь мог попасть внешний мир.
NodePort легко понять, поскольку он напрямую сопоставляется с портом каждого узла, однако обычно это не окончательная архитектура для публичного трафика в production-среде. В больших окружениях NodePort часто располагается за облачным балансировщиком нагрузки или ingress-слоем. В этой лабораторной работе он удобен тем, что делает сетевой путь наглядным для начинающих.
Создайте файл service.yaml в каталоге проекта:
nano service.yaml
Вставьте в него следующий текст. Обратите внимание на раздел selector: пока мы намеренно оставляем в нём значение app: unknown, чтобы показать, как работает селектор.
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
type: NodePort
ports:
- port: 80 ## The port the Service exposes internally
targetPort: 80 ## The port the container is listening on
nodePort: 30008 ## The static door we are opening to the outside
selector:
app: unknown ## This needs to match your Pod's label
Сохраните файл и выйдите из редактора (Ctrl+X, Y, Enter).
Этот файл описывает Service с именем my-service. Сервис прослушивает порт 30008 (параметр nodePort). Весь трафик, поступающий на этот порт, будет перенаправляться к Pod с меткой app: unknown (а таких Pod сейчас не существует!).
Исправьте селектор
Service использует Selector, чтобы найти Pod, которым следует передавать трафик. Это работает как почтовая наклейка. Если адрес на конверте (селектор Service) не совпадает с адресом на доме (метка Pod), письмо не будет доставлено.
Метки и селекторы используются в Kubernetes не только в Service. Это один из основных механизмов группировки платформы. Вместо жёсткой привязки к точным именам Pod ресурсы Kubernetes обычно находят связанные объекты по совпадению меток. Благодаря этому система сохраняет гибкость при пересоздании Pod.
На шаге Setup мы автоматически запустили для вас веб-приложение. Давайте найдём его «адресную метку».
Выполните эту команду, чтобы увидеть метки запущенных Pod:
kubectl get pods --show-labels
Вы должны увидеть примерно такой результат: web-app-xxxxx ... app=web-app
Метка имеет вид app=web-app. Однако наш Service ищет app: unknown. Исправим это.
Откройте файл service.yaml:
nano service.yaml
Измените раздел selector, чтобы он соответствовал метке вашего Pod:
selector:
app: web-app
Сохраните файл и выйдите из редактора. Теперь Service точно знает, куда направлять трафик.
Примените конфигурацию и проверьте результат
Теперь, когда адрес указан правильно, включим Service.
После применения этого манифеста Kubernetes создаст объект Service и настроит внутренние сетевые правила, чтобы запросы, поступающие к Service, могли перенаправляться к конечным точкам соответствующих Pod. Важно понимать, что сам Service не запускает приложение. Это сетевая абстракция, расположенная перед Pod приложения.
Примените конфигурацию к кластеру:
kubectl apply -f service.yaml
Вы должны увидеть сообщение: service/my-service created.
Чтобы убедиться, что Service запущен и работает, проверьте список сервисов:
kubectl get svc my-service
Вы увидите:
- NAME: my-service
- TYPE: NodePort
- PORT(S): 80:30008/TCP
Это подтверждает, что ваш «номер телефона» активен и ожидает обращений на порту 30008.
Получите доступ к приложению
Настал решающий момент. Можно ли получить доступ к приложению «извне» — из вашей командной строки?
Приложение опубликовано на порту 30008 узла Kubernetes.
Здесь задействованы три порта, и начинающие часто путают их. nodePort — это внешний порт, открытый на узле, port — порт, предоставляемый абстракцией Service, а targetPort — порт внутри контейнера, на котором фактически работает приложение. Если различать эти роли, в дальнейшем будет проще избежать путаницы.
Сначала получите IP-адрес узла Minikube:
minikube ip
Предположим, что это 192.168.49.2. Полный адрес вашего приложения: http://192.168.49.2:30008.
С помощью curl можно обратиться по этому адресу и проверить, отвечает ли приложение:
curl http://$(minikube ip):30008
Вы должны увидеть HTML-код страницы «Welcome to nginx!».
Это подтверждает весь путь запроса:
- Вы обратились к внешнему порту Node (
30008). - Service принял запрос.
- Он проверил свой Selector (
app: web-app). - Он перенаправил запрос к одному из Pod, соответствующих этой метке.
- Pod вернул веб-страницу.
Итоги
В этой лабораторной работе вы решили проблему исчезающих IP-адресов Pod с помощью Service.
Вы успешно:
- Создали Service типа NodePort (
service.yaml), открыв шлюз к приложению. - Настроили Selector, чтобы указать Service, какими Pod нужно управлять.
- Получили доступ к запущенному приложению из внешнего мира по его стабильному адресу.
Services связывают приложения Kubernetes в единую систему и позволяют разным её компонентам надёжно взаимодействовать друг с другом.


