外部Webサービスの利用

IstioのBookinfoサンプルに基づいた簡単なシナリオについて説明します。

2018年1月31日 | Vadim Eisenberg

多くの場合、マイクロサービスベースのアプリケーションのすべてのパーツがサービスメッシュ内に存在するわけではありません。マイクロサービスベースのアプリケーションは、メッシュ外に存在するレガシーシステムによって提供される機能を使用することがあります。これらのシステムをサービスメッシュに段階的に移行することを検討する場合もあります。これらのシステムが移行されるまで、メッシュ内のアプリケーションからアクセスする必要があります。その他の場合、アプリケーションはサードパーティが提供するWebサービスを使用します。

このブログ投稿では、Istio Bookinfoサンプルアプリケーションを修正し、外部Webサービス(Google Books APIs)から書籍の詳細情報を取得します。メッシュ外部サービスエントリを使用して、Istioで発信HTTPSトラフィックを有効にする方法を示します。発信HTTPSトラフィックの2つのオプションを提供し、各オプションの長所と短所を説明します。

初期設定

外部Webサービスの利用シナリオを実証するために、IstioがインストールされたKubernetesクラスタから開始します。次に、Istio Bookinfoサンプルアプリケーションをデプロイします。このアプリケーションは、detailsマイクロサービスを使用して、ページ数や出版社などの書籍の詳細情報を取得します。元のdetailsマイクロサービスは、外部サービスを参照せずに書籍の詳細情報を提供します。

このブログ投稿の例のコマンドは、相互TLSが有効になっているかどうかにかかわらず、Istio 1.0以降で動作します。Bookinfoの設定ファイルは、Istioリリースアーカイブの`samples/bookinfo`ディレクトリにあります。

元のBookinfoサンプルアプリケーションからのアプリケーションのエンドツーエンドアーキテクチャのコピーを以下に示します。

The Original Bookinfo Application
元のBookinfoアプリケーション

アプリケーションのデプロイアプリケーションの実行確認デフォルトの宛先ルールの適用セクションの手順を実行し、Istioをデフォルトで発信トラフィックをブロックするポリシーに変更します。

Google Books WebサービスへのHTTPSアクセスを含むBookinfo

Google Books APIsから書籍の詳細情報を取得する、detailsマイクロサービスの新しいバージョンv2をデプロイします。次のコマンドを実行します。これは、サービスのコンテナの`DO_NOT_ENCRYPT`環境変数を`false`に設定します。この設定により、デプロイされたサービスは外部サービスへのアクセスにHTTPS(HTTPではなく)を使用するように指示されます。

Zip
$ kubectl apply -f @samples/bookinfo/platform/kube/bookinfo-details-v2.yaml@ --dry-run -o yaml | kubectl set env --local -f - 'DO_NOT_ENCRYPT=false' -o yaml | kubectl apply -f -

アプリケーションの更新されたアーキテクチャは次のようになります。

The Bookinfo Application with details V2
details V2を含むBookinfoアプリケーション

Google Books WebサービスはIstioサービスメッシュの外側にあり、その境界は破線で示されています。

これで、detailsマイクロサービス宛てのすべてのトラフィックをdetailsバージョンv2に転送します。

Zip
$ kubectl apply -f @samples/bookinfo/networking/virtual-service-details-v2.yaml@

デフォルトの宛先ルールの適用セクションで作成した宛先ルールに仮想サービスが依存していることに注意してください。

イングレスIPとポートを特定した後、アプリケーションのWebページにアクセスします。

しまった…書籍の詳細情報が表示される代わりに、製品詳細の取得エラーというメッセージが表示されます。

The Error Fetching Product Details Message
製品詳細の取得エラーメッセージ

良いニュースは、アプリケーションがクラッシュしなかったことです。優れたマイクロサービス設計では、障害の伝播がありません。この場合、失敗したdetailsマイクロサービスによって、`productpage`マイクロサービスが失敗することはありません。detailsマイクロサービスの障害にもかかわらず、アプリケーションのほとんどの機能はまだ提供されています。サービスのグレースフルな劣化があります。ご覧のように、レビューと評価は正しく表示され、アプリケーションはまだ役に立ちます。

では、何が間違っていたのでしょうか?ああ…答えは、この場合、Google Books Webサービスへのメッシュ内からのトラフィックを有効にすることをお伝えし忘れていたことです。デフォルトでは、Istioサイドカープロキシ(Envoyプロキシ)はクラスタ外部の宛先へのすべてのトラフィックをブロックします。このようなトラフィックを有効にするには、メッシュ外部サービスエントリを定義する必要があります。

Google Books WebサービスへのHTTPSアクセスの有効化

心配ありません。メッシュ外部サービスエントリを定義してアプリケーションを修正します。SNIによるルーティングを実行する仮想サービスも定義する必要があります。

$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
  name: googleapis
spec:
  hosts:
  - www.googleapis.com
  ports:
  - number: 443
    name: https
    protocol: HTTPS
  location: MESH_EXTERNAL
  resolution: DNS
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: googleapis
spec:
  hosts:
  - www.googleapis.com
  tls:
  - match:
    - port: 443
      sni_hosts:
      - www.googleapis.com
    route:
    - destination:
        host: www.googleapis.com
        port:
          number: 443
      weight: 100
EOF

これで、アプリケーションのWebページにアクセスすると、エラーなしで書籍の詳細が表示されます。

Book Details Displayed Correctly
正しく表示された書籍の詳細

サービスエントリを照会できます。

$ kubectl get serviceentries
NAME         AGE
googleapis   8m

サービスエントリを削除できます。

$ kubectl delete serviceentry googleapis
serviceentry "googleapis" deleted

そして、出力でサービスエントリが削除されたことを確認できます。

サービスエントリを削除した後にWebページにアクセスすると、前に経験したのと同じエラー、つまり製品詳細の取得エラーが発生します。ご覧のように、サービスエントリは多くの他のIstio構成アーティファクトと同様に動的に定義されます。Istioオペレーターは、マイクロサービスがアクセスできるドメインを動的に決定できます。マイクロサービスを再デプロイすることなく、外部ドメインへのトラフィックをオンザフライで有効化および無効化できます。

Google Books WebサービスへのHTTPSアクセスのクリーンアップ

ZipZip
$ kubectl delete serviceentry googleapis
$ kubectl delete virtualservice googleapis
$ kubectl delete -f @samples/bookinfo/networking/virtual-service-details-v2.yaml@
$ kubectl delete -f @samples/bookinfo/platform/kube/bookinfo-details-v2.yaml@

IstioによるTLSオリジネーション

この話には注意点があります。マイクロサービスが使用する特定のGoogle APIsのセット(BooksCalendarTasksなど)を監視したいとします。 Books APIsのみの使用を許可するポリシーを適用したいとします。マイクロサービスがアクセスする書籍識別子を監視したいとします。これらの監視およびポリシータスクのために、URLパスを知る必要があります。たとえば、URL www.googleapis.com/books/v1/volumes?q=isbn:0486424618を考えてみましょう。そのURLでは、Books APIsはパスセグメント/booksで、ISBN番号はパスセグメント/volumes?q=isbn:0486424618で指定されています。ただし、HTTPSでは、すべてのHTTPの詳細(ホスト名、パス、ヘッダーなど)が暗号化されているため、サイドカープロキシによるそのような監視とポリシー適用は不可能です。Istioは、SNIServer Name Indication)フィールド、この場合はwww.googleapis.comによって、暗号化されたリクエストのサーバー名のみを知ることができます。

IstioがHTTPの詳細に基づいて発信リクエストの監視とポリシー適用を実行できるようにするには、マイクロサービスがHTTPリクエストを発行する必要があります。次に、Istioは宛先へのHTTPS接続を開きます(TLSオリジネーションを実行します)。マイクロサービスがIstioサービスメッシュの内側または外側のどちらで実行されるかによって、マイクロサービスのコードは異なる方法で記述するか、異なる方法で構成する必要があります。透明性の最大化というIstioの設計目標に反しています。妥協が必要になることもあります…

以下の図は、外部サービスへのHTTPSトラフィックを送信するための2つのオプションを示しています。上部では、マイクロサービスがエンドツーエンドで暗号化された通常のHTTPSリクエストを送信しています。下部では、同じマイクロサービスがポッド内で暗号化されていないHTTPリクエストを送信しており、それらはサイドカーEnvoyプロキシによってインターセプトされます。サイドカープロキシはTLSオリジネーションを実行するため、ポッドと外部サービス間のトラフィックは暗号化されます。

HTTPS traffic to external services, with TLS originated by the microservice vs. by the sidecar proxy
マイクロサービスとサイドカープロキシによって開始されたTLSを使用した外部サービスへのHTTPSトラフィック

Rubyのnet/httpモジュールを使用して、Bookinfo detailsマイクロサービスコードで両方のパターンがどのようにサポートされているかを示します。

uri = URI.parse('https://www.googleapis.com/books/v1/volumes?q=isbn:' + isbn)
http = Net::HTTP.new(uri.host, ENV['DO_NOT_ENCRYPT'] === 'true' ? 80:443)
...
unless ENV['DO_NOT_ENCRYPT'] === 'true' then
     http.use_ssl = true
end

`DO_NOT_ENCRYPT`環境変数が定義されている場合、リクエストはSSLなし(プレーンHTTP)でポート80に実行されます。

details v2のKubernetesデプロイメント仕様の`container`セクションで、`DO_NOT_ENCRYPT`環境変数を「true」に設定できます。

env:
- name: DO_NOT_ENCRYPT
  value: "true"

次のセクションでは、外部WebサービスにアクセスするためのTLSオリジネーションを構成します。

Google Books WebサービスへのTLSオリジネーションを含むBookinfo

  1. Google Books APIsにHTTPリクエストを送信するdetails v2のバージョンをデプロイします。`DO_NOT_ENCRYPT`変数は`bookinfo-details-v2.yaml`でtrueに設定されています。

    Zip
    $ kubectl apply -f @samples/bookinfo/platform/kube/bookinfo-details-v2.yaml@
    
  2. detailsマイクロサービス宛てのトラフィックをdetailsバージョンv2に転送します。

    Zip
    $ kubectl apply -f @samples/bookinfo/networking/virtual-service-details-v2.yaml@
    
  3. www.google.apisのメッシュ外部サービスエントリ、宛先ポートを80から443に書き換える仮想サービス、TLSオリジネーションを実行する宛先ルールを作成します。

    $ kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1alpha3
    kind: ServiceEntry
    metadata:
      name: googleapis
    spec:
      hosts:
      - www.googleapis.com
      ports:
      - number: 80
        name: http
        protocol: HTTP
      - number: 443
        name: https
        protocol: HTTPS
      resolution: DNS
    ---
    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: rewrite-port-for-googleapis
    spec:
      hosts:
      - www.googleapis.com
      http:
      - match:
        - port: 80
        route:
        - destination:
            host: www.googleapis.com
            port:
              number: 443
    ---
    apiVersion: networking.istio.io/v1alpha3
    kind: DestinationRule
    metadata:
      name: originate-tls-for-googleapis
    spec:
      host: www.googleapis.com
      trafficPolicy:
        loadBalancer:
          simple: ROUND_ROBIN
        portLevelSettings:
        - port:
            number: 443
          tls:
            mode: SIMPLE # initiates HTTPS when accessing www.googleapis.com
    EOF
    
  4. アプリケーションのWebページにアクセスし、書籍の詳細がエラーなしで表示されることを確認します。

  5. Envoyのアクセスログの有効化

  6. details v2のサイドカープロキシのログを確認し、HTTPリクエストを確認します。

    $ kubectl logs $(kubectl get pods -l app=details -l version=v2 -o jsonpath='{.items[0].metadata.name}') istio-proxy | grep googleapis
    [2018-08-09T11:32:58.171Z] "GET /books/v1/volumes?q=isbn:0486424618 HTTP/1.1" 200 - 0 1050 264 264 "-" "Ruby" "b993bae7-4288-9241-81a5-4cde93b2e3a6" "www.googleapis.com:80" "172.217.20.74:80"
    EOF
    

    ログ内のURLパスに注意してください。パスを監視し、それに基づいてアクセスポリシーを適用できます。HTTP発信トラフィックの監視とアクセスポリシーの詳細については、このブログ投稿を参照してください。

Google BooksウェブサービスへのTLS発信のクリーンアップ

ZipZip
$ kubectl delete serviceentry googleapis
$ kubectl delete virtualservice rewrite-port-for-googleapis
$ kubectl delete destinationrule originate-tls-for-googleapis
$ kubectl delete -f @samples/bookinfo/networking/virtual-service-details-v2.yaml@
$ kubectl delete -f @samples/bookinfo/platform/kube/bookinfo-details-v2.yaml@

Istio相互TLSとの関係

この場合のTLS発信は、Istioによって適用される相互TLSとは無関係であることに注意してください。外部サービスへのTLS発信は、Istio相互TLSが有効になっているかどうかに関係なく動作します。相互TLSは、サービスメッシュのサービス間の通信を保護し、各サービスに強力なIDを提供します。このブログ投稿の外部サービスには、ウェブブラウザとウェブサーバー間の通信を保護するために使用されるのと同じメカニズムである一方向TLSを使用してアクセスしました。TLSは外部サービスとの通信に適用され、外部サーバーのIDを検証し、トラフィックを暗号化します。

結論

このブログ投稿では、Istioサービスメッシュ内のマイクロサービスがHTTPSによって外部ウェブサービスを利用する方法を示しました。デフォルトでは、Istioはクラスタ外部のすべてのトラフィックをブロックします。このようなトラフィックを有効にするには、サービスメッシュに対してmesh-externalサービスエントリを作成する必要があります。HTTPSリクエストを発行するか、IstioがTLS発信を実行するHTTPリクエストを発行することで、外部サイトにアクセスできます。マイクロサービスがHTTPSリクエストを発行する場合、トラフィックはエンドツーエンドで暗号化されますが、IstioはリクエストのURLパスなどのHTTP詳細を監視できません。マイクロサービスがHTTPリクエストを発行する場合、IstioはリクエストのHTTP詳細を監視し、HTTPベースのアクセスポリシーを適用できます。ただし、その場合、マイクロサービスとサイドカープロキシ間のトラフィックは暗号化されません。非常に厳しいセキュリティ要件を持つ組織では、トラフィックの一部が暗号化されていない状態を禁止することがあります。

この投稿を共有