В предыдущем посте — 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]
Готово






