Cloud FunctionsのフォワードプロキシをHTTPS LBからServerless VPC Access connector + Cloud NATに置き換えた

December 30, 2019
serverless vpc nat

出来上がったアーキテクチャの略図

こんにちは。 今回はCloud FunctionsのフォワードプロキシとしてGCEとHTTPS LBをServerless VPC ConnectorとCloud NATに置き換えた話をします。

Serverless VPC Connectorが12月の初頭にGAになって、東京リージョンも使えるようになったため、多くのユースケースで導入できる段階になってきたのではないかなと思い、検証・導入をしました。

レイテンシが改善するかどうかも実験をして検証してみました。

注意

フォワードプロキシ自体を置き換えたわけではなく、システム全体としてをリアーキテクチャした話です。

はじめに

IPアドレスによるアクセス制限のあるような外部サービスへCloud Functionsからフォワードプロキシを通してアクセスをしていました。

既存のアーキテクチャ

Google HTTPS Load Balancer(GLB)を使ってGCEのインスタンスグループをバックエンドに負荷分散をしていました。 また、主な目的としてはHTTPS化がありました。GLBではマネージド証明書があるため、ドメインを取ってきたら簡単にHTTPS化をすることができました。

背景

このリアーキテクチャには以下のような背景がありました。

1. IP固定化ができない

Serverlessのサービスはゼロスケールをし、トラフィック量やメモリ使用量などに合わせてオンデマンドにスケーラブルなサーバーを構築できるのが強みの一つと言われています。

しかし、問題点として静的IPを得ることができないのが問題点です。そのためGCEインスタンスにNode.jsで書いたフォワードプロキシサーバを置いていました。

Node.jsによるフォワードプロキシ

2. Cloud Functionsは内部IPではアクセスできない

Cloud NATが置けない理由と同じで、同リージョンでGCEインスタンスとCloud Functionsに置いたところで、同サブネット内ではないので内部IPではアクセスすることはできないです。

Cloud Functionsから自分のリソース(GCE,GKEなど)にアクセスするにはインターネットを通さなければならないのです。

インターネットを通したアクセス

このサーバーへのアクセスもインターネットを通して行っており、HTTPSが必須であったり、様々な要求がありました。

3. レイテンシが高い

私がServerlessDays Fukuoka 2019の発表資料で発表した、一つのServerlessのプラクティスに「同期的なコンポーネントはなるべく分離しない」ことがあります。

これはServerlessのメリットを享受するためのプラクティスなのですが今回はフォワードプロキシを使ってアクセスをしており、それがインターネットを通ってアクセスをしているため、レイテンシがひどく、実践できていませんでした。

また課金額も膨らんでいくのも、これが一因ではあります。今回のリアーキテクチャで、課金額とレイテンシの両方を改善することを目指しました。

リアーキテクチャ

リアーキテクチャした後のアーキテクチャは以下の通りです。

リアーキテクチャ後のアーキテクチャの略図

以下のような改善効果がありました。

1. Domain・証明書管理からの開放

Domainを取得したのちにGLBによってマネージド証明書を使っていました。 Domainは有効期限もあり、お金もかかります。 また、マネージド証明書はLet’s Encryptによって発行されており、常時SSLとしてプロダクション利用はなるべく避けたかったです。

実際にOVなどを購入しようかとも考えましたが、比較的高額なため見送っていた現状があります。内部ネットワークで完結し、それらの考慮を一切する必要がなくなったとは非常に嬉しいです。

2. レイテンシの改善

以下のような実験をしてみました。

  • Cloud Pub/Subでメッセージを流す
  • フォワードプロキシのヘルスチェックエンドポイントへアクセスをしてレイテンシを計測する
  • メトリクスはDatadogにアップロードをする

ヘルスチェックエンドポイントは非常に簡単に実装されています。

app.get('/healthz', (_, res) => {
    return res.status(200).json({greeding: 'I am alive hoho:)!'});
})

これらはメッセージを複数回送る必要性は必ずしもなくて、for分でレイテンシを計測しても良かったですが、FaaSとしてのお作法を守り、今回の検証だけに限らない関数を作りたかったのでこのようにしました。

2.0 準備・実験条件

事前のコンポーネントはすでにあるものだとして、今回の実験に特別必要なCloud Pub/Subのトピックだけ作成します。

$ gcloud pubsub topic create nat-test-topic
Created topic [projects/my-keke-project/topics/nat-test-topic].

実験条件は以下の通りです。

  • Runtime: Nodejs10
  • メモリ: 256MB
  • 実装言語: Typescript
  • リージョン: asia-northeast1

2.1 実験条件1: 従来のGLB

従来の構成でのレイテンシ測定をしました。

実験1: 従来のアーキテクチャでのレイテンシ測定

0.2秒ごとにCLIからメッセージをPublishして、合計100000メッセージを使ってレイテンシ測定をしました。 レイテンシの対象はHTTPリクエストのラウンドトリップタイムで定義をしたため、Serverless特有ののCold Startやインスタンス数の増減は考慮する必要がありません。

2.2 実験条件2: リアーキテクチャしたあとの内部IPを使う

実験2: 新アーキテクチャでのレイテンシ測定

そして、次に今回のリアーキテクチャを模倣するようなアーキテクチャでレイテンシの計測をしました。

3. 結果・考察

以下のような結果になりました。

GLBとServerless Access Connectorのレイテンシ測定の結果

青線:GLB,紫線:Serverless Access Connector

特にVPC Access connectorとGLBのレイテンシ的な差異はなかったです。

実際のところCloud NATによってホップが増えてしまったりすることや、GLBはGFE(Front End) Load Balancingをしているため、in-regionのリクエストに対しては低レイテンシでさばくことができるため、そのような結果が内部IPを使っても一緒だったのだろうと思います。

また、Cloud Functionsは並行度は1であるためインスタンスはそれぞれのリクエストに対して別々に起動します。そのためkeep-aliveなどというようなネットワーク的な影響も出ないため、やっぱりレイテンシは変わらないのでしょう。

しかし、統計的にみてみましょう。15分間での統計情報です。

- GLB Serverless VPC Access connector
Max Latency(ms) 256.5 76.0
Avg Latency(ms) 21.94 24.88
Min Latency(ms) 8.00 3.00

パーセンタイルも取れればよかったのですが、今回はそこまでできませんでした。しかし、内部IPを使ったほうがネットワーク的には安定しているようにみえました。外因が入りにくいのが要因かなと思います。

3.1 追試: GLBなしで実行

今度はGLBなしの場合も一緒にやってみます。

以下のような結果になりました。

Directory GCEとGLBとServerless Access Connectorのレイテンシ測定の結果

黄色:Directory GCE,青線:GLB,ピンク線:Serverless Access Connector

- Directory GCE GLB Serverless VPC Access connector
Max Latency(ms) 103 510 163
Avg Latency(ms) 10.9 32.4 24.1
Min Latency(ms) 2.00 6.00 3.00

これはDirectory GCEとはHTTPで通信をしているためHTTPSのTLSハンドシェイクによるレイテンシ改善があったのでしょう。しかしながらGLBを使わずに(=負荷分散をせずに)サービスを運用することは稀だと思うので内部IPを使うのにはレイテンシ以上にセキュリティ的な面などでインセンティブがあるといえるでしょう。

もちろんレイテンシも少しばかりは良いみたいです。もっと実験の期間を長くしてデータを今後取得してみたいです。

今回もやはりGLBを経由するほうがネットワーク的は不安定であり、Serverless VPC Access connectorを使ったほうがレイテンシの値の幅が小さく安定的であるといえるでしょう。


小さな問題点

しかし問題点もあります。

1. Terraform管理が少し面倒になった

まだGoogle Serverless VPC Access connectorはBeta APIであり、terraform-provider-google-betaを入れないと使用することができません。

そのため、APIがGAへ昇格するまではTerraform管理は見送りましたが、すこしもどかしさがあります。

2. Cloud NATによるコスト増加

2020年1月1日からGoogle Cloud は、Cloud NAT を使用している VM インスタンスの数に基づいて料金を請求します。

このような料金体系で稼働時間で課金されます。

Domain取得のための料金やServerlessのフォワードプロキシへアクセスしている間のアイドル状態の課金がなくなりました。リクエストの種類によってどのような課金がマンダトリーなのかは変化しますが、Cloud NATによってコスト増加するので、選定をするときは「運用コストがいかに楽になったか」、「Serverlessの課金額などそれぞれのコンポーネントの料金がいかに減少するのか」を考慮して考えるといいでしょう。

3.Serverless VPC Access connectorのコスト増加

以下のような料金体系です。

f1-micro分のコストが100Mbps毎に課金される

このような料金体系のためServerless VPC Access connectorは「Serverless-likeな」料金体系であるといえます。

4. 権限の付与

以下のような権限をCloud Functionsがデフォルトで使うサービスアカウントに付与する必要があります。

  • roles/vpcaccess.serviceAgent
  • roles/viewer

デフォルトサービスアカウントを使うか別のサービスアカウントを使うかはユーザー次第ですが、権限の付与を忘れないようにしてください。


感想

Serverlessを使っていて、IP制限のあるAPIを叩こうとしたりするとこのような問題に直面すると思います。

また、内部ネットワークを通してのアクセスはGCEインスタンス同士でやるような設定方法とは異なり、Serverless(本記事ではCloud Functions)では特別なServerlesss VPC Access connectorが必要です。Serverlessのネットワーク構成を理解していると何も問題ではないのですが、Serverlessの一つの迷信である「簡単にサービスを実装できる!」を安直に信じると、このような壁にぶつかってしまい、「GCEインスタンスでやればよかったな」などになりかねません。このようなことがないためにもServerlessは懐疑的な目で慎重な技術選定が必要なのです。

また、Managed Cloud Runなどでも同様の機能が今後出るのではないかと予想できます。

このようなアーキテクチャを一つの引き出しとして持っておくといざというときに役立つのではないかなと思います。

最後までありがとうございました!良いお年を!