56chのブログ

そこそこ技術が好きな人

【後編】GKEにgRPCサーバーをデプロイしFlutterクライアントからgRPCする

目標:GKEにデプロイし、AndroidのFlutterクライアント上からTLS通信を繋げる。

その際、Envoyプロキシを使う。

Envoyプロキシを使う理由

  • モバイルクライアントで、クライアントが信用できないのでTLS終端をしたい。
  • gRPCトラフィックを適切なKubernetes Serviceに転送する。

やり方

  • Kubernetes ServiceをL4LBとして公開。 (ここにEXTERNAL_IPが払い出される)
  • ServiceからL7LBであるEnvoyに転送する。

ヘルスチェックは必須。

image block

参考:https://cloud.google.com/kubernetes-engine/docs/tutorials/exposing-grpc-services-on-gke-using-envoy-proxy?hl=ja

クラスタを作り、Kubernetes Secret にcertを登録

APIの有効化

gcloud services enable container.googleapis.com
gcloud container clusters create envoy-grpc \
    --enable-ip-alias \
    --release-channel rapid \
    --scopes cloud-platform \
    --workload-pool <PROJECT_ID>.svc.id.goog \
    --zone asia-northeast1-a
gcloud container clusters create envoy-grpc \
    --enable-ip-alias \
    --release-channel rapid \
    --scopes cloud-platform \
    --workload-pool gke-grpc-go.svc.id.goog \
    --zone asia-northeast1-a

image block

こんな感じのログがでたらよい

kubectl get serviceをしてみる

image block

まだ外部IPがないことがわかる。

デプロイ
ArtifactRegistry

イメージ保管用のArtifactRegistryのロールを追加。

gcloud services enable artifactregistry.googleapis.com

リポジトリを作る

gcloud artifacts repositories create envoy-grpc-images \
    --repository-format docker \
    --location asia-northeast1

VMが使用するGoogleサービスアカウントにロールを付与する

PROJECT_NUMBER=$(gcloud projects describe <PROJECT_ID> --format 'value(projectNumber)')

gcloud artifacts repositories add-iam-policy-binding envoy-grpc-images \
    --location asia-northeast1 \
    --member serviceAccount:$PROJECT_NUMBER-compute@developer.gserviceaccount.com \
    --role roles/artifactregistry.reader

リポジトリのホスト名を認証ヘルパーエントリをCloudShellホームディレクトリのDocker構成ファイルに追加。

gcloud auth configure-docker asia-northeast1-docker.pkg.dev

自己署名TLS証明書と秘密鍵を作る

openssl req -x509 -newkey rsa:4096 -nodes -sha256 -days 365 \
    -keyout privkey.pem -out cert.pem -extensions san \
    -config \
    <(echo "[req]";
      echo distinguished_name=req;
      echo "[san]";
      echo subjectAltName=DNS:grpc.example.com
     ) \
    -subj '/CN=grpc.example.com'

自己署名TLS証明書を格納

kubectl create secret tls envoy-certs \
    --key privkey.pem --cert cert.pem \
    --dry-run=client --output yaml | kubectl apply --filename -

Skaffoldでコンテナイメージを作成し、それをリポジトリにPushして、アプリをGKEクラスタにデプロイする。

skaffold run \
    --default-repo=asia-northeast1-docker.pkg.dev/<PROJECT_ID>/envoy-grpc-images \
    --module=string-grpc \
    --skip-tests
skaffold run --default-repo=asia-northeast1-docker.pkg.dev/gke-grpc-go/envoy-grpc-images --module=string-grpc --skip-tests

同様に、envoyもデプロイ

skaffold run \
    --digest-source=none \
    --module=envoy \
    --skip-tests

この時点でgrpcurlしてみる。

EXTERNAL_IP=$(kubectl get service envoy \
    --output=jsonpath='{.status.loadBalancer.ingress[0].ip}')
grpcurl -v -d '{"content": "["Hello", "World"]"}' -proto example.proto -authority grpc.example.com -cacert ../../cert.pem $EXTERNAL_IP:443 example.string/ProcessStrings

grpcurl -v -d '{"content": "["Hello", "World"]"}' -proto example.proto -authority 56ch.space 56ch.space:443 example.string/ProcessStrings

こんな感じのログが出たらよい

image block

SSL通信

何らかの方法で鍵と証明書を入手し、envoy.yamlで指定された場所(コンテナ上に正しくマウントされるよう)に配置する。

最後。

Flutterアプリ上の設定を変える。

final channel = ClientChannel(
      '<ドメイン>',
      port: 443,
      options: ChannelOptions(credentials: ChannelCredentials.secure(
          authority: "<ドメイン>",
          certificates: data.buffer.asInt8List(data.offsetInBytes,data.lengthInBytes)),
          connectionTimeout: Duration(seconds: 100),
      ),

    );

こんな感じでcredentialsの情報を書く