メインコンテンツへスキップ
  1. Posts/

Istio 導入への道 - 外部へのアクセスでも Fault Injection 編

Istio

Istio シリーズです。

前回の予告通り今回は「外部サービスでも Fault Injection したいぞ」編です。

Fault Injection 編」でその便利さを取り上げましたが、外部の API を使用している時にもそこに Inject したいですよね?依存している外部サービスでエラーが発生したらどうなるのかとか、レスポンスが遅かった場合どうなるかとか、それを Istio の設定で調整することが可能になります。

前回設定した ServiceEntry の確認 #

前回は最終的に次の設定を行いました。これで httpbin.org と www.google.com 宛ては通信が許可されました。(outboundTrafficPolicy は REGISTRY_ONLY でした)

$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1beta1
kind: ServiceEntry
metadata:
  name: external-svc
spec:
  hosts:
  - httpbin.org
  - www.google.com
  location: MESH\_EXTERNAL
  ports:
  - number: 80
    name: http
    protocol: HTTP
  - number: 443
    name: tls
    protocol: TLS
  resolution: DNS
EOF

しかし、これでは直接各サイトに出ていくだけです。 Fault InjectionVirtualService の機能でした。「 VirtualService 編」を読み返しましょう。VirtualService にはそのサービスの転送先として DestinationRule しました。

VirtualService の作成 #

httpbin.org #

httpbin.org 用の VirtualService を作成します。httpbin.org は HTTPS には対応していないため port 80 だけです。全リクエストに3秒の delay を入れ、30% のリクエストは 500 Internal Server Error を返すようにしてみます。DestinationRule は登録せずとも ServiceEntry があるため接続できます。ServiceEntry がない場合は "response_flags":"NR,DI" で Injection の後でエラーになります。これは outboundTrafficPolicy が ALLOW_ANY でも同じです。ServiceEntry が必要です。

$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: httpbin-org
spec:
  hosts:
  - httpbin.org
  http:
  - match:
    - port: 80
    **fault:**
      **delay:**
        **fixedDelay: 3s**
        **percentage:**
          **value: 100**
      **abort:**
        **httpStatus: 500**
        **percentage:**
          **value: 30**
    route:
    - destination:
        host: httpbin.org
EOF

Delay だけが適用された時のログです。

{
  "authority": "httpbin.org",
  "bytes\_received": "0",
  "bytes\_sent": "34",
  "downstream\_local\_address": "35.170.216.115:80",
  "downstream\_remote\_address": "172.17.0.8:43426",
  "duration": "3366",
  "istio\_policy\_status": "-",
  "method": "GET",
  "path": "/ip",
  "protocol": "HTTP/1.1",
  "request\_id": "fa22dc61-1cc5-45c4-bc97-4f02d8a7c468",
  "requested\_server\_name": "-",
  **"response\_code": "200"**,
  **"response\_flags": "DI"**,
  "route\_name": "-",
  "start\_time": "2020-03-11T15:24:31.272Z",
  "upstream\_cluster": "outbound|80||httpbin.org",
  "upstream\_host": "34.230.193.231:80",
  "upstream\_local\_address": "172.17.0.8:54090",
  "upstream\_service\_time": "361",
  "upstream\_transport\_failure\_reason": "-",
  "user\_agent": "curl/7.58.0",
  "x\_forwarded\_for": "-"
}

こちらは Delay と Fault が両方適用された時のログです。

{
  "authority": "httpbin.org",
  "bytes\_received": "0",
  "bytes\_sent": "18",
  "downstream\_local\_address": "3.232.168.170:80",
  "downstream\_remote\_address": "172.17.0.8:50018",
  "duration": "3001",
  "istio\_policy\_status": "-",
  "method": "GET",
  "path": "/ip",
  "protocol": "HTTP/1.1",
  "request\_id": "98a3a4c5-4a0d-4325-a800-da7c3b6db3e3",
  "requested\_server\_name": "-",
  **"response\_code": "500"**,
  **"response\_flags": "DI,FI"**,
  "route\_name": "-",
  "start\_time": "2020-03-11T15:24:47.524Z",
  "upstream\_cluster": "-",
  "upstream\_host": "-",
  "upstream\_local\_address": "-",
  "upstream\_service\_time": "-",
  "upstream\_transport\_failure\_reason": "-",
  "user\_agent": "curl/7.58.0",
  "x\_forwarded\_for": "-"
}

www.google.com #

それでは次に www.google.com の VirtualService を作成します。こちらは https にも対応しています。で、ここがキモですが、そのままでは Envoy は https の中身には関与しませんでした。でも Fault Injection をするためにはそれではいけませんから、http で受けたリクエストを Envoy が proxy する際に https にして接続するようにします。そうすることで元のリクエストは http なので中身をいじることができます。さっきとの違いがわかるように今回は delay を1秒に、abort を 400 Bad Request にしてみます。

今回は destination に tls-origination という subset を指定してあります。これは下に続く DestinationRule で定義してあります。tls.modeSIMPLE にして sniwww.google.com を指定しています。接続先が SNI 必須の場合はこの sni 指定が必要です。SIMPLE って何?って思いますが、他の選択肢は MUTUAL がクライアント証明書を必要とするやつです。あとは PASSTHROUGH とか。

httpbin.org の時と違って DestinationRule はありますが、こちらも ServiceEntry を消すとアクセスできなくなります。

$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: www-google-com
spec:
  hosts:
  - www.google.com
  http:
  - match:
    - port: 80
    **fault:**
      **delay:**
        **fixedDelay: 1s**
        **percentage:**
          **value: 100**
      **abort:**
        **httpStatus: 400**
        **percentage:**
          **value: 30**
    route:
    - destination:
        host: www.google.com
        **subset: tls-origination**
        port:
          number: 443
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: www-google-com
spec:
  host: www.google.com
  subsets:
  **\- name: tls-origination**
    **trafficPolicy:**
      **portLevelSettings:**
      **\- port:**
          **number: 443**
        **tls:**
          **mode: SIMPLE**
          **sni: www.google.com**
EOF

これで http://www.google.com/ にアクセスすれば Envoy が Injection したうえで https で www.google.com に proxy してくれます。

ログです。"downstream_local_address": "216.58.197.196:80" で curl は www.google.com の port 80 にアクセスしようとしたことがわかります。"upstream_cluster": "outbound|443|tls-origination|www.google.com" で Envoy 内の経路がわかります。DestinationRule の tls-origination が使われています。"upstream_host": "216.58.197.196:443" で proxy 先が port 443 (https) であることがわかります。

{
  "authority": "www.google.com",
  "bytes\_received": "0",
  "bytes\_sent": "13062",
  **"downstream\_local\_address": "216.58.197.196:80"**,
  "downstream\_remote\_address": "172.17.0.8:41130",
  "duration": "1189",
  "istio\_policy\_status": "-",
  "method": "GET",
  "path": "/",
  "protocol": "HTTP/1.1",
  "request\_id": "069baa9b-7d0a-4e84-9b4b-a11e34226fce",
  "requested\_server\_name": "-",
  "response\_code": "200",
  **"response\_flags": "DI"**,
  "route\_name": "-",
  "start\_time": "2020-03-11T15:38:11.379Z",
  **"upstream\_cluster": "outbound|443|tls-origination|www.google.com"**,
  **"upstream\_host": "216.58.197.196:443"**,
  "upstream\_local\_address": "172.17.0.8:43430",
  "upstream\_service\_time": "186",
  "upstream\_transport\_failure\_reason": "-",
  "user\_agent": "curl/7.58.0",
  "x\_forwarded\_for": "-"
}

次は Fault が Inject された時のログです。これは proxy せずに 400 を返しているため proxy 先の情報はありません。

{
  "authority": "www.google.com",
  "bytes\_received": "0",
  "bytes\_sent": "18",
  "downstream\_local\_address": "216.58.197.196:80",
  "downstream\_remote\_address": "172.17.0.8:41304",
  "duration": "1001",
  "istio\_policy\_status": "-",
  "method": "GET",
  "path": "/",
  "protocol": "HTTP/1.1",
  "request\_id": "78dabd1b-fe57-41fb-8eaf-44ccc6517e3b",
  "requested\_server\_name": "-",
  **"response\_code": "400"**,
  **"response\_flags": "DI,FI"**,
  "route\_name": "-",
  "start\_time": "2020-03-11T15:38:21.648Z",
  "upstream\_cluster": "-",
  "upstream\_host": "-",
  "upstream\_local\_address": "-",
  "upstream\_service\_time": "-",
  "upstream\_transport\_failure\_reason": "-",
  "user\_agent": "curl/7.58.0",
  "x\_forwarded\_for": "-"
}

www.google.com に https でアクセスしようとするとどうなるか #

本来、https でアクセスするべきところに http でアクセスするようにしなければならないわけですが、https のままアクセスしようとするとどうなるでしょうか。

冒頭で前回までの設定を確認しましたが、そこで ServiceEntry に https の設定が残っているため、https の場合はその設定が有効でそのままアクセスできます。もちろん outboundTrafficPolicy が ALLOW_ANY であればその ServiceEntry も不要ですね。

コンテナ化されたアプリケーションでは外部リソースの定義は ConfigMap などを使って容易に変更可能なはずですよね。Fault Injection 使っていきましょう。

まとめ #

クラスタ外へのアクセスでも VirtualService を使うことができ、Fault Injection することができることを確認しました。https の termination だけじゃなくて orinination も Envoy に任せられることも確認できました。

Accessing External Services とか Egress TLS Origination に書いてあります。

は〜〜、まだまだいろいろあるなあ、先は長い。 続く


Istio 導入への道シリーズ