外部Webサービスの利用
IstioのBookinfoサンプルに基づいた簡単なシナリオについて説明します。
多くの場合、マイクロサービスベースのアプリケーションのすべてのパーツがサービスメッシュ内に存在するわけではありません。マイクロサービスベースのアプリケーションは、メッシュ外に存在するレガシーシステムによって提供される機能を使用することがあります。これらのシステムをサービスメッシュに段階的に移行することを検討する場合もあります。これらのシステムが移行されるまで、メッシュ内のアプリケーションからアクセスする必要があります。その他の場合、アプリケーションはサードパーティが提供する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サンプルアプリケーションからのアプリケーションのエンドツーエンドアーキテクチャのコピーを以下に示します。
アプリケーションのデプロイ、アプリケーションの実行確認、デフォルトの宛先ルールの適用セクションの手順を実行し、Istioをデフォルトで発信トラフィックをブロックするポリシーに変更します。
Google Books WebサービスへのHTTPSアクセスを含むBookinfo
Google Books APIsから書籍の詳細情報を取得する、detailsマイクロサービスの新しいバージョンv2をデプロイします。次のコマンドを実行します。これは、サービスのコンテナの`DO_NOT_ENCRYPT`環境変数を`false`に設定します。この設定により、デプロイされたサービスは外部サービスへのアクセスにHTTPS(HTTPではなく)を使用するように指示されます。
$ 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 -
アプリケーションの更新されたアーキテクチャは次のようになります。
Google Books WebサービスはIstioサービスメッシュの外側にあり、その境界は破線で示されています。
これで、detailsマイクロサービス宛てのすべてのトラフィックをdetailsバージョンv2に転送します。
$ kubectl apply -f @samples/bookinfo/networking/virtual-service-details-v2.yaml@
デフォルトの宛先ルールの適用セクションで作成した宛先ルールに仮想サービスが依存していることに注意してください。
イングレスIPとポートを特定した後、アプリケーションのWebページにアクセスします。
しまった…書籍の詳細情報が表示される代わりに、製品詳細の取得エラーというメッセージが表示されます。
良いニュースは、アプリケーションがクラッシュしなかったことです。優れたマイクロサービス設計では、障害の伝播がありません。この場合、失敗した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ページにアクセスすると、エラーなしで書籍の詳細が表示されます。
サービスエントリを照会できます。
$ kubectl get serviceentries
NAME AGE
googleapis 8m
サービスエントリを削除できます。
$ kubectl delete serviceentry googleapis
serviceentry "googleapis" deleted
そして、出力でサービスエントリが削除されたことを確認できます。
サービスエントリを削除した後にWebページにアクセスすると、前に経験したのと同じエラー、つまり製品詳細の取得エラーが発生します。ご覧のように、サービスエントリは多くの他のIstio構成アーティファクトと同様に動的に定義されます。Istioオペレーターは、マイクロサービスがアクセスできるドメインを動的に決定できます。マイクロサービスを再デプロイすることなく、外部ドメインへのトラフィックをオンザフライで有効化および無効化できます。
Google Books WebサービスへのHTTPSアクセスのクリーンアップ
$ 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のセット(Books、Calendar、Tasksなど)を監視したいとします。 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は、SNI(Server Name Indication)フィールド、この場合はwww.googleapis.com
によって、暗号化されたリクエストのサーバー名のみを知ることができます。
IstioがHTTPの詳細に基づいて発信リクエストの監視とポリシー適用を実行できるようにするには、マイクロサービスがHTTPリクエストを発行する必要があります。次に、Istioは宛先へのHTTPS接続を開きます(TLSオリジネーションを実行します)。マイクロサービスがIstioサービスメッシュの内側または外側のどちらで実行されるかによって、マイクロサービスのコードは異なる方法で記述するか、異なる方法で構成する必要があります。透明性の最大化というIstioの設計目標に反しています。妥協が必要になることもあります…
以下の図は、外部サービスへのHTTPSトラフィックを送信するための2つのオプションを示しています。上部では、マイクロサービスがエンドツーエンドで暗号化された通常のHTTPSリクエストを送信しています。下部では、同じマイクロサービスがポッド内で暗号化されていないHTTPリクエストを送信しており、それらはサイドカーEnvoyプロキシによってインターセプトされます。サイドカープロキシはTLSオリジネーションを実行するため、ポッドと外部サービス間のトラフィックは暗号化されます。
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
Google Books APIsにHTTPリクエストを送信するdetails v2のバージョンをデプロイします。`DO_NOT_ENCRYPT`変数は`bookinfo-details-v2.yaml`でtrueに設定されています。
$ kubectl apply -f @samples/bookinfo/platform/kube/bookinfo-details-v2.yaml@
detailsマイクロサービス宛てのトラフィックをdetailsバージョンv2に転送します。
$ kubectl apply -f @samples/bookinfo/networking/virtual-service-details-v2.yaml@
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
アプリケーションのWebページにアクセスし、書籍の詳細がエラーなしで表示されることを確認します。
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発信のクリーンアップ
$ 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ベースのアクセスポリシーを適用できます。ただし、その場合、マイクロサービスとサイドカープロキシ間のトラフィックは暗号化されません。非常に厳しいセキュリティ要件を持つ組織では、トラフィックの一部が暗号化されていない状態を禁止することがあります。