Enjoy Architecting

Twitter: @taisho6339

Istioで割合でTraffic Managementするときにユーザごとにセッションを固定する

解決したい課題

Istioでweightによってサービスのバージョンを切り替えているとき、 リクエストの割合ベースで切り替えているだけなので、 同一ユーザでも異なるバージョンが表示されてしまう。 ユーザには少なくとも一定期間は同じバージョンを見せたいケースが多いと思うので今回はその方法を検証してみた。

Istioに用意されているSession Affinity機能

IstioではDestinationRuleで、ユーザごとに一定期間バージョンを固定化してルーティングするよう宣言できる。 https://istio.io/docs/reference/config/networking/destination-rule/#LoadBalancerSettings-ConsistentHashLB だがこれはConsistent Hashアルゴリズムによって実装されているため、PodがHPAでスケールアウト、スケールインしたときにルーティングされる向き先が変わってしまう。 また、この機能はweightの指定によってルーティングを設定しているときには併用してつかうことができない。 この機能はIstioというよりは実際にはEnvoyが担っていて、Envoy側のIssueを見る限りまだ解決されている様子はない。 https://github.com/envoyproxy/envoy/issues/8167

ではどうすればよいのか?

今回はWorkAround的な手法として、Cookieを用い、下記の方法を考えた。

  • Cookieがないリクエストに関しては、設定した割合でそれぞれのバージョンに割り振る
  • Cookieを持っているリクエストに関しては、そのCookieが保持しているバージョン情報に基づいて割り振る

あるバージョンに割り振られたトラフィックに対して、レスポンスにSetCookieヘッダーを付与し、 そこにサービスのバージョン情報を付与する、という方針を考えた。

具体的な実装

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: sample-service-gateway
spec:
  selector:
    istio: ingressgateway # use istio default controller
  servers:
    - port:
        number: 80
        name: http
        protocol: HTTP
      hosts:
        - "*"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: sample-service-gateway-vs
spec:
  hosts:
    - "*"
  gateways:
    - sample-service-gateway
  http:
    - match:
        - headers:
            Cookie:
              exact: sample-service-version=v1
      route:
        - destination:
            host: sample-service
            subset: v1
            port:
              number: 8080
    - match:
        - headers:
            Cookie:
              exact: sample-service-version=v2
      route:
        - destination:
            host: sample-service
            subset: v2
            port:
              number: 8080
    - route:
        - destination:
            host: sample-service
            subset: v1
            port:
              number: 8080
          weight: 50
          headers:
            response:
              add:
                "Set-Cookie": sample-service-version=v1; Max-Age=2592000
        - destination:
            host: sample-service
            subset: v2
            port:
              number: 8080
          weight: 50
          headers:
            response:
              add:
                "Set-Cookie": sample-service-version=v2; Max-Age=2592000
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: sample-service
spec:
  host: sample-service
  subsets:
    - name: v1
      labels:
        version: v1
    - name: v2
      labels:
        version: v2

これで実際にアクセスしてみると、最初は50%の確率でどちらかに割り振られ、それ以降のリクエストは同じバージョンへルーティングされる。

まとめ

今回はIstioでCookieを用いることで、バージョンを固定したルーティングを行った。 A/Bテストなど、ユーザには一貫した結果を出したい場合に活用したい。