トラフィック管理のベストプラクティス
このセクションでは、ネットワークやトラフィック管理の問題を回避するための特定のデプロイメントまたは構成ガイドラインを提供します。
サービスに対するデフォルトルートの設定
デフォルトのIstioの動作は、ルールを設定しなくても任意のソースから宛先サービスのすべてのバージョンにトラフィックを都合よく送信しますが、最初からすべてのサービスに対してデフォルトルートを持つVirtualService
を作成することは、一般的にIstioのベストプラクティスと見なされています。
最初にサービスのバージョンが1つしかない場合でも、2番目のバージョンをデプロイすることを決定したらすぐに、新しいバージョンが制御されていない方法でトラフィックをすぐに受信するのを防ぐために、新しいバージョンが開始される**前**にルーティングルールを設定する必要があります。
Istioのデフォルトのラウンドロビンルーティングに依存する場合のもう1つの潜在的な問題は、Istioのデスティネーションルール評価アルゴリズムの微妙な点によるものです。リクエストをルーティングする際、Envoyはまず仮想サービスのルートルールを評価して、特定のサブセットがルーティングされているかどうかを判断します。もしそうなら、対応するサブセットに対応するデスティネーションルールポリシーのみをアクティブにします。したがって、Istioは、対応するサブセットにトラフィックを**明示的に**ルーティングした場合にのみ、特定のサブセットに対して定義したポリシーを適用します。
たとえば、次のデスティネーションルールを*レビュー*サービスに対して定義された唯一の構成と見なしてください。つまり、対応するVirtualService
定義にルートルールはありません。
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: reviews
spec:
host: reviews
subsets:
- name: v1
labels:
version: v1
trafficPolicy:
connectionPool:
tcp:
maxConnections: 100
Istioのデフォルトのラウンドロビンルーティングが「v1」インスタンスを時々呼び出す場合でも、「v1」が唯一の実行中のバージョンである場合は常にそうである場合でも、上記のトラフィックポリシーは決して呼び出されません。
上記例は、次の2つの方法のいずれかで修正できます。トラフィックポリシーをDestinationRule
の1つ上のレベルに移動して、任意のバージョンに適用されるようにすることができます。
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: reviews
spec:
host: reviews
trafficPolicy:
connectionPool:
tcp:
maxConnections: 100
subsets:
- name: v1
labels:
version: v1
または、さらに良いことに、VirtualService
定義でサービスに対して適切なルートルールを定義します。たとえば、「reviews:v1」に対する簡単なルートルールを追加します。
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v1
名前空間を跨いだ構成の共有の制御
仮想サービス、宛先ルール、またはサービスエントリは、ある名前空間で定義し、それらを他の名前空間にエクスポートすれば、他の名前空間で再利用できます。Istio はデフォルトですべてのトラフィック管理リソースをすべての名前空間にエクスポートしますが、exportTo
フィールドを使用して可視性を上書きできます。たとえば、次の仮想サービスは、同じ名前空間のワークロードからのリクエストのみに影響を与えます。
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: myservice
spec:
hosts:
- myservice.com
exportTo:
- "."
http:
- route:
- destination:
host: myservice
特定の名前空間で宛先ルールの可視性を設定しても、そのルールが使用される保証はありません。宛先ルールを他の名前空間にエクスポートすると、それらの名前空間で使用できるようになりますが、リクエスト中に実際に適用されるには、名前空間が宛先ルール検索パス上にある必要もあります。
- クライアントの名前空間
- サービスの名前空間
- 構成済みの
meshconfig.rootNamespace
名前空間(デフォルトはistio-system
)
たとえば、次の宛先ルールを考えてみましょう。
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: myservice
spec:
host: myservice.default.svc.cluster.local
trafficPolicy:
connectionPool:
tcp:
maxConnections: 100
この宛先ルールを名前空間 ns1
で作成すると仮定します。
ns1
のクライアントから myservice
サービスにリクエストを送信すると、宛先ルールは検索パスの最初の名前空間、つまりクライアントの名前空間にあるため適用されます。
ここで、別の名前空間(たとえば ns2
)からリクエストを送信すると、クライアントは宛先ルールと同じ名前空間の ns1
には存在しなくなります。対応するサービスである myservice.default.svc.cluster.local
も ns1
ではなく default
名前空間にあるため、宛先ルールは検索パスの2番目の名前空間であるサービスの名前空間でも見つかりません。
たとえ myservice
サービスがすべての名前空間にエクスポートされていて、ns2
で可視であり、宛先ルールも ns2
を含むすべての名前空間にエクスポートされていても、検索パスのいずれの名前空間にも存在しないため、ns2
からのリクエスト中に適用されることはありません。
この問題を回避するには、対応するサービスと同じ名前空間(この例では default
)に宛先ルールを作成します。そうすれば、どの名前空間のクライアントからのリクエストにも適用されます。また、宛先ルールを検索パスの3番目の名前空間である istio-system
名前空間に移動することもできますが、これは宛先ルールが本当にすべての名前空間に適用可能なグローバル構成である場合を除いて推奨されず、管理者権限が必要になります。
Istio がこの制限された宛先ルール検索パスを使用する理由は2つあります。
- 完全に無関係な名前空間のサービスの動作を上書きできる宛先ルールが定義されるのを防ぐ。
- 同じホストに対して複数の宛先ルールがある場合に、明確な検索順序を持つ。
大規模な仮想サービスとデスティネーションルールを複数のリソースに分割
特定のホストのルートルールまたはポリシーの完全なセットを単一の VirtualService
または DestinationRule
リソースで定義するのが不便な場合は、複数のリソースでホストの構成を段階的に指定する方が望ましい場合があります。コントロールプレーンは、そのような宛先ルールをマージし、仮想サービスがゲートウェイにバインドされている場合は、そのような仮想サービスをマージします。
パスベースの委任を使用して複数の実装サービスを公開するアプリケーションホストを公開するイングレスゲートウェイにバインドされた VirtualService
のケースを考えてみましょう。
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: myapp
spec:
hosts:
- myapp.com
gateways:
- myapp-gateway
http:
- match:
- uri:
prefix: /service1
route:
- destination:
host: service1.default.svc.cluster.local
- match:
- uri:
prefix: /service2
route:
- destination:
host: service2.default.svc.cluster.local
- match:
...
この種の構成の欠点は、基礎となるマイクロサービスの他の構成(たとえば、ルートルール)も、個々のサービスチームに関連付けられ、所有される可能性のある別々のリソースではなく、この単一の構成ファイルに含める必要があることです。詳細については、イングレスゲートウェイのリクエストにルートルールが影響しないを参照してください。
この問題を回避するために、myapp.com
の構成を、バックエンドサービスごとに1つずつ、いくつかの VirtualService
フラグメントに分割する方が望ましい場合があります。例えば
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: myapp-service1
spec:
hosts:
- myapp.com
gateways:
- myapp-gateway
http:
- match:
- uri:
prefix: /service1
route:
- destination:
host: service1.default.svc.cluster.local
---
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: myapp-service2
spec:
hosts:
- myapp.com
gateways:
- myapp-gateway
http:
- match:
- uri:
prefix: /service2
route:
- destination:
host: service2.default.svc.cluster.local
---
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: myapp-...
既存のホストに対して2番目以降の VirtualService
が適用されると、istiod
は追加のルートルールをホストの既存の構成にマージします。ただし、この機能を使用する際には慎重に考慮する必要があるいくつかの注意点があります。
- 特定のソース
VirtualService
のルールの評価順序は保持されますが、リソース間の順序は未定義です。言い換えれば、フラグメント構成全体でのルール評価の保証された順序はないため、フラグメント間のルール間に競合するルールまたは順序の依存関係がない場合にのみ、予測可能な動作になります。 - フラグメントには、「キャッチオール」ルール(つまり、
match
フィールドがないルール)は1つだけにする必要があります。このような「キャッチオール」ルールはすべて、マージされた構成のリストの最後に移動されますが、すべてのリクエストをキャッチするため、最初に適用されるものが他のルールを実質的に上書きして無効にします。 VirtualService
は、ゲートウェイにバインドされている場合にのみ、この方法でフラグメント化できます。ホストのマージはサイドカーではサポートされていません。
DestinationRule
も、同様のマージセマンティクスと制限でフラグメント化できます。
- 同じホストの複数の宛先ルールで、任意の特定のサブセットの定義は1つだけにする必要があります。同じ名前のものが複数ある場合は、最初の定義が使用され、後続の重複は破棄されます。サブセットコンテンツのマージはサポートされていません。
- 同じホストに対して最上位の
trafficPolicy
は1つだけにする必要があります。最上位のトラフィックポリシーが複数の宛先ルールで定義されている場合、最初に処理されたものが使用されます。後続の最上位のtrafficPolicy
構成は破棄されます。 - 仮想サービスのマージとは異なり、宛先ルールのマージはサイドカーとゲートウェイの両方で機能します。
サービスルートの再構成中の503エラーの回避
トラフィックをサービスの特定のバージョン(サブセット)にルーティングするようにルートルールを設定する場合、ルートで使用する前にサブセットが利用可能であることを確認する必要があります。そうしないと、再構成期間中にサービスへの呼び出しが 503 エラーを返す可能性があります。
対応するサブセットを定義する VirtualServices
と DestinationRules
の両方を単一の kubectl
呼び出し(たとえば、kubectl apply -f myVirtualServiceAndDestinationRule.yaml
)を使用して作成することは、リソースが(構成サーバー、つまり Kubernetes API サーバーから)istiod インスタンスに最終的に整合性のある方法で伝播するため、十分ではありません。サブセットを使用する VirtualService
がサブセットが定義されている DestinationRule
より先に到着した場合、istiod によって生成された Envoy 構成は、存在しないアップストリームプールを参照します。これにより、すべての構成オブジェクトが istiod で利用可能になるまで HTTP 503 エラーが発生します。
サブセットを使用してルートを構成する際にサービスがダウンタイムなしになるようにするには、以下に示す「make-before-break」プロセスに従ってください。
新しいサブセットを追加する場合
新しいサブセットを使用する
VirtualServices
を更新する前に、最初にDestinationRules
を更新して新しいサブセットを追加します。kubectl
またはプラットフォーム固有のツールを使用してルールを適用します。DestinationRule
構成が Envoy サイドカーに伝播するまで数秒待ちます。新しく追加されたサブセットを参照するように
VirtualService
を更新します。
サブセットを削除する場合
DestinationRule
からサブセットを削除する前に、サブセットへの参照をすべて削除するようにVirtualServices
を更新します。VirtualService
構成が Envoy サイドカーに伝播するまで数秒待ちます。DestinationRule
を更新して、未使用のサブセットを削除します。