IstioでgRPCのリクエストを振り分ける

前回 はIstioでHTTPのリクエストを振り分けていたので,今度はgRPCを扱ってみようと思う. といっても基本的な構成はそんなに変わらない.

前回とほとんど同じような環境を使うので,

  • Helm
  • Istio
  • Auto injection

あたりの設定は終わっているものとする. また,今回も基本的にクラスタ内でマイクロサービス間通信を扱うことを前提にするため,外部との接続は行わない.

gRPCサーバを立てる

gRPCはいつも使っているexampleのサーバを使うことにする.

github.com

まず,バックエンドとなるgRPCサーバを立てる.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend-0
  namespace: istio-grpc-example
  labels:
    app: backend-0
spec:
  replicas: 1
  selector:
    matchLabels:
      app: backend
      version: "0"
  template:
    metadata:
      labels:
        app: backend
        version: "0"
    spec:
      containers:
        - name: python
          image: h3poteto/grpc_example-server-python:master
          imagePullPolicy: Always
          ports:
            - name: grpc
              containerPort: 9090
              protocol: TCP
          env:
            - name: SERVER_IP
              value: 0.0.0.0
            - name: SERVER_PORT
              value: "9090"

前回同様,あとでバージョンによる分岐をさせるために,version: "0" を付与しておくが,とりあえず今の段階では使わない.

次にこのサーバにアクセスできるようにServiceを作る.

apiVersion: v1
kind: Service
metadata:
  name: backend
  namespace: istio-grpc-example
  labels:
    app: backend
spec:
  ports:
  - port: 50051
    targetPort: 9090
    protocol: TCP
    #ポートの名前に注意
    name: grpc-backend
  selector:
    app: backend

ここで,ポートの名前が非常に重要になるのだが,これについてはIstioのVirtualServiceの仕様によるものなので,VirtualServiceの章で説明する.

ひとまずこれでgRPCサーバが立ち上がるはずだ. gRPCサーバは9090ポートで立ち上がり,Service経由だと50051ポートでアクセスできるようになっている.

Istioによるロードバランスをせずにアクセスする

とりあえず今作ったサーバに,Istioを通しつつも特にアクセスコントロールをせずにアクセスしてみる.

クライアントとなるPodを立てる・

apiVersion: apps/v1
kind: Deployment
metadata:
  name: client-0
  namespace: istio-grpc-example
  labels:
    app: client-0
spec:
  replicas: 1
  selector:
    matchLabels:
      app: client
      version: "0"
  template:
    metadata:
      labels:
        app: client
        version: "0"
    spec:
      containers:
        - name: python
          image: h3poteto/grpc_example-client-python:master
          imagePullPolicy: Always
          env:
            - name: SERVER_IP
              value: "backend"
            - name: SERVER_PORT
              value: "50051"

こいつは, SERVER_IP:SERVER_PORT にあるgRPCサーバにアクセスをし続ける.

Istioによりアクセスを振り分ける

バックエンドのgRPCサーバとして, version: 0version: 1 を作ろう.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend-0
  namespace: istio-grpc-example
  labels:
    app: backend-0
spec:
  replicas: 1
  selector:
    matchLabels:
      app: backend
      version: "0"
  template:
    metadata:
      labels:
        app: backend
        version: "0"
    spec:
      containers:
        - name: python
          image: h3poteto/grpc_example-server-python:master
          imagePullPolicy: Always
          ports:
            - name: grpc
              containerPort: 9090
              protocol: TCP
          env:
            - name: SERVER_IP
              value: 0.0.0.0
            - name: SERVER_PORT
              value: "9090"
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend-1
  namespace: istio-grpc-example
  labels:
    app: backend-1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: backend
      version: "1"
  template:
    metadata:
      labels:
        app: backend
        version: "1"
    spec:
      containers:
        - name: python
          image: h3poteto/grpc_example-server-python:master
          imagePullPolicy: Always
          ports:
            - name: grpc
              containerPort: 9090
              protocol: TCP
          env:
            - name: SERVER_IP
              value: 0.0.0.0
            - name: SERVER_PORT
              value: "9090"

この段階では, version: 0version: 1 の両方にアクセスが飛んできているはずである.

version: 0にだけアクセスさせる

これはHTTPのときとあまり変わらないVirtualServiceを定義する.

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: backend
  namespace: istio-grpc-example
spec:
  hosts:
    - "backend"
  http:
  - match:
    - port: 50051
    route:
    - destination:
        host: backend
        subset: v0
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: backend
  namespace: istio-grpc-example
spec:
  host: backend
  trafficPolicy:
    loadBalancer:
      simple: RANDOM
  subsets:
  - name: v0
    labels:
      version: "0"
  - name: v1
    labels:
      version: "1"

これで version: 0 にのみアクセスが飛ぶようになるはずだ.

Portの名前マジ大事

Serviceを定義する際に少し書いたが,これをやる上でServiceのポート名が大事になってくる.

IstioのVirtualServiceのドキュメントを読むと,

istio.io

httpとtcpというのが定義されている.gRPCなのでうっかりtcpにしそうだが,httpの項目を読むと

service entry ports using HTTP/HTTP2/GRPC protocols

普通にhttpでHTTP2やgRPCをさばける.

ただしすごく重要なことが書いてあって,

HTTP routes will be applied to platform service ports named ‘http-’/‘http2-’/‘grpc-*’, gateway ports with protocol HTTP/HTTP2/GRPC/ TLS-terminated-HTTPS

そう,

  • httpなら http-*
  • http2なら http2-*
  • gRPCなら grpc-*

という名前のポートにしておかないと,VirtualServiceが拾ってくれない.

最初,適当に, backend-port とか名前をつけていたら,疎通はしてくれるんだけどVirtualServiceが全然振り分けてくれなくて,困った. ポート名をちゃんと grpc-* にすることでしっかりVirtualServiceによる振り分けが可能となった.

その他の振り分けパターン

前回とあまり変わらないので,振り分けパターンは1パターンしか紹介しなかったが,まぁだいたい前回と同じようなVirtualServiceでできる.

詳しくはソースを参考にしてほしい.

github.com

前回作ったHTTPの方も置いておく.

github.com

まとめ

ひとまずIstioでやりたかったことはできた.これでgRPCサーバの前段に楽にEnvoyを仕込んでロードバランスできるようになる.

いよいよIstioも1.0になり,GKEにも入ろうとしているので(まだベータ版),俺も本番に入れたい.