Продолжаем баловаться с Istio.
Предыдущие части:
Кроме Istio, мы будем настраивать ExternalDNS, см. Kubernetes: обновление DNS в Route53 при создании Ingress.
Всё описанное ниже пока в статусе Proof of Concept, и деплоится на единый Dev-кластер AWS Elastic Kubernetes Service.
Содержание
Цели
Сейчас есть три задачи:
- протестировать, как будет работать один AWS Application LoadBalancer (AWS ALB) и Istio Ingress Gateway с приложениями в различных неймспейсах
- создать Helm-чарт, в шаблонах которого будет возможность выборочной установки и настройки Ingress, Istio Gateway и Istio VirtualService
- настроить ExternalDNS на создание записей в Route53 при создании/обновлении Istio Gateway или VirtualService
Сначала посмотрим, как Istio Ingress Gateway будет работать с приложениями из разный неймспейсов – создадим Ingress, который создаст AWS Application LoadBalancer, плюс два приложения – каждое со своим Service, Gateway и VirtualService.
В Ingress/AWS ALB описываем хосты, и создаём домены через ExternalDNS, плюс выполняем SSL termination – сюда будем подключать сертификаты.
В Gateway каждого приложения – открываем порты на Istio Ingress Gateway, плюс описываем хосты, для которых принимает трафик этот Gateway.
Общий Ingress создаём в istio-system
namespace, так как ему нужен доступ к Istio Ingress Service.
Gateway можно было бы создать один общий для всех приложений, но в конце дискуссии тут>>> говорят, что более правильным подходом будет создавать отдельный Gateway на каждое приложение, т.е. в отдельных неймспейсах. Выглядит логично – так и сделаем.
Давайте попробуем – потом посмотрим, как это взлетит.
Значит, делаем:
- отдельный Ingress/AWS ALB с двумя тестовыми доменами в неймспейсе
istio-system
- app1 и app2 в неймспейсах
backend-app-1-ns
иbackend-app-2-ns
соответственно: у каждого свой Deployment, Service, Gateway и VirtualService
Вторая задача будет интереснее – создать шаблоны Helm для деплоя приложений на разные окружения с разными настройками Ingress.
В третьей задаче – настроим ExternalDNS на работу с Istio Gateway и VirtualService.
Местами получилось достаточно запутано, особенно во второй и третьей частях, про Helm и ExternalDNS, но постарался описать всё максимально доступно (хотя пока это всё сетапилось – поломать голову пришлось).
Общий Ingress/AWS ALB
Создаём файл манифеста для Ingress/ALB – common-ingress-gateway.yaml
:
--- apiVersion: extensions/v1beta1 kind: Ingress metadata: name: backend-common-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:us-east-2:534***385:certificate/db886018-c173-43d0-b1be-b940de71f2a2" # 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: "app1-test-common-ingress.example.com, app2-test-common-ingress.example.com" spec: rules: - http: paths: - path: /* backend: serviceName: ssl-redirect servicePort: use-annotation - path: /* backend: serviceName: istio-ingressgateway servicePort: 80
Тут в аннотациях задаём имена доменов, которые создаст ExternalDNS и привяжет их к URL создаваемого AWS ALB, а в backend
– шлём весь трафик на Service istio-ingressgateway
.
Деплоим:
[simterm]
$ kubectl apply -f common-ingress-gateway.yaml ingress.extensions/backend-common-alb created
[/simterm]
Проверяем Ingress/ALB:
[simterm]
$ kubectl -n istio-system get ingress backend-common-alb NAME CLASS HOSTS ADDRESS PORTS AGE backend-common-alb <none> * aadca942-***1826302788.us-east-2.elb.amazonaws.com 80 72s
[/simterm]
Проверяем ExternalDNS:
[simterm]
... time="2021-04-12T09:45:02Z" level=info msg="Desired change: CREATE app-1-test-common-ingress.example.com A [Id: /hostedzone/Z30KLN6M3D0LB6]" time="2021-04-12T09:45:02Z" level=info msg="Desired change: CREATE app-1-test-common-ingress.example.com TXT [Id: /hostedzone/Z30KLN6M3D0LB6]" time="2021-04-12T09:45:02Z" level=info msg="Desired change: CREATE app-2-test-common-ingress.example.com A [Id: /hostedzone/Z30KLN6M3D0LB6]" time="2021-04-12T09:45:02Z" level=info msg="Desired change: CREATE app-2-test-common-ingress.example.com TXT [Id: /hostedzone/Z30KLN6M3D0LB6]" time="2021-04-12T09:45:03Z" level=info msg="4 record(s) in zone example.com. [Id: /hostedzone/Z30KLN6M3D0LB6] were successfully updated" ...
[/simterm]
Пробуем URL – должен быть ответ с ошибкой, так как сам Istio Ingress Gateway ещё не настроен:
[simterm]
$ curl -I https://app-1-test-common-ingress.example.com HTTP/2 502 date: Mon, 12 Apr 2021 09:46:11 GMT server: istio-envoy
[/simterm]
Всё верно.
На Istio Ingress Gateway у нас пока нет роутов:
[simterm]
$ istioctl proxy-config routes -n istio-system istio-ingressgateway-8459df68cb-bh76b NOTE: This output only contains routes loaded via RDS. NAME DOMAINS MATCH VIRTUAL SERVICE * /healthz/ready* * /stats/prometheus*
[/simterm]
Добавим тестовые приложения, в которых опишем настройки для Istio Ingress Gateway.
Тестовые приложения
Тестовые приложения одинаковы кроме неймспейсов и имён приложений/сервисов.
Тут создаём ресурсы:
- Namespace
- Deployment
- Service
- Gateway
- VirtualService
Получается такой манифест:
--- apiVersion: v1 kind: Namespace metadata: name: backend-app-1-ns labels: istio-injection: enabled --- apiVersion: apps/v1 kind: Deployment metadata: name: backend-app-1-deploy namespace: backend-app-1-ns labels: app: backend-app-1 version: v1 spec: replicas: 2 selector: matchLabels: app: backend-app-1 template: metadata: labels: app: backend-app-1 version: v1 spec: containers: - name: app1 image: nginxdemos/hello ports: - containerPort: 80 resources: requests: cpu: 100m memory: 100Mi readinessProbe: httpGet: path: / port: 80 --- apiVersion: v1 kind: Service metadata: name: backend-app-1-servcie namespace: backend-app-1-ns spec: selector: app: backend-app-1 ports: - name: http protocol: TCP port: 80 --- apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: backend-app-1-gateway namespace: backend-app-1-ns spec: selector: istio: ingressgateway servers: - port: number: 80 name: http protocol: HTTP hosts: - "app-1-test-common-ingress.example.com" --- apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: backend-app-1-virtualservice namespace: backend-app-1-ns spec: hosts: - "app-1-test-common-ingress.example.com" gateways: - backend-app-1-gateway http: - match: - uri: prefix: / route: - destination: host: backend-app-1-servcie port: number: 80
Применяем:
[simterm]
$ kubectl apply -f app1.yaml namespace/backend-app-1-ns created deployment.apps/backend-app-1-deploy created service/backend-app-1-servcie created gateway.networking.istio.io/backend-app-1-gateway created virtualservice.networking.istio.io/backend-app-1-virtualservice created
[/simterm]
Копируем манифест, меняем app-1 на app-2, деплоим:
[simterm]
$ kubectl apply -f app2.yaml namespace/backend-app-2-ns created deployment.apps/backend-app-2-deploy created service/backend-app-2-servcie created gateway.networking.istio.io/backend-app-2-gateway created virtualservice.networking.istio.io/backend-app-2-virtualservice created
[/simterm]
Проверяем роуты Istio ещё раз:
[simterm]
$ istioctl proxy-config routes -n istio-system istio-ingressgateway-8459df68cb-bh76b NOTE: This output only contains routes loaded via RDS. NAME DOMAINS MATCH VIRTUAL SERVICE http.80 app-1-test-common-ingress.example.com /* backend-app-1-virtualservice.backend-app-1-ns http.80 app-2-test-common-ingress.example.com /* backend-app-2-virtualservice.backend-app-2-ns
[/simterm]
И пробуем обратиться по имени хоста приложения app-1:
[simterm]
$ curl -I https://app-1-test-common-ingress.example.com HTTP/2 200 date: Mon, 12 Apr 2021 09:52:13 GMT content-type: text/html server: istio-envoy
[/simterm]
Отлично – тут всё работает. Теперь у нас есть один общий AWS ALB, который через Istio Ingress Gateway шлёт трафик на два приложения в разных неймспейсах.
Удаляем ресурсы из неймспейсов, Ingress с общим LoadBalancer оставляем для следующих тестов:
[simterm]
$ kubectl delete -f app1.yaml $ kubectl delete -f app2.yaml
[/simterm]
Переходим к Helm.
Istio: общий Ingress/AWS ALB и Helm
Планирование: условия
Следующая задача несколько нетривиальна: у нас есть Dev EKS кластер, есть Production EKS кластер.
На Dev-кластере Istio уже есть, тестируется, но не для всех приложений. На Production – Istio ещё нет, но будет.
Кроме того – сейчас все приложения, каждое в своём Namespace, создают свой отдельный Ingress/AWS ALB.
На Dev-кластере от этого подхода хочется избавиться, и трафик ко всем приложениям слать через Istio Ingress Gateway, а на Production – оставить, как есть, т.е. у каждого приложения свой ALB, но все Ingress/ALB в будущем будут слать трафик через Istio Ingress Gateway.
Но при этом и на Dev, и на Production приложение может уже использовать Istio – или ещё нет, так как всё ещё в процессе внедрения-тестирования, и какое-то время настройки приложений будут отличаться, а потому надо создать такой шаблон для Helm, в котором можно будет переопределять настройки Ingress.
Т.е. во-первых – надо определить, создаёт ли чарт свой собственный Ingress – или использует общий? Во-вторых, если создаёт, то какой бекенд использует – Istio Ingress Gateway, или обычный Service самого приложния? И если не создаёт, а использует Istio Ingress Gateway – то из чарта приложения создавать Gateway и VirtualService.
Следовательно, наш шаблон должен поддерживать три схемы деплоя приложения:
- используется общий LoadBalancer и Istio: Istio Ingress Gateway LoadBalancer и бекенд Istio Ingress Gateway Service
- отдельный LoadBalancer приложения и Istio Ingress Gateway: Ingress приложения и бекенд Istio Ingress Gateway Service
- отдельный LoadBalancer приложения и Istio не используется вообще: отдельный Ingress приложения и бекенд Service приложения
Значит:
- Ingress общий – или свой?
- если общий – то свой Ingress не создаём
- если свой – то создаём, но с возможностью выбора бекенда – Istio Ingress Service, или обычный Service приложения
- бекенд для Ингресса – Istio, или Service приложения?
- если Istio – то указываем serviceName: istio-ingressgateway, плюс создаём Gateway и VirtualService
- если Service приложения – то указываем serviceName: <APPNAME>-service, а Gateway и VirtualService не создаём
Для решения используем values.yaml
– у нас он у Dev свой, у Prod – свой.
В них определим два параметра – istio.enabled
и ingress.enabled
.
Это даст возможность для Dev-кластера, например, указать ingress.enabled=false
, и не создавать Ingress, но указать istio.enabled
– и создать Gateway и VirtualService, на которые будет слать трафик наш общий Ingress/ALB из нейспейса istio-system. А на Production можно будет указать ingress.enabled=true
и istio.enabled=false
, и тогда деплой будет выполняться по старой схеме. А когда там появится Istio – зададим значения ingress.enabled=true
и istio.enabled=true
, и создадим отдельный LoadBalancer, который будет слать трафик через Istio Ingress Gateway.
Попробуем.
Создание Helm шаблона
Генерируем тестовый чарт:
[simterm]
$ helm create app-1 Creating app-1
[/simterm]
Создаём каталоги для values.yaml
Dev-а и Production-а:
[simterm]
$ mkdir -p app-1/env/{dev,prod}
[/simterm]
Удаляем шаблоны и дефолтные values – напишем свой велосипед:
[simterm]
$ rm -r app-1/templates/* app-1/values.yaml
[/simterm]
Пишем файлы конфигов:
[simterm]
$ vim -p app-1/env/dev/values.yaml app-1/env/prod/values.yaml
[/simterm]
Dev – app-1/env/dev/values.yaml
:
appConfig: name: "backend-app-1" version: "v1" url: "dev-app-1-test-common-ingress.example.com" istio: enabled: true ingress: enabled: false
Ingress не создаём, Gateway и VirtualService создаём.
Production – app-1/env/prod/values.yaml
:
appConfig: name: "backend-app-1" version: "v1" url: "prod-app-1-test-common-ingress.example.com" istio: enabled: false ingress: enabled: true
Тут Ingress создаём в виде отдельного ALB, но ресурсы Istio не создаём – Ingress будет слать трафик прямо на Service приложения.
Пишем файлы шаблонов:
[simterm]
$ vim -p app-1/templates/ingress.yaml app-1/templates/service.yaml app-1/templates/deployment.yaml
[/simterm]
Тут все ресурсы (кроме Ingress, см. ниже) описываем без Namespace – он будет создан Helm во время деплоя.
Deployment
Без изменений, только часть параметров берём из values:
--- apiVersion: apps/v1 kind: Deployment metadata: name: {{ .Values.appConfig.name }}-deploy labels: app: {{ .Values.appConfig.name }} version: {{ .Values.appConfig.version }} spec: replicas: 2 selector: matchLabels: app: {{ .Values.appConfig.name }} template: metadata: labels: app: {{ .Values.appConfig.name }} version: {{ .Values.appConfig.version }} spec: containers: - name: web-app image: nginxdemos/hello ports: - containerPort: 80 resources: requests: cpu: 100m memory: 100Mi readinessProbe: httpGet: path: / port: 80
Service, VirtualService, Gateway
Тут мы всегда создаём обычный Service, в котором выполняем проверку: если ingress.enabled
– то задаём тип NodePort, что бы LoadBalancer мог слать трафик на WorkerNode. Если нет – то используем дефолтный ClusterIP, что бы пакет не проходил через дополнительный роутинг, а шёл прямо на WorkerNode, на которой расположен под (категорически советую почитать Kubernetes: Service, балансировка нагрузки, kube-proxy и iptables):
--- apiVersion: v1 kind: Service metadata: name: {{ .Values.appConfig.name }}-service spec: {{- if .Values.ingress.enabled }} type: NodePort {{- end }} selector: app: {{ .Values.appConfig.name }} ports: - name: http protocol: TCP port: 80
Далее, проверяем условие istio.enabled
– если true, то добавляем создание ресурсов Gateway и VirtualService:
{{- if .Values.istio.enabled }} --- apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: {{ .Values.appConfig.name }}-gateway spec: selector: istio: ingressgateway servers: - port: number: 80 name: http protocol: HTTP hosts: - {{ .Values.appConfig.url }} --- apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: {{ .Values.appConfig.name }}-virtualservice spec: hosts: - {{ .Values.appConfig.url }} gateways: - {{ .Values.appConfig.name }}-gateway http: - match: - uri: prefix: / route: - destination: host: {{ .Values.appConfig.name }}-service port: number: 80 {{- end }}
Ingress
Для Ingress в условии ingress.enabled
сначала проверяем – создавать ли его, и если создаётся – то проверяем в каком неймспейсе создавать Ingress, так как при использовании Istio этот Ingress должен создаваться в неймспейсе istio-system
, а если это “обычный” Ingress – то в неймспейсе приложения.
Ниже в условии istio.enabled
определяем куда он будет слать трафик – на Istio, или обычный Service приложения:
{{- if .Values.ingress.enabled }} --- apiVersion: extensions/v1beta1 kind: Ingress metadata: name: {{ .Values.appConfig.name }}-alb {{- if .Values.istio.enabled }} namespace: istio-system {{- end }} annotations: kubernetes.io/ingress.class: alb alb.ingress.kubernetes.io/scheme: internet-facing alb.ingress.kubernetes.io/certificate-arn: "arn:aws:acm:us-east-2:534***385:certificate/db886018-c173-43d0-b1be-b940de71f2a2" alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]' alb.ingress.kubernetes.io/actions.ssl-redirect: '{"Type": "redirect", "RedirectConfig": { "Protocol": "HTTPS", "Port": "443", "StatusCode": "HTTP_301"}}' external-dns.alpha.kubernetes.io/hostname: {{ .Values.appConfig.url }} spec: rules: - http: paths: - path: /* backend: serviceName: ssl-redirect servicePort: use-annotation - path: /* backend: {{- if .Values.istio.enabled }} serviceName: istio-ingressgateway {{ else }} serviceName: {{ .Values.appConfig.name }}-service {{- end }} servicePort: 80 {{- end }}
Проверяем Dev с --debug
и --dry-run
– Ingress быть не должно, но Gateway и VirtualService – должны:
[simterm]
$ helm upgrade --install backend-app-1 --namespace dev-backend-app-1-ns --create-namespace -f app-1/env/dev/values.yaml app-1/ --debug --dry-run
[/simterm]
Если ошибок нет – деплоим:
[simterm]
$ helm upgrade --install backend-app-1 --namespace dev-backend-app-1-ns --create-namespace -f app-1/env/dev/values.yaml app-1/ Release "backend-app-1" does not exist. Installing it now. NAME: backend-app-1 LAST DEPLOYED: Mon Apr 12 18:03:08 2021 NAMESPACE: dev-backend-app-1-ns STATUS: deployed REVISION: 1 TEST SUITE: None
[/simterm]
Проверяем Ingress в namespace dev-backend-app-1-ns
:
[simterm]
$ kubectl -n dev-backend-app-1-ns get ingress No resources found.
[/simterm]
А Gateway и VirtualServce – есть:
[simterm]
$ kubectl -n dev-backend-app-1-ns get gateway NAME AGE backend-app-1-gateway 38s $ kubectl -n dev-backend-app-1-ns get virtualservice NAME GATEWAYS HOSTS AGE backend-app-1-virtualservice [backend-app-1-gateway] [dev-app-1-test-common-ingress.example.com] 65s
[/simterm]
Деплоим Production:
[simterm]
$ helm upgrade --install backend-app-1 --namespace prod-backend-app-1-ns --create-namespace -f app-1/env/prod/values.yaml app-1/
[/simterm]
Проверяем Ingress в неймспейсе prod-backend-app-1-ns
, так как Istio мы тут не используем:
[simterm]
$ kubectl -n prod-backend-app-1-ns get ingress NAME CLASS HOSTS ADDRESS PORTS AGE backend-app-1-alb <none> * aadca942-***-49478225.us-east-2.elb.amazonaws.com 80 4m54s
[/simterm]
Для Dev-окружения обновляем Ingress, созданный в самом начале – добавляем external-dns.alpha.kubernetes.io/hostname
, что бы ExternalDNS его создал и направил на этот LoadBalancer:
... # ExternalDNS settings: https://rtfm.co.ua/en/kubernetes-update-aws-route53-dns-from-an-ingress/ external-dns.alpha.kubernetes.io/hostname: "dev-app-1-test-common-ingress.example.com" ...
Применяем:
[simterm]
$ kubectl apply -f common-ingress-gateway.yaml ingress.extensions/backend-common-alb configured
[/simterm]
Посмотрим роуты Istio Ingress Gateway – сейчас тут должен быть только Dev:
[simterm]
$ istioctl proxy-config routes -n istio-system istio-ingressgateway-8459df68cb-bh76b --name http.80 NOTE: This output only contains routes loaded via RDS. NAME DOMAINS MATCH VIRTUAL SERVICE http.80 dev-app-1-test-common-ingress.example.com /* backend-app-1-virtualservice.dev-backend-app-1-ns
[/simterm]
Или так:
[simterm]
$ istioctl proxy-config routes -n istio-system istio-ingressgateway-8459df68cb-bh76b --name http.80 -o json | jq '.[].virtualHosts[].domains[0], .[].virtualHosts[].routes[].route.cluster' "dev-app-1-test-common-ingress.example.com" "outbound|80||backend-app-1-service.dev-backend-app-1-ns.svc.cluster.local"
[/simterm]
И пробуем обратиться к приложениям.
Прод:
[simterm]
$ curl -I https://prod-app-1-test-common-ingress.example.com HTTP/2 200 date: Tue, 13 Apr 2021 12:47:15 GMT content-type: text/html server: nginx/1.13.8
[/simterm]
server: nginx/1.13.8
– ответ от NGINX, трафик прошёл через LoadBalancer напрямую на Service приложения и к его поду.
Dev:
[simterm]
$ curl -I https://dev-app-1-test-common-ingress.example.com HTTP/2 200 date: Tue, 13 Apr 2021 12:47:18 GMT content-type: text/html server: istio-envoy
[/simterm]
server: istio-envoy
– трафик пошёл через общий LoadBalancer, потом на Istio Ingress Gateway, потом на sidecar-контейнер с Envoy в поде с приложением.
И проверяем третью схему – оставляем отдельный Ingress, но включаем работу через Istio.
В app-1/env/prod/values.yaml
меняем istio.enabled
на true, ingress.enabled
у нас и так в true:
appConfig: name: "backend-app-1" version: "v1" url: "prod-app-1-test-common-ingress.example.com" istio: enabled: true ingress: enabled: true
Обновляем:
[simterm]
$ helm upgrade --install backend-app-1 --namespace prod-backend-app-1-ns --create-namespace -f app-1/env/prod/values.yaml app-1/
[/simterm]
Проверяем routes Istio Ingress Gateway ещё раз:
[simterm]
$ istioctl proxy-config routes -n istio-system istio-ingressgateway-8459df68cb-bh76b --name http.80 NOTE: This output only contains routes loaded via RDS. NAME DOMAINS MATCH VIRTUAL SERVICE http.80 dev-app-1-test-common-ingress.example.com /* backend-app-1-virtualservice.dev-backend-app-1-ns http.80 prod-app-1-test-common-ingress.example.com /* backend-app-1-virtualservice.prod-backend-app-1-ns
[/simterm]
Да – появился маршрут к бекенду Production.
Проверяем Ingress в неймспейсе prod-backend-app-1-ns
:
[simterm]
$ kubectl -n prod-backend-app-1-ns get ingress backend-app-1-alb Error from server (NotFound): ingresses.extensions "backend-app-1-alb" not found
[/simterm]
И в istio-system
:
[simterm]
$ kubectl -n istio-system get ingress backend-app-1-alb NAME CLASS HOSTS ADDRESS PORTS AGE backend-app-1-alb <none> * aadca942-***-1554475105.us-east-2.elb.amazonaws.com 80 8m52s
[/simterm]
Пробуем curl
:
[simterm]
$ curl -I https://prod-app-1-test-common-ingress.example.com HTTP/2 200 date: Tue, 13 Apr 2021 13:14:34 GMT content-type: text/html server: istio-envoy
[/simterm]
server: istio-envoy
– трафик пошёл через Istio.
Istio и ExternalDNS
И последний нюанс – ExternalDNS.
Сейчас при использовании общего LoadBalancer мы указываем URL в аннотациях этого Ingress, при этом делой приложения из Helm-чарта не будет затрагивать этот Ingress – общий Ingress у нас создаётся из отдельного файла common-ingress-gateway.yaml
.
Соответственно, что бы создавать DNS-записи при деплое новых приложений – придётся обновлять ещё и аннотации общего Ingress/ALB – двойная работа, дополнительная автоматизация.
Вместо этого, можем настроить ExternalDNS так, что бы он получал информацию не только с ресурсов Ingress, но и с самого Istio.
Обновляем его деплой, добавляем --source=istio-gateway
и/или --source=istio-virtualservice
, см. документацию тут>>>:
... containers: - args: - --log-level=info - --log-format=text - --events - --policy=upsert-only - --provider=aws - --registry=txt - --interval=2m - --source=service - --source=ingress - --source=istio-gateway - --source=istio-virtualservice ...
Из шаблона общего Ingress common-ingress-gateway.yaml
удаляем:
... external-dns.alpha.kubernetes.io/hostname: "dev-app-1-test-common-ingress.example.com" ...
Теперь имя хоста у нас будет задаваться только в Gateway и VirtualService из их полей spec.servers.hosts
для Gateway или spec.hosts
для VirtualService.
Что бы ExternalDNS мог получать данные от Istio – проверяем ClusterRole external-dns
:
... - apiGroups: - networking.istio.io resources: - virtualservices - gateways verbs: - get - list - watch ...
ExternalDNS не создаёт записи для Istio Gateway и VirtualService: “No endpoints could be generated”
Но тут возникла проблема.
Включаем --debug
в ExternalDNS, смотрим за логами:
[simterm]
... time="2021-04-14T12:53:05Z" level=debug msg="Adding event handler for Istio VirtualService" time="2021-04-14T12:53:05Z" level=debug msg="Adding event handler for Istio Gateway" time="2021-04-14T12:53:05Z" level=debug msg="Adding event handler for service" time="2021-04-14T12:53:05Z" level=debug msg="Adding event handler for ingress" ... level=debug msg="No endpoints could be generated from ingress istio-system/backend-common-alb" ...
[/simterm]
Хенделы добавлены, т.е. ExternalDNS обновления Istio видит, но создавать записи не хочет.
Происходит это потому, что Istio Ingress Gateway Service создаётся с типом NodePort, а не дефолтным LoadBalancer, что бы сделать возможной работу с внешним AWS ALB, и ExternalDNS при парсинге VirtualService не может корректно определить ендпоинты для нашего внешнего Ingress, создаваемого из файла common-ingress-gateway.yaml
.
Поэтому, пришлось делать небольшой костыль – в аннотациях создаваемого VirtualService указываем URL ALB явно.
При этом снова-таки помним, что иногда VirtualService создаётся вместе с отдельным Ingress/ALB – тогда костылить в виде аннотиаций непосредственно в VirtualService не надо.
Следовательно, в VirtualService добавляем ещё одно условие – if not .Values.ingress.enabled
:
apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: {{ .Values.appConfig.name }}-virtualservice {{- if not .Values.ingress.enabled }} annotations: external-dns.alpha.kubernetes.io/target: {{ .Values.istio.externalURL }} {{- end }} ...
А значение istio.externalURL
определяем в app-1/env/dev/values.yaml
– оно у нас будет достаточно постоянным, и использоваться будет только на Dev-кластере:
... istio: enabled: true externalURL: "aadca942-istiosystem-backe-3ee2-700661912.us-east-2.elb.amazonaws.com" ...
Полностью Service, Gateway и VirtualService теперь выглядят так:
--- apiVersion: v1 kind: Service metadata: name: {{ .Values.appConfig.name }}-service spec: {{- if .Values.ingress.enabled }} type: NodePort {{- end }} selector: app: {{ .Values.appConfig.name }} ports: - name: http protocol: TCP port: 80 {{- if .Values.istio.enabled }} --- apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: {{ .Values.appConfig.name }}-gateway spec: selector: istio: ingressgateway servers: - port: number: 80 name: http protocol: HTTP hosts: - "*" --- apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: {{ .Values.appConfig.name }}-virtualservice {{- if not .Values.ingress.enabled }} annotations: external-dns.alpha.kubernetes.io/target: {{ .Values.istio.externalURL }} {{- end }} spec: hosts: - {{ .Values.appConfig.url | quote }} gateways: - {{ .Values.appConfig.name }}-gateway http: - match: - uri: prefix: / route: - destination: host: {{ .Values.appConfig.name }}-service port: number: 80 {{- end }}
Деплоим, проверяем логи ExternalDNS:
[simterm]
... time="2021-04-14T13:05:00Z" level=info msg="Desired change: CREATE dev-app-1-test-common-ingress.example.com A [Id: /hostedzone/Z30KLN6M3D0LB6]" time="2021-04-14T13:05:00Z" level=info msg="Desired change: CREATE dev-app-1-test-common-ingress.example.com TXT [Id: /hostedzone/Z30KLN6M3D0LB6]" ...
[/simterm]
И пробуем снова curl
:
[simterm]
$ curl -I https://dev-app-1-test-common-ingress.example.com HTTP/2 200 date: Wed, 14 Apr 2021 13:21:11 GMT content-type: text/html server: istio-envoy
[/simterm]
Готово.