В предыдущем посте – Istio: обзор и запуск service mesh в Kubernetes – запустили Istio в AWS Elastic Kubernetes Service, познакомились с основными компонентами.
Следующая задача – добавить AWS Application Load Balancer (ALB) перед Istio Inrgress Gateway, так как Istio Gateway Service с типом LoadBalancer создаёт AWS Classic LoadBalancer, к которому можно подключить только один SSL-сертификат из Amazon Certificate Manager.
Сейчас наша схема прохождения трафика следующая:
- в Helm-чарте каждого приложения описываются Ingress и Service, этот Ingress создаёт AWS Application LoadBalancer с SSL
- пакет с ALB попадает на Service приложения
- через Service приложения – в под с приложением
Теперь добавим сюда Istio.
Идея такая:
- устанавливаем Istio, он создаёт Istio Ingress Gateway – Service и Pod
- в Helm-чарте приложения описываются Ingress, Service плюс Gateway и VirtualService
- Ingress приложения создаёт ALB, тут продолжаем выполнять SSL termination – дальше трафик внутри кластера идёт обычным HTTP
- пакет с ALB попадает на под с Istio Ingress Gateway
- с Istio Ingress Gateway в соответствии с настройками Envoy, описанными в Gateway и VirtualService приложения, пакет идёт на Service приложения
- через Service приложения – попадает в под с приложением
Для этого нам надо:
- описать Ingress, который создаст ALB
- обновить Service для Istio Ingress Gateway – вместо типа LoadBalancer задать ему тип NodePort, что бы AWS ALB Ingress Controller создал TargetGroup, на которую будет слать трафик ALB
- задеплоить тестовое приложение с обычным Service
- для тестового приложения описать Gateway и VirtualService, которые настроят Envoy нашего Istio Ingress Gateway на роутинг трафика к Сервису приложения
Поехали.
Содержание
Обновление Istio Ingress Gateway
Istio у нас уже установлен в предыдущей статье – тут уже есть Istio Ingress Gateway сервисом с типом LoadBalancer:
[simterm]
$ kubectl -n istio-system get svc istio-ingressgateway NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE istio-ingressgateway LoadBalancer 172.20.112.213 a6f***599.eu-west-3.elb.amazonaws.com 15021:30218/TCP,80:31246/TCP,443:30486/TCP,15012:32445/TCP,15443:30154/TCP 25h
[/simterm]
Теперь надо его перенастроить – изменить тип Service на NodePort – обновляем через istioctl
и --set
:
[simterm]
$ istioctl install --set profile=default --set values.gateways.istio-ingressgateway.type=NodePort -y ✔ Istio core installed ✔ Istiod installed ✔ Ingress gateways installed ✔ Installation complete
[/simterm]
Проверяем Service:
[simterm]
$ kubectl -n istio-system get svc istio-ingressgateway NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE istio-ingressgateway NodePort 172.20.112.213 <none> 15021:30218/TCP,80:31246/TCP,443:30486/TCP,15012:32445/TCP,15443:30154/TCP 25h
[/simterm]
NodePort, хорошо – тут готово.
Istio Ingress Gateway и AWS Application LoadBalancer healthchecks
Дальше возникает вопрос – как выполнять Health checks на AWS Application LoadBalancer, ведь у Istio Ingress Gateway используются разные порты – 80 для входящего трафика, и 15021 для проверки статуса?
Если в Ingress указать аннотацию alb.ingress.kubernetes.io/healthcheck-port
– то ALB Ingress Controller вообще игнорирует этот Ingress, и хоть бы что-то в логах написал! Нет, ничего – полная тишина и игнор. Ingress создаётся, а AWS LoadBalancer – нет.
Решение нагуглилось на Github – Health Checks do not work if using multiple pods on routes: выносим аннотации, связанные с healthcheck, в Service самого Istio Gateway.
Редактируем Service istio-ingressgateway
:
[simterm]
$ kubectl -n istio-system edit svc istio-ingressgateway
[/simterm]
В spec.ports
находим status-port
и его nodePort
:
... spec: clusterIP: 172.20.112.213 externalTrafficPolicy: Cluster ports: - name: status-port nodePort: 30218 port: 15021 protocol: TCP targetPort: 15021 ...
Для указания alb.ingress.kubernetes.io/alb.ingress.kubernetes.io/healthcheck-path
– берём readinessProbe
из Deployment-а, который создаёт поды с istio-ingressgateway
:
[simterm]
$ kubectl -n istio-system get deploy istio-ingressgateway -o yaml ... readinessProbe: failureThreshold: 30 httpGet: path: /healthz/ready ...
[/simterm]
Задаём аннотации для istio-ingressgateway
Service – в healthchek-port
указываем nodePort
из status-port
, а в healthcheck-path
– путь из readinessProbe
:
... alb.ingress.kubernetes.io/healthcheck-path: /healthz/ready alb.ingress.kubernetes.io/healthcheck-port: "30218" ...
При создании Ingress – ALB Ingress контроллер найдёт сервис, указанный в backend.serviceName
Ingress-манифеста, оттуда считает аннотации, и применит их к TargetGroup, которая будет создана для ALB.
При деплое с Helm – аннотации можно будет указать через values.gateways.istio-ingressgateway.serviceAnnotations
.
Создание Ingress и AWS Application LoadBalancer
Добавляем Ingress – это будет основной LoadBalancer приложения, на котором мы выполняем SSL termination.
В нём указываем ARN SSL-сертификата из AWS Certificate Manager. Ingress создаём в istio-system
namespace, так как ему нужен доступ к istio-ingressgateway
Service.
--- apiVersion: extensions/v1beta1 kind: Ingress metadata: name: test-alb namespace: istio-system annotations: # create AWS Application LoadBalancer kubernetes.io/ingress.class: alb # external type alb.ingress.kubernetes.io/scheme: internet-facing # AWS Certificate Manager certificate's ARN alb.ingress.kubernetes.io/certificate-arn: "arn:aws:acm:eu-west-3:***:certificate/fcaa9fd2-1b55-48d7-92f2-e829f7bafafd" # open ports 80 and 443 alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]' # redirect all HTTP to HTTPS alb.ingress.kubernetes.io/actions.ssl-redirect: '{"Type": "redirect", "RedirectConfig": { "Protocol": "HTTPS", "Port": "443", "StatusCode": "HTTP_301"}}' # ExternalDNS settings: https://rtfm.co.ua/en/kubernetes-update-aws-route53-dns-from-an-ingress/ external-dns.alpha.kubernetes.io/hostname: "istio-test-alb.example.com" spec: rules: - http: paths: - path: /* backend: serviceName: ssl-redirect servicePort: use-annotation - path: /* backend: serviceName: istio-ingressgateway servicePort: 80
Деплоим его:
[simterm]
$ kubectl apply -f test-ingress.yaml ingress.extensions/test-alb created
[/simterm]
Проверяем Ingress в неймспейсе istio-system
:
[simterm]
$ kubectl -n istio-system get ingress NAME CLASS HOSTS ADDRESS PORTS AGE test-alb <none> * cc2***514.eu-west-3.elb.amazonaws.com 80 2m49s
[/simterm]
AWS ALB создан:
В Health checks его TargetGroup есть наш порт и URI:
И таргеты здоровы:
Проверяем по домену – он был создан автоматически по аннотации external-dns.alpha.kubernetes.io/hostname
в Ingress, см. Kubernetes: обновление DNS в Route53 при создании Ingress:
[simterm]
$ curl -I https://istio-test-alb.example.com HTTP/2 502 server: awselb/2.0
[/simterm]
Отлично – за Istio пока нет приложения, потому роутить трафик некуда, более того – на поде Ingress Gateway нет даже листенера на порту 80, т.е. Envoy-прокси его не слушает:
[simterm]
$ istioctl proxy-config listeners -n istio-system istio-ingressgateway-d45fb4b48-jsz9z ADDRESS PORT MATCH DESTINATION 0.0.0.0 15021 ALL Inline Route: /healthz/ready* 0.0.0.0 15090 ALL Inline Route: /stats/prometheus*
[/simterm]
А вот 15021 открыт, поэтому healh-checks с ALB проходят.
Тестовое приложение
Описываем стандартное тестовое приложение – отдельный неймспейс, два пода с nginxdemos/hello
, и перед ними Service:
--- apiVersion: v1 kind: Namespace metadata: name: test-ns labels: istio-injection: enabled --- apiVersion: apps/v1 kind: Deployment metadata: name: test-deploy namespace: test-ns labels: app: test-app version: v1 spec: replicas: 2 selector: matchLabels: app: test-app template: metadata: labels: app: test-app version: v1 spec: containers: - name: web image: nginxdemos/hello ports: - containerPort: 80 resources: requests: cpu: 100m memory: 100Mi readinessProbe: httpGet: path: / port: 80 nodeSelector: role: common-workers --- apiVersion: v1 kind: Service metadata: name: test-svc namespace: test-namespace spec: selector: app: test-app ports: - name: http protocol: TCP port: 80 targetPort: 80
Деплоим:
[simterm]
$ kubectl apply -f test-ingress.yaml ingress.extensions/test-alb configured namespace/test-ns created deployment.apps/test-deploy created service/test-svc created
[/simterm]
Но ALB всё-ещё отдаёт ошибку 502, т.к. Istio Ingress Gateway пока не настроен.
Istio Gateway
Описываем Gateway и VirtualService.
В Gateway указываем порт, который будем слушать – 80, и Istio Ingress, который настраиваем – ingressgateway
. В hosts
описываем наш тестовый домен:
--- apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: test-gateway namespace: test-ns spec: selector: istio: ingressgateway servers: - port: number: 80 name: http protocol: HTTP hosts: - "istio-test-alb.example.com"
Деплоим:
[simterm]
$ kubectl apply -f test-ingress.yaml ingress.extensions/test-alb configured namespace/test-ns unchanged deployment.apps/test-deploy configured service/test-svc unchanged gateway.networking.istio.io/test-gateway created
[/simterm]
Проверяем listeners
нашего Istio Ingress Gateway ещё раз:
[simterm]
$ istioctl proxy-config listeners -n istio-system istio-ingressgateway-d45fb4b48-jsz9z ADDRESS PORT MATCH DESTINATION 0.0.0.0 8080 ALL Route: http.80 0.0.0.0 15021 ALL Inline Route: /healthz/ready* 0.0.0.0 15090 ALL Inline Route: /stats/prometheus*
[/simterm]
Порт 80 появился, но маршрут ведёт в никуда:
[simterm]
$ istioctl proxy-config routes -n istio-system istio-ingressgateway-d45fb4b48-jsz9z NOTE: This output only contains routes loaded via RDS. NAME DOMAINS MATCH VIRTUAL SERVICE http.80 * /* 404 * /healthz/ready* * /stats/prometheus*
[/simterm]
И обращение по домену нам сейчас выдаёт 404, но уже не от awselb/2.0
, а от istio-envoy
, так как пакет теперь доходит до пода с Ingress Gateway:
[simterm]
$ curl -I https://istio-test-alb.example.com HTTP/2 404 date: Fri, 26 Mar 2021 11:02:57 GMT server: istio-envoy
[/simterm]
Istio VirtualService
В VirtualService описываем Gateway, к корому роуты будут применены, и сам роут – шлём трафик на Service нашего тестового приложения:
--- apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: test-virtualservice namespace: test-ns spec: hosts: - "istio-test-alb.example.com" gateways: - test-gateway http: - match: - uri: prefix: / route: - destination: host: test-svc port: number: 80
Деплоим, проверяем роуты Istio Ingress Gateway ещё раз:
[simterm]
$ istioctl proxy-config routes -n istio-system istio-ingressgateway-d45fb4b48-jsz9z NOTE: This output only contains routes loaded via RDS. NAME DOMAINS MATCH VIRTUAL SERVICE http.80 istio-test-alb.example.com /* test-virtualservice.test-ns
[/simterm]
Теперь появился маршрут к тестовому сервису, а от него – к тестовым подам:
[simterm]
$ curl -I https://istio-test-alb.example.com HTTP/2 200 date: Fri, 26 Mar 2021 11:06:52 GMT content-type: text/html server: istio-envoy
[/simterm]
Готово