DNSプロキシ

Istioはアプリケーショントラフィックのキャプチャに加え、メッシュのパフォーマンスと可用性を向上させるためにDNSリクエストもキャプチャできます。DNSプロキシを使用すると、アプリケーションからのすべてのDNSリクエストはサイドカーにリダイレクトされ、そこでドメイン名とIPアドレスのローカルマッピングが保存されます。リクエストがサイドカーで処理できる場合、アップストリームのDNSサーバーへのラウンドトリップを回避し、アプリケーションに直接レスポンスを返します。そうでない場合は、標準の/etc/resolv.conf DNS設定に従って、リクエストはアップストリームに転送されます。

KubernetesはKubernetes Serviceに対するDNS解決を標準で提供しますが、カスタムServiceEntryは認識されません。この機能により、DNSサーバーのカスタム設定を必要とせずに、ServiceEntryアドレスを解決できます。Kubernetes Serviceの場合、DNSレスポンスは同じですが、kube-dnsの負荷が軽減され、パフォーマンスが向上します。

この機能は、Kubernetes外で実行されているサービスでも利用できます。つまり、クラスタ外のKubernetes DNSエントリを公開するための面倒な回避策なしに、すべての内部サービスを解決できます。

はじめに

この機能は現在、デフォルトでは有効になっていません。有効にするには、次の設定でIstioをインストールします。

$ cat <<EOF | istioctl install -y -f -
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  meshConfig:
    defaultConfig:
      proxyMetadata:
        # Enable basic DNS proxying
        ISTIO_META_DNS_CAPTURE: "true"
        # Enable automatic address allocation, optional
        ISTIO_META_DNS_AUTO_ALLOCATE: "true"
EOF

proxy.istio.io/configアノテーションを使用して、ポッドごとに有効にすることもできます。

kind: Deployment
metadata:
  name: curl
spec:
...
  template:
    metadata:
      annotations:
        proxy.istio.io/config: |
          proxyMetadata:
            ISTIO_META_DNS_CAPTURE: "true"
            ISTIO_META_DNS_AUTO_ALLOCATE: "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リクエストをアップストリームに転送する必要があります。

これは特にTCPトラフィックで問題になります。Hostヘッダーに基づいてルーティングされるHTTPリクエストとは異なり、TCPははるかに少ない情報しか運びません。宛先IPアドレスとポート番号でのみルーティングできます。バックエンドに安定したIPアドレスがないため、それにも基づいてルーティングすることもできず、ポート番号しか残らず、複数のTCPサービスのServiceEntryが同じポートを共有する場合に競合が発生します。詳細については、次のセクションを参照してください。

これらの問題を回避するために、DNSプロキシは、明示的に定義されていないServiceEntryにアドレスを自動的に割り当てる機能もサポートしています。これは、ISTIO_META_DNS_AUTO_ALLOCATEオプションで設定されます。

この機能が有効になっている場合、DNSレスポンスには、各ServiceEntryに固有の自動的に割り当てられたアドレスが含まれます。次に、プロキシは、このIPアドレスへのリクエストを一致させ、対応するServiceEntryにリクエストを転送するように構成されます。ISTIO_META_DNS_AUTO_ALLOCATEを使用する場合、Istioは、ワイルドカードホストを使用していない限り、そのようなサービスにルーティング不可能なVIP(Class Eサブネットから)を自動的に割り当てます。サイドカー上のIstioエージェントは、アプリケーションからのDNSルックアップクエリに対するレスポンスとしてVIPを使用します。Envoyは、各外部TCPサービスにバインドされたトラフィックを明確に区別し、適切なターゲットに転送できるようになりました。詳細については、スマートDNSプロキシに関するIstioブログを参照してください。

これを試すには、別の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に送信されます。これらのアドレスは、実際のサービスとの競合を避けるために、240.240.0.0/16の予約済みIPアドレス範囲から選択されます。

VIPなしの外部TCPサービス

デフォルトでは、Istioは、複数のTCPサービスを同じポートで区別できないため、外部TCPトラフィックをルーティングする際に制限があります。この制限は、AWS Relational Database Serviceなどのサードパーティデータベース、または地理的冗長性を持つデータベース設定を使用する場合に特に顕著です。類似しているが異なる外部TCPサービスは、デフォルトでは個別に処理できません。サイドカーがメッシュ外の2つの異なるTCPサービス間のトラフィックを区別するには、サービスが異なるポートにあるか、グローバルに一意のVIPを持つ必要があります。

たとえば、2つの外部データベースサービスmysql-instance1mysql-instance2があり、両方のサービスエントリを作成した場合、クライアントサイドカーは依然として0.0.0.0:{port}で単一のリスナーを持ち、パブリックDNSサーバーからmysql-instance1のIPアドレスのみをルックアップし、トラフィックをそこに転送します。0.0.0.0:{port}に到着したトラフィックがmysql-instance1mysql-instance2のどちらにバインドされているかを区別する方法がないため、mysql-instance2へのトラフィックをルーティングできません。

次の例は、DNSプロキシを使用してこの問題を解決する方法を示しています。仮想IPアドレスが各サービスエントリに割り当てられ、クライアントサイドカーは各外部TCPサービスにバインドされたトラフィックを明確に区別できるようになります。

  1. はじめにセクションで指定されたIstio構成を更新して、istio-injectionが有効になっている名前空間をメッシュに制限するdiscoverySelectorsも構成します。これにより、クラスタ内の他の名前空間を使用して、メッシュ外のTCPサービスを実行できます。

    $ cat <<EOF | istioctl install -y -f -
    apiVersion: install.istio.io/v1alpha1
    kind: IstioOperator
    spec:
      meshConfig:
        defaultConfig:
          proxyMetadata:
            # Enable basic DNS proxying
            ISTIO_META_DNS_CAPTURE: "true"
            # Enable automatic address allocation, optional
            ISTIO_META_DNS_AUTO_ALLOCATE: "true"
        # discoverySelectors configuration below is just used for simulating the external service TCP scenario,
        # so that we do not have to use an external site for testing.
        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. 2番目の外部サンプル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
    

クリーンアップ

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-
この情報は役に立ちましたか?
改善のための提案はありますか?

ご意見ありがとうございます!