Проксіювання DNS

Окрім перехоплення трафіку застосунків, Istio також може перехоплювати DNS-запити для покращення продуктивності та зручності використання вашої mesh-мережі. При проксіюванні DNS усі DNS-запити з застосунку будуть перенаправлені на sidecar, який зберігає локальне відображення доменних імен на IP-адреси. Якщо запит може бути оброблений sidecar, він безпосередньо поверне відповідь застосунку, уникаючи запиту до upstream DNS-сервера. В іншому випадку запит пересилається на upstream відповідно до стандартної конфігурації DNS з /etc/resolv.conf.

Хоча Kubernetes забезпечує DNS-резолюцію для Kubernetes Serviceів “із коробки”, будь-які користувацькі ServiceEntry не будуть розпізнані. Завдяки цій функції адреси ServiceEntry можуть бути розвʼязані без необхідності налаштування DNS-сервера. Для Kubernetes Serviceів відповідь DNS залишиться такою ж, але зменшиться навантаження на kube-dns і підвищиться продуктивність.

Ця функціональність також доступна для сервісів, що працюють за межами Kubernetes. Це означає, що всі внутрішні сервіси можуть бути розвʼязані без громіздких обхідних рішень для експонування DNS-записів Kubernetes поза межами кластера.

Початок роботи

Ця функція наразі не увімкнена стандартно. Щоб її ввімкнути, встановіть Istio з такими налаштуваннями:

$ cat <<EOF | istioctl install -y -f -
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  meshConfig:
    defaultConfig:
      proxyMetadata:
        # Увімкнути базове проксіювання DNS
        ISTIO_META_DNS_CAPTURE: "true"
EOF

Її також можна ввімкнути на рівні окремих pod за допомогою анотації proxy.istio.io/config:

kind: Deployment
metadata:
  name: curl
spec:
...
  template:
    metadata:
      annotations:
        proxy.istio.io/config: |
          proxyMetadata:
            ISTIO_META_DNS_CAPTURE: "true"
...

Перехоплення DNS в дії

Щоб спробувати перехоплення DNS, спочатку налаштуйте ServiceEntry для деякого зовнішнього сервісу:

$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
  name: external-address
spec:
  addresses:
  - 198.51.100.1
  hosts:
  - address.internal
  ports:
  - name: http
    number: 80
    protocol: HTTP
EOF

Запустіть клієнтський застосунок для ініціації DNS-запиту:

Zip
$ kubectl label namespace default istio-injection=enabled --overwrite
$ kubectl apply -f @samples/curl/curl.yaml@

Без перехоплення DNS запит до address.internal, ймовірно, не буде успішно розвʼязаний. Після ввімкнення цієї функції ви маєте отримати відповідь на основі налаштованої address:

$ kubectl exec deploy/curl -- curl -sS -v address.internal
*   Trying 198.51.100.1:80...

Автоматичний розподіл адрес

У наведеному вище прикладі у вас була заздалегідь визначена IP-адреса для сервісу, на який було надіслано запит. Однак часто виникає необхідність доступу до зовнішніх сервісів, які не мають стабільних адрес і натомість використовують DNS. У цьому випадку DNS-проксі не матиме достатньо інформації для повернення відповіді та має переслати DNS-запит upstream.

Це особливо проблематично з TCP-трафіком. На відміну від HTTP-запитів, які маршрутизуються на основі заголовків Host, TCP містить значно менше інформації; ви можете маршрутизувати лише за IP-адресою призначення і номером порту. Оскільки ви не маєте стабільної IP-адреси для backend, маршрутизація на основі цієї адреси також неможлива, що залишає лише номер порту, що призводить до конфліктів, коли кілька ServiceEntry для TCP-сервісів використовують один і той самий порт. Зверніться до наступного розділу для отримання детальнішої інформації.

Щоб обійти ці проблеми, DNS-проксі додатково підтримує автоматичний розподіл адрес для ServiceEntry, які явно не визначають адресу. Відповідь DNS включатиме унікальну автоматично призначену адресу для кожного ServiceEntry. Проксі налаштовується для відповідності запитам до цієї IP-адреси та пересилає запит до відповідного ServiceEntry. При використанні ISTIO_META_DNS_AUTO_ALLOCATE Istio автоматично розподілятиме нерозвʼязувані VIP (з підмережі Class E) для таких сервісів, якщо вони не використовують узагальнений хост. Агент Istio на sidecar використовуватиме VIP як відповіді на запити DNS-запитів з застосунку. Envoy тепер може чітко розрізняти трафік, спрямований на кожен зовнішній TCP-сервіс, і пересилати його до правильного призначення.

Щоб спробувати це, налаштуйте інший ServiceEntry:

$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
  name: external-auto
spec:
  hosts:
  - auto.internal
  ports:
  - name: http
    number: 80
    protocol: HTTP
  resolution: DNS
EOF

Тепер надішліть запит:

$ kubectl exec deploy/curl -- curl -sS -v auto.internal
*   Trying 240.240.0.1:80...

Як бачите, запит було надіслано на автоматично призначену адресу, 240.240.0.1. Ці адреси будуть вибиратися з зарезервованого діапазону IP-адрес 240.240.0.0/16, щоб уникнути конфліктів із реальними сервісами.

Користувачі також можуть гнучко налаштувати більш детальну конфігурацію, додавши мітку networking.istio.io/enable-autoallocate-ip="true/false" для свого ServiceEntry. Ця мітка визначає, чи має ServiceEntry, для якого не вказано жодної spec.addresses, автоматично отримувати IP-адресу.

Щоб спробувати це, оновіть ServiceEntry міткою відмови:

$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
  name: external-auto
  labels:
    networking.istio.io/enable-autoallocate-ip: "false"
spec:
  hosts:
  - auto.internal
  ports:
  - name: http
    number: 80
    protocol: HTTP
  resolution: DNS
EOF

Тепер надішліть запит і переконайтеся, що автоматичне виділення ресурсів більше не відбувається:

$ kubectl exec deploy/curl -- curl -sS -v auto.internal
* Could not resolve host: auto.internal
* shutting down connection #0

Зовнішні TCP сервіси без VIP

Стандартно в Istio існує обмеження під час маршрутизації зовнішнього TCP-трафіку, оскільки він не може розрізняти кілька TCP-сервісів на одному порту. Це обмеження особливо помітне при використанні сторонніх баз даних, таких як AWS Relational Database Service або будь-якого налаштування баз даних з географічною надмірністю. Подібні, але різні зовнішні TCP-сервіси не можуть бути стандартно оброблені окремо. Щоб sidecar міг розрізняти трафік між двома різними TCP-сервісами, які знаходяться поза мережею, сервіси повинні бути на різних портах або мати глобально унікальні VIP-адреси.

Наприклад, якщо у вас є два зовнішні сервіси баз даних, mysql-instance1 і mysql-instance2, і ви створите записи сервісів для обох, sidecarʼи клієнтів все одно матимуть єдиного слухача на 0.0.0.0:{port}, який шукатиме IP-адресу лише для mysql-instance1 через публічні DNS-сервери та передаватиме трафік на нього. Він не може маршрутизувати трафік до mysql-instance2, оскільки не має способу розрізнити, чи трафік, що надходить на 0.0.0.0:{port}, призначений для mysql-instance1 чи mysql-instance2.

У наступному прикладі показано, як проксіювання DNS можна використати для розвʼязання цієї проблеми. Віртуальна IP-адреса буде призначена кожному запису сервісу, щоб sidecarʼи клієнтів могли чітко розрізняти трафік, що надходить для кожного зовнішнього TCP-сервісу.

  1. Оновіть конфігурацію Istio, зазначену в розділі Початок роботи, щоб також налаштувати discoverySelectors, які обмежують mesh просторами назв із ввімкненим istio-injection. Це дозволить використовувати будь-які інші простори назв у кластері для запуску TCP-сервісів поза мережею.

    $ cat <<EOF | istioctl install -y -f -
    apiVersion: install.istio.io/v1alpha1
    kind: IstioOperator
    spec:
      meshConfig:
        defaultConfig:
          proxyMetadata:
            # Увімкніть базове проксіювання DNS
            ISTIO_META_DNS_CAPTURE: "true"
        # конфігурація discoverySelectors нижче використовується лише для симуляції сценарію зовнішнього сервісу TCP,
        # щоб нам не потрібно було використовувати зовнішній сайт для тестування.
        discoverySelectors:
        - matchLabels:
            istio-injection: enabled
    EOF
  2. Розгорніть перший зовнішній демонстраційний TCP-застосунок:

    $ kubectl create ns external-1
    $ kubectl -n external-1 apply -f samples/tcp-echo/tcp-echo.yaml
  3. Розгорніть другий зовнішній демонстраційний TCP-застосунок:

    $ kubectl create ns external-2
    $ kubectl -n external-2 apply -f samples/tcp-echo/tcp-echo.yaml
  4. Налаштуйте ServiceEntry для доступу до зовнішніх сервісів:

    $ kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1
    kind: ServiceEntry
    metadata:
      name: external-svc-1
    spec:
      hosts:
      - tcp-echo.external-1.svc.cluster.local
      ports:
      - name: external-svc-1
        number: 9000
        protocol: TCP
      resolution: DNS
    ---
    apiVersion: networking.istio.io/v1
    kind: ServiceEntry
    metadata:
      name: external-svc-2
    spec:
      hosts:
      - tcp-echo.external-2.svc.cluster.local
      ports:
      - name: external-svc-2
        number: 9000
        protocol: TCP
      resolution: DNS
    EOF
  5. Перевірте, чи слухачі налаштовані окремо для кожного сервісу на стороні клієнта:

    $ istioctl pc listener deploy/curl | grep tcp-echo | awk '{printf "ADDRESS=%s, DESTINATION=%s %s\n", $1, $4, $5}'
    ADDRESS=240.240.105.94, DESTINATION=Cluster: outbound|9000||tcp-echo.external-2.svc.cluster.local
    ADDRESS=240.240.69.138, DESTINATION=Cluster: outbound|9000||tcp-echo.external-1.svc.cluster.local

Автоматичний розподіл DNS V2

Istio тепер пропонує розширену реалізацію автоматичного розподілу DNS. Щоб скористатися новою функцією, замініть прапорець MeshConfig ISTIO_META_DNS_AUTO_ALLOCATE, який було використано у попередньому прикладі, на змінну пілотного середовища PILOT_ENABLE_IP_AUTOALLOCATE під час встановлення Istio. Всі приклади, наведені до цього часу, працюватимуть як є.

$ cat <<EOF | istioctl install -y -f -
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  values:
    pilot:
      env:
        # Вмикання автоматичного присвоєння адреси, необовʼязково
        PILOT_ENABLE_IP_AUTOALLOCATE: "true"
  meshConfig:
    defaultConfig:
      proxyMetadata:
        # Активація базового проксі-сервера DNS
        ISTIO_META_DNS_CAPTURE: "true"
    # конфігурація discoverySelectors, наведена нижче, використовується лише для імітації сценарію зовнішнього сервісу TCP,
    # щоб нам не довелося використовувати зовнішній сайт для тестування.
    discoverySelectors:
    - matchLabels:
        istio-injection: enabled
EOF

Користувачі також можуть гнучко налаштувати більш детальну конфігурацію, додавши мітку networking.istio.io/enable-autoallocate-ip="true/false" до свого ServiceEntry. Ця мітка визначає, чи має ServiceEntry, для якого не вказано жодної spec.addresses, автоматично отримувати IP-адресу.

Щоб спробувати це, оновіть наявний ServiceEntry міткою відмови:

$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
  name: external-auto
  labels:
    networking.istio.io/enable-autoallocate-ip: "false"
spec:
  hosts:
  - auto.internal
  ports:
  - name: http
    number: 80
    protocol: HTTP
  resolution: DNS
EOF

Тепер надішліть запит і переконайтеся, що автоматичний розподіл більше не відбувається:

$ kubectl exec deploy/curl -- curl -sS -v auto.internal
* Could not resolve host: auto.internal
* shutting down connection #0

Очищення

ZipZipZip
$ kubectl -n external-1 delete -f @samples/tcp-echo/tcp-echo.yaml@
$ kubectl -n external-2 delete -f @samples/tcp-echo/tcp-echo.yaml@
$ kubectl delete -f @samples/curl/curl.yaml@
$ istioctl uninstall --purge -y
$ kubectl delete ns istio-system external-1 external-2
$ kubectl label namespace default istio-injection-
Чи була ця інформація корисною?
Чи є у вас пропозиції щодо покращення?

Дякуємо за ваш відгук!