====== Сервисы/Сеть ======
===== Services =====
Для доступа к поду (наборам подов) используются сервисы (service). Service - абстракция, определяющая набор подов и политику доступа к ним. Так же берет на себя вопрос распределения нагрузки\\
При определении сервиса нужно указать селектор, определяющий выбор набора подов. Сервисы без селектора, обычно используются для обращения во вне кластера, там ендпоинт создается вручную\\
apiVersion: v1
kind Service
metadata:
name: service-my-pods
spec:
selector:
app: my-pods
ports:
- protocol: TCP
port: 80
targetPort: 8080
Типы сервисов:
* "ClusterIP" - дефолтный, доступ только внутри кластера
* "ExternalName" - создает ДНС запись внутри кластера, внешнего доступа тоже нет, Endpoint не создается. Точнее даже соответствие ДНС имени из внутри кластера с внешним адресом в интернете
* "LoadBalancer" - используется внешний балансер, предоставляемый например облачным провайдером
* "NodePort" - открывает порты на ноде для доступа к сервису из вне кластера, доступ по IP ноды
:!: more
Шлет входящие запросы на поды с указанным селектором. В качестве targetPort можно использовать имена портов, если они определены в подах, это дает большую гибкость т.к. могут быть разные\\
Если targetPort (порт назначения) не указать то по умолчанию используется тот же что и port (входящий)\\
Создается сервис, присваивается IP и куб добавляет его в DNS, после чего к сервису можно обращаться по имени, к том же NS: "http://service-my-pods", если из другого то нужно указать NS: "service-my-pods.namespace.svc"\\
В деплойменте должен быть прописан порт, чтобы контейнер мог принимать на нем запросы, в т.ч от сервиса собсна\\
Стратегия распределения запросов между подами, по умолчанию "round robin"\\
:!: Примеры
apiVersion: v1
kind: Service
metadata:
name: tomcat-main
spec:
selector:
app: tomcat
ports:
- protocol: TCP
port: 80
targetPort: tomcat
apiVersion: apps/v1
kind: Deployment
metadata:
name: tomcat-deployment
spec:
replicas: 2
selector:
matchLabels:
app: tomcat
template:
metadata:
labels:
app: tomcat
spec:
containers:
- name: tomcat
image: tomcat:10-jdk15
ports:
- containerPort: 8080
name: tomcat
protocol: TCP
Пару примеров как смотреть в консоли
kubectl get pods -o wide
kubectl get svc -o wide
# Так виден назначенный IP
kubectl get svc -o yaml
==== Endpoint ====
**kubectl get ep**\\
В случае сервисов использующих селекторы, ендпоинты создаются автоматом, система сама находит IP нужных подов и формирует запись в endpoint\\
Под капотом происходит по сути NAT преобразование, этим заведует kube-proxy\\
**EndpointSlice** - это просто разбиение объектов на части, для практичности размеров записей, происходит автоматически\\
=== Headless Service ===
По сути сервис без указания (создания) своего IP, так он напрямую ссылается на IP пода (подов, через запятую). В таком варианте отсутствует NAT преобразование, но так же отсутствует и балансировка. В случае какой то специфики, может пригодится\\
===== Доступ к приложению =====
==== ClusterIP ====
Сервис k8s по умолчанию. Обеспечивает сервис внутри кластера, к которому могут обращаться другие приложения внутри кластера. Внешнего доступа нет\\
:!: Пример
apiVersion: v1
kind: Service
metadata:
name: my-internal-service
selector:
app: my-app
spec:
type: ClusterIP
ports:
- name: http
port: 80
targetPort: 80
protocol: TCP
:!: В целях тестирования можно открыть доступ запустив Proxy в консоли
kubectl proxy --port=8080
# Затем обратится по такому адресу
http://localhost:8080/api/v1/proxy/namespaces//services/:/
http://localhost:8080/api/v1/proxy/namespaces/default/services/my-internal-service:http/
{{:k8s:proxy.png?direct&400|}}
==== NodePort ====
Самый примитивный способ направить внешний трафик в сервис. Открывает указанный порт для всех Nodes и трафик на этот порт перенаправляется сервису\\
Открываемый порт можно указать самому (nodePort), но лучше оставить автоматике\\
Из минусов то что один порт-одно приложение, кол-во портов ограничено, но самое главное что IP могут меняться и они разные на всех нодах\\
{{:k8s:nodeport.png?direct&400|}}
:!: Создается сервис типа "NodePort"
apiVersion: v1
kind: Service
metadata:
name: service-front
namespace: my-namespace
spec:
selector:
app: my-front
ports:
- protocol: TCP
port: 3000
На всех нодах открывается рандомный порт, по IP ноды и этому порту, будет доступен сервис. Порт виден в **oc describe svc/my-service** (NodePort)\\
:!: Еще пример
apiVersion: v1
kind: Service
metadata:
name: my-nodeport-service
selector:
app: my-app
spec:
type: NodePort
ports:
- name: http
port: 80
targetPort: 80
nodePort: 30036
protocol: TCP
==== LoadBalancer ====
Стандартный способ предоставления сервиса в интернете. Сервис предоставляет IP адрес, этот адрес будет направлять весь трафик на сервис\\
Весь трафик указанного порта направляется н сервис, нет фильтрации, маршрутизации и проч, направляется трафик разных типов. НО, каждому сервису нужен свой IP\\
Еще вырезка: "используется для внешних облачных балансировщиков, таких как Google Cloud, которые имеют своего провайдера. Сервис будет доступен через внешний балансировщик вашего провайдера, при этом создаются NodePort с портами, куда будет приходить трафик от провайдера и ClusterIP."
{{:k8s:loadbalancer.png?direct&400|}}
==== Ingress ====
**Ingress** - ресурс k8s где создается описание конфигурации Ingress Controller\\
**Ingress Controller** - обрабатывает трафик, его конфигурация формируется из всех Ingress объектов внутри кластера\\
Вместе создают единую точку входа для трафика и выполняют роль прокси и балансировщика (?)\\
Ingress Controller - физический софт, расположенный на одном из подов, в основном на сервисной ноде, выполняет роль единой точки входа графика в кластер, а так же роль балансировки.\\
В одном кластере может быть установлено несколько IC, в зависимости от конфигурации и настроек кластера. Так же , в функции IC входит первичная маршрутизация, согласно правилам, описанным в Ingress. Реализаций IC очень много\\
Ingress это yaml/json шаблон k8s, который описывает правила маршрутизации графика из IC до сервисов кластера\\
В системах "openshift" шаблон "Ingress" заменяет шаблон "route", выполняющий аналогичные функции\\
{{:k8s:ingress.png?direct&400|}}
:!: Сервисы для трафика
Revers proxy в Ingress Controller слушает порты с HTTP/HTTPS-соединениями, трафик может попадать на порты тремя способами:
* NodePort
* HostPort (можно повесить на порты 80 и 443)
* Host network (под повесит свои порты на публичном интерфейсе, т.е. будут открыты все порты контейнера)
Тут неясно, в рабочем варианте сервис был в дефолтном "ClusterIP", IC сам перенаправляет извне внутри кластера\\
:!: Возможный вариант конфига
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: my-ingress
spec:
backend:
serviceName: other
servicePort: 8080
rules:
- host: foo.mydomain.com
http:
paths:
- backend:
serviceName: foo
servicePort: 8080
- host: mydomain.com
http:
paths:
- path: /bar/*
backend:
serviceName: bar
servicePort: 8080
Два location на разные сервисы
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-test-site
namespace: test-ingress-app
spec:
ingressClassName: nginx
rules:
- host: es.moysite.ru
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: spa
port:
number: 80
- path: /test
pathType: Prefix
backend:
service:
name: frontend-api
port:
number: 80
:!: Annotations
"nginx.ingress.kubernetes.io/":\\
**ssl-redirect** - принудительное использование HTTPS на стороне сервера, с помощью перенаправления. По умолчанию контроллер перенаправляет на HTTPS если для этого входа включен TLS\\
**force-ssl-redirect** - принудительно перенаправлять на HTTPS, даже если TLS недоступен.\\
**ssl-passthrough** - передача данных по протоколу SSL. Указание контроллеру отправлять соединения по протоколу TLS непосредственно на серверную часть, вместо того чтобы позволять Nginx расшифровывать обмен данными\\
:!: т.к. передача по протоколу SSL работает на уровне 4 модели OSI (TCP), а не на 7 (HTTP), то эта аннотация **делает недействительными** все остальные аннотации для входящего объекта\\
:!: В том числе и маршрутизация на основе пути (т.е. Location фактически не работает, тк путь зашифрован)\\
Блок "TLS" вероятно по этой же причине не работает, тк подставляется дефолтный сертификат кубера
**** -
**** -
=== Еще про Ingress Controller ===
**Ingress Controller** обычно располагают на отдельных нодах, с внешними IP адресами, которые используются точкой подключения к сервисам\\
Обеспечивает контроль доступа к приложениям и маршрутизацию трафика между ними. Обрабатывает запросы из внешней сети и направляет их на определенные внутренние сервисы, основываясь на правилах ingress\\
Может как принимать весь внешний трафик так и стоять за каким то внешним балансером\\
:!: Мат. часть
Ingress работает на 7ом (прикладном) уровне OSI, для работы на третьем (сетевом) можно использовать другие методы публикации приложений:
* ClusterIP
* LoadBalancer
* NodePort
* ExternalName
* ExternalIP
Внутри Ingress Controller обычный Nginx, поэтому можно настроить распределение запросов по доменам или location\\
:!:(?) Важно отметить, что Ingress Controller-ы должны быть запущены на каждом узле, где развернуты приложения, к которым нужно получить доступ извне кластера.\\
:!: Терминирование TLS: если IC настроен на обработку HTTPS, он выполняет терминирование, те расшифровку входящего трафика и пересылку его во внутреннюю службу уже по нешифрованному соединению\\
=== HTTPS Ingress ===
Ресурс Ingress поддерживает только один порт TLS, 443, и предполагает завершение TLS в точке входа (трафик к Службе и ее Pods передается в виде открытого текста).\\
Ссылка на этот секрет в Ingress сообщает контроллеру Ingress о необходимости защитить канал от клиента до балансировщика нагрузки с помощью TLS\\
spec:
tls:
- hosts:
- https-example.foo.com
secretName: testsecret-tls
:!: Важно не забывать что серт должен быть валидным, для этого URL\\
Для того чтобы использовать сертификаты в конфигурации Ingress, их нужно загрузить в кластер Kubernetes. Серты хранятся в секретах Кубера, Создать такой секрет можно следующим образом:\\
kubectl create secret tls my-tls --key my-tls.key --cert my-tls.cert
:!: Далее этот серт можно использовать в конфигурации Ingress
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-my-tls
spec:
tls:
- hosts:
- ingress.example.com
secretName: my-tls
rules:
- host: ingress.example.com
http:
paths:
- path: /
backend:
serviceName: nginx
servicePort: 80
:!: Пересылка шифрованного трафика через Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-ing
annotations:
# указывает на использование nginx, зависит от настроек контроллера видимо
kubernetes.io/ingress.class: nginx
# Указываем контроллеру пересылать шифрованный трафик дальше, в приложение
nginx.ingress.kubernetes.io/ssl-passthrough: 'true'
labels:
app: myapp
spec:
rules:
- host: "myapp.apps.url-cluster.ru"
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp-svc
port:
number: 9444
:!: Вариант с Lets Encrypt
[[https://habr.com/ru/articles/668098|Есть статья по этому поводу]]\\
[[https://serveradmin.ru/kubernetes-ingress|Еще статья]]\\
"Cert-manager" - утилита в кластере Кубера, которая умеет автоматически полуать и продлевать сертификаты, в том числе и от бесплатного "Lets Encrypt"\\
Суть в том что в кластер устанавливается объект "cert-manager", типа "issuer", он занимается выпуском/перевыпуском сертов\\
В манифесте Ingress, в блоке "tls", указывается этот объект, точнее секрет, куда эмитент сохраняет серт\\
Пример конфига ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-test-site
namespace: test-ingress-app
annotations:
cert-manager.io/issuer: letsencrypt-production
spec:
ingressClassName: nginx
rules:
- host: es.moysite.ru
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: spa
port:
number: 80
- path: /test
pathType: Prefix
backend:
service:
name: frontend-api
port:
number: 80
tls:
- hosts:
- es.moysite.ru
secretName: test-ingress-app-production
Еще пример
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-socks-tls
annotations:
kubernetes.io/ingress.class: "nginx"
cert-manager.io/cluster-issuer: "letsencrypt"
spec:
rules:
- host: kuber.zeroxzed.ru
http:
paths:
- backend:
serviceName: front-end
servicePort: 80
tls:
- hosts:
- kuber.zeroxzed.ru
secretName: socks-tls-2
:!: Еще про TLS
[[https://stackoverflow-com.translate.goog/questions/54459015/how-to-configure-ingress-to-direct-traffic-to-an-https-backend-using-https?_x_tr_sl=en&_x_tr_tl=ru&_x_tr_hl=ru&_x_tr_pto=sc|Хорошая инфа по этому поводу]]\\
Конфигурация с расшифровкой TLS на Ingress контроллере
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-name
namespace: namespace-name
annotations:
# это хз, возможно не обязательно
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
spec:
# блок, задающий терминацию на ингрессе, далее трафик не шифрованный, внутри кластера
tls:
- hosts:
- app.myorg.com
secretName: tls-secret
rules:
- http:
paths:
- backend:
serviceName: service
servicePort: 9443
path: /carbon
- backend:
serviceName: service2
servicePort: 9443
path: /oauth
тоже самое, только пересылка шифрованного трафика, расшифровка в приложении
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-name
namespace: namespace-name
annotations:
nginx.ingress.kubernetes.io/ssl-passthrough: "true"
spec:
rules:
- http:
paths:
- backend:
serviceName: service
servicePort: 9443
path: /carbon
- backend:
serviceName: service2
servicePort: 9443
path: /oauth
:!: