Ранее для доступа к нашему поду, мы через утилиту kubectl
реализовывали проброс портов во внутрь пода. Эта история хоть и работает, но вообще не годиться для ежедневной эксплуатации.
Так как состояние пода эфемерно, то есть под может сломаться или перезапуститься, изчезнуть или появиться на другой ноде. Соответствено, kubernetes назначает поду новый ip, и клиенты нашего приложения даже не догадываются об изменениях. В дополнение у нашего приложения может быть несколько его реплик.
В данном топике, мы рассмотрим решение этих проблем через использование ресурса - Service.
Сервис объединяет несколько подов в единую группу, при помощи уже известного нам механизма - селекторов и меток, и образует так называемую точку входа (endpoint). Проще говоря сервис выбирает на какие поды перенаправлять трафик, используя селекторы и метки. Клиенты спокойно ходят на выделенный ip и порт, при этому даже не догадываясь о колличестве реплик и адресации подов.
(Схема взаимосвязи подов и сервиса по меткам.)
Важно отметить, что назначение сервисов это предоставление доступа к группе подов, для других подов. Созданная точка входа будет доступна только изнутри кластера.
Перейдем к практике, давайте создадим деплоймент состоящий из трех реплик nginx. В контейнер будет монтированится конфигмепа с конфигурацией nginx. Начнем с создание конфигурации nginx. У нас будет простенький веб-сервер, который на все запросы будет отвечать своим hostname.
server {
listen 8080;
server_name _;
default_type text/plain;
location / {
return 200 'Hello, I am $hostname\n';
}
}
Создадим новую конфигмапу через kubectl
:
$ kubectl create cm nginx-svc-conf --from-file=./nginx-svc.conf
Теперь напишем новый деплоймент на запуск трех реплик c nginx:
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-svc-dp
spec:
replicas: 3
selector:
matchLabels:
front: nginx-svc
strategy:
rollingUpdate:
maxSurge: 3
maxUnavailable: 3
type: RollingUpdate
template:
metadata:
labels:
front: nginx-svc
spec:
containers:
- image: nginx:1.22
name: nginx
ports:
- containerPort: 8080
readinessProbe:
failureThreshold: 3
successThreshold: 1
httpGet:
port: 8080
path: /
periodSeconds: 15
timeoutSeconds: 5
livenessProbe:
failureThreshold: 3
successThreshold: 1
httpGet:
port: 8080
path: /
periodSeconds: 15
timeoutSeconds: 5
resources:
requests:
cpu: 250m
memory: 250Mi
limits:
cpu: 250m
memory: 250Mi
volumeMounts:
- name: nginx-conf
mountPath: /etc/nginx/conf.d/
volumes:
- name: nginx-conf
configMap:
name: nginx-svc-conf
Применим манифест к кластеру, и смотрим состояние подов.
$ kubectl apply -f nginx-deployment.yml
$ kubectl get po
Теперь напишем манифест для сервиса, откроем доступ к нашему деплойменту:
apiVersion: v1
kind: Service
metadata:
name: nginx-front-service
spec:
selector:
front: nginx-svc
ports:
- port: 80
targetPort: 8080
type: ClusterIP
Отметим что версия api изменилась, так же тип используемого объекта изменился на Service
. В поле спецификации (spec), как говорилось ранее, указываем selector
по которому сервис будет определять поды относящиеся к его группе. Далее описываются порты, указывается порт нашего сервиса (на каком порту принимает подключения) и таргет порт который слушается нашими подами. И в конце указывается тип сервиса, в нашем случаи это ClusterIP
, как раз таки этот тип открывает доступ к сервису внутри кластера, по внетреннему IP-адресу.
Применяем манифест к кластеру:
$ kubectl apply -f nginx-svc.yml
--
service/nginx-front-service created
Отлично, теперь мы можем пролистить все созданные объекты и посмотрим их зависимость друг от друга. Листим сервис:
$ kubectl get svc nginx-front-service -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
nginx-front-service ClusterIP 10.100.95.219 <none> 80/TCP 10m front=nginx-svc
Как видим, наш сервис слушает кластерный ip - 10.100.95.219
на порту 80. И балансит весь трафик на поды с меткой - front=nginx-svc
.
После добалении нового сервиса, кубернетес создает еще один объект - endpoint
. Этот объект является некой абстрактной прослойкой между сервисами и подами. Далее куб, находит все поды по лейблу, и включает или исключает их из некого списка (Все зависит от состояния пода, и результата выполнения Readiness пробы). В случаи, если мы создаем сервис без указания селектора, endpoint не будет создан автоматически.
Если пролистим созданный endpoint, то увидем адреса наших подов
$ kubectl get ep
NAME ENDPOINTS AGE
nginx-front-service 172.17.0.3:8080,172.17.0.4:8080,172.17.0.7:8080 11h
Теперь выведем список подов с этим лейблом.:
$ kubectl get po --selector front=nginx-svc -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-svc-dp-687bd7956b-qlbkk 1/1 Running 1 (92s ago) 11h 172.17.0.7 minikube <none> <none>
nginx-svc-dp-687bd7956b-vjlgc 1/1 Running 1 (92s ago) 11h 172.17.0.4 minikube <none> <none>
nginx-svc-dp-687bd7956b-w9jfn 1/1 Running 1 (92s ago) 11h 172.17.0.3 minikube <none> <none>
Как видно адрес каждого пода соответствует списку в endpoints.
Поднимем еще один под, и попробуем курлом постучаться на наш сервис,
$ kubectl run -ti --rm --image centosadmin/utils test bash
--
bash-5.0# curl http://10.100.95.219
I am nginx-svc-dp-687bd7956b-w9jfn
bash-5.0# curl http://10.100.95.219
I am nginx-svc-dp-687bd7956b-qlbkk
bash-5.0# curl http://10.100.95.219
I am nginx-svc-dp-687bd7956b-w9jfn
bash-5.0# curl http://10.100.95.219
I am nginx-svc-dp-687bd7956b-vjlgc
На мои запросы ответ прилетает каждый раз от нового пода.
В случаи если мы создаем новый сервис, без указания селекторов, то объект endpoints не будет создан автоматом. Нам придется в ручным спобосом запускать точку входа. Такой поинт может быть полезен, когда мы хотим открыть доступ на ресурс вне кластера. Запустить еще один сервис без указания меток:
---
apiVersion: v1
kind: Service
metadata:
name: proxy-example
spec:
ports:
- port: 80
targetPort: 80
Применяем манифест.
Если же мы пролистим список endpoints, то не найдет точку с аналогичным именем нашего сервиса. Как ранее отмечалось, сервисы без селекторов не создают endpoint. Добавим новый endpoint, в качестве target-адреса укажим сторонний сайт:
---
apiVersion: v1
kind: Endpoints
metadata:
name: proxy-example
subsets:
- addresses:
- ip: 176.126.166.193
ports:
- port: 80
Поднимаем тестовый под и стучимся на сервис:
$ kubectl run -ti --rm --image centosadmin/utils test bash
---
bash-5.0# curl http://176.126.166.193
<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx/1.20.1</center>
</body>
</html>